blob: 78e2da8aed93b82015d0a53a5c98443d45663c2f [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
371static proftime_T *syn_tm;
372#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
492/*
493 * Start the syntax recognition for a line. This function is normally called
494 * from the screen updating, once for each displayed line.
495 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
496 * it. Careful: curbuf and curwin are likely to point to another buffer and
497 * window.
498 */
499 void
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200500syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501{
502 synstate_T *p;
503 synstate_T *last_valid = NULL;
504 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000505 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 linenr_T parsed_lnum;
507 linenr_T first_stored;
508 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100509 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200511#ifdef FEAT_CONCEAL
512 current_sub_char = NUL;
513#endif
514
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 /*
516 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000517 * Also do this when a change was made, the current state may be invalid
518 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200520 if (syn_block != wp->w_s
521 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100522 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 invalidate_current_state();
525 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200526 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100528 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 syn_win = wp;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200530#ifdef FEAT_RELTIME
531 syn_tm = syntax_tm;
532#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533
534 /*
535 * Allocate syntax stack when needed.
536 */
537 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200538 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000539 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541
542 /*
543 * If the state of the end of the previous line is useful, store it.
544 */
545 if (VALID_STATE(&current_state)
546 && current_lnum < lnum
547 && current_lnum < syn_buf->b_ml.ml_line_count)
548 {
549 (void)syn_finish_line(FALSE);
550 if (!current_state_stored)
551 {
552 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000553 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000554 }
555
556 /*
557 * If the current_lnum is now the same as "lnum", keep the current
558 * state (this happens very often!). Otherwise invalidate
559 * current_state and figure it out below.
560 */
561 if (current_lnum != lnum)
562 invalidate_current_state();
563 }
564 else
565 invalidate_current_state();
566
567 /*
568 * Try to synchronize from a saved state in b_sst_array[].
569 * Only do this if lnum is not before and not to far beyond a saved state.
570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200571 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572 {
573 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 {
576 if (p->sst_lnum > lnum)
577 break;
578 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
579 {
580 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200581 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000582 last_min_valid = p;
583 }
584 }
585 if (last_min_valid != NULL)
586 load_current_state(last_min_valid);
587 }
588
589 /*
590 * If "lnum" is before or far beyond a line with a saved state, need to
591 * re-synchronize.
592 */
593 if (INVALID_STATE(&current_state))
594 {
595 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200596 if (current_lnum == 1)
597 /* First line is always valid, no matter "minlines". */
598 first_stored = 1;
599 else
600 /* Need to parse "minlines" lines before state can be considered
601 * valid to store. */
602 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603 }
604 else
605 first_stored = current_lnum;
606
607 /*
608 * Advance from the sync point or saved state until the current line.
609 * Save some entries for syncing with later on.
610 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200611 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000612 dist = 999999;
613 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200614 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615 while (current_lnum < lnum)
616 {
617 syn_start_line();
618 (void)syn_finish_line(FALSE);
619 ++current_lnum;
620
621 /* If we parsed at least "minlines" lines or started at a valid
622 * state, the current state is considered valid. */
623 if (current_lnum >= first_stored)
624 {
625 /* Check if the saved state entry is for the current line and is
626 * equal to the current state. If so, then validate all saved
627 * states that depended on a change before the parsed line. */
628 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000629 prev = syn_stack_find_entry(current_lnum - 1);
630 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200631 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000632 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000633 sp = prev;
634 while (sp != NULL && sp->sst_lnum < current_lnum)
635 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 if (sp != NULL
637 && sp->sst_lnum == current_lnum
638 && syn_stack_equal(sp))
639 {
640 parsed_lnum = current_lnum;
641 prev = sp;
642 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
643 {
644 if (sp->sst_lnum <= lnum)
645 /* valid state before desired line, use this one */
646 prev = sp;
647 else if (sp->sst_change_lnum == 0)
648 /* past saved states depending on change, break here. */
649 break;
650 sp->sst_change_lnum = 0;
651 sp = sp->sst_next;
652 }
653 load_current_state(prev);
654 }
655 /* Store the state at this line when it's the first one, the line
656 * where we start parsing, or some distance from the previously
657 * saved state. But only when parsed at least 'minlines'. */
658 else if (prev == NULL
659 || current_lnum == lnum
660 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000661 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662 }
663
664 /* This can take a long time: break when CTRL-C pressed. The current
665 * state will be wrong then. */
666 line_breakcheck();
667 if (got_int)
668 {
669 current_lnum = lnum;
670 break;
671 }
672 }
673
674 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675}
676
677/*
678 * We cannot simply discard growarrays full of state_items or buf_states; we
679 * have to manually release their extmatch pointers first.
680 */
681 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100682clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683{
684 int i;
685 garray_T *gap;
686
687 if (p->sst_stacksize > SST_FIX_STATES)
688 {
689 gap = &(p->sst_union.sst_ga);
690 for (i = 0; i < gap->ga_len; i++)
691 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
692 ga_clear(gap);
693 }
694 else
695 {
696 for (i = 0; i < p->sst_stacksize; i++)
697 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
698 }
699}
700
701/*
702 * Cleanup the current_state stack.
703 */
704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100705clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706{
707 int i;
708 stateitem_T *sip;
709
710 sip = (stateitem_T *)(current_state.ga_data);
711 for (i = 0; i < current_state.ga_len; i++)
712 unref_extmatch(sip[i].si_extmatch);
713 ga_clear(&current_state);
714}
715
716/*
717 * Try to find a synchronisation point for line "lnum".
718 *
719 * This sets current_lnum and the current state. One of three methods is
720 * used:
721 * 1. Search backwards for the end of a C-comment.
722 * 2. Search backwards for given sync patterns.
723 * 3. Simply start on a given number of lines above "lnum".
724 */
725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100726syn_sync(
727 win_T *wp,
728 linenr_T start_lnum,
729 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730{
731 buf_T *curbuf_save;
732 win_T *curwin_save;
733 pos_T cursor_save;
734 int idx;
735 linenr_T lnum;
736 linenr_T end_lnum;
737 linenr_T break_lnum;
738 int had_sync_point;
739 stateitem_T *cur_si;
740 synpat_T *spp;
741 char_u *line;
742 int found_flags = 0;
743 int found_match_idx = 0;
744 linenr_T found_current_lnum = 0;
745 int found_current_col= 0;
746 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000747 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748
749 /*
750 * Clear any current state that might be hanging around.
751 */
752 invalidate_current_state();
753
754 /*
755 * Start at least "minlines" back. Default starting point for parsing is
756 * there.
757 * Start further back, to avoid that scrolling backwards will result in
758 * resyncing for every line. Now it resyncs only one out of N lines,
759 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
760 * Watch out for overflow when minlines is MAXLNUM.
761 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200762 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763 start_lnum = 1;
764 else
765 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200766 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200768 else if (syn_block->b_syn_sync_minlines < 10)
769 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
772 if (syn_block->b_syn_sync_maxlines != 0
773 && lnum > syn_block->b_syn_sync_maxlines)
774 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775 if (lnum >= start_lnum)
776 start_lnum = 1;
777 else
778 start_lnum -= lnum;
779 }
780 current_lnum = start_lnum;
781
782 /*
783 * 1. Search backwards for the end of a C-style comment.
784 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200785 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 {
787 /* Need to make syn_buf the current buffer for a moment, to be able to
788 * use find_start_comment(). */
789 curwin_save = curwin;
790 curwin = wp;
791 curbuf_save = curbuf;
792 curbuf = syn_buf;
793
794 /*
795 * Skip lines that end in a backslash.
796 */
797 for ( ; start_lnum > 1; --start_lnum)
798 {
799 line = ml_get(start_lnum - 1);
800 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
801 break;
802 }
803 current_lnum = start_lnum;
804
805 /* set cursor to start of search */
806 cursor_save = wp->w_cursor;
807 wp->w_cursor.lnum = start_lnum;
808 wp->w_cursor.col = 0;
809
810 /*
811 * If the line is inside a comment, need to find the syntax item that
812 * defines the comment.
813 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
814 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200815 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200817 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
818 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
819 == syn_block->b_syn_sync_id
820 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821 {
822 validate_current_state();
823 if (push_current_state(idx) == OK)
824 update_si_attr(current_state.ga_len - 1);
825 break;
826 }
827 }
828
829 /* restore cursor and buffer */
830 wp->w_cursor = cursor_save;
831 curwin = curwin_save;
832 curbuf = curbuf_save;
833 }
834
835 /*
836 * 2. Search backwards for given sync patterns.
837 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200838 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200840 if (syn_block->b_syn_sync_maxlines != 0
841 && start_lnum > syn_block->b_syn_sync_maxlines)
842 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000843 else
844 break_lnum = 0;
845
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000846 found_m_endpos.lnum = 0;
847 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000848 end_lnum = start_lnum;
849 lnum = start_lnum;
850 while (--lnum > break_lnum)
851 {
852 /* This can take a long time: break when CTRL-C pressed. */
853 line_breakcheck();
854 if (got_int)
855 {
856 invalidate_current_state();
857 current_lnum = start_lnum;
858 break;
859 }
860
861 /* Check if we have run into a valid saved state stack now. */
862 if (last_valid != NULL && lnum == last_valid->sst_lnum)
863 {
864 load_current_state(last_valid);
865 break;
866 }
867
868 /*
869 * Check if the previous line has the line-continuation pattern.
870 */
871 if (lnum > 1 && syn_match_linecont(lnum - 1))
872 continue;
873
874 /*
875 * Start with nothing on the state stack
876 */
877 validate_current_state();
878
879 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
880 {
881 syn_start_line();
882 for (;;)
883 {
884 had_sync_point = syn_finish_line(TRUE);
885 /*
886 * When a sync point has been found, remember where, and
887 * continue to look for another one, further on in the line.
888 */
889 if (had_sync_point && current_state.ga_len)
890 {
891 cur_si = &CUR_STATE(current_state.ga_len - 1);
892 if (cur_si->si_m_endpos.lnum > start_lnum)
893 {
894 /* ignore match that goes to after where started */
895 current_lnum = end_lnum;
896 break;
897 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000898 if (cur_si->si_idx < 0)
899 {
900 /* Cannot happen? */
901 found_flags = 0;
902 found_match_idx = KEYWORD_IDX;
903 }
904 else
905 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200906 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000907 found_flags = spp->sp_flags;
908 found_match_idx = spp->sp_sync_idx;
909 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 found_current_lnum = current_lnum;
911 found_current_col = current_col;
912 found_m_endpos = cur_si->si_m_endpos;
913 /*
914 * Continue after the match (be aware of a zero-length
915 * match).
916 */
917 if (found_m_endpos.lnum > current_lnum)
918 {
919 current_lnum = found_m_endpos.lnum;
920 current_col = found_m_endpos.col;
921 if (current_lnum >= end_lnum)
922 break;
923 }
924 else if (found_m_endpos.col > current_col)
925 current_col = found_m_endpos.col;
926 else
927 ++current_col;
928
929 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000930 * an item that ends here, need to do that now. Be
931 * careful not to go past the NUL. */
932 prev_current_col = current_col;
933 if (syn_getcurline()[current_col] != NUL)
934 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000936 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 }
938 else
939 break;
940 }
941 }
942
943 /*
944 * If a sync point was encountered, break here.
945 */
946 if (found_flags)
947 {
948 /*
949 * Put the item that was specified by the sync point on the
950 * state stack. If there was no item specified, make the
951 * state stack empty.
952 */
953 clear_current_state();
954 if (found_match_idx >= 0
955 && push_current_state(found_match_idx) == OK)
956 update_si_attr(current_state.ga_len - 1);
957
958 /*
959 * When using "grouphere", continue from the sync point
960 * match, until the end of the line. Parsing starts at
961 * the next line.
962 * For "groupthere" the parsing starts at start_lnum.
963 */
964 if (found_flags & HL_SYNC_HERE)
965 {
966 if (current_state.ga_len)
967 {
968 cur_si = &CUR_STATE(current_state.ga_len - 1);
969 cur_si->si_h_startpos.lnum = found_current_lnum;
970 cur_si->si_h_startpos.col = found_current_col;
971 update_si_end(cur_si, (int)current_col, TRUE);
972 check_keepend();
973 }
974 current_col = found_m_endpos.col;
975 current_lnum = found_m_endpos.lnum;
976 (void)syn_finish_line(FALSE);
977 ++current_lnum;
978 }
979 else
980 current_lnum = start_lnum;
981
982 break;
983 }
984
985 end_lnum = lnum;
986 invalidate_current_state();
987 }
988
989 /* Ran into start of the file or exceeded maximum number of lines */
990 if (lnum <= break_lnum)
991 {
992 invalidate_current_state();
993 current_lnum = break_lnum + 1;
994 }
995 }
996
997 validate_current_state();
998}
999
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001000 static void
1001save_chartab(char_u *chartab)
1002{
1003 if (syn_block->b_syn_isk != empty_option)
1004 {
1005 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1006 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1007 (size_t)32);
1008 }
1009}
1010
1011 static void
1012restore_chartab(char_u *chartab)
1013{
1014 if (syn_win->w_s->b_syn_isk != empty_option)
1015 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1016}
1017
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018/*
1019 * Return TRUE if the line-continuation pattern matches in line "lnum".
1020 */
1021 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001022syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023{
1024 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001025 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001026 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027
Bram Moolenaar860cae12010-06-05 23:22:07 +02001028 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001030 /* use syntax iskeyword option */
1031 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001032 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1033 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001034 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001035 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001036 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001037 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001038 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 }
1040 return FALSE;
1041}
1042
1043/*
1044 * Prepare the current state for the start of a line.
1045 */
1046 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001047syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048{
1049 current_finished = FALSE;
1050 current_col = 0;
1051
1052 /*
1053 * Need to update the end of a start/skip/end that continues from the
1054 * previous line and regions that have "keepend".
1055 */
1056 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001057 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001059 check_state_ends();
1060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001061
1062 next_match_idx = -1;
1063 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001064#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001065 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001066#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067}
1068
1069/*
1070 * Check for items in the stack that need their end updated.
1071 * When "startofline" is TRUE the last item is always updated.
1072 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1073 */
1074 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001075syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076{
1077 stateitem_T *cur_si;
1078 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001079 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080
1081 if (startofline)
1082 {
1083 /* Check for a match carried over from a previous line with a
1084 * contained region. The match ends as soon as the region ends. */
1085 for (i = 0; i < current_state.ga_len; ++i)
1086 {
1087 cur_si = &CUR_STATE(i);
1088 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001089 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001090 == SPTYPE_MATCH
1091 && cur_si->si_m_endpos.lnum < current_lnum)
1092 {
1093 cur_si->si_flags |= HL_MATCHCONT;
1094 cur_si->si_m_endpos.lnum = 0;
1095 cur_si->si_m_endpos.col = 0;
1096 cur_si->si_h_endpos = cur_si->si_m_endpos;
1097 cur_si->si_ends = TRUE;
1098 }
1099 }
1100 }
1101
1102 /*
1103 * Need to update the end of a start/skip/end that continues from the
1104 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001105 * influence contained items. If we've just removed "extend"
1106 * (startofline == 0) then we should update ends of normal regions
1107 * contained inside "keepend" because "extend" could have extended
1108 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001109 * Then check for items ending in column 0.
1110 */
1111 i = current_state.ga_len - 1;
1112 if (keepend_level >= 0)
1113 for ( ; i > keepend_level; --i)
1114 if (CUR_STATE(i).si_flags & HL_EXTEND)
1115 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001116
1117 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001118 for ( ; i < current_state.ga_len; ++i)
1119 {
1120 cur_si = &CUR_STATE(i);
1121 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001122 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123 || (i == current_state.ga_len - 1 && startofline))
1124 {
1125 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1126 cur_si->si_h_startpos.lnum = current_lnum;
1127
1128 if (!(cur_si->si_flags & HL_MATCHCONT))
1129 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001130
1131 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1132 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133 }
1134 }
1135 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001136}
1137
1138/****************************************
1139 * Handling of the state stack cache.
1140 */
1141
1142/*
1143 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1144 *
1145 * To speed up syntax highlighting, the state stack for the start of some
1146 * lines is cached. These entries can be used to start parsing at that point.
1147 *
1148 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1149 * valid entries. b_sst_first points to the first one, then follow sst_next.
1150 * The entries are sorted on line number. The first entry is often for line 2
1151 * (line 1 always starts with an empty stack).
1152 * There is also a list for free entries. This construction is used to avoid
1153 * having to allocate and free memory blocks too often.
1154 *
1155 * When making changes to the buffer, this is logged in b_mod_*. When calling
1156 * update_screen() to update the display, it will call
1157 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1158 * entries. The entries which are inside the changed area are removed,
1159 * because they must be recomputed. Entries below the changed have their line
1160 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1161 * set to indicate that a check must be made if the changed lines would change
1162 * the cached entry.
1163 *
1164 * When later displaying lines, an entry is stored for each line. Displayed
1165 * lines are likely to be displayed again, in which case the state at the
1166 * start of the line is needed.
1167 * For not displayed lines, an entry is stored for every so many lines. These
1168 * entries will be used e.g., when scrolling backwards. The distance between
1169 * entries depends on the number of lines in the buffer. For small buffers
1170 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1171 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1172 */
1173
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001175syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001176{
1177 synstate_T *p;
1178
1179 if (block->b_sst_array != NULL)
1180 {
1181 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1182 clear_syn_state(p);
1183 vim_free(block->b_sst_array);
1184 block->b_sst_array = NULL;
1185 block->b_sst_len = 0;
1186 }
1187}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188/*
1189 * Free b_sst_array[] for buffer "buf".
1190 * Used when syntax items changed to force resyncing everywhere.
1191 */
1192 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001193syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001195#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001197#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198
Bram Moolenaar860cae12010-06-05 23:22:07 +02001199 syn_stack_free_block(block);
1200
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201#ifdef FEAT_FOLDING
1202 /* When using "syntax" fold method, must update all folds. */
1203 FOR_ALL_WINDOWS(wp)
1204 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001205 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 foldUpdateAll(wp);
1207 }
1208#endif
1209}
1210
1211/*
1212 * Allocate the syntax state stack for syn_buf when needed.
1213 * If the number of entries in b_sst_array[] is much too big or a bit too
1214 * small, reallocate it.
1215 * Also used to allocate b_sst_array[] for the first time.
1216 */
1217 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001218syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219{
1220 long len;
1221 synstate_T *to, *from;
1222 synstate_T *sstp;
1223
1224 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1225 if (len < SST_MIN_ENTRIES)
1226 len = SST_MIN_ENTRIES;
1227 else if (len > SST_MAX_ENTRIES)
1228 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 {
1231 /* Allocate 50% too much, to avoid reallocating too often. */
1232 len = syn_buf->b_ml.ml_line_count;
1233 len = (len + len / 2) / 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;
1238
Bram Moolenaar860cae12010-06-05 23:22:07 +02001239 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240 {
1241 /* When shrinking the array, cleanup the existing stack.
1242 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 && syn_stack_cleanup())
1245 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001246 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1247 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 }
1249
1250 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1251 if (sstp == NULL) /* out of memory! */
1252 return;
1253
1254 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001255 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 {
1257 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 from = from->sst_next)
1260 {
1261 ++to;
1262 *to = *from;
1263 to->sst_next = to + 1;
1264 }
1265 }
1266 if (to != sstp - 1)
1267 {
1268 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 syn_block->b_sst_first = sstp;
1270 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 }
1272 else
1273 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001274 syn_block->b_sst_first = NULL;
1275 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001276 }
1277
1278 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280 while (++to < sstp + len)
1281 to->sst_next = to + 1;
1282 (sstp + len - 1)->sst_next = NULL;
1283
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 vim_free(syn_block->b_sst_array);
1285 syn_block->b_sst_array = sstp;
1286 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 }
1288}
1289
1290/*
1291 * Check for changes in a buffer to affect stored syntax states. Uses the
1292 * b_mod_* fields.
1293 * Called from update_screen(), before screen is being updated, once for each
1294 * displayed buffer.
1295 */
1296 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001297syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001299 win_T *wp;
1300
1301 syn_stack_apply_changes_block(&buf->b_s, buf);
1302
1303 FOR_ALL_WINDOWS(wp)
1304 {
1305 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1306 syn_stack_apply_changes_block(wp->w_s, buf);
1307 }
1308}
1309
1310 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001311syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001312{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 synstate_T *p, *prev, *np;
1314 linenr_T n;
1315
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 return;
1318
1319 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001320 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 {
1324 n = p->sst_lnum + buf->b_mod_xlines;
1325 if (n <= buf->b_mod_bot)
1326 {
1327 /* this state is inside the changed area, remove it */
1328 np = p->sst_next;
1329 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 else
1332 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001333 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334 p = np;
1335 continue;
1336 }
1337 /* This state is below the changed area. Remember the line
1338 * that needs to be parsed before this entry can be made valid
1339 * again. */
1340 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1341 {
1342 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1343 p->sst_change_lnum += buf->b_mod_xlines;
1344 else
1345 p->sst_change_lnum = buf->b_mod_top;
1346 }
1347 if (p->sst_change_lnum == 0
1348 || p->sst_change_lnum < buf->b_mod_bot)
1349 p->sst_change_lnum = buf->b_mod_bot;
1350
1351 p->sst_lnum = n;
1352 }
1353 prev = p;
1354 p = p->sst_next;
1355 }
1356}
1357
1358/*
1359 * Reduce the number of entries in the state stack for syn_buf.
1360 * Returns TRUE if at least one entry was freed.
1361 */
1362 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001363syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364{
1365 synstate_T *p, *prev;
1366 disptick_T tick;
1367 int above;
1368 int dist;
1369 int retval = FALSE;
1370
Bram Moolenaar860cae12010-06-05 23:22:07 +02001371 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372 return retval;
1373
1374 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001376 dist = 999999;
1377 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001378 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379
1380 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001381 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 * be removed. Set "above" when the "tick" for the oldest entry is above
1383 * "b_sst_lasttick" (the display tick wraps around).
1384 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001387 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001388 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1389 {
1390 if (prev->sst_lnum + dist > p->sst_lnum)
1391 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001392 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 {
1394 if (!above || p->sst_tick < tick)
1395 tick = p->sst_tick;
1396 above = TRUE;
1397 }
1398 else if (!above && p->sst_tick < tick)
1399 tick = p->sst_tick;
1400 }
1401 }
1402
1403 /*
1404 * Go through the list to make the entries for the oldest tick at an
1405 * interval of several lines.
1406 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001407 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1409 {
1410 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1411 {
1412 /* Move this entry from used list to free list */
1413 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001414 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415 p = prev;
1416 retval = TRUE;
1417 }
1418 }
1419 return retval;
1420}
1421
1422/*
1423 * Free the allocated memory for a syn_state item.
1424 * Move the entry into the free list.
1425 */
1426 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001427syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428{
1429 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001430 p->sst_next = block->b_sst_firstfree;
1431 block->b_sst_firstfree = p;
1432 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433}
1434
1435/*
1436 * Find an entry in the list of state stacks at or before "lnum".
1437 * Returns NULL when there is no entry or the first entry is after "lnum".
1438 */
1439 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001440syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441{
1442 synstate_T *p, *prev;
1443
1444 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001445 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446 {
1447 if (p->sst_lnum == lnum)
1448 return p;
1449 if (p->sst_lnum > lnum)
1450 break;
1451 }
1452 return prev;
1453}
1454
1455/*
1456 * Try saving the current state in b_sst_array[].
1457 * The current state must be valid for the start of the current_lnum line!
1458 */
1459 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001460store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461{
1462 int i;
1463 synstate_T *p;
1464 bufstate_T *bp;
1465 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001466 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467
1468 /*
1469 * If the current state contains a start or end pattern that continues
1470 * from the previous line, we can't use it. Don't store it then.
1471 */
1472 for (i = current_state.ga_len - 1; i >= 0; --i)
1473 {
1474 cur_si = &CUR_STATE(i);
1475 if (cur_si->si_h_startpos.lnum >= current_lnum
1476 || cur_si->si_m_endpos.lnum >= current_lnum
1477 || cur_si->si_h_endpos.lnum >= current_lnum
1478 || (cur_si->si_end_idx
1479 && cur_si->si_eoe_pos.lnum >= current_lnum))
1480 break;
1481 }
1482 if (i >= 0)
1483 {
1484 if (sp != NULL)
1485 {
1486 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001487 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001489 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 else
1491 {
1492 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001493 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 if (p->sst_next == sp)
1495 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001496 if (p != NULL) /* just in case */
1497 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 sp = NULL;
1501 }
1502 }
1503 else if (sp == NULL || sp->sst_lnum != current_lnum)
1504 {
1505 /*
1506 * Add a new entry
1507 */
1508 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001509 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 {
1511 (void)syn_stack_cleanup();
1512 /* "sp" may have been moved to the freelist now */
1513 sp = syn_stack_find_entry(current_lnum);
1514 }
1515 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001516 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 sp = NULL;
1518 else
1519 {
1520 /* Take the first item from the free list and put it in the used
1521 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001522 p = syn_block->b_sst_firstfree;
1523 syn_block->b_sst_firstfree = p->sst_next;
1524 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 if (sp == NULL)
1526 {
1527 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001528 p->sst_next = syn_block->b_sst_first;
1529 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530 }
1531 else
1532 {
1533 /* insert in list after *sp */
1534 p->sst_next = sp->sst_next;
1535 sp->sst_next = p;
1536 }
1537 sp = p;
1538 sp->sst_stacksize = 0;
1539 sp->sst_lnum = current_lnum;
1540 }
1541 }
1542 if (sp != NULL)
1543 {
1544 /* When overwriting an existing state stack, clear it first */
1545 clear_syn_state(sp);
1546 sp->sst_stacksize = current_state.ga_len;
1547 if (current_state.ga_len > SST_FIX_STATES)
1548 {
1549 /* Need to clear it, might be something remaining from when the
1550 * length was less than SST_FIX_STATES. */
1551 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1552 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1553 sp->sst_stacksize = 0;
1554 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001555 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1557 }
1558 else
1559 bp = sp->sst_union.sst_stack;
1560 for (i = 0; i < sp->sst_stacksize; ++i)
1561 {
1562 bp[i].bs_idx = CUR_STATE(i).si_idx;
1563 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001564#ifdef FEAT_CONCEAL
1565 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1566 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1567#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1569 }
1570 sp->sst_next_flags = current_next_flags;
1571 sp->sst_next_list = current_next_list;
1572 sp->sst_tick = display_tick;
1573 sp->sst_change_lnum = 0;
1574 }
1575 current_state_stored = TRUE;
1576 return sp;
1577}
1578
1579/*
1580 * Copy a state stack from "from" in b_sst_array[] to current_state;
1581 */
1582 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001583load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584{
1585 int i;
1586 bufstate_T *bp;
1587
1588 clear_current_state();
1589 validate_current_state();
1590 keepend_level = -1;
1591 if (from->sst_stacksize
1592 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1593 {
1594 if (from->sst_stacksize > SST_FIX_STATES)
1595 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1596 else
1597 bp = from->sst_union.sst_stack;
1598 for (i = 0; i < from->sst_stacksize; ++i)
1599 {
1600 CUR_STATE(i).si_idx = bp[i].bs_idx;
1601 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001602#ifdef FEAT_CONCEAL
1603 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1604 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1605#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1607 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1608 keepend_level = i;
1609 CUR_STATE(i).si_ends = FALSE;
1610 CUR_STATE(i).si_m_lnum = 0;
1611 if (CUR_STATE(i).si_idx >= 0)
1612 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001613 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614 else
1615 CUR_STATE(i).si_next_list = NULL;
1616 update_si_attr(i);
1617 }
1618 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619 }
1620 current_next_list = from->sst_next_list;
1621 current_next_flags = from->sst_next_flags;
1622 current_lnum = from->sst_lnum;
1623}
1624
1625/*
1626 * Compare saved state stack "*sp" with the current state.
1627 * Return TRUE when they are equal.
1628 */
1629 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001630syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631{
1632 int i, j;
1633 bufstate_T *bp;
1634 reg_extmatch_T *six, *bsx;
1635
1636 /* First a quick check if the stacks have the same size end nextlist. */
1637 if (sp->sst_stacksize == current_state.ga_len
1638 && sp->sst_next_list == current_next_list)
1639 {
1640 /* Need to compare all states on both stacks. */
1641 if (sp->sst_stacksize > SST_FIX_STATES)
1642 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1643 else
1644 bp = sp->sst_union.sst_stack;
1645
1646 for (i = current_state.ga_len; --i >= 0; )
1647 {
1648 /* If the item has another index the state is different. */
1649 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1650 break;
1651 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1652 {
1653 /* When the extmatch pointers are different, the strings in
1654 * them can still be the same. Check if the extmatch
1655 * references are equal. */
1656 bsx = bp[i].bs_extmatch;
1657 six = CUR_STATE(i).si_extmatch;
1658 /* If one of the extmatch pointers is NULL the states are
1659 * different. */
1660 if (bsx == NULL || six == NULL)
1661 break;
1662 for (j = 0; j < NSUBEXP; ++j)
1663 {
1664 /* Check each referenced match string. They must all be
1665 * equal. */
1666 if (bsx->matches[j] != six->matches[j])
1667 {
1668 /* If the pointer is different it can still be the
1669 * same text. Compare the strings, ignore case when
1670 * the start item has the sp_ic flag set. */
1671 if (bsx->matches[j] == NULL
1672 || six->matches[j] == NULL)
1673 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001674 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675 ? MB_STRICMP(bsx->matches[j],
1676 six->matches[j]) != 0
1677 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1678 break;
1679 }
1680 }
1681 if (j != NSUBEXP)
1682 break;
1683 }
1684 }
1685 if (i < 0)
1686 return TRUE;
1687 }
1688 return FALSE;
1689}
1690
1691/*
1692 * We stop parsing syntax above line "lnum". If the stored state at or below
1693 * this line depended on a change before it, it now depends on the line below
1694 * the last parsed line.
1695 * The window looks like this:
1696 * line which changed
1697 * displayed line
1698 * displayed line
1699 * lnum -> line below window
1700 */
1701 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001702syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703{
1704 synstate_T *sp;
1705
1706 sp = syn_stack_find_entry(lnum);
1707 if (sp != NULL && sp->sst_lnum < lnum)
1708 sp = sp->sst_next;
1709
1710 if (sp != NULL && sp->sst_change_lnum != 0)
1711 sp->sst_change_lnum = lnum;
1712}
1713
1714/*
1715 * End of handling of the state stack.
1716 ****************************************/
1717
1718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001719invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720{
1721 clear_current_state();
1722 current_state.ga_itemsize = 0; /* mark current_state invalid */
1723 current_next_list = NULL;
1724 keepend_level = -1;
1725}
1726
1727 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001728validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729{
1730 current_state.ga_itemsize = sizeof(stateitem_T);
1731 current_state.ga_growsize = 3;
1732}
1733
1734/*
1735 * Return TRUE if the syntax at start of lnum changed since last time.
1736 * This will only be called just after get_syntax_attr() for the previous
1737 * line, to check if the next line needs to be redrawn too.
1738 */
1739 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001740syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741{
1742 int retval = TRUE;
1743 synstate_T *sp;
1744
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 /*
1746 * Check the state stack when:
1747 * - lnum is just below the previously syntaxed line.
1748 * - lnum is not before the lines with saved states.
1749 * - lnum is not past the lines with saved states.
1750 * - lnum is at or before the last changed line.
1751 */
1752 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1753 {
1754 sp = syn_stack_find_entry(lnum);
1755 if (sp != NULL && sp->sst_lnum == lnum)
1756 {
1757 /*
1758 * finish the previous line (needed when not all of the line was
1759 * drawn)
1760 */
1761 (void)syn_finish_line(FALSE);
1762
1763 /*
1764 * Compare the current state with the previously saved state of
1765 * the line.
1766 */
1767 if (syn_stack_equal(sp))
1768 retval = FALSE;
1769
1770 /*
1771 * Store the current state in b_sst_array[] for later use.
1772 */
1773 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001774 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 }
1776 }
1777
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 return retval;
1779}
1780
1781/*
1782 * Finish the current line.
1783 * This doesn't return any attributes, it only gets the state at the end of
1784 * the line. It can start anywhere in the line, as long as the current state
1785 * is valid.
1786 */
1787 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001788syn_finish_line(
1789 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790{
1791 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001792 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001794 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001796 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1797 /*
1798 * When syncing, and found some item, need to check the item.
1799 */
1800 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001803 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001805 cur_si = &CUR_STATE(current_state.ga_len - 1);
1806 if (cur_si->si_idx >= 0
1807 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1808 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1809 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001811 /* syn_current_attr() will have skipped the check for an item
1812 * that ends here, need to do that now. Be careful not to go
1813 * past the NUL. */
1814 prev_current_col = current_col;
1815 if (syn_getcurline()[current_col] != NUL)
1816 ++current_col;
1817 check_state_ends();
1818 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001820 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821 }
1822 return FALSE;
1823}
1824
1825/*
1826 * Return highlight attributes for next character.
1827 * Must first call syntax_start() once for the line.
1828 * "col" is normally 0 for the first use in a line, and increments by one each
1829 * time. It's allowed to skip characters and to stop before the end of the
1830 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001831 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1832 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 */
1834 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001835get_syntax_attr(
1836 colnr_T col,
1837 int *can_spell,
1838 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839{
1840 int attr = 0;
1841
Bram Moolenaar349955a2007-08-14 21:07:36 +00001842 if (can_spell != NULL)
1843 /* Default: Only do spelling when there is no @Spell cluster or when
1844 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001845 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1846 ? (syn_block->b_spell_cluster_id == 0)
1847 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001848
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001850 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 return 0;
1852
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001853 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001854 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001855 {
1856 clear_current_state();
1857#ifdef FEAT_EVAL
1858 current_id = 0;
1859 current_trans_id = 0;
1860#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001861#ifdef FEAT_CONCEAL
1862 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001863 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001864#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001865 return 0;
1866 }
1867
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868 /* Make sure current_state is valid */
1869 if (INVALID_STATE(&current_state))
1870 validate_current_state();
1871
1872 /*
1873 * Skip from the current column to "col", get the attributes for "col".
1874 */
1875 while (current_col <= col)
1876 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001877 attr = syn_current_attr(FALSE, TRUE, can_spell,
1878 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879 ++current_col;
1880 }
1881
Bram Moolenaar071d4272004-06-13 20:20:40 +00001882 return attr;
1883}
1884
1885/*
1886 * Get syntax attributes for current_lnum, current_col.
1887 */
1888 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001889syn_current_attr(
1890 int syncing, /* When 1: called for syncing */
1891 int displaying, /* result will be displayed */
1892 int *can_spell, /* return: do spell checking */
1893 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894{
1895 int syn_id;
1896 lpos_T endpos; /* was: char_u *endp; */
1897 lpos_T hl_startpos; /* was: int hl_startcol; */
1898 lpos_T hl_endpos;
1899 lpos_T eos_pos; /* end-of-start match (start region) */
1900 lpos_T eoe_pos; /* end-of-end pattern */
1901 int end_idx; /* group ID for end pattern */
1902 int idx;
1903 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001904 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905 int startcol;
1906 int endcol;
1907 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001908 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909 short *next_list;
1910 int found_match; /* found usable match */
1911 static int try_next_column = FALSE; /* must try in next col */
1912 int do_keywords;
1913 regmmatch_T regmatch;
1914 lpos_T pos;
1915 int lc_col;
1916 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001917 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 char_u *line; /* current line. NOTE: becomes invalid after
1919 looking for a pattern match! */
1920
1921 /* variables for zero-width matches that have a "nextgroup" argument */
1922 int keep_next_list;
1923 int zero_width_next_list = FALSE;
1924 garray_T zero_width_next_ga;
1925
1926 /*
1927 * No character, no attributes! Past end of line?
1928 * Do try matching with an empty line (could be the start of a region).
1929 */
1930 line = syn_getcurline();
1931 if (line[current_col] == NUL && current_col != 0)
1932 {
1933 /*
1934 * If we found a match after the last column, use it.
1935 */
1936 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1937 && next_match_col != MAXCOL)
1938 (void)push_next_match(NULL);
1939
1940 current_finished = TRUE;
1941 current_state_stored = FALSE;
1942 return 0;
1943 }
1944
1945 /* if the current or next character is NUL, we will finish the line now */
1946 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1947 {
1948 current_finished = TRUE;
1949 current_state_stored = FALSE;
1950 }
1951
1952 /*
1953 * When in the previous column there was a match but it could not be used
1954 * (empty match or already matched in this column) need to try again in
1955 * the next column.
1956 */
1957 if (try_next_column)
1958 {
1959 next_match_idx = -1;
1960 try_next_column = FALSE;
1961 }
1962
1963 /* Only check for keywords when not syncing and there are some. */
1964 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001965 && (syn_block->b_keywtab.ht_used > 0
1966 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967
1968 /* Init the list of zero-width matches with a nextlist. This is used to
1969 * avoid matching the same item in the same position twice. */
1970 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1971
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001972 /* use syntax iskeyword option */
1973 save_chartab(buf_chartab);
1974
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975 /*
1976 * Repeat matching keywords and patterns, to find contained items at the
1977 * same column. This stops when there are no extra matches at the current
1978 * column.
1979 */
1980 do
1981 {
1982 found_match = FALSE;
1983 keep_next_list = FALSE;
1984 syn_id = 0;
1985
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001986
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987 /*
1988 * 1. Check for a current state.
1989 * Only when there is no current state, or if the current state may
1990 * contain other things, we need to check for keywords and patterns.
1991 * Always need to check for contained items if some item has the
1992 * "containedin" argument (takes extra time!).
1993 */
1994 if (current_state.ga_len)
1995 cur_si = &CUR_STATE(current_state.ga_len - 1);
1996 else
1997 cur_si = NULL;
1998
Bram Moolenaar860cae12010-06-05 23:22:07 +02001999 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002000 || cur_si->si_cont_list != NULL)
2001 {
2002 /*
2003 * 2. Check for keywords, if on a keyword char after a non-keyword
2004 * char. Don't do this when syncing.
2005 */
2006 if (do_keywords)
2007 {
2008 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002009 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002011 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012#ifdef FEAT_MBYTE
2013 - (has_mbyte
2014 ? (*mb_head_off)(line, line + current_col - 1)
2015 : 0)
2016#endif
2017 , syn_buf)))
2018 {
2019 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002020 &endcol, &flags, &next_list, cur_si,
2021 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002022 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 {
2024 if (push_current_state(KEYWORD_IDX) == OK)
2025 {
2026 cur_si = &CUR_STATE(current_state.ga_len - 1);
2027 cur_si->si_m_startcol = current_col;
2028 cur_si->si_h_startpos.lnum = current_lnum;
2029 cur_si->si_h_startpos.col = 0; /* starts right away */
2030 cur_si->si_m_endpos.lnum = current_lnum;
2031 cur_si->si_m_endpos.col = endcol;
2032 cur_si->si_h_endpos.lnum = current_lnum;
2033 cur_si->si_h_endpos.col = endcol;
2034 cur_si->si_ends = TRUE;
2035 cur_si->si_end_idx = 0;
2036 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002037#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002038 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002039 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002040 if (current_state.ga_len > 1)
2041 cur_si->si_flags |=
2042 CUR_STATE(current_state.ga_len - 2).si_flags
2043 & HL_CONCEAL;
2044#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045 cur_si->si_id = syn_id;
2046 cur_si->si_trans_id = syn_id;
2047 if (flags & HL_TRANSP)
2048 {
2049 if (current_state.ga_len < 2)
2050 {
2051 cur_si->si_attr = 0;
2052 cur_si->si_trans_id = 0;
2053 }
2054 else
2055 {
2056 cur_si->si_attr = CUR_STATE(
2057 current_state.ga_len - 2).si_attr;
2058 cur_si->si_trans_id = CUR_STATE(
2059 current_state.ga_len - 2).si_trans_id;
2060 }
2061 }
2062 else
2063 cur_si->si_attr = syn_id2attr(syn_id);
2064 cur_si->si_cont_list = NULL;
2065 cur_si->si_next_list = next_list;
2066 check_keepend();
2067 }
2068 else
2069 vim_free(next_list);
2070 }
2071 }
2072 }
2073
2074 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002075 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002077 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 {
2079 /*
2080 * If we didn't check for a match yet, or we are past it, check
2081 * for any match with a pattern.
2082 */
2083 if (next_match_idx < 0 || next_match_col < (int)current_col)
2084 {
2085 /*
2086 * Check all relevant patterns for a match at this
2087 * position. This is complicated, because matching with a
2088 * pattern takes quite a bit of time, thus we want to
2089 * avoid doing it when it's not needed.
2090 */
2091 next_match_idx = 0; /* no match in this line yet */
2092 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002093 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002094 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002095 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 if ( spp->sp_syncing == syncing
2097 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2098 && (spp->sp_type == SPTYPE_MATCH
2099 || spp->sp_type == SPTYPE_START)
2100 && (current_next_list != NULL
2101 ? in_id_list(NULL, current_next_list,
2102 &spp->sp_syn, 0)
2103 : (cur_si == NULL
2104 ? !(spp->sp_flags & HL_CONTAINED)
2105 : in_id_list(cur_si,
2106 cur_si->si_cont_list, &spp->sp_syn,
2107 spp->sp_flags & HL_CONTAINED))))
2108 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002109 int r;
2110
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111 /* If we already tried matching in this line, and
2112 * there isn't a match before next_match_col, skip
2113 * this item. */
2114 if (spp->sp_line_id == current_line_id
2115 && spp->sp_startcol >= next_match_col)
2116 continue;
2117 spp->sp_line_id = current_line_id;
2118
2119 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2120 if (lc_col < 0)
2121 lc_col = 0;
2122
2123 regmatch.rmm_ic = spp->sp_ic;
2124 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002125 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002126 current_lnum,
2127 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002128 IF_SYN_TIME(&spp->sp_time));
2129 spp->sp_prog = regmatch.regprog;
2130 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 {
2132 /* no match in this line, try another one */
2133 spp->sp_startcol = MAXCOL;
2134 continue;
2135 }
2136
2137 /*
2138 * Compute the first column of the match.
2139 */
2140 syn_add_start_off(&pos, &regmatch,
2141 spp, SPO_MS_OFF, -1);
2142 if (pos.lnum > current_lnum)
2143 {
2144 /* must have used end of match in a next line,
2145 * we can't handle that */
2146 spp->sp_startcol = MAXCOL;
2147 continue;
2148 }
2149 startcol = pos.col;
2150
2151 /* remember the next column where this pattern
2152 * matches in the current line */
2153 spp->sp_startcol = startcol;
2154
2155 /*
2156 * If a previously found match starts at a lower
2157 * column number, don't use this one.
2158 */
2159 if (startcol >= next_match_col)
2160 continue;
2161
2162 /*
2163 * If we matched this pattern at this position
2164 * before, skip it. Must retry in the next
2165 * column, because it may match from there.
2166 */
2167 if (did_match_already(idx, &zero_width_next_ga))
2168 {
2169 try_next_column = TRUE;
2170 continue;
2171 }
2172
2173 endpos.lnum = regmatch.endpos[0].lnum;
2174 endpos.col = regmatch.endpos[0].col;
2175
2176 /* Compute the highlight start. */
2177 syn_add_start_off(&hl_startpos, &regmatch,
2178 spp, SPO_HS_OFF, -1);
2179
2180 /* Compute the region start. */
2181 /* Default is to use the end of the match. */
2182 syn_add_end_off(&eos_pos, &regmatch,
2183 spp, SPO_RS_OFF, 0);
2184
2185 /*
2186 * Grab the external submatches before they get
2187 * overwritten. Reference count doesn't change.
2188 */
2189 unref_extmatch(cur_extmatch);
2190 cur_extmatch = re_extmatch_out;
2191 re_extmatch_out = NULL;
2192
2193 flags = 0;
2194 eoe_pos.lnum = 0; /* avoid warning */
2195 eoe_pos.col = 0;
2196 end_idx = 0;
2197 hl_endpos.lnum = 0;
2198
2199 /*
2200 * For a "oneline" the end must be found in the
2201 * same line too. Search for it after the end of
2202 * the match with the start pattern. Set the
2203 * resulting end positions at the same time.
2204 */
2205 if (spp->sp_type == SPTYPE_START
2206 && (spp->sp_flags & HL_ONELINE))
2207 {
2208 lpos_T startpos;
2209
2210 startpos = endpos;
2211 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2212 &flags, &eoe_pos, &end_idx, cur_extmatch);
2213 if (endpos.lnum == 0)
2214 continue; /* not found */
2215 }
2216
2217 /*
2218 * For a "match" the size must be > 0 after the
2219 * end offset needs has been added. Except when
2220 * syncing.
2221 */
2222 else if (spp->sp_type == SPTYPE_MATCH)
2223 {
2224 syn_add_end_off(&hl_endpos, &regmatch, spp,
2225 SPO_HE_OFF, 0);
2226 syn_add_end_off(&endpos, &regmatch, spp,
2227 SPO_ME_OFF, 0);
2228 if (endpos.lnum == current_lnum
2229 && (int)endpos.col + syncing < startcol)
2230 {
2231 /*
2232 * If an empty string is matched, may need
2233 * to try matching again at next column.
2234 */
2235 if (regmatch.startpos[0].col
2236 == regmatch.endpos[0].col)
2237 try_next_column = TRUE;
2238 continue;
2239 }
2240 }
2241
2242 /*
2243 * keep the best match so far in next_match_*
2244 */
2245 /* Highlighting must start after startpos and end
2246 * before endpos. */
2247 if (hl_startpos.lnum == current_lnum
2248 && (int)hl_startpos.col < startcol)
2249 hl_startpos.col = startcol;
2250 limit_pos_zero(&hl_endpos, &endpos);
2251
2252 next_match_idx = idx;
2253 next_match_col = startcol;
2254 next_match_m_endpos = endpos;
2255 next_match_h_endpos = hl_endpos;
2256 next_match_h_startpos = hl_startpos;
2257 next_match_flags = flags;
2258 next_match_eos_pos = eos_pos;
2259 next_match_eoe_pos = eoe_pos;
2260 next_match_end_idx = end_idx;
2261 unref_extmatch(next_match_extmatch);
2262 next_match_extmatch = cur_extmatch;
2263 cur_extmatch = NULL;
2264 }
2265 }
2266 }
2267
2268 /*
2269 * If we found a match at the current column, use it.
2270 */
2271 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2272 {
2273 synpat_T *lspp;
2274
2275 /* When a zero-width item matched which has a nextgroup,
2276 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002277 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278 if (next_match_m_endpos.lnum == current_lnum
2279 && next_match_m_endpos.col == current_col
2280 && lspp->sp_next_list != NULL)
2281 {
2282 current_next_list = lspp->sp_next_list;
2283 current_next_flags = lspp->sp_flags;
2284 keep_next_list = TRUE;
2285 zero_width_next_list = TRUE;
2286
2287 /* Add the index to a list, so that we can check
2288 * later that we don't match it again (and cause an
2289 * endless loop). */
2290 if (ga_grow(&zero_width_next_ga, 1) == OK)
2291 {
2292 ((int *)(zero_width_next_ga.ga_data))
2293 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002294 }
2295 next_match_idx = -1;
2296 }
2297 else
2298 cur_si = push_next_match(cur_si);
2299 found_match = TRUE;
2300 }
2301 }
2302 }
2303
2304 /*
2305 * Handle searching for nextgroup match.
2306 */
2307 if (current_next_list != NULL && !keep_next_list)
2308 {
2309 /*
2310 * If a nextgroup was not found, continue looking for one if:
2311 * - this is an empty line and the "skipempty" option was given
2312 * - we are on white space and the "skipwhite" option was given
2313 */
2314 if (!found_match)
2315 {
2316 line = syn_getcurline();
2317 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002318 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319 || ((current_next_flags & HL_SKIPEMPTY)
2320 && *line == NUL))
2321 break;
2322 }
2323
2324 /*
2325 * If a nextgroup was found: Use it, and continue looking for
2326 * contained matches.
2327 * If a nextgroup was not found: Continue looking for a normal
2328 * match.
2329 * When did set current_next_list for a zero-width item and no
2330 * match was found don't loop (would get stuck).
2331 */
2332 current_next_list = NULL;
2333 next_match_idx = -1;
2334 if (!zero_width_next_list)
2335 found_match = TRUE;
2336 }
2337
2338 } while (found_match);
2339
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002340 restore_chartab(buf_chartab);
2341
Bram Moolenaar071d4272004-06-13 20:20:40 +00002342 /*
2343 * Use attributes from the current state, if within its highlighting.
2344 * If not, use attributes from the current-but-one state, etc.
2345 */
2346 current_attr = 0;
2347#ifdef FEAT_EVAL
2348 current_id = 0;
2349 current_trans_id = 0;
2350#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002351#ifdef FEAT_CONCEAL
2352 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002353 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002354#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 if (cur_si != NULL)
2356 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002357#ifndef FEAT_EVAL
2358 int current_trans_id = 0;
2359#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2361 {
2362 sip = &CUR_STATE(idx);
2363 if ((current_lnum > sip->si_h_startpos.lnum
2364 || (current_lnum == sip->si_h_startpos.lnum
2365 && current_col >= sip->si_h_startpos.col))
2366 && (sip->si_h_endpos.lnum == 0
2367 || current_lnum < sip->si_h_endpos.lnum
2368 || (current_lnum == sip->si_h_endpos.lnum
2369 && current_col < sip->si_h_endpos.col)))
2370 {
2371 current_attr = sip->si_attr;
2372#ifdef FEAT_EVAL
2373 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002375 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002376#ifdef FEAT_CONCEAL
2377 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002378 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002379 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002380#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002381 break;
2382 }
2383 }
2384
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 if (can_spell != NULL)
2386 {
2387 struct sp_syn sps;
2388
2389 /*
2390 * set "can_spell" to TRUE if spell checking is supposed to be
2391 * done in the current item.
2392 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002393 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002394 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002395 /* There is no @Spell cluster: Do spelling for items without
2396 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002397 if (syn_block->b_nospell_cluster_id == 0
2398 || current_trans_id == 0)
2399 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002400 else
2401 {
2402 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002403 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002404 sps.cont_in_list = NULL;
2405 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2406 }
2407 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002408 else
2409 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002410 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002411 * the @Spell cluster. But not when @NoSpell is also there.
2412 * At the toplevel only spell check when ":syn spell toplevel"
2413 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002414 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002415 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002416 else
2417 {
2418 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002419 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002420 sps.cont_in_list = NULL;
2421 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2422
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002425 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002426 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2427 *can_spell = FALSE;
2428 }
2429 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002430 }
2431 }
2432
2433
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 /*
2435 * Check for end of current state (and the states before it) at the
2436 * next column. Don't do this for syncing, because we would miss a
2437 * single character match.
2438 * First check if the current state ends at the current column. It
2439 * may be for an empty match and a containing item might end in the
2440 * current column.
2441 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002442 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 {
2444 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002445 if (current_state.ga_len > 0
2446 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447 {
2448 ++current_col;
2449 check_state_ends();
2450 --current_col;
2451 }
2452 }
2453 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002454 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002455 /* Default: Only do spelling when there is no @Spell cluster or when
2456 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002457 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2458 ? (syn_block->b_spell_cluster_id == 0)
2459 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002461 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462 if (current_next_list != NULL
2463 && syn_getcurline()[current_col + 1] == NUL
2464 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2465 current_next_list = NULL;
2466
2467 if (zero_width_next_ga.ga_len > 0)
2468 ga_clear(&zero_width_next_ga);
2469
2470 /* No longer need external matches. But keep next_match_extmatch. */
2471 unref_extmatch(re_extmatch_out);
2472 re_extmatch_out = NULL;
2473 unref_extmatch(cur_extmatch);
2474
2475 return current_attr;
2476}
2477
2478
2479/*
2480 * Check if we already matched pattern "idx" at the current column.
2481 */
2482 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002483did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484{
2485 int i;
2486
2487 for (i = current_state.ga_len; --i >= 0; )
2488 if (CUR_STATE(i).si_m_startcol == (int)current_col
2489 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2490 && CUR_STATE(i).si_idx == idx)
2491 return TRUE;
2492
2493 /* Zero-width matches with a nextgroup argument are not put on the syntax
2494 * stack, and can only be matched once anyway. */
2495 for (i = gap->ga_len; --i >= 0; )
2496 if (((int *)(gap->ga_data))[i] == idx)
2497 return TRUE;
2498
2499 return FALSE;
2500}
2501
2502/*
2503 * Push the next match onto the stack.
2504 */
2505 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002506push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507{
2508 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002509#ifdef FEAT_CONCEAL
2510 int save_flags;
2511#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512
Bram Moolenaar860cae12010-06-05 23:22:07 +02002513 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514
2515 /*
2516 * Push the item in current_state stack;
2517 */
2518 if (push_current_state(next_match_idx) == OK)
2519 {
2520 /*
2521 * If it's a start-skip-end type that crosses lines, figure out how
2522 * much it continues in this line. Otherwise just fill in the length.
2523 */
2524 cur_si = &CUR_STATE(current_state.ga_len - 1);
2525 cur_si->si_h_startpos = next_match_h_startpos;
2526 cur_si->si_m_startcol = current_col;
2527 cur_si->si_m_lnum = current_lnum;
2528 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002529#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002530 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002531 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002532 if (current_state.ga_len > 1)
2533 cur_si->si_flags |=
2534 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536 cur_si->si_next_list = spp->sp_next_list;
2537 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2538 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2539 {
2540 /* Try to find the end pattern in the current line */
2541 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2542 check_keepend();
2543 }
2544 else
2545 {
2546 cur_si->si_m_endpos = next_match_m_endpos;
2547 cur_si->si_h_endpos = next_match_h_endpos;
2548 cur_si->si_ends = TRUE;
2549 cur_si->si_flags |= next_match_flags;
2550 cur_si->si_eoe_pos = next_match_eoe_pos;
2551 cur_si->si_end_idx = next_match_end_idx;
2552 }
2553 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2554 keepend_level = current_state.ga_len - 1;
2555 check_keepend();
2556 update_si_attr(current_state.ga_len - 1);
2557
Bram Moolenaar860cae12010-06-05 23:22:07 +02002558#ifdef FEAT_CONCEAL
2559 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2560#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 /*
2562 * If the start pattern has another highlight group, push another item
2563 * on the stack for the start pattern.
2564 */
2565 if ( spp->sp_type == SPTYPE_START
2566 && spp->sp_syn_match_id != 0
2567 && push_current_state(next_match_idx) == OK)
2568 {
2569 cur_si = &CUR_STATE(current_state.ga_len - 1);
2570 cur_si->si_h_startpos = next_match_h_startpos;
2571 cur_si->si_m_startcol = current_col;
2572 cur_si->si_m_lnum = current_lnum;
2573 cur_si->si_m_endpos = next_match_eos_pos;
2574 cur_si->si_h_endpos = next_match_eos_pos;
2575 cur_si->si_ends = TRUE;
2576 cur_si->si_end_idx = 0;
2577 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002578#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002579 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002580 cur_si->si_flags |= save_flags;
2581 if (cur_si->si_flags & HL_CONCEALENDS)
2582 cur_si->si_flags |= HL_CONCEAL;
2583#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584 cur_si->si_next_list = NULL;
2585 check_keepend();
2586 update_si_attr(current_state.ga_len - 1);
2587 }
2588 }
2589
2590 next_match_idx = -1; /* try other match next time */
2591
2592 return cur_si;
2593}
2594
2595/*
2596 * Check for end of current state (and the states before it).
2597 */
2598 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002599check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600{
2601 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002602 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603
2604 cur_si = &CUR_STATE(current_state.ga_len - 1);
2605 for (;;)
2606 {
2607 if (cur_si->si_ends
2608 && (cur_si->si_m_endpos.lnum < current_lnum
2609 || (cur_si->si_m_endpos.lnum == current_lnum
2610 && cur_si->si_m_endpos.col <= current_col)))
2611 {
2612 /*
2613 * If there is an end pattern group ID, highlight the end pattern
2614 * now. No need to pop the current item from the stack.
2615 * Only do this if the end pattern continues beyond the current
2616 * position.
2617 */
2618 if (cur_si->si_end_idx
2619 && (cur_si->si_eoe_pos.lnum > current_lnum
2620 || (cur_si->si_eoe_pos.lnum == current_lnum
2621 && cur_si->si_eoe_pos.col > current_col)))
2622 {
2623 cur_si->si_idx = cur_si->si_end_idx;
2624 cur_si->si_end_idx = 0;
2625 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2626 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2627 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002628#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002629 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002630 if (cur_si->si_flags & HL_CONCEALENDS)
2631 cur_si->si_flags |= HL_CONCEAL;
2632#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002633 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002634
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002635 /* nextgroup= should not match in the end pattern */
2636 current_next_list = NULL;
2637
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002638 /* what matches next may be different now, clear it */
2639 next_match_idx = 0;
2640 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 break;
2642 }
2643 else
2644 {
2645 /* handle next_list, unless at end of line and no "skipnl" or
2646 * "skipempty" */
2647 current_next_list = cur_si->si_next_list;
2648 current_next_flags = cur_si->si_flags;
2649 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2650 && syn_getcurline()[current_col] == NUL)
2651 current_next_list = NULL;
2652
2653 /* When the ended item has "extend", another item with
2654 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002655 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656
2657 pop_current_state();
2658
2659 if (current_state.ga_len == 0)
2660 break;
2661
Bram Moolenaar81993f42008-01-11 20:27:45 +00002662 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 {
2664 syn_update_ends(FALSE);
2665 if (current_state.ga_len == 0)
2666 break;
2667 }
2668
2669 cur_si = &CUR_STATE(current_state.ga_len - 1);
2670
2671 /*
2672 * Only for a region the search for the end continues after
2673 * the end of the contained item. If the contained match
2674 * included the end-of-line, break here, the region continues.
2675 * Don't do this when:
2676 * - "keepend" is used for the contained item
2677 * - not at the end of the line (could be end="x$"me=e-1).
2678 * - "excludenl" is used (HL_HAS_EOL won't be set)
2679 */
2680 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002681 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682 == SPTYPE_START
2683 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2684 {
2685 update_si_end(cur_si, (int)current_col, TRUE);
2686 check_keepend();
2687 if ((current_next_flags & HL_HAS_EOL)
2688 && keepend_level < 0
2689 && syn_getcurline()[current_col] == NUL)
2690 break;
2691 }
2692 }
2693 }
2694 else
2695 break;
2696 }
2697}
2698
2699/*
2700 * Update an entry in the current_state stack for a match or region. This
2701 * fills in si_attr, si_next_list and si_cont_list.
2702 */
2703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002704update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705{
2706 stateitem_T *sip = &CUR_STATE(idx);
2707 synpat_T *spp;
2708
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002709 /* This should not happen... */
2710 if (sip->si_idx < 0)
2711 return;
2712
Bram Moolenaar860cae12010-06-05 23:22:07 +02002713 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 if (sip->si_flags & HL_MATCH)
2715 sip->si_id = spp->sp_syn_match_id;
2716 else
2717 sip->si_id = spp->sp_syn.id;
2718 sip->si_attr = syn_id2attr(sip->si_id);
2719 sip->si_trans_id = sip->si_id;
2720 if (sip->si_flags & HL_MATCH)
2721 sip->si_cont_list = NULL;
2722 else
2723 sip->si_cont_list = spp->sp_cont_list;
2724
2725 /*
2726 * For transparent items, take attr from outer item.
2727 * Also take cont_list, if there is none.
2728 * Don't do this for the matchgroup of a start or end pattern.
2729 */
2730 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2731 {
2732 if (idx == 0)
2733 {
2734 sip->si_attr = 0;
2735 sip->si_trans_id = 0;
2736 if (sip->si_cont_list == NULL)
2737 sip->si_cont_list = ID_LIST_ALL;
2738 }
2739 else
2740 {
2741 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2742 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002743 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2744 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 if (sip->si_cont_list == NULL)
2746 {
2747 sip->si_flags |= HL_TRANS_CONT;
2748 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2749 }
2750 }
2751 }
2752}
2753
2754/*
2755 * Check the current stack for patterns with "keepend" flag.
2756 * Propagate the match-end to contained items, until a "skipend" item is found.
2757 */
2758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002759check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760{
2761 int i;
2762 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002763 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002764 stateitem_T *sip;
2765
2766 /*
2767 * This check can consume a lot of time; only do it from the level where
2768 * there really is a keepend.
2769 */
2770 if (keepend_level < 0)
2771 return;
2772
2773 /*
2774 * Find the last index of an "extend" item. "keepend" items before that
2775 * won't do anything. If there is no "extend" item "i" will be
2776 * "keepend_level" and all "keepend" items will work normally.
2777 */
2778 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2779 if (CUR_STATE(i).si_flags & HL_EXTEND)
2780 break;
2781
2782 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002783 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002784 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002785 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 for ( ; i < current_state.ga_len; ++i)
2787 {
2788 sip = &CUR_STATE(i);
2789 if (maxpos.lnum != 0)
2790 {
2791 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002792 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2794 sip->si_ends = TRUE;
2795 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002796 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2797 {
2798 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799 || maxpos.lnum > sip->si_m_endpos.lnum
2800 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002801 && maxpos.col > sip->si_m_endpos.col))
2802 maxpos = sip->si_m_endpos;
2803 if (maxpos_h.lnum == 0
2804 || maxpos_h.lnum > sip->si_h_endpos.lnum
2805 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2806 && maxpos_h.col > sip->si_h_endpos.col))
2807 maxpos_h = sip->si_h_endpos;
2808 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809 }
2810}
2811
2812/*
2813 * Update an entry in the current_state stack for a start-skip-end pattern.
2814 * This finds the end of the current item, if it's in the current line.
2815 *
2816 * Return the flags for the matched END.
2817 */
2818 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002819update_si_end(
2820 stateitem_T *sip,
2821 int startcol, /* where to start searching for the end */
2822 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823{
2824 lpos_T startpos;
2825 lpos_T endpos;
2826 lpos_T hl_endpos;
2827 lpos_T end_endpos;
2828 int end_idx;
2829
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002830 /* return quickly for a keyword */
2831 if (sip->si_idx < 0)
2832 return;
2833
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 /* Don't update when it's already done. Can be a match of an end pattern
2835 * that started in a previous line. Watch out: can also be a "keepend"
2836 * from a containing item. */
2837 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2838 return;
2839
2840 /*
2841 * We need to find the end of the region. It may continue in the next
2842 * line.
2843 */
2844 end_idx = 0;
2845 startpos.lnum = current_lnum;
2846 startpos.col = startcol;
2847 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2848 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2849
2850 if (endpos.lnum == 0)
2851 {
2852 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002853 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 {
2855 /* a "oneline" never continues in the next line */
2856 sip->si_ends = TRUE;
2857 sip->si_m_endpos.lnum = current_lnum;
2858 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2859 }
2860 else
2861 {
2862 /* continues in the next line */
2863 sip->si_ends = FALSE;
2864 sip->si_m_endpos.lnum = 0;
2865 }
2866 sip->si_h_endpos = sip->si_m_endpos;
2867 }
2868 else
2869 {
2870 /* match within this line */
2871 sip->si_m_endpos = endpos;
2872 sip->si_h_endpos = hl_endpos;
2873 sip->si_eoe_pos = end_endpos;
2874 sip->si_ends = TRUE;
2875 sip->si_end_idx = end_idx;
2876 }
2877}
2878
2879/*
2880 * Add a new state to the current state stack.
2881 * It is cleared and the index set to "idx".
2882 * Return FAIL if it's not possible (out of memory).
2883 */
2884 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002885push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886{
2887 if (ga_grow(&current_state, 1) == FAIL)
2888 return FAIL;
2889 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2890 CUR_STATE(current_state.ga_len).si_idx = idx;
2891 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 return OK;
2893}
2894
2895/*
2896 * Remove a state from the current_state stack.
2897 */
2898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002899pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900{
2901 if (current_state.ga_len)
2902 {
2903 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2904 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 }
2906 /* after the end of a pattern, try matching a keyword or pattern */
2907 next_match_idx = -1;
2908
2909 /* if first state with "keepend" is popped, reset keepend_level */
2910 if (keepend_level >= current_state.ga_len)
2911 keepend_level = -1;
2912}
2913
2914/*
2915 * Find the end of a start/skip/end syntax region after "startpos".
2916 * Only checks one line.
2917 * Also handles a match item that continued from a previous line.
2918 * If not found, the syntax item continues in the next line. m_endpos->lnum
2919 * will be 0.
2920 * If found, the end of the region and the end of the highlighting is
2921 * computed.
2922 */
2923 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002924find_endpos(
2925 int idx, /* index of the pattern */
2926 lpos_T *startpos, /* where to start looking for an END match */
2927 lpos_T *m_endpos, /* return: end of match */
2928 lpos_T *hl_endpos, /* return: end of highlighting */
2929 long *flagsp, /* return: flags of matching END */
2930 lpos_T *end_endpos, /* return: end of end pattern match */
2931 int *end_idx, /* return: group ID for end pat. match, or 0 */
2932 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933{
2934 colnr_T matchcol;
2935 synpat_T *spp, *spp_skip;
2936 int start_idx;
2937 int best_idx;
2938 regmmatch_T regmatch;
2939 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2940 lpos_T pos;
2941 char_u *line;
2942 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002943 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002945 /* just in case we are invoked for a keyword */
2946 if (idx < 0)
2947 return;
2948
Bram Moolenaar071d4272004-06-13 20:20:40 +00002949 /*
2950 * Check for being called with a START pattern.
2951 * Can happen with a match that continues to the next line, because it
2952 * contained a region.
2953 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002954 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 if (spp->sp_type != SPTYPE_START)
2956 {
2957 *hl_endpos = *startpos;
2958 return;
2959 }
2960
2961 /*
2962 * Find the SKIP or first END pattern after the last START pattern.
2963 */
2964 for (;;)
2965 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002966 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967 if (spp->sp_type != SPTYPE_START)
2968 break;
2969 ++idx;
2970 }
2971
2972 /*
2973 * Lookup the SKIP pattern (if present)
2974 */
2975 if (spp->sp_type == SPTYPE_SKIP)
2976 {
2977 spp_skip = spp;
2978 ++idx;
2979 }
2980 else
2981 spp_skip = NULL;
2982
2983 /* Setup external matches for syn_regexec(). */
2984 unref_extmatch(re_extmatch_in);
2985 re_extmatch_in = ref_extmatch(start_ext);
2986
2987 matchcol = startpos->col; /* start looking for a match at sstart */
2988 start_idx = idx; /* remember the first END pattern. */
2989 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002990
2991 /* use syntax iskeyword option */
2992 save_chartab(buf_chartab);
2993
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 for (;;)
2995 {
2996 /*
2997 * Find end pattern that matches first after "matchcol".
2998 */
2999 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003000 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 {
3002 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003003 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004
Bram Moolenaar860cae12010-06-05 23:22:07 +02003005 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003006 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3007 break;
3008 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3009 if (lc_col < 0)
3010 lc_col = 0;
3011
3012 regmatch.rmm_ic = spp->sp_ic;
3013 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003014 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3015 IF_SYN_TIME(&spp->sp_time));
3016 spp->sp_prog = regmatch.regprog;
3017 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 {
3019 if (best_idx == -1 || regmatch.startpos[0].col
3020 < best_regmatch.startpos[0].col)
3021 {
3022 best_idx = idx;
3023 best_regmatch.startpos[0] = regmatch.startpos[0];
3024 best_regmatch.endpos[0] = regmatch.endpos[0];
3025 }
3026 }
3027 }
3028
3029 /*
3030 * If all end patterns have been tried, and there is no match, the
3031 * item continues until end-of-line.
3032 */
3033 if (best_idx == -1)
3034 break;
3035
3036 /*
3037 * If the skip pattern matches before the end pattern,
3038 * continue searching after the skip pattern.
3039 */
3040 if (spp_skip != NULL)
3041 {
3042 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003043 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044
3045 if (lc_col < 0)
3046 lc_col = 0;
3047 regmatch.rmm_ic = spp_skip->sp_ic;
3048 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003049 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3050 IF_SYN_TIME(&spp_skip->sp_time));
3051 spp_skip->sp_prog = regmatch.regprog;
3052 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 <= best_regmatch.startpos[0].col)
3054 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003055 int line_len;
3056
Bram Moolenaar071d4272004-06-13 20:20:40 +00003057 /* Add offset to skip pattern match */
3058 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3059
3060 /* If the skip pattern goes on to the next line, there is no
3061 * match with an end pattern in this line. */
3062 if (pos.lnum > startpos->lnum)
3063 break;
3064
3065 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003066 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067
3068 /* take care of an empty match or negative offset */
3069 if (pos.col <= matchcol)
3070 ++matchcol;
3071 else if (pos.col <= regmatch.endpos[0].col)
3072 matchcol = pos.col;
3073 else
3074 /* Be careful not to jump over the NUL at the end-of-line */
3075 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003076 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077 ++matchcol)
3078 ;
3079
3080 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003081 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 break;
3083
3084 continue; /* start with first end pattern again */
3085 }
3086 }
3087
3088 /*
3089 * Match from start pattern to end pattern.
3090 * Correct for match and highlight offset of end pattern.
3091 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003092 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3094 /* can't end before the start */
3095 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3096 m_endpos->col = startpos->col;
3097
3098 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3099 /* can't end before the start */
3100 if (end_endpos->lnum == startpos->lnum
3101 && end_endpos->col < startpos->col)
3102 end_endpos->col = startpos->col;
3103 /* can't end after the match */
3104 limit_pos(end_endpos, m_endpos);
3105
3106 /*
3107 * If the end group is highlighted differently, adjust the pointers.
3108 */
3109 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3110 {
3111 *end_idx = best_idx;
3112 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3113 {
3114 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3115 hl_endpos->col = best_regmatch.endpos[0].col;
3116 }
3117 else
3118 {
3119 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3120 hl_endpos->col = best_regmatch.startpos[0].col;
3121 }
3122 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3123
3124 /* can't end before the start */
3125 if (hl_endpos->lnum == startpos->lnum
3126 && hl_endpos->col < startpos->col)
3127 hl_endpos->col = startpos->col;
3128 limit_pos(hl_endpos, m_endpos);
3129
3130 /* now the match ends where the highlighting ends, it is turned
3131 * into the matchgroup for the end */
3132 *m_endpos = *hl_endpos;
3133 }
3134 else
3135 {
3136 *end_idx = 0;
3137 *hl_endpos = *end_endpos;
3138 }
3139
3140 *flagsp = spp->sp_flags;
3141
3142 had_match = TRUE;
3143 break;
3144 }
3145
3146 /* no match for an END pattern in this line */
3147 if (!had_match)
3148 m_endpos->lnum = 0;
3149
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003150 restore_chartab(buf_chartab);
3151
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152 /* Remove external matches. */
3153 unref_extmatch(re_extmatch_in);
3154 re_extmatch_in = NULL;
3155}
3156
3157/*
3158 * Limit "pos" not to be after "limit".
3159 */
3160 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003161limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162{
3163 if (pos->lnum > limit->lnum)
3164 *pos = *limit;
3165 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3166 pos->col = limit->col;
3167}
3168
3169/*
3170 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3171 */
3172 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003173limit_pos_zero(
3174 lpos_T *pos,
3175 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176{
3177 if (pos->lnum == 0)
3178 *pos = *limit;
3179 else
3180 limit_pos(pos, limit);
3181}
3182
3183/*
3184 * Add offset to matched text for end of match or highlight.
3185 */
3186 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003187syn_add_end_off(
3188 lpos_T *result, /* returned position */
3189 regmmatch_T *regmatch, /* start/end of match */
3190 synpat_T *spp, /* matched pattern */
3191 int idx, /* index of offset */
3192 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193{
3194 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003195 int off;
3196 char_u *base;
3197 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198
3199 if (spp->sp_off_flags & (1 << idx))
3200 {
3201 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003202 col = regmatch->startpos[0].col;
3203 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204 }
3205 else
3206 {
3207 result->lnum = regmatch->endpos[0].lnum;
3208 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003209 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003211 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3212 * is a matchgroup. Watch out for match with last NL in the buffer. */
3213 if (result->lnum > syn_buf->b_ml.ml_line_count)
3214 col = 0;
3215 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003216 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3218 p = base + col;
3219 if (off > 0)
3220 {
3221 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003222 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003223 }
3224 else if (off < 0)
3225 {
3226 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003227 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003228 }
3229 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003230 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003231 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232}
3233
3234/*
3235 * Add offset to matched text for start of match or highlight.
3236 * Avoid resulting column to become negative.
3237 */
3238 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003239syn_add_start_off(
3240 lpos_T *result, /* returned position */
3241 regmmatch_T *regmatch, /* start/end of match */
3242 synpat_T *spp,
3243 int idx,
3244 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245{
3246 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003247 int off;
3248 char_u *base;
3249 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250
3251 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3252 {
3253 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003254 col = regmatch->endpos[0].col;
3255 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 }
3257 else
3258 {
3259 result->lnum = regmatch->startpos[0].lnum;
3260 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003261 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003263 if (result->lnum > syn_buf->b_ml.ml_line_count)
3264 {
3265 /* a "\n" at the end of the pattern may take us below the last line */
3266 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003267 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003268 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003269 if (off != 0)
3270 {
3271 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3272 p = base + col;
3273 if (off > 0)
3274 {
3275 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003276 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003277 }
3278 else if (off < 0)
3279 {
3280 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003281 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003282 }
3283 col = (int)(p - base);
3284 }
3285 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286}
3287
3288/*
3289 * Get current line in syntax buffer.
3290 */
3291 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003292syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293{
3294 return ml_get_buf(syn_buf, current_lnum, FALSE);
3295}
3296
3297/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003298 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299 * Returns TRUE when there is a match.
3300 */
3301 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003302syn_regexec(
3303 regmmatch_T *rmp,
3304 linenr_T lnum,
3305 colnr_T col,
3306 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003308 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003309#ifdef FEAT_RELTIME
3310 int timed_out = FALSE;
3311#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003312#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003313 proftime_T pt;
3314
3315 if (syn_time_on)
3316 profile_start(&pt);
3317#endif
3318
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003319 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003320 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3321#ifdef FEAT_RELTIME
3322 syn_tm, &timed_out
3323#else
3324 NULL, NULL
3325#endif
3326 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003327
Bram Moolenaarf7512552013-06-06 14:55:19 +02003328#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003329 if (syn_time_on)
3330 {
3331 profile_end(&pt);
3332 profile_add(&st->total, &pt);
3333 if (profile_cmp(&pt, &st->slowest) < 0)
3334 st->slowest = pt;
3335 ++st->count;
3336 if (r > 0)
3337 ++st->match;
3338 }
3339#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003340#ifdef FEAT_RELTIME
3341 if (timed_out)
3342 syn_win->w_s->b_syn_slow = TRUE;
3343#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003344
3345 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 {
3347 rmp->startpos[0].lnum += lnum;
3348 rmp->endpos[0].lnum += lnum;
3349 return TRUE;
3350 }
3351 return FALSE;
3352}
3353
3354/*
3355 * Check one position in a line for a matching keyword.
3356 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003357 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 */
3359 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003360check_keyword_id(
3361 char_u *line,
3362 int startcol, /* position in line to check for keyword */
3363 int *endcolp, /* return: character after found keyword */
3364 long *flagsp, /* return: flags of matching keyword */
3365 short **next_listp, /* return: next_list of matching keyword */
3366 stateitem_T *cur_si, /* item at the top of the stack */
3367 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003369 keyentry_T *kp;
3370 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003372 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003374 hashtab_T *ht;
3375 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376
3377 /* Find first character after the keyword. First character was already
3378 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003379 kwp = line + startcol;
3380 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 do
3382 {
3383#ifdef FEAT_MBYTE
3384 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003385 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386 else
3387#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003390 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003393 return 0;
3394
3395 /*
3396 * Must make a copy of the keyword, so we can add a NUL and make it
3397 * lowercase.
3398 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003399 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400
3401 /*
3402 * Try twice:
3403 * 1. matching case
3404 * 2. ignoring case
3405 */
3406 for (round = 1; round <= 2; ++round)
3407 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003409 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003410 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003411 if (round == 2) /* ignore case */
3412 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413
3414 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003415 * Find keywords that match. There can be several with different
3416 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417 * When current_next_list is non-zero accept only that group, otherwise:
3418 * Accept a not-contained keyword at toplevel.
3419 * Accept a keyword at other levels only if it is in the contains list.
3420 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003421 hi = hash_find(ht, keyword);
3422 if (!HASHITEM_EMPTY(hi))
3423 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003425 if (current_next_list != 0
3426 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3427 : (cur_si == NULL
3428 ? !(kp->flags & HL_CONTAINED)
3429 : in_id_list(cur_si, cur_si->si_cont_list,
3430 &kp->k_syn, kp->flags & HL_CONTAINED)))
3431 {
3432 *endcolp = startcol + kwlen;
3433 *flagsp = kp->flags;
3434 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003435#ifdef FEAT_CONCEAL
3436 *ccharp = kp->k_char;
3437#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003438 return kp->k_syn.id;
3439 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440 }
3441 }
3442 return 0;
3443}
3444
3445/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003446 * Handle ":syntax conceal" command.
3447 */
3448 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003449syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003450{
3451#ifdef FEAT_CONCEAL
3452 char_u *arg = eap->arg;
3453 char_u *next;
3454
3455 eap->nextcmd = find_nextcmd(arg);
3456 if (eap->skip)
3457 return;
3458
3459 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003460 if (*arg == NUL)
3461 {
3462 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003463 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003464 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003465 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003466 }
3467 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468 curwin->w_s->b_syn_conceal = TRUE;
3469 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3470 curwin->w_s->b_syn_conceal = FALSE;
3471 else
3472 EMSG2(_("E390: Illegal argument: %s"), arg);
3473#endif
3474}
3475
3476/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 * Handle ":syntax case" command.
3478 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003480syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481{
3482 char_u *arg = eap->arg;
3483 char_u *next;
3484
3485 eap->nextcmd = find_nextcmd(arg);
3486 if (eap->skip)
3487 return;
3488
3489 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003490 if (*arg == NUL)
3491 {
3492 if (curwin->w_s->b_syn_ic)
3493 MSG(_("syntax case ignore"));
3494 else
3495 MSG(_("syntax case match"));
3496 }
3497 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 else
3502 EMSG2(_("E390: Illegal argument: %s"), arg);
3503}
3504
3505/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003506 * Handle ":syntax spell" command.
3507 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003508 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003509syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003510{
3511 char_u *arg = eap->arg;
3512 char_u *next;
3513
3514 eap->nextcmd = find_nextcmd(arg);
3515 if (eap->skip)
3516 return;
3517
3518 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003519 if (*arg == NUL)
3520 {
3521 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3522 MSG(_("syntax spell toplevel"));
3523 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3524 MSG(_("syntax spell notoplevel"));
3525 else
3526 MSG(_("syntax spell default"));
3527 }
3528 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003530 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003531 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003532 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003533 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003534 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003535 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003536 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003537 return;
3538 }
3539
3540 /* assume spell checking changed, force a redraw */
3541 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003542}
3543
3544/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003545 * Handle ":syntax iskeyword" command.
3546 */
3547 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003548syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003549{
3550 char_u *arg = eap->arg;
3551 char_u save_chartab[32];
3552 char_u *save_isk;
3553
3554 if (eap->skip)
3555 return;
3556
3557 arg = skipwhite(arg);
3558 if (*arg == NUL)
3559 {
3560 MSG_PUTS("\n");
3561 MSG_PUTS(_("syntax iskeyword "));
3562 if (curwin->w_s->b_syn_isk != empty_option)
3563 msg_outtrans(curwin->w_s->b_syn_isk);
3564 else
3565 msg_outtrans((char_u *)"not set");
3566 }
3567 else
3568 {
3569 if (STRNICMP(arg, "clear", 5) == 0)
3570 {
3571 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3572 (size_t)32);
3573 clear_string_option(&curwin->w_s->b_syn_isk);
3574 }
3575 else
3576 {
3577 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3578 save_isk = curbuf->b_p_isk;
3579 curbuf->b_p_isk = vim_strsave(arg);
3580
3581 buf_init_chartab(curbuf, FALSE);
3582 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3583 (size_t)32);
3584 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3585 clear_string_option(&curwin->w_s->b_syn_isk);
3586 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3587 curbuf->b_p_isk = save_isk;
3588 }
3589 }
3590 redraw_win_later(curwin, NOT_VALID);
3591}
3592
3593/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003594 * Clear all syntax info for one buffer.
3595 */
3596 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003597syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598{
3599 int i;
3600
Bram Moolenaar860cae12010-06-05 23:22:07 +02003601 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003602#ifdef FEAT_RELTIME
3603 block->b_syn_slow = FALSE; /* clear previous timeout */
3604#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003605 block->b_syn_ic = FALSE; /* Use case, by default */
3606 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3607 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003608#ifdef FEAT_CONCEAL
3609 block->b_syn_conceal = FALSE;
3610#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611
3612 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 clear_keywtab(&block->b_keywtab);
3614 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615
3616 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3618 syn_clear_pattern(block, i);
3619 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620
3621 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003622 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3623 syn_clear_cluster(block, i);
3624 ga_clear(&block->b_syn_clusters);
3625 block->b_spell_cluster_id = 0;
3626 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627
Bram Moolenaar860cae12010-06-05 23:22:07 +02003628 block->b_syn_sync_flags = 0;
3629 block->b_syn_sync_minlines = 0;
3630 block->b_syn_sync_maxlines = 0;
3631 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632
Bram Moolenaar473de612013-06-08 18:19:48 +02003633 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003634 block->b_syn_linecont_prog = NULL;
3635 vim_free(block->b_syn_linecont_pat);
3636 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003640 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641
3642 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003645
3646 /* Reset the counter for ":syn include" */
3647 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003648}
3649
3650/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003651 * Get rid of ownsyntax for window "wp".
3652 */
3653 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003654reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003655{
3656 if (wp->w_s != &wp->w_buffer->b_s)
3657 {
3658 syntax_clear(wp->w_s);
3659 vim_free(wp->w_s);
3660 wp->w_s = &wp->w_buffer->b_s;
3661 }
3662}
3663
3664/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 * Clear syncing info for one buffer.
3666 */
3667 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003668syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669{
3670 int i;
3671
3672 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003673 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3674 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3675 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676
Bram Moolenaar860cae12010-06-05 23:22:07 +02003677 curwin->w_s->b_syn_sync_flags = 0;
3678 curwin->w_s->b_syn_sync_minlines = 0;
3679 curwin->w_s->b_syn_sync_maxlines = 0;
3680 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681
Bram Moolenaar473de612013-06-08 18:19:48 +02003682 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 curwin->w_s->b_syn_linecont_prog = NULL;
3684 vim_free(curwin->w_s->b_syn_linecont_pat);
3685 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003686 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003688 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689}
3690
3691/*
3692 * Remove one pattern from the buffer's pattern list.
3693 */
3694 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003695syn_remove_pattern(
3696 synblock_T *block,
3697 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698{
3699 synpat_T *spp;
3700
Bram Moolenaar860cae12010-06-05 23:22:07 +02003701 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702#ifdef FEAT_FOLDING
3703 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003704 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003706 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003708 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3709 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710}
3711
3712/*
3713 * Clear and free one syntax pattern. When clearing all, must be called from
3714 * last to first!
3715 */
3716 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003717syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003719 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003720 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003722 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003724 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3725 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3726 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 }
3728}
3729
3730/*
3731 * Clear and free one syntax cluster.
3732 */
3733 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003734syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003736 vim_free(SYN_CLSTR(block)[i].scl_name);
3737 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3738 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739}
3740
3741/*
3742 * Handle ":syntax clear" command.
3743 */
3744 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003745syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746{
3747 char_u *arg = eap->arg;
3748 char_u *arg_end;
3749 int id;
3750
3751 eap->nextcmd = find_nextcmd(arg);
3752 if (eap->skip)
3753 return;
3754
3755 /*
3756 * We have to disable this within ":syn include @group filename",
3757 * because otherwise @group would get deleted.
3758 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3759 * clear".
3760 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003761 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762 return;
3763
3764 if (ends_excmd(*arg))
3765 {
3766 /*
3767 * No argument: Clear all syntax items.
3768 */
3769 if (syncing)
3770 syntax_sync_clear();
3771 else
3772 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003773 syntax_clear(curwin->w_s);
3774 if (curwin->w_s == &curwin->w_buffer->b_s)
3775 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003776 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777 }
3778 }
3779 else
3780 {
3781 /*
3782 * Clear the group IDs that are in the argument.
3783 */
3784 while (!ends_excmd(*arg))
3785 {
3786 arg_end = skiptowhite(arg);
3787 if (*arg == '@')
3788 {
3789 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3790 if (id == 0)
3791 {
3792 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3793 break;
3794 }
3795 else
3796 {
3797 /*
3798 * We can't physically delete a cluster without changing
3799 * the IDs of other clusters, so we do the next best thing
3800 * and make it empty.
3801 */
3802 short scl_id = id - SYNID_CLUSTER;
3803
Bram Moolenaar860cae12010-06-05 23:22:07 +02003804 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3805 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003806 }
3807 }
3808 else
3809 {
3810 id = syn_namen2id(arg, (int)(arg_end - arg));
3811 if (id == 0)
3812 {
3813 EMSG2(_(e_nogroup), arg);
3814 break;
3815 }
3816 else
3817 syn_clear_one(id, syncing);
3818 }
3819 arg = skipwhite(arg_end);
3820 }
3821 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003822 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003823 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824}
3825
3826/*
3827 * Clear one syntax group for the current buffer.
3828 */
3829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003830syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831{
3832 synpat_T *spp;
3833 int idx;
3834
3835 /* Clear keywords only when not ":syn sync clear group-name" */
3836 if (!syncing)
3837 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003838 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3839 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 }
3841
3842 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003843 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003845 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3847 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 }
3850}
3851
3852/*
3853 * Handle ":syntax on" command.
3854 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003856syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857{
3858 syn_cmd_onoff(eap, "syntax");
3859}
3860
3861/*
3862 * Handle ":syntax enable" command.
3863 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003865syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866{
3867 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3868 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003869 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870}
3871
3872/*
3873 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003874 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003877syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003878{
3879 eap->nextcmd = check_nextcmd(eap->arg);
3880 if (!eap->skip)
3881 {
3882 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3883 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003884 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 }
3886}
3887
3888/*
3889 * Handle ":syntax manual" command.
3890 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003892syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893{
3894 syn_cmd_onoff(eap, "manual");
3895}
3896
3897/*
3898 * Handle ":syntax off" command.
3899 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003901syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902{
3903 syn_cmd_onoff(eap, "nosyntax");
3904}
3905
3906 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003907syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908{
3909 char_u buf[100];
3910
3911 eap->nextcmd = check_nextcmd(eap->arg);
3912 if (!eap->skip)
3913 {
3914 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003915 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916 do_cmdline_cmd(buf);
3917 }
3918}
3919
3920/*
3921 * Handle ":syntax [list]" command: list current syntax words.
3922 */
3923 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003924syn_cmd_list(
3925 exarg_T *eap,
3926 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927{
3928 char_u *arg = eap->arg;
3929 int id;
3930 char_u *arg_end;
3931
3932 eap->nextcmd = find_nextcmd(arg);
3933 if (eap->skip)
3934 return;
3935
Bram Moolenaar860cae12010-06-05 23:22:07 +02003936 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003938 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003939 return;
3940 }
3941
3942 if (syncing)
3943 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003944 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 {
3946 MSG_PUTS(_("syncing on C-style comments"));
3947 syn_lines_msg();
3948 syn_match_msg();
3949 return;
3950 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003951 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003953 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 MSG_PUTS(_("no syncing"));
3955 else
3956 {
3957 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003958 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 MSG_PUTS(_(" lines before top line"));
3960 syn_match_msg();
3961 }
3962 return;
3963 }
3964 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003965 if (curwin->w_s->b_syn_sync_minlines > 0
3966 || curwin->w_s->b_syn_sync_maxlines > 0
3967 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 {
3969 MSG_PUTS(_("\nsyncing on items"));
3970 syn_lines_msg();
3971 syn_match_msg();
3972 }
3973 }
3974 else
3975 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3976 if (ends_excmd(*arg))
3977 {
3978 /*
3979 * No argument: List all group IDs and all syntax clusters.
3980 */
3981 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3982 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003983 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 syn_list_cluster(id);
3985 }
3986 else
3987 {
3988 /*
3989 * List the group IDs and syntax clusters that are in the argument.
3990 */
3991 while (!ends_excmd(*arg) && !got_int)
3992 {
3993 arg_end = skiptowhite(arg);
3994 if (*arg == '@')
3995 {
3996 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3997 if (id == 0)
3998 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3999 else
4000 syn_list_cluster(id - SYNID_CLUSTER);
4001 }
4002 else
4003 {
4004 id = syn_namen2id(arg, (int)(arg_end - arg));
4005 if (id == 0)
4006 EMSG2(_(e_nogroup), arg);
4007 else
4008 syn_list_one(id, syncing, TRUE);
4009 }
4010 arg = skipwhite(arg_end);
4011 }
4012 }
4013 eap->nextcmd = check_nextcmd(arg);
4014}
4015
4016 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004017syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004019 if (curwin->w_s->b_syn_sync_maxlines > 0
4020 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 {
4022 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004023 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004024 {
4025 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004026 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4027 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 MSG_PUTS(", ");
4029 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004030 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 {
4032 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004033 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 }
4035 MSG_PUTS(_(" lines before top line"));
4036 }
4037}
4038
4039 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004040syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004042 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 {
4044 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004045 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 MSG_PUTS(_(" line breaks"));
4047 }
4048}
4049
4050static int last_matchgroup;
4051
4052struct name_list
4053{
4054 int flag;
4055 char *name;
4056};
4057
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004058static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059
4060/*
4061 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4062 */
4063 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004064syn_list_one(
4065 int id,
4066 int syncing, /* when TRUE: list syncing items */
4067 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068{
4069 int attr;
4070 int idx;
4071 int did_header = FALSE;
4072 synpat_T *spp;
4073 static struct name_list namelist1[] =
4074 {
4075 {HL_DISPLAY, "display"},
4076 {HL_CONTAINED, "contained"},
4077 {HL_ONELINE, "oneline"},
4078 {HL_KEEPEND, "keepend"},
4079 {HL_EXTEND, "extend"},
4080 {HL_EXCLUDENL, "excludenl"},
4081 {HL_TRANSP, "transparent"},
4082 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004083#ifdef FEAT_CONCEAL
4084 {HL_CONCEAL, "conceal"},
4085 {HL_CONCEALENDS, "concealends"},
4086#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 {0, NULL}
4088 };
4089 static struct name_list namelist2[] =
4090 {
4091 {HL_SKIPWHITE, "skipwhite"},
4092 {HL_SKIPNL, "skipnl"},
4093 {HL_SKIPEMPTY, "skipempty"},
4094 {0, NULL}
4095 };
4096
Bram Moolenaar8820b482017-03-16 17:23:31 +01004097 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004098
4099 /* list the keywords for "id" */
4100 if (!syncing)
4101 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004102 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4103 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104 did_header, attr);
4105 }
4106
4107 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004108 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004110 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4112 continue;
4113
4114 (void)syn_list_header(did_header, 999, id);
4115 did_header = TRUE;
4116 last_matchgroup = 0;
4117 if (spp->sp_type == SPTYPE_MATCH)
4118 {
4119 put_pattern("match", ' ', spp, attr);
4120 msg_putchar(' ');
4121 }
4122 else if (spp->sp_type == SPTYPE_START)
4123 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004124 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4125 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4126 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4127 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4128 while (idx < curwin->w_s->b_syn_patterns.ga_len
4129 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4130 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131 --idx;
4132 msg_putchar(' ');
4133 }
4134 syn_list_flags(namelist1, spp->sp_flags, attr);
4135
4136 if (spp->sp_cont_list != NULL)
4137 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4138
4139 if (spp->sp_syn.cont_in_list != NULL)
4140 put_id_list((char_u *)"containedin",
4141 spp->sp_syn.cont_in_list, attr);
4142
4143 if (spp->sp_next_list != NULL)
4144 {
4145 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4146 syn_list_flags(namelist2, spp->sp_flags, attr);
4147 }
4148 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4149 {
4150 if (spp->sp_flags & HL_SYNC_HERE)
4151 msg_puts_attr((char_u *)"grouphere", attr);
4152 else
4153 msg_puts_attr((char_u *)"groupthere", attr);
4154 msg_putchar(' ');
4155 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004156 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4158 else
4159 MSG_PUTS("NONE");
4160 msg_putchar(' ');
4161 }
4162 }
4163
4164 /* list the link, if there is one */
4165 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4166 {
4167 (void)syn_list_header(did_header, 999, id);
4168 msg_puts_attr((char_u *)"links to", attr);
4169 msg_putchar(' ');
4170 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4171 }
4172}
4173
4174 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004175syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176{
4177 int i;
4178
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004179 for (i = 0; nlist[i].flag != 0; ++i)
4180 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004182 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 msg_putchar(' ');
4184 }
4185}
4186
4187/*
4188 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4189 */
4190 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004191syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192{
4193 int endcol = 15;
4194
4195 /* slight hack: roughly duplicate the guts of syn_list_header() */
4196 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004197 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198
4199 if (msg_col >= endcol) /* output at least one space */
4200 endcol = msg_col + 1;
4201 if (Columns <= endcol) /* avoid hang for tiny window */
4202 endcol = Columns - 1;
4203
4204 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004205 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004207 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004208 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 }
4210 else
4211 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004212 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 msg_puts((char_u *)"=NONE");
4214 }
4215}
4216
4217 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004218put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219{
4220 short *p;
4221
4222 msg_puts_attr(name, attr);
4223 msg_putchar('=');
4224 for (p = list; *p; ++p)
4225 {
4226 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4227 {
4228 if (p[1])
4229 MSG_PUTS("ALLBUT");
4230 else
4231 MSG_PUTS("ALL");
4232 }
4233 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4234 {
4235 MSG_PUTS("TOP");
4236 }
4237 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4238 {
4239 MSG_PUTS("CONTAINED");
4240 }
4241 else if (*p >= SYNID_CLUSTER)
4242 {
4243 short scl_id = *p - SYNID_CLUSTER;
4244
4245 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004246 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247 }
4248 else
4249 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4250 if (p[1])
4251 msg_putchar(',');
4252 }
4253 msg_putchar(' ');
4254}
4255
4256 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004257put_pattern(
4258 char *s,
4259 int c,
4260 synpat_T *spp,
4261 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262{
4263 long n;
4264 int mask;
4265 int first;
4266 static char *sepchars = "/+=-#@\"|'^&";
4267 int i;
4268
4269 /* May have to write "matchgroup=group" */
4270 if (last_matchgroup != spp->sp_syn_match_id)
4271 {
4272 last_matchgroup = spp->sp_syn_match_id;
4273 msg_puts_attr((char_u *)"matchgroup", attr);
4274 msg_putchar('=');
4275 if (last_matchgroup == 0)
4276 msg_outtrans((char_u *)"NONE");
4277 else
4278 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4279 msg_putchar(' ');
4280 }
4281
4282 /* output the name of the pattern and an '=' or ' ' */
4283 msg_puts_attr((char_u *)s, attr);
4284 msg_putchar(c);
4285
4286 /* output the pattern, in between a char that is not in the pattern */
4287 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4288 if (sepchars[++i] == NUL)
4289 {
4290 i = 0; /* no good char found, just use the first one */
4291 break;
4292 }
4293 msg_putchar(sepchars[i]);
4294 msg_outtrans(spp->sp_pattern);
4295 msg_putchar(sepchars[i]);
4296
4297 /* output any pattern options */
4298 first = TRUE;
4299 for (i = 0; i < SPO_COUNT; ++i)
4300 {
4301 mask = (1 << i);
4302 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4303 {
4304 if (!first)
4305 msg_putchar(','); /* separate with commas */
4306 msg_puts((char_u *)spo_name_tab[i]);
4307 n = spp->sp_offsets[i];
4308 if (i != SPO_LC_OFF)
4309 {
4310 if (spp->sp_off_flags & mask)
4311 msg_putchar('s');
4312 else
4313 msg_putchar('e');
4314 if (n > 0)
4315 msg_putchar('+');
4316 }
4317 if (n || i == SPO_LC_OFF)
4318 msg_outnum(n);
4319 first = FALSE;
4320 }
4321 }
4322 msg_putchar(' ');
4323}
4324
4325/*
4326 * List or clear the keywords for one syntax group.
4327 * Return TRUE if the header has been printed.
4328 */
4329 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004330syn_list_keywords(
4331 int id,
4332 hashtab_T *ht,
4333 int did_header, /* header has already been printed */
4334 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 hashitem_T *hi;
4338 keyentry_T *kp;
4339 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340 int prev_contained = 0;
4341 short *prev_next_list = NULL;
4342 short *prev_cont_in_list = NULL;
4343 int prev_skipnl = 0;
4344 int prev_skipwhite = 0;
4345 int prev_skipempty = 0;
4346
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 /*
4348 * Unfortunately, this list of keywords is not sorted on alphabet but on
4349 * hash value...
4350 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004351 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 --todo;
4357 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 if (prev_contained != (kp->flags & HL_CONTAINED)
4362 || prev_skipnl != (kp->flags & HL_SKIPNL)
4363 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4364 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4365 || prev_cont_in_list != kp->k_syn.cont_in_list
4366 || prev_next_list != kp->next_list)
4367 outlen = 9999;
4368 else
4369 outlen = (int)STRLEN(kp->keyword);
4370 /* output "contained" and "nextgroup" on each line */
4371 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 prev_contained = 0;
4374 prev_next_list = NULL;
4375 prev_cont_in_list = NULL;
4376 prev_skipnl = 0;
4377 prev_skipwhite = 0;
4378 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 did_header = TRUE;
4381 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 put_id_list((char_u *)"containedin",
4390 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 if (kp->next_list != prev_next_list)
4395 {
4396 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4397 msg_putchar(' ');
4398 prev_next_list = kp->next_list;
4399 if (kp->flags & HL_SKIPNL)
4400 {
4401 msg_puts_attr((char_u *)"skipnl", attr);
4402 msg_putchar(' ');
4403 prev_skipnl = (kp->flags & HL_SKIPNL);
4404 }
4405 if (kp->flags & HL_SKIPWHITE)
4406 {
4407 msg_puts_attr((char_u *)"skipwhite", attr);
4408 msg_putchar(' ');
4409 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4410 }
4411 if (kp->flags & HL_SKIPEMPTY)
4412 {
4413 msg_puts_attr((char_u *)"skipempty", attr);
4414 msg_putchar(' ');
4415 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4416 }
4417 }
4418 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 }
4421 }
4422 }
4423
4424 return did_header;
4425}
4426
4427 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004428syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004430 hashitem_T *hi;
4431 keyentry_T *kp;
4432 keyentry_T *kp_prev;
4433 keyentry_T *kp_next;
4434 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435
Bram Moolenaardad6b692005-01-25 22:14:34 +00004436 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004437 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004438 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 --todo;
4443 kp_prev = NULL;
4444 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 if (kp->k_syn.id == id)
4447 {
4448 kp_next = kp->ke_next;
4449 if (kp_prev == NULL)
4450 {
4451 if (kp_next == NULL)
4452 hash_remove(ht, hi);
4453 else
4454 hi->hi_key = KE2HIKEY(kp_next);
4455 }
4456 else
4457 kp_prev->ke_next = kp_next;
4458 vim_free(kp->next_list);
4459 vim_free(kp->k_syn.cont_in_list);
4460 vim_free(kp);
4461 kp = kp_next;
4462 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 {
4465 kp_prev = kp;
4466 kp = kp->ke_next;
4467 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 }
4469 }
4470 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004471 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472}
4473
4474/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004475 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476 */
4477 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004478clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004480 hashitem_T *hi;
4481 int todo;
4482 keyentry_T *kp;
4483 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004485 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004486 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004488 if (!HASHITEM_EMPTY(hi))
4489 {
4490 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004491 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 kp_next = kp->ke_next;
4494 vim_free(kp->next_list);
4495 vim_free(kp->k_syn.cont_in_list);
4496 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 hash_clear(ht);
4501 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502}
4503
4504/*
4505 * Add a keyword to the list of keywords.
4506 */
4507 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004508add_keyword(
4509 char_u *name, /* name of keyword */
4510 int id, /* group ID for this keyword */
4511 int flags, /* flags for this keyword */
4512 short *cont_in_list, /* containedin for this keyword */
4513 short *next_list, /* nextgroup for this keyword */
4514 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004516 keyentry_T *kp;
4517 hashtab_T *ht;
4518 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004520 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004521 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522
Bram Moolenaar860cae12010-06-05 23:22:07 +02004523 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 name_ic = str_foldcase(name, (int)STRLEN(name),
4525 name_folded, MAXKEYWLEN + 1);
4526 else
4527 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004528 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4529 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004531 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004532 kp->k_syn.id = id;
4533 kp->k_syn.inc_tag = current_syn_inc_tag;
4534 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004535 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004536 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004537 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004538 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004539 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540
Bram Moolenaar860cae12010-06-05 23:22:07 +02004541 if (curwin->w_s->b_syn_ic)
4542 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004544 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545
Bram Moolenaardad6b692005-01-25 22:14:34 +00004546 hash = hash_hash(kp->keyword);
4547 hi = hash_lookup(ht, kp->keyword, hash);
4548 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004550 /* new keyword, add to hashtable */
4551 kp->ke_next = NULL;
4552 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004554 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004556 /* keyword already exists, prepend to list */
4557 kp->ke_next = HI2KE(hi);
4558 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560}
4561
4562/*
4563 * Get the start and end of the group name argument.
4564 * Return a pointer to the first argument.
4565 * Return NULL if the end of the command was found instead of further args.
4566 */
4567 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004568get_group_name(
4569 char_u *arg, /* start of the argument */
4570 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004571{
4572 char_u *rest;
4573
4574 *name_end = skiptowhite(arg);
4575 rest = skipwhite(*name_end);
4576
4577 /*
4578 * Check if there are enough arguments. The first argument may be a
4579 * pattern, where '|' is allowed, so only check for NUL.
4580 */
4581 if (ends_excmd(*arg) || *rest == NUL)
4582 return NULL;
4583 return rest;
4584}
4585
4586/*
4587 * Check for syntax command option arguments.
4588 * This can be called at any place in the list of arguments, and just picks
4589 * out the arguments that are known. Can be called several times in a row to
4590 * collect all options in between other arguments.
4591 * Return a pointer to the next argument (which isn't an option).
4592 * Return NULL for any error;
4593 */
4594 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004595get_syn_options(
4596 char_u *arg, /* next argument to be checked */
4597 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004598 int *conceal_char UNUSED,
4599 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601 char_u *gname_start, *gname;
4602 int syn_id;
4603 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004604 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 int i;
4606 int fidx;
4607 static struct flag
4608 {
4609 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 int argtype;
4611 int flags;
4612 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4613 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4614 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4615 {"eExXtTeEnNdD", 0, HL_EXTEND},
4616 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4617 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4618 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4619 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4620 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4621 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4622 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4623 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4624 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004625 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4626 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4627 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 {"cCoOnNtTaAiInNsS", 1, 0},
4629 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4630 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004632 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633
4634 if (arg == NULL) /* already detected error */
4635 return NULL;
4636
Bram Moolenaar860cae12010-06-05 23:22:07 +02004637#ifdef FEAT_CONCEAL
4638 if (curwin->w_s->b_syn_conceal)
4639 opt->flags |= HL_CONCEAL;
4640#endif
4641
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642 for (;;)
4643 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004644 /*
4645 * This is used very often when a large number of keywords is defined.
4646 * Need to skip quickly when no option name is found.
4647 * Also avoid tolower(), it's slow.
4648 */
4649 if (strchr(first_letters, *arg) == NULL)
4650 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651
4652 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4653 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004654 p = flagtab[fidx].name;
4655 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4656 if (arg[len] != p[i] && arg[len] != p[i + 1])
4657 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004658 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004659 || (flagtab[fidx].argtype > 0
4660 ? arg[len] == '='
4661 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004663 if (opt->keyword
4664 && (flagtab[fidx].flags == HL_DISPLAY
4665 || flagtab[fidx].flags == HL_FOLD
4666 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 /* treat "display", "fold" and "extend" as a keyword */
4668 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 break;
4670 }
4671 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 if (fidx < 0) /* no match found */
4673 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004675 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004677 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 {
4679 EMSG(_("E395: contains argument not accepted here"));
4680 return NULL;
4681 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004682 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 return NULL;
4684 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004685 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004687 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 return NULL;
4689 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004690 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004692 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 return NULL;
4694 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004695 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4696 {
4697#ifdef FEAT_MBYTE
4698 /* cchar=? */
4699 if (has_mbyte)
4700 {
4701# ifdef FEAT_CONCEAL
4702 *conceal_char = mb_ptr2char(arg + 6);
4703# endif
4704 arg += mb_ptr2len(arg + 6) - 1;
4705 }
4706 else
4707#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004708 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004709#ifdef FEAT_CONCEAL
4710 *conceal_char = arg[6];
4711#else
4712 ;
4713#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004714 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004715#ifdef FEAT_CONCEAL
4716 if (!vim_isprintc_strict(*conceal_char))
4717 {
4718 EMSG(_("E844: invalid cchar value"));
4719 return NULL;
4720 }
4721#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004722 arg = skipwhite(arg + 7);
4723 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004725 {
4726 opt->flags |= flagtab[fidx].flags;
4727 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004729 if (flagtab[fidx].flags == HL_SYNC_HERE
4730 || flagtab[fidx].flags == HL_SYNC_THERE)
4731 {
4732 if (opt->sync_idx == NULL)
4733 {
4734 EMSG(_("E393: group[t]here not accepted here"));
4735 return NULL;
4736 }
4737 gname_start = arg;
4738 arg = skiptowhite(arg);
4739 if (gname_start == arg)
4740 return NULL;
4741 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4742 if (gname == NULL)
4743 return NULL;
4744 if (STRCMP(gname, "NONE") == 0)
4745 *opt->sync_idx = NONE_IDX;
4746 else
4747 {
4748 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004749 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4750 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4751 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004752 {
4753 *opt->sync_idx = i;
4754 break;
4755 }
4756 if (i < 0)
4757 {
4758 EMSG2(_("E394: Didn't find region item for %s"), gname);
4759 vim_free(gname);
4760 return NULL;
4761 }
4762 }
4763
4764 vim_free(gname);
4765 arg = skipwhite(arg);
4766 }
4767#ifdef FEAT_FOLDING
4768 else if (flagtab[fidx].flags == HL_FOLD
4769 && foldmethodIsSyntax(curwin))
4770 /* Need to update folds later. */
4771 foldUpdateAll(curwin);
4772#endif
4773 }
4774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775
4776 return arg;
4777}
4778
4779/*
4780 * Adjustments to syntax item when declared in a ":syn include"'d file.
4781 * Set the contained flag, and if the item is not already contained, add it
4782 * to the specified top-level group, if any.
4783 */
4784 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004785syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004787 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 return;
4789 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004790 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791 {
4792 /* We have to alloc this, because syn_combine_list() will free it. */
4793 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004794 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795
4796 if (grp_list != NULL)
4797 {
4798 grp_list[0] = id;
4799 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004800 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004801 CLUSTER_ADD);
4802 }
4803 }
4804}
4805
4806/*
4807 * Handle ":syntax include [@{group-name}] filename" command.
4808 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004810syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811{
4812 char_u *arg = eap->arg;
4813 int sgl_id = 1;
4814 char_u *group_name_end;
4815 char_u *rest;
4816 char_u *errormsg = NULL;
4817 int prev_toplvl_grp;
4818 int prev_syn_inc_tag;
4819 int source = FALSE;
4820
4821 eap->nextcmd = find_nextcmd(arg);
4822 if (eap->skip)
4823 return;
4824
4825 if (arg[0] == '@')
4826 {
4827 ++arg;
4828 rest = get_group_name(arg, &group_name_end);
4829 if (rest == NULL)
4830 {
4831 EMSG((char_u *)_("E397: Filename required"));
4832 return;
4833 }
4834 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004835 if (sgl_id == 0)
4836 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837 /* separate_nextcmd() and expand_filename() depend on this */
4838 eap->arg = rest;
4839 }
4840
4841 /*
4842 * Everything that's left, up to the next command, should be the
4843 * filename to include.
4844 */
4845 eap->argt |= (XFILE | NOSPC);
4846 separate_nextcmd(eap);
4847 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4848 {
4849 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4850 * file. Need to expand the file name first. In other cases
4851 * ":runtime!" is used. */
4852 source = TRUE;
4853 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4854 {
4855 if (errormsg != NULL)
4856 EMSG(errormsg);
4857 return;
4858 }
4859 }
4860
4861 /*
4862 * Save and restore the existing top-level grouplist id and ":syn
4863 * include" tag around the actual inclusion.
4864 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004865 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4866 {
4867 EMSG((char_u *)_("E847: Too many syntax includes"));
4868 return;
4869 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 prev_syn_inc_tag = current_syn_inc_tag;
4871 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004872 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4873 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004874 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004875 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004877 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 current_syn_inc_tag = prev_syn_inc_tag;
4879}
4880
4881/*
4882 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4883 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004885syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886{
4887 char_u *arg = eap->arg;
4888 char_u *group_name_end;
4889 int syn_id;
4890 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004891 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004893 char_u *kw;
4894 syn_opt_arg_T syn_opt_arg;
4895 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004896 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897
4898 rest = get_group_name(arg, &group_name_end);
4899
4900 if (rest != NULL)
4901 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004902 if (eap->skip)
4903 syn_id = -1;
4904 else
4905 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004906 if (syn_id != 0)
4907 /* allocate a buffer, for removing backslashes in the keyword */
4908 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 if (keyword_copy != NULL)
4910 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004911 syn_opt_arg.flags = 0;
4912 syn_opt_arg.keyword = TRUE;
4913 syn_opt_arg.sync_idx = NULL;
4914 syn_opt_arg.has_cont_list = FALSE;
4915 syn_opt_arg.cont_in_list = NULL;
4916 syn_opt_arg.next_list = NULL;
4917
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 /*
4919 * The options given apply to ALL keywords, so all options must be
4920 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004921 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004922 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004923 cnt = 0;
4924 p = keyword_copy;
4925 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004927 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4928 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004929 if (rest == NULL || ends_excmd(*rest))
4930 break;
4931 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004932 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004934 if (*rest == '\\' && rest[1] != NUL)
4935 ++rest;
4936 *p++ = *rest++;
4937 }
4938 *p++ = NUL;
4939 ++cnt;
4940 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004942 if (!eap->skip)
4943 {
4944 /* Adjust flags for use of ":syn include". */
4945 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4946
4947 /*
4948 * 2: Add an entry for each keyword.
4949 */
4950 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4951 {
4952 for (p = vim_strchr(kw, '['); ; )
4953 {
4954 if (p != NULL)
4955 *p = NUL;
4956 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004957 syn_opt_arg.cont_in_list,
4958 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004959 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004960 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004961 if (p[1] == NUL)
4962 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004963 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004964 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004965 }
4966 if (p[1] == ']')
4967 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004968 if (p[2] != NUL)
4969 {
4970 EMSG3(_("E890: trailing char after ']': %s]%s"),
4971 kw, &p[2]);
4972 goto error;
4973 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004974 kw = p + 1; /* skip over the "]" */
4975 break;
4976 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004977#ifdef FEAT_MBYTE
4978 if (has_mbyte)
4979 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004980 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004981
4982 mch_memmove(p, p + 1, l);
4983 p += l;
4984 }
4985 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004987 {
4988 p[0] = p[1];
4989 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990 }
4991 }
4992 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004994error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004996 vim_free(syn_opt_arg.cont_in_list);
4997 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
4999 }
5000
5001 if (rest != NULL)
5002 eap->nextcmd = check_nextcmd(rest);
5003 else
5004 EMSG2(_(e_invarg2), arg);
5005
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005006 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005007 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005008}
5009
5010/*
5011 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5012 *
5013 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5014 */
5015 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005016syn_cmd_match(
5017 exarg_T *eap,
5018 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019{
5020 char_u *arg = eap->arg;
5021 char_u *group_name_end;
5022 char_u *rest;
5023 synpat_T item; /* the item found in the line */
5024 int syn_id;
5025 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005026 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005028 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005029
5030 /* Isolate the group name, check for validity */
5031 rest = get_group_name(arg, &group_name_end);
5032
5033 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005034 syn_opt_arg.flags = 0;
5035 syn_opt_arg.keyword = FALSE;
5036 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5037 syn_opt_arg.has_cont_list = TRUE;
5038 syn_opt_arg.cont_list = NULL;
5039 syn_opt_arg.cont_in_list = NULL;
5040 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005041 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005042
5043 /* get the pattern. */
5044 init_syn_patterns();
5045 vim_memset(&item, 0, sizeof(item));
5046 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005047 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5048 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049
5050 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005051 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052
5053 if (rest != NULL) /* all arguments are valid */
5054 {
5055 /*
5056 * Check for trailing command and illegal trailing arguments.
5057 */
5058 eap->nextcmd = check_nextcmd(rest);
5059 if (!ends_excmd(*rest) || eap->skip)
5060 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005061 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005062 && (syn_id = syn_check_group(arg,
5063 (int)(group_name_end - arg))) != 0)
5064 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005065 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066 /*
5067 * Store the pattern in the syn_items list
5068 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005069 idx = curwin->w_s->b_syn_patterns.ga_len;
5070 SYN_ITEMS(curwin->w_s)[idx] = item;
5071 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5072 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5073 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5074 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5075 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5076 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5077 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5078 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005079 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005080#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005081 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005082#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005083 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005084 curwin->w_s->b_syn_containedin = TRUE;
5085 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5086 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087
5088 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005089 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005090 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005091#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005092 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005093 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094#endif
5095
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005096 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005097 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 return; /* don't free the progs and patterns now */
5099 }
5100 }
5101
5102 /*
5103 * Something failed, free the allocated memory.
5104 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005105 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005107 vim_free(syn_opt_arg.cont_list);
5108 vim_free(syn_opt_arg.cont_in_list);
5109 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110
5111 if (rest == NULL)
5112 EMSG2(_(e_invarg2), arg);
5113}
5114
5115/*
5116 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5117 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5118 */
5119 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005120syn_cmd_region(
5121 exarg_T *eap,
5122 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005123{
5124 char_u *arg = eap->arg;
5125 char_u *group_name_end;
5126 char_u *rest; /* next arg, NULL on error */
5127 char_u *key_end;
5128 char_u *key = NULL;
5129 char_u *p;
5130 int item;
5131#define ITEM_START 0
5132#define ITEM_SKIP 1
5133#define ITEM_END 2
5134#define ITEM_MATCHGROUP 3
5135 struct pat_ptr
5136 {
5137 synpat_T *pp_synp; /* pointer to syn_pattern */
5138 int pp_matchgroup_id; /* matchgroup ID */
5139 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5140 } *(pat_ptrs[3]);
5141 /* patterns found in the line */
5142 struct pat_ptr *ppp;
5143 struct pat_ptr *ppp_next;
5144 int pat_count = 0; /* nr of syn_patterns found */
5145 int syn_id;
5146 int matchgroup_id = 0;
5147 int not_enough = FALSE; /* not enough arguments */
5148 int illegal = FALSE; /* illegal arguments */
5149 int success = FALSE;
5150 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005151 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005152 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153
5154 /* Isolate the group name, check for validity */
5155 rest = get_group_name(arg, &group_name_end);
5156
5157 pat_ptrs[0] = NULL;
5158 pat_ptrs[1] = NULL;
5159 pat_ptrs[2] = NULL;
5160
5161 init_syn_patterns();
5162
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005163 syn_opt_arg.flags = 0;
5164 syn_opt_arg.keyword = FALSE;
5165 syn_opt_arg.sync_idx = NULL;
5166 syn_opt_arg.has_cont_list = TRUE;
5167 syn_opt_arg.cont_list = NULL;
5168 syn_opt_arg.cont_in_list = NULL;
5169 syn_opt_arg.next_list = NULL;
5170
Bram Moolenaar071d4272004-06-13 20:20:40 +00005171 /*
5172 * get the options, patterns and matchgroup.
5173 */
5174 while (rest != NULL && !ends_excmd(*rest))
5175 {
5176 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005177 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178 if (rest == NULL || ends_excmd(*rest))
5179 break;
5180
5181 /* must be a pattern or matchgroup then */
5182 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005183 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184 ++key_end;
5185 vim_free(key);
5186 key = vim_strnsave_up(rest, (int)(key_end - rest));
5187 if (key == NULL) /* out of memory */
5188 {
5189 rest = NULL;
5190 break;
5191 }
5192 if (STRCMP(key, "MATCHGROUP") == 0)
5193 item = ITEM_MATCHGROUP;
5194 else if (STRCMP(key, "START") == 0)
5195 item = ITEM_START;
5196 else if (STRCMP(key, "END") == 0)
5197 item = ITEM_END;
5198 else if (STRCMP(key, "SKIP") == 0)
5199 {
5200 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5201 {
5202 illegal = TRUE;
5203 break;
5204 }
5205 item = ITEM_SKIP;
5206 }
5207 else
5208 break;
5209 rest = skipwhite(key_end);
5210 if (*rest != '=')
5211 {
5212 rest = NULL;
5213 EMSG2(_("E398: Missing '=': %s"), arg);
5214 break;
5215 }
5216 rest = skipwhite(rest + 1);
5217 if (*rest == NUL)
5218 {
5219 not_enough = TRUE;
5220 break;
5221 }
5222
5223 if (item == ITEM_MATCHGROUP)
5224 {
5225 p = skiptowhite(rest);
5226 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5227 matchgroup_id = 0;
5228 else
5229 {
5230 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5231 if (matchgroup_id == 0)
5232 {
5233 illegal = TRUE;
5234 break;
5235 }
5236 }
5237 rest = skipwhite(p);
5238 }
5239 else
5240 {
5241 /*
5242 * Allocate room for a syn_pattern, and link it in the list of
5243 * syn_patterns for this item, at the start (because the list is
5244 * used from end to start).
5245 */
5246 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5247 if (ppp == NULL)
5248 {
5249 rest = NULL;
5250 break;
5251 }
5252 ppp->pp_next = pat_ptrs[item];
5253 pat_ptrs[item] = ppp;
5254 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5255 if (ppp->pp_synp == NULL)
5256 {
5257 rest = NULL;
5258 break;
5259 }
5260
5261 /*
5262 * Get the syntax pattern and the following offset(s).
5263 */
5264 /* Enable the appropriate \z specials. */
5265 if (item == ITEM_START)
5266 reg_do_extmatch = REX_SET;
5267 else if (item == ITEM_SKIP || item == ITEM_END)
5268 reg_do_extmatch = REX_USE;
5269 rest = get_syn_pattern(rest, ppp->pp_synp);
5270 reg_do_extmatch = 0;
5271 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005272 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005273 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5274 ppp->pp_matchgroup_id = matchgroup_id;
5275 ++pat_count;
5276 }
5277 }
5278 vim_free(key);
5279 if (illegal || not_enough)
5280 rest = NULL;
5281
5282 /*
5283 * Must have a "start" and "end" pattern.
5284 */
5285 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5286 pat_ptrs[ITEM_END] == NULL))
5287 {
5288 not_enough = TRUE;
5289 rest = NULL;
5290 }
5291
5292 if (rest != NULL)
5293 {
5294 /*
5295 * Check for trailing garbage or command.
5296 * If OK, add the item.
5297 */
5298 eap->nextcmd = check_nextcmd(rest);
5299 if (!ends_excmd(*rest) || eap->skip)
5300 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005301 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 && (syn_id = syn_check_group(arg,
5303 (int)(group_name_end - arg))) != 0)
5304 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005305 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306 /*
5307 * Store the start/skip/end in the syn_items list
5308 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310 for (item = ITEM_START; item <= ITEM_END; ++item)
5311 {
5312 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5313 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005314 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5315 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5316 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005317 (item == ITEM_START) ? SPTYPE_START :
5318 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005319 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5320 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005321 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5322 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005323 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005324 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005325#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005326 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005327#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005328 if (item == ITEM_START)
5329 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005330 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005331 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005332 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005333 syn_opt_arg.cont_in_list;
5334 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005335 curwin->w_s->b_syn_containedin = TRUE;
5336 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005337 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005338 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005339 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340 ++idx;
5341#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005342 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005343 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005344#endif
5345 }
5346 }
5347
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005348 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005349 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005350 success = TRUE; /* don't free the progs and patterns now */
5351 }
5352 }
5353
5354 /*
5355 * Free the allocated memory.
5356 */
5357 for (item = ITEM_START; item <= ITEM_END; ++item)
5358 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5359 {
5360 if (!success)
5361 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005362 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005363 vim_free(ppp->pp_synp->sp_pattern);
5364 }
5365 vim_free(ppp->pp_synp);
5366 ppp_next = ppp->pp_next;
5367 vim_free(ppp);
5368 }
5369
5370 if (!success)
5371 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005372 vim_free(syn_opt_arg.cont_list);
5373 vim_free(syn_opt_arg.cont_in_list);
5374 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005375 if (not_enough)
5376 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5377 else if (illegal || rest == NULL)
5378 EMSG2(_(e_invarg2), arg);
5379 }
5380}
5381
5382/*
5383 * A simple syntax group ID comparison function suitable for use in qsort()
5384 */
5385 static int
5386#ifdef __BORLANDC__
5387_RTLENTRYF
5388#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005389syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005390{
5391 const short *s1 = v1;
5392 const short *s2 = v2;
5393
5394 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5395}
5396
5397/*
5398 * Combines lists of syntax clusters.
5399 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5400 */
5401 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005402syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403{
5404 int count1 = 0;
5405 int count2 = 0;
5406 short *g1;
5407 short *g2;
5408 short *clstr = NULL;
5409 int count;
5410 int round;
5411
5412 /*
5413 * Handle degenerate cases.
5414 */
5415 if (*clstr2 == NULL)
5416 return;
5417 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5418 {
5419 if (list_op == CLUSTER_REPLACE)
5420 vim_free(*clstr1);
5421 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5422 *clstr1 = *clstr2;
5423 else
5424 vim_free(*clstr2);
5425 return;
5426 }
5427
5428 for (g1 = *clstr1; *g1; g1++)
5429 ++count1;
5430 for (g2 = *clstr2; *g2; g2++)
5431 ++count2;
5432
5433 /*
5434 * For speed purposes, sort both lists.
5435 */
5436 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5437 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5438
5439 /*
5440 * We proceed in two passes; in round 1, we count the elements to place
5441 * in the new list, and in round 2, we allocate and populate the new
5442 * list. For speed, we use a mergesort-like method, adding the smaller
5443 * of the current elements in each list to the new list.
5444 */
5445 for (round = 1; round <= 2; round++)
5446 {
5447 g1 = *clstr1;
5448 g2 = *clstr2;
5449 count = 0;
5450
5451 /*
5452 * First, loop through the lists until one of them is empty.
5453 */
5454 while (*g1 && *g2)
5455 {
5456 /*
5457 * We always want to add from the first list.
5458 */
5459 if (*g1 < *g2)
5460 {
5461 if (round == 2)
5462 clstr[count] = *g1;
5463 count++;
5464 g1++;
5465 continue;
5466 }
5467 /*
5468 * We only want to add from the second list if we're adding the
5469 * lists.
5470 */
5471 if (list_op == CLUSTER_ADD)
5472 {
5473 if (round == 2)
5474 clstr[count] = *g2;
5475 count++;
5476 }
5477 if (*g1 == *g2)
5478 g1++;
5479 g2++;
5480 }
5481
5482 /*
5483 * Now add the leftovers from whichever list didn't get finished
5484 * first. As before, we only want to add from the second list if
5485 * we're adding the lists.
5486 */
5487 for (; *g1; g1++, count++)
5488 if (round == 2)
5489 clstr[count] = *g1;
5490 if (list_op == CLUSTER_ADD)
5491 for (; *g2; g2++, count++)
5492 if (round == 2)
5493 clstr[count] = *g2;
5494
5495 if (round == 1)
5496 {
5497 /*
5498 * If the group ended up empty, we don't need to allocate any
5499 * space for it.
5500 */
5501 if (count == 0)
5502 {
5503 clstr = NULL;
5504 break;
5505 }
5506 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5507 if (clstr == NULL)
5508 break;
5509 clstr[count] = 0;
5510 }
5511 }
5512
5513 /*
5514 * Finally, put the new list in place.
5515 */
5516 vim_free(*clstr1);
5517 vim_free(*clstr2);
5518 *clstr1 = clstr;
5519}
5520
5521/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005522 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005523 * If it is not found, 0 is returned.
5524 */
5525 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005526syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527{
5528 int i;
5529 char_u *name_u;
5530
5531 /* Avoid using stricmp() too much, it's slow on some systems */
5532 name_u = vim_strsave_up(name);
5533 if (name_u == NULL)
5534 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005535 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5536 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5537 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538 break;
5539 vim_free(name_u);
5540 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5541}
5542
5543/*
5544 * Like syn_scl_name2id(), but take a pointer + length argument.
5545 */
5546 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005547syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548{
5549 char_u *name;
5550 int id = 0;
5551
5552 name = vim_strnsave(linep, len);
5553 if (name != NULL)
5554 {
5555 id = syn_scl_name2id(name);
5556 vim_free(name);
5557 }
5558 return id;
5559}
5560
5561/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005562 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 * The argument is a pointer to the name and the length of the name.
5564 * If it doesn't exist yet, a new entry is created.
5565 * Return 0 for failure.
5566 */
5567 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005568syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569{
5570 int id;
5571 char_u *name;
5572
5573 name = vim_strnsave(pp, len);
5574 if (name == NULL)
5575 return 0;
5576
5577 id = syn_scl_name2id(name);
5578 if (id == 0) /* doesn't exist yet */
5579 id = syn_add_cluster(name);
5580 else
5581 vim_free(name);
5582 return id;
5583}
5584
5585/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005586 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587 * "name" must be an allocated string, it will be consumed.
5588 * Return 0 for failure.
5589 */
5590 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005591syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005593 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594
5595 /*
5596 * First call for this growarray: init growing array.
5597 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005598 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005600 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5601 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602 }
5603
Bram Moolenaar42431a72011-04-01 14:44:59 +02005604 len = curwin->w_s->b_syn_clusters.ga_len;
5605 if (len >= MAX_CLUSTER_ID)
5606 {
5607 EMSG((char_u *)_("E848: Too many syntax clusters"));
5608 vim_free(name);
5609 return 0;
5610 }
5611
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612 /*
5613 * Make room for at least one other cluster entry.
5614 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005615 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616 {
5617 vim_free(name);
5618 return 0;
5619 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620
Bram Moolenaar860cae12010-06-05 23:22:07 +02005621 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5622 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5623 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5624 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5625 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626
Bram Moolenaar217ad922005-03-20 22:37:15 +00005627 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005628 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005629 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005630 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005631
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632 return len + SYNID_CLUSTER;
5633}
5634
5635/*
5636 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5637 * [add={groupname},..] [remove={groupname},..]".
5638 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005640syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641{
5642 char_u *arg = eap->arg;
5643 char_u *group_name_end;
5644 char_u *rest;
5645 int scl_id;
5646 short *clstr_list;
5647 int got_clstr = FALSE;
5648 int opt_len;
5649 int list_op;
5650
5651 eap->nextcmd = find_nextcmd(arg);
5652 if (eap->skip)
5653 return;
5654
5655 rest = get_group_name(arg, &group_name_end);
5656
5657 if (rest != NULL)
5658 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005659 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5660 if (scl_id == 0)
5661 return;
5662 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663
5664 for (;;)
5665 {
5666 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005667 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 {
5669 opt_len = 3;
5670 list_op = CLUSTER_ADD;
5671 }
5672 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005673 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005674 {
5675 opt_len = 6;
5676 list_op = CLUSTER_SUBTRACT;
5677 }
5678 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005679 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 {
5681 opt_len = 8;
5682 list_op = CLUSTER_REPLACE;
5683 }
5684 else
5685 break;
5686
5687 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005688 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689 {
5690 EMSG2(_(e_invarg2), rest);
5691 break;
5692 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005693 if (scl_id >= 0)
5694 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005696 else
5697 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698 got_clstr = TRUE;
5699 }
5700
5701 if (got_clstr)
5702 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005703 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005704 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705 }
5706 }
5707
5708 if (!got_clstr)
5709 EMSG(_("E400: No cluster specified"));
5710 if (rest == NULL || !ends_excmd(*rest))
5711 EMSG2(_(e_invarg2), arg);
5712}
5713
5714/*
5715 * On first call for current buffer: Init growing array.
5716 */
5717 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005718init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005719{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005720 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5721 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722}
5723
5724/*
5725 * Get one pattern for a ":syntax match" or ":syntax region" command.
5726 * Stores the pattern and program in a synpat_T.
5727 * Returns a pointer to the next argument, or NULL in case of an error.
5728 */
5729 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005730get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731{
5732 char_u *end;
5733 int *p;
5734 int idx;
5735 char_u *cpo_save;
5736
5737 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005738 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 return NULL;
5740
5741 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5742 if (*end != *arg) /* end delimiter not found */
5743 {
5744 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5745 return NULL;
5746 }
5747 /* store the pattern and compiled regexp program */
5748 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5749 return NULL;
5750
5751 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5752 cpo_save = p_cpo;
5753 p_cpo = (char_u *)"";
5754 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5755 p_cpo = cpo_save;
5756
5757 if (ci->sp_prog == NULL)
5758 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005759 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005760#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005761 syn_clear_time(&ci->sp_time);
5762#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005763
5764 /*
5765 * Check for a match, highlight or region offset.
5766 */
5767 ++end;
5768 do
5769 {
5770 for (idx = SPO_COUNT; --idx >= 0; )
5771 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5772 break;
5773 if (idx >= 0)
5774 {
5775 p = &(ci->sp_offsets[idx]);
5776 if (idx != SPO_LC_OFF)
5777 switch (end[3])
5778 {
5779 case 's': break;
5780 case 'b': break;
5781 case 'e': idx += SPO_COUNT; break;
5782 default: idx = -1; break;
5783 }
5784 if (idx >= 0)
5785 {
5786 ci->sp_off_flags |= (1 << idx);
5787 if (idx == SPO_LC_OFF) /* lc=99 */
5788 {
5789 end += 3;
5790 *p = getdigits(&end);
5791
5792 /* "lc=" offset automatically sets "ms=" offset */
5793 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5794 {
5795 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5796 ci->sp_offsets[SPO_MS_OFF] = *p;
5797 }
5798 }
5799 else /* yy=x+99 */
5800 {
5801 end += 4;
5802 if (*end == '+')
5803 {
5804 ++end;
5805 *p = getdigits(&end); /* positive offset */
5806 }
5807 else if (*end == '-')
5808 {
5809 ++end;
5810 *p = -getdigits(&end); /* negative offset */
5811 }
5812 }
5813 if (*end != ',')
5814 break;
5815 ++end;
5816 }
5817 }
5818 } while (idx >= 0);
5819
Bram Moolenaar1c465442017-03-12 20:10:05 +01005820 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 {
5822 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5823 return NULL;
5824 }
5825 return skipwhite(end);
5826}
5827
5828/*
5829 * Handle ":syntax sync .." command.
5830 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005832syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833{
5834 char_u *arg_start = eap->arg;
5835 char_u *arg_end;
5836 char_u *key = NULL;
5837 char_u *next_arg;
5838 int illegal = FALSE;
5839 int finished = FALSE;
5840 long n;
5841 char_u *cpo_save;
5842
5843 if (ends_excmd(*arg_start))
5844 {
5845 syn_cmd_list(eap, TRUE);
5846 return;
5847 }
5848
5849 while (!ends_excmd(*arg_start))
5850 {
5851 arg_end = skiptowhite(arg_start);
5852 next_arg = skipwhite(arg_end);
5853 vim_free(key);
5854 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5855 if (STRCMP(key, "CCOMMENT") == 0)
5856 {
5857 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005858 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005859 if (!ends_excmd(*next_arg))
5860 {
5861 arg_end = skiptowhite(next_arg);
5862 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864 (int)(arg_end - next_arg));
5865 next_arg = skipwhite(arg_end);
5866 }
5867 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 }
5870 else if ( STRNCMP(key, "LINES", 5) == 0
5871 || STRNCMP(key, "MINLINES", 8) == 0
5872 || STRNCMP(key, "MAXLINES", 8) == 0
5873 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5874 {
5875 if (key[4] == 'S')
5876 arg_end = key + 6;
5877 else if (key[0] == 'L')
5878 arg_end = key + 11;
5879 else
5880 arg_end = key + 9;
5881 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5882 {
5883 illegal = TRUE;
5884 break;
5885 }
5886 n = getdigits(&arg_end);
5887 if (!eap->skip)
5888 {
5889 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005890 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005891 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005892 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005893 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005894 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005895 }
5896 }
5897 else if (STRCMP(key, "FROMSTART") == 0)
5898 {
5899 if (!eap->skip)
5900 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005901 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5902 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903 }
5904 }
5905 else if (STRCMP(key, "LINECONT") == 0)
5906 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005907 if (*next_arg == NUL) /* missing pattern */
5908 {
5909 illegal = TRUE;
5910 break;
5911 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005912 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 {
5914 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5915 finished = TRUE;
5916 break;
5917 }
5918 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5919 if (*arg_end != *next_arg) /* end delimiter not found */
5920 {
5921 illegal = TRUE;
5922 break;
5923 }
5924
5925 if (!eap->skip)
5926 {
5927 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005928 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005929 (int)(arg_end - next_arg - 1))) == NULL)
5930 {
5931 finished = TRUE;
5932 break;
5933 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005934 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005935
5936 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5937 cpo_save = p_cpo;
5938 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005939 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005940 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005941 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005942#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005943 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5944#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005945
Bram Moolenaar860cae12010-06-05 23:22:07 +02005946 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005947 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005948 vim_free(curwin->w_s->b_syn_linecont_pat);
5949 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 finished = TRUE;
5951 break;
5952 }
5953 }
5954 next_arg = skipwhite(arg_end + 1);
5955 }
5956 else
5957 {
5958 eap->arg = next_arg;
5959 if (STRCMP(key, "MATCH") == 0)
5960 syn_cmd_match(eap, TRUE);
5961 else if (STRCMP(key, "REGION") == 0)
5962 syn_cmd_region(eap, TRUE);
5963 else if (STRCMP(key, "CLEAR") == 0)
5964 syn_cmd_clear(eap, TRUE);
5965 else
5966 illegal = TRUE;
5967 finished = TRUE;
5968 break;
5969 }
5970 arg_start = next_arg;
5971 }
5972 vim_free(key);
5973 if (illegal)
5974 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5975 else if (!finished)
5976 {
5977 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005978 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005979 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005980 }
5981}
5982
5983/*
5984 * Convert a line of highlight group names into a list of group ID numbers.
5985 * "arg" should point to the "contains" or "nextgroup" keyword.
5986 * "arg" is advanced to after the last group name.
5987 * Careful: the argument is modified (NULs added).
5988 * returns FAIL for some error, OK for success.
5989 */
5990 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005991get_id_list(
5992 char_u **arg,
5993 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005994 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005995 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005996 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005997{
5998 char_u *p = NULL;
5999 char_u *end;
6000 int round;
6001 int count;
6002 int total_count = 0;
6003 short *retval = NULL;
6004 char_u *name;
6005 regmatch_T regmatch;
6006 int id;
6007 int i;
6008 int failed = FALSE;
6009
6010 /*
6011 * We parse the list twice:
6012 * round == 1: count the number of items, allocate the array.
6013 * round == 2: fill the array with the items.
6014 * In round 1 new groups may be added, causing the number of items to
6015 * grow when a regexp is used. In that case round 1 is done once again.
6016 */
6017 for (round = 1; round <= 2; ++round)
6018 {
6019 /*
6020 * skip "contains"
6021 */
6022 p = skipwhite(*arg + keylen);
6023 if (*p != '=')
6024 {
6025 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6026 break;
6027 }
6028 p = skipwhite(p + 1);
6029 if (ends_excmd(*p))
6030 {
6031 EMSG2(_("E406: Empty argument: %s"), *arg);
6032 break;
6033 }
6034
6035 /*
6036 * parse the arguments after "contains"
6037 */
6038 count = 0;
6039 while (!ends_excmd(*p))
6040 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006041 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 ;
6043 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6044 if (name == NULL)
6045 {
6046 failed = TRUE;
6047 break;
6048 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006049 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006050 if ( STRCMP(name + 1, "ALLBUT") == 0
6051 || STRCMP(name + 1, "ALL") == 0
6052 || STRCMP(name + 1, "TOP") == 0
6053 || STRCMP(name + 1, "CONTAINED") == 0)
6054 {
6055 if (TOUPPER_ASC(**arg) != 'C')
6056 {
6057 EMSG2(_("E407: %s not allowed here"), name + 1);
6058 failed = TRUE;
6059 vim_free(name);
6060 break;
6061 }
6062 if (count != 0)
6063 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006064 EMSG2(_("E408: %s must be first in contains list"),
6065 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006066 failed = TRUE;
6067 vim_free(name);
6068 break;
6069 }
6070 if (name[1] == 'A')
6071 id = SYNID_ALLBUT;
6072 else if (name[1] == 'T')
6073 id = SYNID_TOP;
6074 else
6075 id = SYNID_CONTAINED;
6076 id += current_syn_inc_tag;
6077 }
6078 else if (name[1] == '@')
6079 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006080 if (skip)
6081 id = -1;
6082 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006083 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006084 }
6085 else
6086 {
6087 /*
6088 * Handle full group name.
6089 */
6090 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6091 id = syn_check_group(name + 1, (int)(end - p));
6092 else
6093 {
6094 /*
6095 * Handle match of regexp with group names.
6096 */
6097 *name = '^';
6098 STRCAT(name, "$");
6099 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6100 if (regmatch.regprog == NULL)
6101 {
6102 failed = TRUE;
6103 vim_free(name);
6104 break;
6105 }
6106
6107 regmatch.rm_ic = TRUE;
6108 id = 0;
6109 for (i = highlight_ga.ga_len; --i >= 0; )
6110 {
6111 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6112 (colnr_T)0))
6113 {
6114 if (round == 2)
6115 {
6116 /* Got more items than expected; can happen
6117 * when adding items that match:
6118 * "contains=a.*b,axb".
6119 * Go back to first round */
6120 if (count >= total_count)
6121 {
6122 vim_free(retval);
6123 round = 1;
6124 }
6125 else
6126 retval[count] = i + 1;
6127 }
6128 ++count;
6129 id = -1; /* remember that we found one */
6130 }
6131 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006132 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006133 }
6134 }
6135 vim_free(name);
6136 if (id == 0)
6137 {
6138 EMSG2(_("E409: Unknown group name: %s"), p);
6139 failed = TRUE;
6140 break;
6141 }
6142 if (id > 0)
6143 {
6144 if (round == 2)
6145 {
6146 /* Got more items than expected, go back to first round */
6147 if (count >= total_count)
6148 {
6149 vim_free(retval);
6150 round = 1;
6151 }
6152 else
6153 retval[count] = id;
6154 }
6155 ++count;
6156 }
6157 p = skipwhite(end);
6158 if (*p != ',')
6159 break;
6160 p = skipwhite(p + 1); /* skip comma in between arguments */
6161 }
6162 if (failed)
6163 break;
6164 if (round == 1)
6165 {
6166 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6167 if (retval == NULL)
6168 break;
6169 retval[count] = 0; /* zero means end of the list */
6170 total_count = count;
6171 }
6172 }
6173
6174 *arg = p;
6175 if (failed || retval == NULL)
6176 {
6177 vim_free(retval);
6178 return FAIL;
6179 }
6180
6181 if (*list == NULL)
6182 *list = retval;
6183 else
6184 vim_free(retval); /* list already found, don't overwrite it */
6185
6186 return OK;
6187}
6188
6189/*
6190 * Make a copy of an ID list.
6191 */
6192 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006193copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006194{
6195 int len;
6196 int count;
6197 short *retval;
6198
6199 if (list == NULL)
6200 return NULL;
6201
6202 for (count = 0; list[count]; ++count)
6203 ;
6204 len = (count + 1) * sizeof(short);
6205 retval = (short *)alloc((unsigned)len);
6206 if (retval != NULL)
6207 mch_memmove(retval, list, (size_t)len);
6208
6209 return retval;
6210}
6211
6212/*
6213 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6214 * "cur_si" can be NULL if not checking the "containedin" list.
6215 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6216 * the current item.
6217 * This function is called very often, keep it fast!!
6218 */
6219 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006220in_id_list(
6221 stateitem_T *cur_si, /* current item or NULL */
6222 short *list, /* id list */
6223 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6224 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225{
6226 int retval;
6227 short *scl_list;
6228 short item;
6229 short id = ssp->id;
6230 static int depth = 0;
6231 int r;
6232
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006233 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006234 if (cur_si != NULL && ssp->cont_in_list != NULL
6235 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006236 {
6237 /* Ignore transparent items without a contains argument. Double check
6238 * that we don't go back past the first one. */
6239 while ((cur_si->si_flags & HL_TRANS_CONT)
6240 && cur_si > (stateitem_T *)(current_state.ga_data))
6241 --cur_si;
6242 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6243 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006244 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6245 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006246 return TRUE;
6247 }
6248
6249 if (list == NULL)
6250 return FALSE;
6251
6252 /*
6253 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6254 * inside anything. Only allow not-contained groups.
6255 */
6256 if (list == ID_LIST_ALL)
6257 return !contained;
6258
6259 /*
6260 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6261 * contains list. We also require that "id" is at the same ":syn include"
6262 * level as the list.
6263 */
6264 item = *list;
6265 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6266 {
6267 if (item < SYNID_TOP)
6268 {
6269 /* ALL or ALLBUT: accept all groups in the same file */
6270 if (item - SYNID_ALLBUT != ssp->inc_tag)
6271 return FALSE;
6272 }
6273 else if (item < SYNID_CONTAINED)
6274 {
6275 /* TOP: accept all not-contained groups in the same file */
6276 if (item - SYNID_TOP != ssp->inc_tag || contained)
6277 return FALSE;
6278 }
6279 else
6280 {
6281 /* CONTAINED: accept all contained groups in the same file */
6282 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6283 return FALSE;
6284 }
6285 item = *++list;
6286 retval = FALSE;
6287 }
6288 else
6289 retval = TRUE;
6290
6291 /*
6292 * Return "retval" if id is in the contains list.
6293 */
6294 while (item != 0)
6295 {
6296 if (item == id)
6297 return retval;
6298 if (item >= SYNID_CLUSTER)
6299 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006300 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006301 /* restrict recursiveness to 30 to avoid an endless loop for a
6302 * cluster that includes itself (indirectly) */
6303 if (scl_list != NULL && depth < 30)
6304 {
6305 ++depth;
6306 r = in_id_list(NULL, scl_list, ssp, contained);
6307 --depth;
6308 if (r)
6309 return retval;
6310 }
6311 }
6312 item = *++list;
6313 }
6314 return !retval;
6315}
6316
6317struct subcommand
6318{
Bram Moolenaard99df422016-01-29 23:20:40 +01006319 char *name; /* subcommand name */
6320 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006321};
6322
6323static struct subcommand subcommands[] =
6324{
6325 {"case", syn_cmd_case},
6326 {"clear", syn_cmd_clear},
6327 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006328 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006329 {"enable", syn_cmd_enable},
6330 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006331 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332 {"keyword", syn_cmd_keyword},
6333 {"list", syn_cmd_list},
6334 {"manual", syn_cmd_manual},
6335 {"match", syn_cmd_match},
6336 {"on", syn_cmd_on},
6337 {"off", syn_cmd_off},
6338 {"region", syn_cmd_region},
6339 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006340 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341 {"sync", syn_cmd_sync},
6342 {"", syn_cmd_list},
6343 {NULL, NULL}
6344};
6345
6346/*
6347 * ":syntax".
6348 * This searches the subcommands[] table for the subcommand name, and calls a
6349 * syntax_subcommand() function to do the rest.
6350 */
6351 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006352ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353{
6354 char_u *arg = eap->arg;
6355 char_u *subcmd_end;
6356 char_u *subcmd_name;
6357 int i;
6358
6359 syn_cmdlinep = eap->cmdlinep;
6360
6361 /* isolate subcommand name */
6362 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6363 ;
6364 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6365 if (subcmd_name != NULL)
6366 {
6367 if (eap->skip) /* skip error messages for all subcommands */
6368 ++emsg_skip;
6369 for (i = 0; ; ++i)
6370 {
6371 if (subcommands[i].name == NULL)
6372 {
6373 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6374 break;
6375 }
6376 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6377 {
6378 eap->arg = skipwhite(subcmd_end);
6379 (subcommands[i].func)(eap, FALSE);
6380 break;
6381 }
6382 }
6383 vim_free(subcmd_name);
6384 if (eap->skip)
6385 --emsg_skip;
6386 }
6387}
6388
Bram Moolenaar860cae12010-06-05 23:22:07 +02006389 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006390ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006391{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006392 char_u *old_value;
6393 char_u *new_value;
6394
Bram Moolenaar860cae12010-06-05 23:22:07 +02006395 if (curwin->w_s == &curwin->w_buffer->b_s)
6396 {
6397 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6398 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006399 hash_init(&curwin->w_s->b_keywtab);
6400 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006401#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006402 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006403 curwin->w_p_spell = FALSE; /* No spell checking */
6404 clear_string_option(&curwin->w_s->b_p_spc);
6405 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006406 clear_string_option(&curwin->w_s->b_p_spl);
6407#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006408 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006409 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006410
6411 /* save value of b:current_syntax */
6412 old_value = get_var_value((char_u *)"b:current_syntax");
6413 if (old_value != NULL)
6414 old_value = vim_strsave(old_value);
6415
Bram Moolenaard1413d92016-03-02 21:51:56 +01006416#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006417 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6418 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006419 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006420#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006421
6422 /* move value of b:current_syntax to w:current_syntax */
6423 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006424 if (new_value != NULL)
6425 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006426
6427 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006428 if (old_value == NULL)
6429 do_unlet((char_u *)"b:current_syntax", TRUE);
6430 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006431 {
6432 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6433 vim_free(old_value);
6434 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006435}
6436
6437 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006438syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006439{
6440 return (win->w_s->b_syn_patterns.ga_len != 0
6441 || win->w_s->b_syn_clusters.ga_len != 0
6442 || win->w_s->b_keywtab.ht_used > 0
6443 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006444}
6445
6446#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6447
6448static enum
6449{
6450 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006451 EXP_CASE, /* expand ":syn case" arguments */
6452 EXP_SPELL, /* expand ":syn spell" arguments */
6453 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454} expand_what;
6455
Bram Moolenaar4f688582007-07-24 12:34:30 +00006456/*
6457 * Reset include_link, include_default, include_none to 0.
6458 * Called when we are done expanding.
6459 */
6460 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006461reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006462{
6463 include_link = include_default = include_none = 0;
6464}
6465
6466/*
6467 * Handle command line completion for :match and :echohl command: Add "None"
6468 * as highlight group.
6469 */
6470 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006471set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006472{
6473 xp->xp_context = EXPAND_HIGHLIGHT;
6474 xp->xp_pattern = arg;
6475 include_none = 1;
6476}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006477
6478/*
6479 * Handle command line completion for :syntax command.
6480 */
6481 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006482set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483{
6484 char_u *p;
6485
6486 /* Default: expand subcommands */
6487 xp->xp_context = EXPAND_SYNTAX;
6488 expand_what = EXP_SUBCMD;
6489 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006490 include_link = 0;
6491 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492
6493 /* (part of) subcommand already typed */
6494 if (*arg != NUL)
6495 {
6496 p = skiptowhite(arg);
6497 if (*p != NUL) /* past first word */
6498 {
6499 xp->xp_pattern = skipwhite(p);
6500 if (*skiptowhite(xp->xp_pattern) != NUL)
6501 xp->xp_context = EXPAND_NOTHING;
6502 else if (STRNICMP(arg, "case", p - arg) == 0)
6503 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006504 else if (STRNICMP(arg, "spell", p - arg) == 0)
6505 expand_what = EXP_SPELL;
6506 else if (STRNICMP(arg, "sync", p - arg) == 0)
6507 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6509 || STRNICMP(arg, "region", p - arg) == 0
6510 || STRNICMP(arg, "match", p - arg) == 0
6511 || STRNICMP(arg, "list", p - arg) == 0)
6512 xp->xp_context = EXPAND_HIGHLIGHT;
6513 else
6514 xp->xp_context = EXPAND_NOTHING;
6515 }
6516 }
6517}
6518
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519/*
6520 * Function given to ExpandGeneric() to obtain the list syntax names for
6521 * expansion.
6522 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006524get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006525{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006526 switch (expand_what)
6527 {
6528 case EXP_SUBCMD:
6529 return (char_u *)subcommands[idx].name;
6530 case EXP_CASE:
6531 {
6532 static char *case_args[] = {"match", "ignore", NULL};
6533 return (char_u *)case_args[idx];
6534 }
6535 case EXP_SPELL:
6536 {
6537 static char *spell_args[] =
6538 {"toplevel", "notoplevel", "default", NULL};
6539 return (char_u *)spell_args[idx];
6540 }
6541 case EXP_SYNC:
6542 {
6543 static char *sync_args[] =
6544 {"ccomment", "clear", "fromstart",
6545 "linebreaks=", "linecont", "lines=", "match",
6546 "maxlines=", "minlines=", "region", NULL};
6547 return (char_u *)sync_args[idx];
6548 }
6549 }
6550 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551}
6552
6553#endif /* FEAT_CMDL_COMPL */
6554
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555/*
6556 * Function called for expression evaluation: get syntax ID at file position.
6557 */
6558 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006559syn_get_id(
6560 win_T *wp,
6561 long lnum,
6562 colnr_T col,
6563 int trans, /* remove transparency */
6564 int *spellp, /* return: can do spell checking */
6565 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006566{
6567 /* When the position is not after the current position and in the same
6568 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006569 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006570 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006571 || col < current_col)
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006572 syntax_start(wp, lnum, NULL);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006573 else if (wp->w_buffer == syn_buf
6574 && lnum == current_lnum
6575 && col > current_col)
6576 /* next_match may not be correct when moving around, e.g. with the
6577 * "skip" expression in searchpair() */
6578 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006580 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581
6582 return (trans ? current_trans_id : current_id);
6583}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584
Bram Moolenaar860cae12010-06-05 23:22:07 +02006585#if defined(FEAT_CONCEAL) || defined(PROTO)
6586/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006587 * Get extra information about the syntax item. Must be called right after
6588 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006589 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006590 * Returns the current flags.
6591 */
6592 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006593get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006594{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006595 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006596 return current_flags;
6597}
6598
6599/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006600 * Return conceal substitution character
6601 */
6602 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006603syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006604{
6605 return current_sub_char;
6606}
6607#endif
6608
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006609#if defined(FEAT_EVAL) || defined(PROTO)
6610/*
6611 * Return the syntax ID at position "i" in the current stack.
6612 * The caller must have called syn_get_id() before to fill the stack.
6613 * Returns -1 when "i" is out of range.
6614 */
6615 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006616syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006617{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006618 if (i >= current_state.ga_len)
6619 {
6620 /* Need to invalidate the state, because we didn't properly finish it
6621 * for the last character, "keep_state" was TRUE. */
6622 invalidate_current_state();
6623 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006624 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006625 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006626 return CUR_STATE(i).si_id;
6627}
6628#endif
6629
Bram Moolenaar071d4272004-06-13 20:20:40 +00006630#if defined(FEAT_FOLDING) || defined(PROTO)
6631/*
6632 * Function called to get folding level for line "lnum" in window "wp".
6633 */
6634 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006635syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006636{
6637 int level = 0;
6638 int i;
6639
6640 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006641 if (wp->w_s->b_syn_folditems != 0
6642 && !wp->w_s->b_syn_error
6643# ifdef SYN_TIME_LIMIT
6644 && !wp->w_s->b_syn_slow
6645# endif
6646 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006647 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006648 syntax_start(wp, lnum, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006649
6650 for (i = 0; i < current_state.ga_len; ++i)
6651 if (CUR_STATE(i).si_flags & HL_FOLD)
6652 ++level;
6653 }
6654 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006655 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006657 if (level < 0)
6658 level = 0;
6659 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006660 return level;
6661}
6662#endif
6663
Bram Moolenaar01615492015-02-03 13:00:38 +01006664#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006665/*
6666 * ":syntime".
6667 */
6668 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006669ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006670{
6671 if (STRCMP(eap->arg, "on") == 0)
6672 syn_time_on = TRUE;
6673 else if (STRCMP(eap->arg, "off") == 0)
6674 syn_time_on = FALSE;
6675 else if (STRCMP(eap->arg, "clear") == 0)
6676 syntime_clear();
6677 else if (STRCMP(eap->arg, "report") == 0)
6678 syntime_report();
6679 else
6680 EMSG2(_(e_invarg2), eap->arg);
6681}
6682
6683 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006684syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006685{
6686 profile_zero(&st->total);
6687 profile_zero(&st->slowest);
6688 st->count = 0;
6689 st->match = 0;
6690}
6691
6692/*
6693 * Clear the syntax timing for the current buffer.
6694 */
6695 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006696syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006697{
6698 int idx;
6699 synpat_T *spp;
6700
6701 if (!syntax_present(curwin))
6702 {
6703 MSG(_(msg_no_items));
6704 return;
6705 }
6706 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6707 {
6708 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6709 syn_clear_time(&spp->sp_time);
6710 }
6711}
6712
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006713#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6714/*
6715 * Function given to ExpandGeneric() to obtain the possible arguments of the
6716 * ":syntime {on,off,clear,report}" command.
6717 */
6718 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006719get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006720{
6721 switch (idx)
6722 {
6723 case 0: return (char_u *)"on";
6724 case 1: return (char_u *)"off";
6725 case 2: return (char_u *)"clear";
6726 case 3: return (char_u *)"report";
6727 }
6728 return NULL;
6729}
6730#endif
6731
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006732typedef struct
6733{
6734 proftime_T total;
6735 int count;
6736 int match;
6737 proftime_T slowest;
6738 proftime_T average;
6739 int id;
6740 char_u *pattern;
6741} time_entry_T;
6742
6743 static int
6744#ifdef __BORLANDC__
6745_RTLENTRYF
6746#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006747syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006748{
6749 const time_entry_T *s1 = v1;
6750 const time_entry_T *s2 = v2;
6751
6752 return profile_cmp(&s1->total, &s2->total);
6753}
6754
6755/*
6756 * Clear the syntax timing for the current buffer.
6757 */
6758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006759syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006760{
6761 int idx;
6762 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006763# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006764 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006765# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006766 int len;
6767 proftime_T total_total;
6768 int total_count = 0;
6769 garray_T ga;
6770 time_entry_T *p;
6771
6772 if (!syntax_present(curwin))
6773 {
6774 MSG(_(msg_no_items));
6775 return;
6776 }
6777
6778 ga_init2(&ga, sizeof(time_entry_T), 50);
6779 profile_zero(&total_total);
6780 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6781 {
6782 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6783 if (spp->sp_time.count > 0)
6784 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006785 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006786 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6787 p->total = spp->sp_time.total;
6788 profile_add(&total_total, &spp->sp_time.total);
6789 p->count = spp->sp_time.count;
6790 p->match = spp->sp_time.match;
6791 total_count += spp->sp_time.count;
6792 p->slowest = spp->sp_time.slowest;
6793# ifdef FEAT_FLOAT
6794 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6795 p->average = tm;
6796# endif
6797 p->id = spp->sp_syn.id;
6798 p->pattern = spp->sp_pattern;
6799 ++ga.ga_len;
6800 }
6801 }
6802
Bram Moolenaara2162552017-01-08 17:46:20 +01006803 /* Sort on total time. Skip if there are no items to avoid passing NULL
6804 * pointer to qsort(). */
6805 if (ga.ga_len > 1)
6806 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006807 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006808
6809 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6810 MSG_PUTS("\n");
6811 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6812 {
6813 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6814 p = ((time_entry_T *)ga.ga_data) + idx;
6815
6816 MSG_PUTS(profile_msg(&p->total));
6817 MSG_PUTS(" "); /* make sure there is always a separating space */
6818 msg_advance(13);
6819 msg_outnum(p->count);
6820 MSG_PUTS(" ");
6821 msg_advance(20);
6822 msg_outnum(p->match);
6823 MSG_PUTS(" ");
6824 msg_advance(26);
6825 MSG_PUTS(profile_msg(&p->slowest));
6826 MSG_PUTS(" ");
6827 msg_advance(38);
6828# ifdef FEAT_FLOAT
6829 MSG_PUTS(profile_msg(&p->average));
6830 MSG_PUTS(" ");
6831# endif
6832 msg_advance(50);
6833 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6834 MSG_PUTS(" ");
6835
6836 msg_advance(69);
6837 if (Columns < 80)
6838 len = 20; /* will wrap anyway */
6839 else
6840 len = Columns - 70;
6841 if (len > (int)STRLEN(p->pattern))
6842 len = (int)STRLEN(p->pattern);
6843 msg_outtrans_len(p->pattern, len);
6844 MSG_PUTS("\n");
6845 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006846 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006847 if (!got_int)
6848 {
6849 MSG_PUTS("\n");
6850 MSG_PUTS(profile_msg(&total_total));
6851 msg_advance(13);
6852 msg_outnum(total_count);
6853 MSG_PUTS("\n");
6854 }
6855}
6856#endif
6857
Bram Moolenaar071d4272004-06-13 20:20:40 +00006858#endif /* FEAT_SYN_HL */
6859
Bram Moolenaar071d4272004-06-13 20:20:40 +00006860/**************************************
6861 * Highlighting stuff *
6862 **************************************/
6863
6864/*
6865 * The default highlight groups. These are compiled-in for fast startup and
6866 * they still work when the runtime files can't be found.
6867 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006868 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6869 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006870 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006871#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006872# define CENT(a, b) b
6873#else
6874# define CENT(a, b) a
6875#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006876static char *(highlight_init_both[]) = {
6877 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6878 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6879 CENT("IncSearch term=reverse cterm=reverse",
6880 "IncSearch term=reverse cterm=reverse gui=reverse"),
6881 CENT("ModeMsg term=bold cterm=bold",
6882 "ModeMsg term=bold cterm=bold gui=bold"),
6883 CENT("NonText term=bold ctermfg=Blue",
6884 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6885 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6886 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6887 CENT("StatusLineNC term=reverse cterm=reverse",
6888 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6889 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006890#ifdef FEAT_WINDOWS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006891 CENT("VertSplit term=reverse cterm=reverse",
6892 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006893#endif
6894#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006895 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6896 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006897#endif
6898#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006899 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6900 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#endif
6902#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006903 CENT("PmenuSbar ctermbg=Grey",
6904 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006905#endif
6906#ifdef FEAT_WINDOWS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006907 CENT("TabLineSel term=bold cterm=bold",
6908 "TabLineSel term=bold cterm=bold gui=bold"),
6909 CENT("TabLineFill term=reverse cterm=reverse",
6910 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006911#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006912#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006913 "Cursor guibg=fg guifg=bg",
6914 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006915#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006916 "default link QuickFixLine Search",
6917 NULL
6918};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006919
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006920/* Default colors only used with a light background. */
6921static char *(highlight_init_light[]) = {
6922 CENT("Directory term=bold ctermfg=DarkBlue",
6923 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6924 CENT("LineNr term=underline ctermfg=Brown",
6925 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6926 CENT("CursorLineNr term=bold ctermfg=Brown",
6927 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6928 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6929 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6930 CENT("Question term=standout ctermfg=DarkGreen",
6931 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6932 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6933 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006934#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006935 CENT("SpellBad term=reverse ctermbg=LightRed",
6936 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6937 CENT("SpellCap term=reverse ctermbg=LightBlue",
6938 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6939 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6940 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6941 CENT("SpellLocal term=underline ctermbg=Cyan",
6942 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006943#endif
6944#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006945 CENT("PmenuThumb ctermbg=Black",
6946 "PmenuThumb ctermbg=Black guibg=Black"),
6947 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6948 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6949 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6950 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006951#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006952 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6953 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6954 CENT("Title term=bold ctermfg=DarkMagenta",
6955 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6956 CENT("WarningMsg term=standout ctermfg=DarkRed",
6957 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006958#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006959 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6960 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006961#endif
6962#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006963 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6964 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6965 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6966 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006967#endif
6968#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006969 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6970 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006971#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006972 CENT("Visual term=reverse",
6973 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006974#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006975 CENT("DiffAdd term=bold ctermbg=LightBlue",
6976 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6977 CENT("DiffChange term=bold ctermbg=LightMagenta",
6978 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6979 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6980 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006981#endif
6982#ifdef FEAT_WINDOWS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006983 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6984 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006985#endif
6986#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006987 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6988 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6989 CENT("CursorLine term=underline cterm=underline",
6990 "CursorLine term=underline cterm=underline guibg=Grey90"),
6991 CENT("ColorColumn term=reverse ctermbg=LightRed",
6992 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006993#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006994#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006995 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6996 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02006997#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006998#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006999 CENT("MatchParen term=reverse ctermbg=Cyan",
7000 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007001#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007002#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007003 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007004#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007005#ifdef FEAT_TERMINAL
7006 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7007 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7008 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7009 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7010#endif
7011 NULL
7012};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007013
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007014/* Default colors only used with a dark background. */
7015static char *(highlight_init_dark[]) = {
7016 CENT("Directory term=bold ctermfg=LightCyan",
7017 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7018 CENT("LineNr term=underline ctermfg=Yellow",
7019 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7020 CENT("CursorLineNr term=bold ctermfg=Yellow",
7021 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7022 CENT("MoreMsg term=bold ctermfg=LightGreen",
7023 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7024 CENT("Question term=standout ctermfg=LightGreen",
7025 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7026 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7027 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7028 CENT("SpecialKey term=bold ctermfg=LightBlue",
7029 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007030#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007031 CENT("SpellBad term=reverse ctermbg=Red",
7032 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7033 CENT("SpellCap term=reverse ctermbg=Blue",
7034 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7035 CENT("SpellRare term=reverse ctermbg=Magenta",
7036 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7037 CENT("SpellLocal term=underline ctermbg=Cyan",
7038 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007039#endif
7040#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007041 CENT("PmenuThumb ctermbg=White",
7042 "PmenuThumb ctermbg=White guibg=White"),
7043 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7044 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7045 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7046 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007047#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007048 CENT("Title term=bold ctermfg=LightMagenta",
7049 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7050 CENT("WarningMsg term=standout ctermfg=LightRed",
7051 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007052#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007053 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7054 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007055#endif
7056#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007057 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7058 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7059 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7060 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007061#endif
7062#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007063 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7064 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007065#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007066 CENT("Visual term=reverse",
7067 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007068#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007069 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7070 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7071 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7072 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7073 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7074 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007075#endif
7076#ifdef FEAT_WINDOWS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007077 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7078 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007079#endif
7080#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007081 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7082 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7083 CENT("CursorLine term=underline cterm=underline",
7084 "CursorLine term=underline cterm=underline guibg=Grey40"),
7085 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7086 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007087#endif
7088#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007089 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7090 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007091#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007092#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007093 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7094 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007095#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007096#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007097 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007098#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007099#ifdef FEAT_TERMINAL
7100 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7101 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7102 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7103 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7104#endif
7105 NULL
7106};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007107
7108 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007109init_highlight(
7110 int both, /* include groups where 'bg' doesn't matter */
7111 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007112{
7113 int i;
7114 char **pp;
7115 static int had_both = FALSE;
7116#ifdef FEAT_EVAL
7117 char_u *p;
7118
7119 /*
7120 * Try finding the color scheme file. Used when a color file was loaded
7121 * and 'background' or 't_Co' is changed.
7122 */
7123 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007124 if (p != NULL)
7125 {
7126 /* The value of g:colors_name could be freed when sourcing the script,
7127 * making "p" invalid, so copy it. */
7128 char_u *copy_p = vim_strsave(p);
7129 int r;
7130
7131 if (copy_p != NULL)
7132 {
7133 r = load_colors(copy_p);
7134 vim_free(copy_p);
7135 if (r == OK)
7136 return;
7137 }
7138 }
7139
Bram Moolenaar071d4272004-06-13 20:20:40 +00007140#endif
7141
7142 /*
7143 * Didn't use a color file, use the compiled-in colors.
7144 */
7145 if (both)
7146 {
7147 had_both = TRUE;
7148 pp = highlight_init_both;
7149 for (i = 0; pp[i] != NULL; ++i)
7150 do_highlight((char_u *)pp[i], reset, TRUE);
7151 }
7152 else if (!had_both)
7153 /* Don't do anything before the call with both == TRUE from main().
7154 * Not everything has been setup then, and that call will overrule
7155 * everything anyway. */
7156 return;
7157
7158 if (*p_bg == 'l')
7159 pp = highlight_init_light;
7160 else
7161 pp = highlight_init_dark;
7162 for (i = 0; pp[i] != NULL; ++i)
7163 do_highlight((char_u *)pp[i], reset, TRUE);
7164
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007165 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007166 * depend on the number of colors available.
7167 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007168 * to avoid Statement highlighted text disappears.
7169 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007170 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007171 do_highlight((char_u *)(*p_bg == 'l'
7172 ? "Visual cterm=NONE ctermbg=LightGrey"
7173 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007174 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007175 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007176 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7177 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007178 if (*p_bg == 'l')
7179 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7180 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007181
Bram Moolenaar071d4272004-06-13 20:20:40 +00007182#ifdef FEAT_SYN_HL
7183 /*
7184 * If syntax highlighting is enabled load the highlighting for it.
7185 */
7186 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007187 {
7188 static int recursive = 0;
7189
7190 if (recursive >= 5)
7191 EMSG(_("E679: recursive loop loading syncolor.vim"));
7192 else
7193 {
7194 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007195 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007196 --recursive;
7197 }
7198 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007199#endif
7200}
7201
7202/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007203 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007204 * Return OK for success, FAIL for failure.
7205 */
7206 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007207load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208{
7209 char_u *buf;
7210 int retval = FAIL;
7211 static int recursive = FALSE;
7212
7213 /* When being called recursively, this is probably because setting
7214 * 'background' caused the highlighting to be reloaded. This means it is
7215 * working, thus we should return OK. */
7216 if (recursive)
7217 return OK;
7218
7219 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007220 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007221 if (buf != NULL)
7222 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007223 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007224 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007225 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007226#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007227 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007228#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007229 }
7230 recursive = FALSE;
7231
7232 return retval;
7233}
7234
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007235static char *(color_names[28]) = {
7236 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7237 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7238 "Gray", "Grey", "LightGray", "LightGrey",
7239 "DarkGray", "DarkGrey",
7240 "Blue", "LightBlue", "Green", "LightGreen",
7241 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7242 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7243 /* indices:
7244 * 0, 1, 2, 3,
7245 * 4, 5, 6, 7,
7246 * 8, 9, 10, 11,
7247 * 12, 13,
7248 * 14, 15, 16, 17,
7249 * 18, 19, 20, 21, 22,
7250 * 23, 24, 25, 26, 27 */
7251static int color_numbers_16[28] = {0, 1, 2, 3,
7252 4, 5, 6, 6,
7253 7, 7, 7, 7,
7254 8, 8,
7255 9, 9, 10, 10,
7256 11, 11, 12, 12, 13,
7257 13, 14, 14, 15, -1};
7258/* for xterm with 88 colors... */
7259static int color_numbers_88[28] = {0, 4, 2, 6,
7260 1, 5, 32, 72,
7261 84, 84, 7, 7,
7262 82, 82,
7263 12, 43, 10, 61,
7264 14, 63, 9, 74, 13,
7265 75, 11, 78, 15, -1};
7266/* for xterm with 256 colors... */
7267static int color_numbers_256[28] = {0, 4, 2, 6,
7268 1, 5, 130, 130,
7269 248, 248, 7, 7,
7270 242, 242,
7271 12, 81, 10, 121,
7272 14, 159, 9, 224, 13,
7273 225, 11, 229, 15, -1};
7274/* for terminals with less than 16 colors... */
7275static int color_numbers_8[28] = {0, 4, 2, 6,
7276 1, 5, 3, 3,
7277 7, 7, 7, 7,
7278 0+8, 0+8,
7279 4+8, 4+8, 2+8, 2+8,
7280 6+8, 6+8, 1+8, 1+8, 5+8,
7281 5+8, 3+8, 3+8, 7+8, -1};
7282
7283/*
7284 * Lookup the "cterm" value to be used for color with index "idx" in
7285 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007286 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7287 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007288 */
7289 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007290lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007291{
7292 int color = color_numbers_16[idx];
7293 char_u *p;
7294
7295 /* Use the _16 table to check if it's a valid color name. */
7296 if (color < 0)
7297 return -1;
7298
7299 if (t_colors == 8)
7300 {
7301 /* t_Co is 8: use the 8 colors table */
7302#if defined(__QNXNTO__)
7303 color = color_numbers_8_qansi[idx];
7304#else
7305 color = color_numbers_8[idx];
7306#endif
7307 if (foreground)
7308 {
7309 /* set/reset bold attribute to get light foreground
7310 * colors (on some terminals, e.g. "linux") */
7311 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007312 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007313 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007314 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007315 }
7316 color &= 7; /* truncate to 8 colors */
7317 }
7318 else if (t_colors == 16 || t_colors == 88
7319 || t_colors >= 256)
7320 {
7321 /*
7322 * Guess: if the termcap entry ends in 'm', it is
7323 * probably an xterm-like terminal. Use the changed
7324 * order for colors.
7325 */
7326 if (*T_CAF != NUL)
7327 p = T_CAF;
7328 else
7329 p = T_CSF;
7330 if (*p != NUL && (t_colors > 256
7331 || *(p + STRLEN(p) - 1) == 'm'))
7332 {
7333 if (t_colors == 88)
7334 color = color_numbers_88[idx];
7335 else if (t_colors >= 256)
7336 color = color_numbers_256[idx];
7337 else
7338 color = color_numbers_8[idx];
7339 }
7340 }
7341 return color;
7342}
7343
Bram Moolenaar071d4272004-06-13 20:20:40 +00007344/*
7345 * Handle the ":highlight .." command.
7346 * When using ":hi clear" this is called recursively for each group with
7347 * "forceit" and "init" both TRUE.
7348 */
7349 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007350do_highlight(
7351 char_u *line,
7352 int forceit,
7353 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007354{
7355 char_u *name_end;
7356 char_u *p;
7357 char_u *linep;
7358 char_u *key_start;
7359 char_u *arg_start;
7360 char_u *key = NULL, *arg = NULL;
7361 long i;
7362 int off;
7363 int len;
7364 int attr;
7365 int id;
7366 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007367 struct hl_group item_before;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007368 int dodefault = FALSE;
7369 int doclear = FALSE;
7370 int dolink = FALSE;
7371 int error = FALSE;
7372 int color;
7373 int is_normal_group = FALSE; /* "Normal" group */
7374#ifdef FEAT_GUI_X11
7375 int is_menu_group = FALSE; /* "Menu" group */
7376 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7377 int is_tooltip_group = FALSE; /* "Tooltip" group */
7378 int do_colors = FALSE; /* need to update colors? */
7379#else
7380# define is_menu_group 0
7381# define is_tooltip_group 0
7382#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007383#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7384 int did_highlight_changed = FALSE;
7385#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007386
7387 /*
7388 * If no argument, list current highlighting.
7389 */
7390 if (ends_excmd(*line))
7391 {
7392 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7393 /* TODO: only call when the group has attributes set */
7394 highlight_list_one((int)i);
7395 return;
7396 }
7397
7398 /*
7399 * Isolate the name.
7400 */
7401 name_end = skiptowhite(line);
7402 linep = skipwhite(name_end);
7403
7404 /*
7405 * Check for "default" argument.
7406 */
7407 if (STRNCMP(line, "default", name_end - line) == 0)
7408 {
7409 dodefault = TRUE;
7410 line = linep;
7411 name_end = skiptowhite(line);
7412 linep = skipwhite(name_end);
7413 }
7414
7415 /*
7416 * Check for "clear" or "link" argument.
7417 */
7418 if (STRNCMP(line, "clear", name_end - line) == 0)
7419 doclear = TRUE;
7420 if (STRNCMP(line, "link", name_end - line) == 0)
7421 dolink = TRUE;
7422
7423 /*
7424 * ":highlight {group-name}": list highlighting for one group.
7425 */
7426 if (!doclear && !dolink && ends_excmd(*linep))
7427 {
7428 id = syn_namen2id(line, (int)(name_end - line));
7429 if (id == 0)
7430 EMSG2(_("E411: highlight group not found: %s"), line);
7431 else
7432 highlight_list_one(id);
7433 return;
7434 }
7435
7436 /*
7437 * Handle ":highlight link {from} {to}" command.
7438 */
7439 if (dolink)
7440 {
7441 char_u *from_start = linep;
7442 char_u *from_end;
7443 char_u *to_start;
7444 char_u *to_end;
7445 int from_id;
7446 int to_id;
7447
7448 from_end = skiptowhite(from_start);
7449 to_start = skipwhite(from_end);
7450 to_end = skiptowhite(to_start);
7451
7452 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7453 {
7454 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7455 from_start);
7456 return;
7457 }
7458
7459 if (!ends_excmd(*skipwhite(to_end)))
7460 {
7461 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7462 return;
7463 }
7464
7465 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7466 if (STRNCMP(to_start, "NONE", 4) == 0)
7467 to_id = 0;
7468 else
7469 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7470
Bram Moolenaar414168d2017-09-10 15:21:55 +02007471 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007472 {
7473 /*
7474 * Don't allow a link when there already is some highlighting
7475 * for the group, unless '!' is used
7476 */
7477 if (to_id > 0 && !forceit && !init
7478 && hl_has_settings(from_id - 1, dodefault))
7479 {
7480 if (sourcing_name == NULL && !dodefault)
7481 EMSG(_("E414: group has settings, highlight link ignored"));
7482 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007483 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007484#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007485 || HL_TABLE()[from_id - 1].sg_scriptID != current_SID
Bram Moolenaar99433292017-09-08 12:37:47 +02007486#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007487 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007488 {
7489 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007490 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7491 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007492#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007493 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007494#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007495 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007496 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007497
7498 /* Only call highlight_changed() once after multiple changes. */
7499 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007500 }
7501 }
7502
Bram Moolenaar071d4272004-06-13 20:20:40 +00007503 return;
7504 }
7505
7506 if (doclear)
7507 {
7508 /*
7509 * ":highlight clear [group]" command.
7510 */
7511 line = linep;
7512 if (ends_excmd(*line))
7513 {
7514#ifdef FEAT_GUI
7515 /* First, we do not destroy the old values, but allocate the new
7516 * ones and update the display. THEN we destroy the old values.
7517 * If we destroy the old values first, then the old values
7518 * (such as GuiFont's or GuiFontset's) will still be displayed but
7519 * invalid because they were free'd.
7520 */
7521 if (gui.in_use)
7522 {
7523# ifdef FEAT_BEVAL_TIP
7524 gui_init_tooltip_font();
7525# endif
7526# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7527 gui_init_menu_font();
7528# endif
7529 }
7530# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7531 gui_mch_def_colors();
7532# endif
7533# ifdef FEAT_GUI_X11
7534# ifdef FEAT_MENU
7535
7536 /* This only needs to be done when there is no Menu highlight
7537 * group defined by default, which IS currently the case.
7538 */
7539 gui_mch_new_menu_colors();
7540# endif
7541 if (gui.in_use)
7542 {
7543 gui_new_scrollbar_colors();
7544# ifdef FEAT_BEVAL
7545 gui_mch_new_tooltip_colors();
7546# endif
7547# ifdef FEAT_MENU
7548 gui_mch_new_menu_font();
7549# endif
7550 }
7551# endif
7552
7553 /* Ok, we're done allocating the new default graphics items.
7554 * The screen should already be refreshed at this point.
7555 * It is now Ok to clear out the old data.
7556 */
7557#endif
7558#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007559 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560#endif
7561 restore_cterm_colors();
7562
7563 /*
7564 * Clear all default highlight groups and load the defaults.
7565 */
7566 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7567 highlight_clear(idx);
7568 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007569#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007570 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007571 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007572 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007573#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007574 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007575 redraw_later_clear();
7576 return;
7577 }
7578 name_end = skiptowhite(line);
7579 linep = skipwhite(name_end);
7580 }
7581
7582 /*
7583 * Find the group name in the table. If it does not exist yet, add it.
7584 */
7585 id = syn_check_group(line, (int)(name_end - line));
7586 if (id == 0) /* failed (out of memory) */
7587 return;
7588 idx = id - 1; /* index is ID minus one */
7589
7590 /* Return if "default" was used and the group already has settings. */
7591 if (dodefault && hl_has_settings(idx, TRUE))
7592 return;
7593
Bram Moolenaar99433292017-09-08 12:37:47 +02007594 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007595 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007596
Bram Moolenaar414168d2017-09-10 15:21:55 +02007597 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007598 is_normal_group = TRUE;
7599#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007600 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007601 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007602 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007603 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007604 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007605 is_tooltip_group = TRUE;
7606#endif
7607
7608 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7609 if (doclear || (forceit && init))
7610 {
7611 highlight_clear(idx);
7612 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007613 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007614 }
7615
7616 if (!doclear)
7617 while (!ends_excmd(*linep))
7618 {
7619 key_start = linep;
7620 if (*linep == '=')
7621 {
7622 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7623 error = TRUE;
7624 break;
7625 }
7626
7627 /*
7628 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7629 * "guibg").
7630 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007631 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007632 ++linep;
7633 vim_free(key);
7634 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7635 if (key == NULL)
7636 {
7637 error = TRUE;
7638 break;
7639 }
7640 linep = skipwhite(linep);
7641
7642 if (STRCMP(key, "NONE") == 0)
7643 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007644 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007645 {
7646 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007647 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007648 highlight_clear(idx);
7649 }
7650 continue;
7651 }
7652
7653 /*
7654 * Check for the equal sign.
7655 */
7656 if (*linep != '=')
7657 {
7658 EMSG2(_("E416: missing equal sign: %s"), key_start);
7659 error = TRUE;
7660 break;
7661 }
7662 ++linep;
7663
7664 /*
7665 * Isolate the argument.
7666 */
7667 linep = skipwhite(linep);
7668 if (*linep == '\'') /* guifg='color name' */
7669 {
7670 arg_start = ++linep;
7671 linep = vim_strchr(linep, '\'');
7672 if (linep == NULL)
7673 {
7674 EMSG2(_(e_invarg2), key_start);
7675 error = TRUE;
7676 break;
7677 }
7678 }
7679 else
7680 {
7681 arg_start = linep;
7682 linep = skiptowhite(linep);
7683 }
7684 if (linep == arg_start)
7685 {
7686 EMSG2(_("E417: missing argument: %s"), key_start);
7687 error = TRUE;
7688 break;
7689 }
7690 vim_free(arg);
7691 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7692 if (arg == NULL)
7693 {
7694 error = TRUE;
7695 break;
7696 }
7697 if (*linep == '\'')
7698 ++linep;
7699
7700 /*
7701 * Store the argument.
7702 */
7703 if ( STRCMP(key, "TERM") == 0
7704 || STRCMP(key, "CTERM") == 0
7705 || STRCMP(key, "GUI") == 0)
7706 {
7707 attr = 0;
7708 off = 0;
7709 while (arg[off] != NUL)
7710 {
7711 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7712 {
7713 len = (int)STRLEN(hl_name_table[i]);
7714 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7715 {
7716 attr |= hl_attr_table[i];
7717 off += len;
7718 break;
7719 }
7720 }
7721 if (i < 0)
7722 {
7723 EMSG2(_("E418: Illegal value: %s"), arg);
7724 error = TRUE;
7725 break;
7726 }
7727 if (arg[off] == ',') /* another one follows */
7728 ++off;
7729 }
7730 if (error)
7731 break;
7732 if (*key == 'T')
7733 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007734 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007735 {
7736 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007737 HL_TABLE()[idx].sg_set |= SG_TERM;
7738 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007739 }
7740 }
7741 else if (*key == 'C')
7742 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007743 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007744 {
7745 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007746 HL_TABLE()[idx].sg_set |= SG_CTERM;
7747 HL_TABLE()[idx].sg_cterm = attr;
7748 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007749 }
7750 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007751#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007752 else
7753 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007754 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007755 {
7756 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007757 HL_TABLE()[idx].sg_set |= SG_GUI;
7758 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007759 }
7760 }
7761#endif
7762 }
7763 else if (STRCMP(key, "FONT") == 0)
7764 {
7765 /* in non-GUI fonts are simply ignored */
7766#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007767 if (HL_TABLE()[idx].sg_font_name != NULL
7768 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007769 {
7770 /* Font name didn't change, ignore. */
7771 }
7772 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773 {
7774 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007775 vim_free(HL_TABLE()[idx].sg_font_name);
7776 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777 }
7778 else
7779 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007780 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007781# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007782 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007783# endif
7784 /* First, save the current font/fontset.
7785 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007786 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007787 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7788 */
7789
Bram Moolenaar414168d2017-09-10 15:21:55 +02007790 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007791# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007792 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007793# endif
7794 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007795 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007796
7797# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007798 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007799 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007800 /* New fontset was accepted. Free the old one, if there
7801 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007802 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007803 vim_free(HL_TABLE()[idx].sg_font_name);
7804 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805 }
7806 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007807 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007809 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007810 {
7811 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007812 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007814 vim_free(HL_TABLE()[idx].sg_font_name);
7815 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007816 }
7817 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007818 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007819 }
7820#endif
7821 }
7822 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7823 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007824 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007825 {
7826 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007827 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828
7829 /* When setting the foreground color, and previously the "bold"
7830 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007831 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007832 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007833 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7834 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007835 }
7836
7837 if (VIM_ISDIGIT(*arg))
7838 color = atoi((char *)arg);
7839 else if (STRICMP(arg, "fg") == 0)
7840 {
7841 if (cterm_normal_fg_color)
7842 color = cterm_normal_fg_color - 1;
7843 else
7844 {
7845 EMSG(_("E419: FG color unknown"));
7846 error = TRUE;
7847 break;
7848 }
7849 }
7850 else if (STRICMP(arg, "bg") == 0)
7851 {
7852 if (cterm_normal_bg_color > 0)
7853 color = cterm_normal_bg_color - 1;
7854 else
7855 {
7856 EMSG(_("E420: BG color unknown"));
7857 error = TRUE;
7858 break;
7859 }
7860 }
7861 else
7862 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007863 int bold = MAYBE;
7864
Bram Moolenaar071d4272004-06-13 20:20:40 +00007865#if defined(__QNXNTO__)
7866 static int *color_numbers_8_qansi = color_numbers_8;
7867 /* On qnx, the 8 & 16 color arrays are the same */
7868 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7869 color_numbers_8_qansi = color_numbers_16;
7870#endif
7871
7872 /* reduce calls to STRICMP a bit, it can be slow */
7873 off = TOUPPER_ASC(*arg);
7874 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7875 if (off == color_names[i][0]
7876 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7877 break;
7878 if (i < 0)
7879 {
7880 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7881 error = TRUE;
7882 break;
7883 }
7884
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007885 color = lookup_color(i, key[5] == 'F', &bold);
7886
7887 /* set/reset bold attribute to get light foreground
7888 * colors (on some terminals, e.g. "linux") */
7889 if (bold == TRUE)
7890 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007891 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7892 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007893 }
7894 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007895 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007896 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007897
Bram Moolenaarccbab932010-05-13 15:40:30 +02007898 /* Add one to the argument, to avoid zero. Zero is used for
7899 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007900 if (key[5] == 'F')
7901 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007902 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007903 if (is_normal_group)
7904 {
7905 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007906 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007907#ifdef FEAT_GUI
7908 /* Don't do this if the GUI is used. */
7909 if (!gui.in_use && !gui.starting)
7910#endif
7911 {
7912 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007913 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007914 term_fg_color(color);
7915 }
7916 }
7917 }
7918 else
7919 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007920 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007921 if (is_normal_group)
7922 {
7923 cterm_normal_bg_color = color + 1;
7924#ifdef FEAT_GUI
7925 /* Don't mess with 'background' if the GUI is used. */
7926 if (!gui.in_use && !gui.starting)
7927#endif
7928 {
7929 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007930 if (color >= 0)
7931 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007932 int dark = -1;
7933
Bram Moolenaarccbab932010-05-13 15:40:30 +02007934 if (termcap_active)
7935 term_bg_color(color);
7936 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007937 dark = (color == 0 || color == 4);
7938 /* Limit the heuristic to the standard 16 colors */
7939 else if (color < 16)
7940 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007941 /* Set the 'background' option if the value is
7942 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007943 if (dark != -1
7944 && dark != (*p_bg == 'd')
7945 && !option_was_set((char_u *)"bg"))
7946 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007947 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007948 (char_u *)(dark ? "dark" : "light"), 0);
7949 reset_option_was_set((char_u *)"bg");
7950 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007951 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007952 }
7953 }
7954 }
7955 }
7956 }
7957 else if (STRCMP(key, "GUIFG") == 0)
7958 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007959#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007960 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007961 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007962 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007963 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007964
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007965# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007966 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007967 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007968 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007969 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007970 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007971# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007972 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
Bram Moolenaar26af85d2017-07-23 16:45:10 +02007973 if (STRCMP(arg, "NONE") != 0)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007974 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007975 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007976 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007977# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007978# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007979 if (is_menu_group)
7980 gui.menu_fg_pixel = i;
7981 if (is_scrollbar_group)
7982 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007983# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007984 if (is_tooltip_group)
7985 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007986# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007987 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007988# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007989 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007990# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007991 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007992#endif
7993 }
7994 else if (STRCMP(key, "GUIBG") == 0)
7995 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007996#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007997 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007998 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007999 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008000 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008001
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008002# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008003 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008004 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008005 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008006 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008007 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008008# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02008009 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008010 if (STRCMP(arg, "NONE") != 0)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008011 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008012 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02008013 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008014# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008015# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008016 if (is_menu_group)
8017 gui.menu_bg_pixel = i;
8018 if (is_scrollbar_group)
8019 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008020# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008021 if (is_tooltip_group)
8022 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008023# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008024 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02008025# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008026 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008027# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008028 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008029#endif
8030 }
8031 else if (STRCMP(key, "GUISP") == 0)
8032 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008033#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008034 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008035 {
8036 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008037 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008038
Bram Moolenaar61623362010-07-14 22:04:22 +02008039# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008040 i = color_name2handle(arg);
8041 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8042 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008043 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008044# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02008045 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008046 if (STRCMP(arg, "NONE") != 0)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008047 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008048 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02008049 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008050# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008051 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008052# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008053 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008054#endif
8055 }
8056 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8057 {
8058 char_u buf[100];
8059 char_u *tname;
8060
8061 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008062 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008063
8064 /*
8065 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008066 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008067 */
8068 if (STRNCMP(arg, "t_", 2) == 0)
8069 {
8070 off = 0;
8071 buf[0] = 0;
8072 while (arg[off] != NUL)
8073 {
8074 /* Isolate one termcap name */
8075 for (len = 0; arg[off + len] &&
8076 arg[off + len] != ','; ++len)
8077 ;
8078 tname = vim_strnsave(arg + off, len);
8079 if (tname == NULL) /* out of memory */
8080 {
8081 error = TRUE;
8082 break;
8083 }
8084 /* lookup the escape sequence for the item */
8085 p = get_term_code(tname);
8086 vim_free(tname);
8087 if (p == NULL) /* ignore non-existing things */
8088 p = (char_u *)"";
8089
8090 /* Append it to the already found stuff */
8091 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8092 {
8093 EMSG2(_("E422: terminal code too long: %s"), arg);
8094 error = TRUE;
8095 break;
8096 }
8097 STRCAT(buf, p);
8098
8099 /* Advance to the next item */
8100 off += len;
8101 if (arg[off] == ',') /* another one follows */
8102 ++off;
8103 }
8104 }
8105 else
8106 {
8107 /*
8108 * Copy characters from arg[] to buf[], translating <> codes.
8109 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008110 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008111 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008112 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008113 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114 off += len;
8115 else /* copy as normal char */
8116 buf[off++] = *p++;
8117 }
8118 buf[off] = NUL;
8119 }
8120 if (error)
8121 break;
8122
8123 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8124 p = NULL;
8125 else
8126 p = vim_strsave(buf);
8127 if (key[2] == 'A')
8128 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008129 vim_free(HL_TABLE()[idx].sg_start);
8130 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008131 }
8132 else
8133 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008134 vim_free(HL_TABLE()[idx].sg_stop);
8135 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008136 }
8137 }
8138 else
8139 {
8140 EMSG2(_("E423: Illegal argument: %s"), key_start);
8141 error = TRUE;
8142 break;
8143 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008144 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145
8146 /*
8147 * When highlighting has been given for a group, don't link it.
8148 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008149 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8150 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008151
8152 /*
8153 * Continue with next argument.
8154 */
8155 linep = skipwhite(linep);
8156 }
8157
8158 /*
8159 * If there is an error, and it's a new entry, remove it from the table.
8160 */
8161 if (error && idx == highlight_ga.ga_len)
8162 syn_unadd_group();
8163 else
8164 {
8165 if (is_normal_group)
8166 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008167 HL_TABLE()[idx].sg_term_attr = 0;
8168 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008170 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008171 /*
8172 * Need to update all groups, because they might be using "bg"
8173 * and/or "fg", which have been changed now.
8174 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008175#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008176#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008177 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008178 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008180 did_highlight_changed = TRUE;
8181 redraw_all_later(NOT_VALID);
8182 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008183#endif
8184 }
8185#ifdef FEAT_GUI_X11
8186# ifdef FEAT_MENU
8187 else if (is_menu_group)
8188 {
8189 if (gui.in_use && do_colors)
8190 gui_mch_new_menu_colors();
8191 }
8192# endif
8193 else if (is_scrollbar_group)
8194 {
8195 if (gui.in_use && do_colors)
8196 gui_new_scrollbar_colors();
8197 }
8198# ifdef FEAT_BEVAL
8199 else if (is_tooltip_group)
8200 {
8201 if (gui.in_use && do_colors)
8202 gui_mch_new_tooltip_colors();
8203 }
8204# endif
8205#endif
8206 else
8207 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008208#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02008209 HL_TABLE()[idx].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008210#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008212
Bram Moolenaar071d4272004-06-13 20:20:40 +00008213 vim_free(key);
8214 vim_free(arg);
8215
Bram Moolenaar99433292017-09-08 12:37:47 +02008216 /* Only call highlight_changed() once, after a sequence of highlight
8217 * commands, and only if an attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008218 if (memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008219#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8220 && !did_highlight_changed
8221#endif
8222 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008223 {
8224 redraw_all_later(NOT_VALID);
8225 need_highlight_changed = TRUE;
8226 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227}
8228
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008229#if defined(EXITFREE) || defined(PROTO)
8230 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008231free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008232{
8233 int i;
8234
8235 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008236 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008237 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008238 vim_free(HL_TABLE()[i].sg_name);
8239 vim_free(HL_TABLE()[i].sg_name_u);
8240 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008241 ga_clear(&highlight_ga);
8242}
8243#endif
8244
Bram Moolenaar071d4272004-06-13 20:20:40 +00008245/*
8246 * Reset the cterm colors to what they were before Vim was started, if
8247 * possible. Otherwise reset them to zero.
8248 */
8249 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008250restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008251{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008252#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008253 /* Since t_me has been set, this probably means that the user
8254 * wants to use this as default colors. Need to reset default
8255 * background/foreground colors. */
8256 mch_set_normal_colors();
8257#else
8258 cterm_normal_fg_color = 0;
8259 cterm_normal_fg_bold = 0;
8260 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008261# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008262 cterm_normal_fg_gui_color = INVALCOLOR;
8263 cterm_normal_bg_gui_color = INVALCOLOR;
8264# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008265#endif
8266}
8267
8268/*
8269 * Return TRUE if highlight group "idx" has any settings.
8270 * When "check_link" is TRUE also check for an existing link.
8271 */
8272 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008273hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274{
8275 return ( HL_TABLE()[idx].sg_term_attr != 0
8276 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008277 || HL_TABLE()[idx].sg_cterm_fg != 0
8278 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008279#ifdef FEAT_GUI
8280 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008281 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8282 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8283 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008284 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008285#endif
8286 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8287}
8288
8289/*
8290 * Clear highlighting for one group.
8291 */
8292 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008293highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008294{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008295 HL_TABLE()[idx].sg_cleared = TRUE;
8296
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297 HL_TABLE()[idx].sg_term = 0;
8298 vim_free(HL_TABLE()[idx].sg_start);
8299 HL_TABLE()[idx].sg_start = NULL;
8300 vim_free(HL_TABLE()[idx].sg_stop);
8301 HL_TABLE()[idx].sg_stop = NULL;
8302 HL_TABLE()[idx].sg_term_attr = 0;
8303 HL_TABLE()[idx].sg_cterm = 0;
8304 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8305 HL_TABLE()[idx].sg_cterm_fg = 0;
8306 HL_TABLE()[idx].sg_cterm_bg = 0;
8307 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008308#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008309 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008310 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8311 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008312 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8313 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008314 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8315 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008316#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008317#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008318 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8319 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008320#endif
8321#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008322 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008323 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8324 HL_TABLE()[idx].sg_font = NOFONT;
8325# ifdef FEAT_XFONTSET
8326 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8327 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8328# endif
8329 vim_free(HL_TABLE()[idx].sg_font_name);
8330 HL_TABLE()[idx].sg_font_name = NULL;
8331 HL_TABLE()[idx].sg_gui_attr = 0;
8332#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008333#ifdef FEAT_EVAL
8334 /* Clear the script ID only when there is no link, since that is not
8335 * cleared. */
8336 if (HL_TABLE()[idx].sg_link == 0)
8337 HL_TABLE()[idx].sg_scriptID = 0;
8338#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339}
8340
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008341#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008342/*
8343 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008344 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008345 * "Tooltip" colors.
8346 */
8347 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008348set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008349{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008350#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008351# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008352 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008354 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008355 if (set_group_colors((char_u *)"Normal",
8356 &gui.norm_pixel, &gui.back_pixel,
8357 FALSE, TRUE, FALSE))
8358 {
8359 gui_mch_new_colors();
8360 must_redraw = CLEAR;
8361 }
8362# ifdef FEAT_GUI_X11
8363 if (set_group_colors((char_u *)"Menu",
8364 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8365 TRUE, FALSE, FALSE))
8366 {
8367# ifdef FEAT_MENU
8368 gui_mch_new_menu_colors();
8369# endif
8370 must_redraw = CLEAR;
8371 }
8372# ifdef FEAT_BEVAL
8373 if (set_group_colors((char_u *)"Tooltip",
8374 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8375 FALSE, FALSE, TRUE))
8376 {
8377# ifdef FEAT_TOOLBAR
8378 gui_mch_new_tooltip_colors();
8379# endif
8380 must_redraw = CLEAR;
8381 }
8382# endif
8383 if (set_group_colors((char_u *)"Scrollbar",
8384 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8385 FALSE, FALSE, FALSE))
8386 {
8387 gui_new_scrollbar_colors();
8388 must_redraw = CLEAR;
8389 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008390# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008391 }
8392#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008393#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008394# ifdef FEAT_GUI
8395 else
8396# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008397 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008398 int idx;
8399
8400 idx = syn_name2id((char_u *)"Normal") - 1;
8401 if (idx >= 0)
8402 {
8403 gui_do_one_color(idx, FALSE, FALSE);
8404
8405 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8406 {
8407 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8408 must_redraw = CLEAR;
8409 }
8410 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8411 {
8412 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8413 must_redraw = CLEAR;
8414 }
8415 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008416 }
8417#endif
8418}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008420
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008421#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008422/*
8423 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8424 */
8425 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008426set_group_colors(
8427 char_u *name,
8428 guicolor_T *fgp,
8429 guicolor_T *bgp,
8430 int do_menu,
8431 int use_norm,
8432 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008433{
8434 int idx;
8435
8436 idx = syn_name2id(name) - 1;
8437 if (idx >= 0)
8438 {
8439 gui_do_one_color(idx, do_menu, do_tooltip);
8440
8441 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8442 *fgp = HL_TABLE()[idx].sg_gui_fg;
8443 else if (use_norm)
8444 *fgp = gui.def_norm_pixel;
8445 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8446 *bgp = HL_TABLE()[idx].sg_gui_bg;
8447 else if (use_norm)
8448 *bgp = gui.def_back_pixel;
8449 return TRUE;
8450 }
8451 return FALSE;
8452}
8453
8454/*
8455 * Get the font of the "Normal" group.
8456 * Returns "" when it's not found or not set.
8457 */
8458 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008459hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008460{
8461 int id;
8462 char_u *s;
8463
8464 id = syn_name2id((char_u *)"Normal");
8465 if (id > 0)
8466 {
8467 s = HL_TABLE()[id - 1].sg_font_name;
8468 if (s != NULL)
8469 return s;
8470 }
8471 return (char_u *)"";
8472}
8473
8474/*
8475 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8476 * actually chosen to be used.
8477 */
8478 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008479hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008480{
8481 int id;
8482
8483 id = syn_name2id((char_u *)"Normal");
8484 if (id > 0)
8485 {
8486 vim_free(HL_TABLE()[id - 1].sg_font_name);
8487 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8488 }
8489}
8490
8491/*
8492 * Set background color for "Normal" group. Called by gui_set_bg_color()
8493 * when the color is known.
8494 */
8495 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008496hl_set_bg_color_name(
8497 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008498{
8499 int id;
8500
8501 if (name != NULL)
8502 {
8503 id = syn_name2id((char_u *)"Normal");
8504 if (id > 0)
8505 {
8506 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8507 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8508 }
8509 }
8510}
8511
8512/*
8513 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8514 * when the color is known.
8515 */
8516 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008517hl_set_fg_color_name(
8518 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008519{
8520 int id;
8521
8522 if (name != NULL)
8523 {
8524 id = syn_name2id((char_u *)"Normal");
8525 if (id > 0)
8526 {
8527 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8528 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8529 }
8530 }
8531}
8532
8533/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008534 * Return the handle for a font name.
8535 * Returns NOFONT when failed.
8536 */
8537 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008538font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008539{
8540 if (STRCMP(name, "NONE") == 0)
8541 return NOFONT;
8542
8543 return gui_mch_get_font(name, TRUE);
8544}
8545
8546# ifdef FEAT_XFONTSET
8547/*
8548 * Return the handle for a fontset name.
8549 * Returns NOFONTSET when failed.
8550 */
8551 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008552fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008553{
8554 if (STRCMP(name, "NONE") == 0)
8555 return NOFONTSET;
8556
8557 return gui_mch_get_fontset(name, TRUE, fixed_width);
8558}
8559# endif
8560
8561/*
8562 * Get the font or fontset for one highlight group.
8563 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008564 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008565hl_do_font(
8566 int idx,
8567 char_u *arg,
8568 int do_normal, /* set normal font */
8569 int do_menu UNUSED, /* set menu font */
8570 int do_tooltip UNUSED, /* set tooltip font */
8571 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008572{
8573# ifdef FEAT_XFONTSET
8574 /* If 'guifontset' is not empty, first try using the name as a
8575 * fontset. If that doesn't work, use it as a font name. */
8576 if (*p_guifontset != NUL
8577# ifdef FONTSET_ALWAYS
8578 || do_menu
8579# endif
8580# ifdef FEAT_BEVAL_TIP
8581 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8582 || do_tooltip
8583# endif
8584 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008585 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008586 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008587 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008588 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8589# ifdef FONTSET_ALWAYS
8590 || do_menu
8591# endif
8592# ifdef FEAT_BEVAL_TIP
8593 || do_tooltip
8594# endif
8595 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008596 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008597 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8598 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008599 /* If it worked and it's the Normal group, use it as the normal
8600 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601 if (do_normal)
8602 gui_init_font(arg, TRUE);
8603# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8604 if (do_menu)
8605 {
8606# ifdef FONTSET_ALWAYS
8607 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8608# else
8609 /* YIKES! This is a bug waiting to crash the program */
8610 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8611# endif
8612 gui_mch_new_menu_font();
8613 }
8614# ifdef FEAT_BEVAL
8615 if (do_tooltip)
8616 {
8617 /* The Athena widget set cannot currently handle switching between
8618 * displaying a single font and a fontset.
8619 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008620 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008621 * XFontStruct is used.
8622 */
8623 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8624 gui_mch_new_tooltip_font();
8625 }
8626# endif
8627# endif
8628 }
8629 else
8630# endif
8631 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008632 if (free_font)
8633 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008634 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8635 /* If it worked and it's the Normal group, use it as the
8636 * normal font. Same for the Menu group. */
8637 if (HL_TABLE()[idx].sg_font != NOFONT)
8638 {
8639 if (do_normal)
8640 gui_init_font(arg, FALSE);
8641#ifndef FONTSET_ALWAYS
8642# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8643 if (do_menu)
8644 {
8645 gui.menu_font = HL_TABLE()[idx].sg_font;
8646 gui_mch_new_menu_font();
8647 }
8648# endif
8649#endif
8650 }
8651 }
8652}
8653
8654#endif /* FEAT_GUI */
8655
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008656#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008657/*
8658 * Return the handle for a color name.
8659 * Returns INVALCOLOR when failed.
8660 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008661 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008662color_name2handle(char_u *name)
8663{
8664 if (STRCMP(name, "NONE") == 0)
8665 return INVALCOLOR;
8666
8667 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8668 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008669#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008670 if (gui.in_use)
8671#endif
8672#ifdef FEAT_GUI
8673 return gui.norm_pixel;
8674#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008675#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008676 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008677 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008678 /* Guess that the foreground is black or white. */
8679 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008680#endif
8681 }
8682 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8683 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008684#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008685 if (gui.in_use)
8686#endif
8687#ifdef FEAT_GUI
8688 return gui.back_pixel;
8689#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008690#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008691 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008692 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008693 /* Guess that the background is white or black. */
8694 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008695#endif
8696 }
8697
8698 return GUI_GET_COLOR(name);
8699}
8700#endif
8701
Bram Moolenaar071d4272004-06-13 20:20:40 +00008702/*
8703 * Table with the specifications for an attribute number.
8704 * Note that this table is used by ALL buffers. This is required because the
8705 * GUI can redraw at any time for any buffer.
8706 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008707static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008708
8709#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8710
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008711static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008712
8713#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8714
8715#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008716static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008717
8718#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8719#endif
8720
8721/*
8722 * Return the attr number for a set of colors and font.
8723 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8724 * if the combination is new.
8725 * Return 0 for error (no more room).
8726 */
8727 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008728get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008729{
8730 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008731 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008732 static int recursive = FALSE;
8733
8734 /*
8735 * Init the table, in case it wasn't done yet.
8736 */
8737 table->ga_itemsize = sizeof(attrentry_T);
8738 table->ga_growsize = 7;
8739
8740 /*
8741 * Try to find an entry with the same specifications.
8742 */
8743 for (i = 0; i < table->ga_len; ++i)
8744 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008745 taep = &(((attrentry_T *)table->ga_data)[i]);
8746 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008747 && (
8748#ifdef FEAT_GUI
8749 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008750 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8751 && aep->ae_u.gui.bg_color
8752 == taep->ae_u.gui.bg_color
8753 && aep->ae_u.gui.sp_color
8754 == taep->ae_u.gui.sp_color
8755 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008756# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008757 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008758# endif
8759 ))
8760 ||
8761#endif
8762 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008763 && (aep->ae_u.term.start == NULL)
8764 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008765 && (aep->ae_u.term.start == NULL
8766 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008767 taep->ae_u.term.start) == 0)
8768 && (aep->ae_u.term.stop == NULL)
8769 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008770 && (aep->ae_u.term.stop == NULL
8771 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008772 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008773 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008774 && aep->ae_u.cterm.fg_color
8775 == taep->ae_u.cterm.fg_color
8776 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008777 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008778#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008779 && aep->ae_u.cterm.fg_rgb
8780 == taep->ae_u.cterm.fg_rgb
8781 && aep->ae_u.cterm.bg_rgb
8782 == taep->ae_u.cterm.bg_rgb
8783#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008784 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008785
8786 return i + ATTR_OFF;
8787 }
8788
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008789 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008790 {
8791 /*
8792 * Running out of attribute entries! remove all attributes, and
8793 * compute new ones for all groups.
8794 * When called recursively, we are really out of numbers.
8795 */
8796 if (recursive)
8797 {
8798 EMSG(_("E424: Too many different highlighting attributes in use"));
8799 return 0;
8800 }
8801 recursive = TRUE;
8802
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008803 clear_hl_tables();
8804
Bram Moolenaar071d4272004-06-13 20:20:40 +00008805 must_redraw = CLEAR;
8806
8807 for (i = 0; i < highlight_ga.ga_len; ++i)
8808 set_hl_attr(i);
8809
8810 recursive = FALSE;
8811 }
8812
8813 /*
8814 * This is a new combination of colors and font, add an entry.
8815 */
8816 if (ga_grow(table, 1) == FAIL)
8817 return 0;
8818
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008819 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8820 vim_memset(taep, 0, sizeof(attrentry_T));
8821 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008822#ifdef FEAT_GUI
8823 if (table == &gui_attr_table)
8824 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008825 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8826 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8827 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8828 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008829# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008830 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008831# endif
8832 }
8833#endif
8834 if (table == &term_attr_table)
8835 {
8836 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008837 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008838 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008839 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008840 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008841 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008842 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008843 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008844 }
8845 else if (table == &cterm_attr_table)
8846 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008847 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8848 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008849#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008850 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8851 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8852#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008853 }
8854 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008855 return (table->ga_len - 1 + ATTR_OFF);
8856}
8857
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008858/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008859 * Get an attribute index for a cterm entry.
8860 * Uses an existing entry when possible or adds one when needed.
8861 */
8862 int
8863get_cterm_attr_idx(int attr, int fg, int bg)
8864{
8865 attrentry_T at_en;
8866
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008867 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaareeac6772017-07-23 15:48:37 +02008868 at_en.ae_attr = attr;
8869 at_en.ae_u.cterm.fg_color = fg;
8870 at_en.ae_u.cterm.bg_color = bg;
8871 return get_attr_entry(&cterm_attr_table, &at_en);
8872}
8873
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008874#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8875/*
8876 * Get an attribute index for a 'termguicolors' entry.
8877 * Uses an existing entry when possible or adds one when needed.
8878 */
8879 int
8880get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8881{
8882 attrentry_T at_en;
8883
8884 vim_memset(&at_en, 0, sizeof(attrentry_T));
8885 at_en.ae_attr = attr;
8886 at_en.ae_u.cterm.fg_rgb = fg;
8887 at_en.ae_u.cterm.bg_rgb = bg;
8888 return get_attr_entry(&cterm_attr_table, &at_en);
8889}
8890#endif
8891
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008892#if defined(FEAT_GUI) || defined(PROTO)
8893/*
8894 * Get an attribute index for a cterm entry.
8895 * Uses an existing entry when possible or adds one when needed.
8896 */
8897 int
8898get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8899{
8900 attrentry_T at_en;
8901
8902 vim_memset(&at_en, 0, sizeof(attrentry_T));
8903 at_en.ae_attr = attr;
8904 at_en.ae_u.gui.fg_color = fg;
8905 at_en.ae_u.gui.bg_color = bg;
8906 return get_attr_entry(&gui_attr_table, &at_en);
8907}
8908#endif
8909
Bram Moolenaareeac6772017-07-23 15:48:37 +02008910/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008911 * Clear all highlight tables.
8912 */
8913 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008914clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008915{
8916 int i;
8917 attrentry_T *taep;
8918
8919#ifdef FEAT_GUI
8920 ga_clear(&gui_attr_table);
8921#endif
8922 for (i = 0; i < term_attr_table.ga_len; ++i)
8923 {
8924 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8925 vim_free(taep->ae_u.term.start);
8926 vim_free(taep->ae_u.term.stop);
8927 }
8928 ga_clear(&term_attr_table);
8929 ga_clear(&cterm_attr_table);
8930}
8931
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008932#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008933/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008934 * Combine special attributes (e.g., for spelling) with other attributes
8935 * (e.g., for syntax highlighting).
8936 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008937 * This creates a new group when required.
8938 * Since we expect there to be few spelling mistakes we don't cache the
8939 * result.
8940 * Return the resulting attributes.
8941 */
8942 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008943hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008944{
8945 attrentry_T *char_aep = NULL;
8946 attrentry_T *spell_aep;
8947 attrentry_T new_en;
8948
8949 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008950 return prim_attr;
8951 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008952 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008953#ifdef FEAT_GUI
8954 if (gui.in_use)
8955 {
8956 if (char_attr > HL_ALL)
8957 char_aep = syn_gui_attr2entry(char_attr);
8958 if (char_aep != NULL)
8959 new_en = *char_aep;
8960 else
8961 {
8962 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008963 new_en.ae_u.gui.fg_color = INVALCOLOR;
8964 new_en.ae_u.gui.bg_color = INVALCOLOR;
8965 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008966 if (char_attr <= HL_ALL)
8967 new_en.ae_attr = char_attr;
8968 }
8969
Bram Moolenaar30abd282005-06-22 22:35:10 +00008970 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008971 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008972 else
8973 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008974 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008975 if (spell_aep != NULL)
8976 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008977 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
8978 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008979 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8980 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8981 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8982 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8983 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8984 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8985 if (spell_aep->ae_u.gui.font != NOFONT)
8986 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8987# ifdef FEAT_XFONTSET
8988 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8989 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8990# endif
8991 }
8992 }
8993 return get_attr_entry(&gui_attr_table, &new_en);
8994 }
8995#endif
8996
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008997 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008998 {
8999 if (char_attr > HL_ALL)
9000 char_aep = syn_cterm_attr2entry(char_attr);
9001 if (char_aep != NULL)
9002 new_en = *char_aep;
9003 else
9004 {
9005 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009006#ifdef FEAT_TERMGUICOLORS
9007 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9008 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9009#endif
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_cterm_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.cterm.fg_color > 0)
9024 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9025 if (spell_aep->ae_u.cterm.bg_color > 0)
9026 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009027#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009028 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009029 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009030 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009031 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9032#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009033 }
9034 }
9035 return get_attr_entry(&cterm_attr_table, &new_en);
9036 }
9037
9038 if (char_attr > HL_ALL)
9039 char_aep = syn_term_attr2entry(char_attr);
9040 if (char_aep != NULL)
9041 new_en = *char_aep;
9042 else
9043 {
9044 vim_memset(&new_en, 0, sizeof(new_en));
9045 if (char_attr <= HL_ALL)
9046 new_en.ae_attr = char_attr;
9047 }
9048
Bram Moolenaar30abd282005-06-22 22:35:10 +00009049 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009050 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009051 else
9052 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009053 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009054 if (spell_aep != NULL)
9055 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009056 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009057 if (spell_aep->ae_u.term.start != NULL)
9058 {
9059 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9060 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9061 }
9062 }
9063 }
9064 return get_attr_entry(&term_attr_table, &new_en);
9065}
9066#endif
9067
Bram Moolenaar071d4272004-06-13 20:20:40 +00009068#ifdef FEAT_GUI
9069
9070 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009071syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009072{
9073 attr -= ATTR_OFF;
9074 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9075 return NULL;
9076 return &(GUI_ATTR_ENTRY(attr));
9077}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009078#endif /* FEAT_GUI */
9079
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009080/*
9081 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9082 * Only to be used when "attr" > HL_ALL.
9083 */
9084 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009085syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009086{
9087 attrentry_T *aep;
9088
9089#ifdef FEAT_GUI
9090 if (gui.in_use)
9091 aep = syn_gui_attr2entry(attr);
9092 else
9093#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009094 if (IS_CTERM)
9095 aep = syn_cterm_attr2entry(attr);
9096 else
9097 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009098
9099 if (aep == NULL) /* highlighting not set */
9100 return 0;
9101 return aep->ae_attr;
9102}
9103
9104
Bram Moolenaar071d4272004-06-13 20:20:40 +00009105 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009106syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009107{
9108 attr -= ATTR_OFF;
9109 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9110 return NULL;
9111 return &(TERM_ATTR_ENTRY(attr));
9112}
9113
9114 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009115syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009116{
9117 attr -= ATTR_OFF;
9118 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9119 return NULL;
9120 return &(CTERM_ATTR_ENTRY(attr));
9121}
9122
9123#define LIST_ATTR 1
9124#define LIST_STRING 2
9125#define LIST_INT 3
9126
9127 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009128highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009129{
9130 struct hl_group *sgp;
9131 int didh = FALSE;
9132
9133 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9134
9135 didh = highlight_list_arg(id, didh, LIST_ATTR,
9136 sgp->sg_term, NULL, "term");
9137 didh = highlight_list_arg(id, didh, LIST_STRING,
9138 0, sgp->sg_start, "start");
9139 didh = highlight_list_arg(id, didh, LIST_STRING,
9140 0, sgp->sg_stop, "stop");
9141
9142 didh = highlight_list_arg(id, didh, LIST_ATTR,
9143 sgp->sg_cterm, NULL, "cterm");
9144 didh = highlight_list_arg(id, didh, LIST_INT,
9145 sgp->sg_cterm_fg, NULL, "ctermfg");
9146 didh = highlight_list_arg(id, didh, LIST_INT,
9147 sgp->sg_cterm_bg, NULL, "ctermbg");
9148
Bram Moolenaar61623362010-07-14 22:04:22 +02009149#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009150 didh = highlight_list_arg(id, didh, LIST_ATTR,
9151 sgp->sg_gui, NULL, "gui");
9152 didh = highlight_list_arg(id, didh, LIST_STRING,
9153 0, sgp->sg_gui_fg_name, "guifg");
9154 didh = highlight_list_arg(id, didh, LIST_STRING,
9155 0, sgp->sg_gui_bg_name, "guibg");
9156 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009157 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009158#endif
9159#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009160 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009161 0, sgp->sg_font_name, "font");
9162#endif
9163
Bram Moolenaar661b1822005-07-28 22:36:45 +00009164 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009165 {
9166 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009167 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009168 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009169 msg_putchar(' ');
9170 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9171 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009172
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009173 if (!didh)
9174 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009175#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009176 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009177 last_set_msg(sgp->sg_scriptID);
9178#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009179}
9180
9181 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009182highlight_list_arg(
9183 int id,
9184 int didh,
9185 int type,
9186 int iarg,
9187 char_u *sarg,
9188 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009189{
9190 char_u buf[100];
9191 char_u *ts;
9192 int i;
9193
Bram Moolenaar661b1822005-07-28 22:36:45 +00009194 if (got_int)
9195 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009196 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9197 {
9198 ts = buf;
9199 if (type == LIST_INT)
9200 sprintf((char *)buf, "%d", iarg - 1);
9201 else if (type == LIST_STRING)
9202 ts = sarg;
9203 else /* type == LIST_ATTR */
9204 {
9205 buf[0] = NUL;
9206 for (i = 0; hl_attr_table[i] != 0; ++i)
9207 {
9208 if (iarg & hl_attr_table[i])
9209 {
9210 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009211 vim_strcat(buf, (char_u *)",", 100);
9212 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009213 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9214 }
9215 }
9216 }
9217
9218 (void)syn_list_header(didh,
9219 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9220 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009221 if (!got_int)
9222 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009223 if (*name != NUL)
9224 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009225 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9226 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009227 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009228 msg_outtrans(ts);
9229 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009230 }
9231 return didh;
9232}
9233
9234#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9235/*
9236 * Return "1" if highlight group "id" has attribute "flag".
9237 * Return NULL otherwise.
9238 */
9239 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009240highlight_has_attr(
9241 int id,
9242 int flag,
9243 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009244{
9245 int attr;
9246
9247 if (id <= 0 || id > highlight_ga.ga_len)
9248 return NULL;
9249
Bram Moolenaar61623362010-07-14 22:04:22 +02009250#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009251 if (modec == 'g')
9252 attr = HL_TABLE()[id - 1].sg_gui;
9253 else
9254#endif
9255 if (modec == 'c')
9256 attr = HL_TABLE()[id - 1].sg_cterm;
9257 else
9258 attr = HL_TABLE()[id - 1].sg_term;
9259
9260 if (attr & flag)
9261 return (char_u *)"1";
9262 return NULL;
9263}
9264#endif
9265
9266#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9267/*
9268 * Return color name of highlight group "id".
9269 */
9270 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009271highlight_color(
9272 int id,
9273 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9274 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009275{
9276 static char_u name[20];
9277 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009278 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009279 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009280 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009281
9282 if (id <= 0 || id > highlight_ga.ga_len)
9283 return NULL;
9284
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009285 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009286 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009287 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009288 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009289 font = TRUE;
9290 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009291 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009292 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9293 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009294 if (modec == 'g')
9295 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009296# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009297# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009298 /* return font name */
9299 if (font)
9300 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009301# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009302
Bram Moolenaar071d4272004-06-13 20:20:40 +00009303 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009304 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009305 {
9306 guicolor_T color;
9307 long_u rgb;
9308 static char_u buf[10];
9309
9310 if (fg)
9311 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009312 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009313# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009314 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009315# else
9316 color = INVALCOLOR;
9317# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009318 else
9319 color = HL_TABLE()[id - 1].sg_gui_bg;
9320 if (color == INVALCOLOR)
9321 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009322 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009323 sprintf((char *)buf, "#%02x%02x%02x",
9324 (unsigned)(rgb >> 16),
9325 (unsigned)(rgb >> 8) & 255,
9326 (unsigned)rgb & 255);
9327 return buf;
9328 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009329# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009330 if (fg)
9331 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009332 if (sp)
9333 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009334 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9335 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009336 if (font || sp)
9337 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009338 if (modec == 'c')
9339 {
9340 if (fg)
9341 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9342 else
9343 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009344 if (n < 0)
9345 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009346 sprintf((char *)name, "%d", n);
9347 return name;
9348 }
9349 /* term doesn't have color */
9350 return NULL;
9351}
9352#endif
9353
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009354#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009355 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009356 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009357/*
9358 * Return color name of highlight group "id" as RGB value.
9359 */
9360 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009361highlight_gui_color_rgb(
9362 int id,
9363 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009364{
9365 guicolor_T color;
9366
9367 if (id <= 0 || id > highlight_ga.ga_len)
9368 return 0L;
9369
9370 if (fg)
9371 color = HL_TABLE()[id - 1].sg_gui_fg;
9372 else
9373 color = HL_TABLE()[id - 1].sg_gui_bg;
9374
9375 if (color == INVALCOLOR)
9376 return 0L;
9377
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009378 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009379}
9380#endif
9381
9382/*
9383 * Output the syntax list header.
9384 * Return TRUE when started a new line.
9385 */
9386 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009387syn_list_header(
9388 int did_header, /* did header already */
9389 int outlen, /* length of string that comes */
9390 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009391{
9392 int endcol = 19;
9393 int newline = TRUE;
9394
9395 if (!did_header)
9396 {
9397 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009398 if (got_int)
9399 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009400 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9401 endcol = 15;
9402 }
9403 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009404 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009405 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009406 if (got_int)
9407 return TRUE;
9408 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009409 else
9410 {
9411 if (msg_col >= endcol) /* wrap around is like starting a new line */
9412 newline = FALSE;
9413 }
9414
9415 if (msg_col >= endcol) /* output at least one space */
9416 endcol = msg_col + 1;
9417 if (Columns <= endcol) /* avoid hang for tiny window */
9418 endcol = Columns - 1;
9419
9420 msg_advance(endcol);
9421
9422 /* Show "xxx" with the attributes. */
9423 if (!did_header)
9424 {
9425 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9426 msg_putchar(' ');
9427 }
9428
9429 return newline;
9430}
9431
9432/*
9433 * Set the attribute numbers for a highlight group.
9434 * Called after one of the attributes has changed.
9435 */
9436 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009437set_hl_attr(
9438 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009439{
9440 attrentry_T at_en;
9441 struct hl_group *sgp = HL_TABLE() + idx;
9442
9443 /* The "Normal" group doesn't need an attribute number */
9444 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9445 return;
9446
9447#ifdef FEAT_GUI
9448 /*
9449 * For the GUI mode: If there are other than "normal" highlighting
9450 * attributes, need to allocate an attr number.
9451 */
9452 if (sgp->sg_gui_fg == INVALCOLOR
9453 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009454 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009455 && sgp->sg_font == NOFONT
9456# ifdef FEAT_XFONTSET
9457 && sgp->sg_fontset == NOFONTSET
9458# endif
9459 )
9460 {
9461 sgp->sg_gui_attr = sgp->sg_gui;
9462 }
9463 else
9464 {
9465 at_en.ae_attr = sgp->sg_gui;
9466 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9467 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009468 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009469 at_en.ae_u.gui.font = sgp->sg_font;
9470# ifdef FEAT_XFONTSET
9471 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9472# endif
9473 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9474 }
9475#endif
9476 /*
9477 * For the term mode: If there are other than "normal" highlighting
9478 * attributes, need to allocate an attr number.
9479 */
9480 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9481 sgp->sg_term_attr = sgp->sg_term;
9482 else
9483 {
9484 at_en.ae_attr = sgp->sg_term;
9485 at_en.ae_u.term.start = sgp->sg_start;
9486 at_en.ae_u.term.stop = sgp->sg_stop;
9487 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9488 }
9489
9490 /*
9491 * For the color term mode: If there are other than "normal"
9492 * highlighting attributes, need to allocate an attr number.
9493 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009494 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009495# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009496 && sgp->sg_gui_fg == INVALCOLOR
9497 && sgp->sg_gui_bg == INVALCOLOR
9498# endif
9499 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009500 sgp->sg_cterm_attr = sgp->sg_cterm;
9501 else
9502 {
9503 at_en.ae_attr = sgp->sg_cterm;
9504 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9505 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009506# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009507 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9508 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009509# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009510 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9511 }
9512}
9513
9514/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009515 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009516 * If it is not found, 0 is returned.
9517 */
9518 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009519syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009520{
9521 int i;
9522 char_u name_u[200];
9523
9524 /* Avoid using stricmp() too much, it's slow on some systems */
9525 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9526 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009527 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009528 vim_strup(name_u);
9529 for (i = highlight_ga.ga_len; --i >= 0; )
9530 if (HL_TABLE()[i].sg_name_u != NULL
9531 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9532 break;
9533 return i + 1;
9534}
9535
9536#if defined(FEAT_EVAL) || defined(PROTO)
9537/*
9538 * Return TRUE if highlight group "name" exists.
9539 */
9540 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009541highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009542{
9543 return (syn_name2id(name) > 0);
9544}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009545
9546# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9547/*
9548 * Return the name of highlight group "id".
9549 * When not a valid ID return an empty string.
9550 */
9551 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009552syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009553{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009554 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009555 return (char_u *)"";
9556 return HL_TABLE()[id - 1].sg_name;
9557}
9558# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009559#endif
9560
9561/*
9562 * Like syn_name2id(), but take a pointer + length argument.
9563 */
9564 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009565syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009566{
9567 char_u *name;
9568 int id = 0;
9569
9570 name = vim_strnsave(linep, len);
9571 if (name != NULL)
9572 {
9573 id = syn_name2id(name);
9574 vim_free(name);
9575 }
9576 return id;
9577}
9578
9579/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009580 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009581 * The argument is a pointer to the name and the length of the name.
9582 * If it doesn't exist yet, a new entry is created.
9583 * Return 0 for failure.
9584 */
9585 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009586syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009587{
9588 int id;
9589 char_u *name;
9590
9591 name = vim_strnsave(pp, len);
9592 if (name == NULL)
9593 return 0;
9594
9595 id = syn_name2id(name);
9596 if (id == 0) /* doesn't exist yet */
9597 id = syn_add_group(name);
9598 else
9599 vim_free(name);
9600 return id;
9601}
9602
9603/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009604 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009605 * "name" must be an allocated string, it will be consumed.
9606 * Return 0 for failure.
9607 */
9608 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009609syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009610{
9611 char_u *p;
9612
9613 /* Check that the name is ASCII letters, digits and underscore. */
9614 for (p = name; *p != NUL; ++p)
9615 {
9616 if (!vim_isprintc(*p))
9617 {
9618 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009619 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009620 return 0;
9621 }
9622 else if (!ASCII_ISALNUM(*p) && *p != '_')
9623 {
9624 /* This is an error, but since there previously was no check only
9625 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009626 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009627 MSG(_("W18: Invalid character in group name"));
9628 break;
9629 }
9630 }
9631
9632 /*
9633 * First call for this growarray: init growing array.
9634 */
9635 if (highlight_ga.ga_data == NULL)
9636 {
9637 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9638 highlight_ga.ga_growsize = 10;
9639 }
9640
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009641 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009642 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009643 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009644 vim_free(name);
9645 return 0;
9646 }
9647
Bram Moolenaar071d4272004-06-13 20:20:40 +00009648 /*
9649 * Make room for at least one other syntax_highlight entry.
9650 */
9651 if (ga_grow(&highlight_ga, 1) == FAIL)
9652 {
9653 vim_free(name);
9654 return 0;
9655 }
9656
9657 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9658 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9659 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009660#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009661 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9662 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009663# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009664 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009665# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009666#endif
9667 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009668
9669 return highlight_ga.ga_len; /* ID is index plus one */
9670}
9671
9672/*
9673 * When, just after calling syn_add_group(), an error is discovered, this
9674 * function deletes the new name.
9675 */
9676 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009677syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009678{
9679 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009680 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9681 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9682}
9683
9684/*
9685 * Translate a group ID to highlight attributes.
9686 */
9687 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009688syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009689{
9690 int attr;
9691 struct hl_group *sgp;
9692
9693 hl_id = syn_get_final_id(hl_id);
9694 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9695
9696#ifdef FEAT_GUI
9697 /*
9698 * Only use GUI attr when the GUI is being used.
9699 */
9700 if (gui.in_use)
9701 attr = sgp->sg_gui_attr;
9702 else
9703#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009704 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009705 attr = sgp->sg_cterm_attr;
9706 else
9707 attr = sgp->sg_term_attr;
9708
9709 return attr;
9710}
9711
9712#ifdef FEAT_GUI
9713/*
9714 * Get the GUI colors and attributes for a group ID.
9715 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9716 */
9717 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009718syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009719{
9720 struct hl_group *sgp;
9721
9722 hl_id = syn_get_final_id(hl_id);
9723 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9724
9725 *fgp = sgp->sg_gui_fg;
9726 *bgp = sgp->sg_gui_bg;
9727 return sgp->sg_gui;
9728}
9729#endif
9730
9731/*
9732 * Translate a group ID to the final group ID (following links).
9733 */
9734 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009735syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009736{
9737 int count;
9738 struct hl_group *sgp;
9739
9740 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9741 return 0; /* Can be called from eval!! */
9742
9743 /*
9744 * Follow links until there is no more.
9745 * Look out for loops! Break after 100 links.
9746 */
9747 for (count = 100; --count >= 0; )
9748 {
9749 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9750 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9751 break;
9752 hl_id = sgp->sg_link;
9753 }
9754
9755 return hl_id;
9756}
9757
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009758#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009759/*
9760 * Call this function just after the GUI has started.
9761 * It finds the font and color handles for the highlighting groups.
9762 */
9763 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009764highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009765{
9766 int idx;
9767
9768 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009769# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9770# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009771 if (USE_24BIT)
9772# endif
9773 set_normal_colors();
9774# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009775
9776 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9777 gui_do_one_color(idx, FALSE, FALSE);
9778
9779 highlight_changed();
9780}
9781
9782 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009783gui_do_one_color(
9784 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009785 int do_menu UNUSED, /* TRUE: might set the menu font */
9786 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009787{
9788 int didit = FALSE;
9789
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009790# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009791# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009792 if (gui.in_use)
9793# endif
9794 if (HL_TABLE()[idx].sg_font_name != NULL)
9795 {
9796 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009797 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009798 didit = TRUE;
9799 }
9800# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009801 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9802 {
9803 HL_TABLE()[idx].sg_gui_fg =
9804 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9805 didit = TRUE;
9806 }
9807 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9808 {
9809 HL_TABLE()[idx].sg_gui_bg =
9810 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9811 didit = TRUE;
9812 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009813# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009814 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9815 {
9816 HL_TABLE()[idx].sg_gui_sp =
9817 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9818 didit = TRUE;
9819 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009820# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009821 if (didit) /* need to get a new attr number */
9822 set_hl_attr(idx);
9823}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009824#endif
9825
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009826#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9827/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009828 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009829 */
9830 static void
9831combine_stl_hlt(
9832 int id,
9833 int id_S,
9834 int id_alt,
9835 int hlcnt,
9836 int i,
9837 int hlf,
9838 int *table)
9839{
9840 struct hl_group *hlt = HL_TABLE();
9841
9842 if (id_alt == 0)
9843 {
9844 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9845 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9846 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9847# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9848 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9849# endif
9850 }
9851 else
9852 mch_memmove(&hlt[hlcnt + i],
9853 &hlt[id_alt - 1],
9854 sizeof(struct hl_group));
9855 hlt[hlcnt + i].sg_link = 0;
9856
9857 hlt[hlcnt + i].sg_term ^=
9858 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9859 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9860 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9861 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9862 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9863 hlt[hlcnt + i].sg_cterm ^=
9864 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9865 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9866 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9867 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9868 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9869# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9870 hlt[hlcnt + i].sg_gui ^=
9871 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9872# endif
9873# ifdef FEAT_GUI
9874 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9875 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9876 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9877 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9878 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9879 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9880 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9881 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9882# ifdef FEAT_XFONTSET
9883 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9884 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9885# endif
9886# endif
9887 highlight_ga.ga_len = hlcnt + i + 1;
9888 set_hl_attr(hlcnt + i); /* At long last we can apply */
9889 table[i] = syn_id2attr(hlcnt + i + 1);
9890}
9891#endif
9892
Bram Moolenaar071d4272004-06-13 20:20:40 +00009893/*
9894 * Translate the 'highlight' option into attributes in highlight_attr[] and
9895 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9896 * corresponding highlights to use on top of HLF_SNC is computed.
9897 * Called only when the 'highlight' option has been changed and upon first
9898 * screen redraw after any :highlight command.
9899 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9900 */
9901 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009902highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009903{
9904 int hlf;
9905 int i;
9906 char_u *p;
9907 int attr;
9908 char_u *end;
9909 int id;
9910#ifdef USER_HIGHLIGHT
9911 char_u userhl[10];
9912# ifdef FEAT_STL_OPT
9913 int id_SNC = -1;
9914 int id_S = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009915# ifdef FEAT_TERMINAL
9916 int id_ST = -1;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009917 int id_STNC = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009918# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009919 int hlcnt;
9920# endif
9921#endif
9922 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9923
9924 need_highlight_changed = FALSE;
9925
9926 /*
9927 * Clear all attributes.
9928 */
9929 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9930 highlight_attr[hlf] = 0;
9931
9932 /*
9933 * First set all attributes to their default value.
9934 * Then use the attributes from the 'highlight' option.
9935 */
9936 for (i = 0; i < 2; ++i)
9937 {
9938 if (i)
9939 p = p_hl;
9940 else
9941 p = get_highlight_default();
9942 if (p == NULL) /* just in case */
9943 continue;
9944
9945 while (*p)
9946 {
9947 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9948 if (hl_flags[hlf] == *p)
9949 break;
9950 ++p;
9951 if (hlf == (int)HLF_COUNT || *p == NUL)
9952 return FAIL;
9953
9954 /*
9955 * Allow several hl_flags to be combined, like "bu" for
9956 * bold-underlined.
9957 */
9958 attr = 0;
9959 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9960 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01009961 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009962 continue;
9963
9964 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9965 return FAIL;
9966
9967 switch (*p)
9968 {
9969 case 'b': attr |= HL_BOLD;
9970 break;
9971 case 'i': attr |= HL_ITALIC;
9972 break;
9973 case '-':
9974 case 'n': /* no highlighting */
9975 break;
9976 case 'r': attr |= HL_INVERSE;
9977 break;
9978 case 's': attr |= HL_STANDOUT;
9979 break;
9980 case 'u': attr |= HL_UNDERLINE;
9981 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009982 case 'c': attr |= HL_UNDERCURL;
9983 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02009984 case 't': attr |= HL_STRIKETHROUGH;
9985 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009986 case ':': ++p; /* highlight group name */
9987 if (attr || *p == NUL) /* no combinations */
9988 return FAIL;
9989 end = vim_strchr(p, ',');
9990 if (end == NULL)
9991 end = p + STRLEN(p);
9992 id = syn_check_group(p, (int)(end - p));
9993 if (id == 0)
9994 return FAIL;
9995 attr = syn_id2attr(id);
9996 p = end - 1;
9997#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9998 if (hlf == (int)HLF_SNC)
9999 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010000# ifdef FEAT_TERMINAL
10001 else if (hlf == (int)HLF_ST)
10002 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010003 else if (hlf == (int)HLF_STNC)
10004 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010005# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010006 else if (hlf == (int)HLF_S)
10007 id_S = syn_get_final_id(id);
10008#endif
10009 break;
10010 default: return FAIL;
10011 }
10012 }
10013 highlight_attr[hlf] = attr;
10014
10015 p = skip_to_option_part(p); /* skip comma and spaces */
10016 }
10017 }
10018
10019#ifdef USER_HIGHLIGHT
10020 /* Setup the user highlights
10021 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010022 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010023 * 9 for User1-User9 combined with StatusLineNC
10024 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010025 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010026 * 1 for StatusLine default
10027 * Have to be in there simultaneously in case of table overflows in
10028 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010029 */
10030# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010031 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010032 return FAIL;
10033 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010034 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010035 {
10036 /* Make sure id_S is always valid to simplify code below. Use the last
10037 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010038 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010039 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10040 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010041 }
10042# endif
10043 for (i = 0; i < 9; i++)
10044 {
10045 sprintf((char *)userhl, "User%d", i + 1);
10046 id = syn_name2id(userhl);
10047 if (id == 0)
10048 {
10049 highlight_user[i] = 0;
10050# ifdef FEAT_STL_OPT
10051 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010052# ifdef FEAT_TERMINAL
10053 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010054 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010055# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010056# endif
10057 }
10058 else
10059 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010060 highlight_user[i] = syn_id2attr(id);
10061# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010062 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10063 HLF_SNC, highlight_stlnc);
10064# ifdef FEAT_TERMINAL
10065 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10066 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010067 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10068 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010069# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010070# endif
10071 }
10072 }
10073# ifdef FEAT_STL_OPT
10074 highlight_ga.ga_len = hlcnt;
10075# endif
10076
10077#endif /* USER_HIGHLIGHT */
10078
10079 return OK;
10080}
10081
Bram Moolenaar4f688582007-07-24 12:34:30 +000010082#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010083
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010084static void highlight_list(void);
10085static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010086
10087/*
10088 * Handle command line completion for :highlight command.
10089 */
10090 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010091set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010092{
10093 char_u *p;
10094
10095 /* Default: expand group names */
10096 xp->xp_context = EXPAND_HIGHLIGHT;
10097 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010098 include_link = 2;
10099 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010100
10101 /* (part of) subcommand already typed */
10102 if (*arg != NUL)
10103 {
10104 p = skiptowhite(arg);
10105 if (*p != NUL) /* past "default" or group name */
10106 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010107 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010108 if (STRNCMP("default", arg, p - arg) == 0)
10109 {
10110 arg = skipwhite(p);
10111 xp->xp_pattern = arg;
10112 p = skiptowhite(arg);
10113 }
10114 if (*p != NUL) /* past group name */
10115 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010116 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010117 if (arg[1] == 'i' && arg[0] == 'N')
10118 highlight_list();
10119 if (STRNCMP("link", arg, p - arg) == 0
10120 || STRNCMP("clear", arg, p - arg) == 0)
10121 {
10122 xp->xp_pattern = skipwhite(p);
10123 p = skiptowhite(xp->xp_pattern);
10124 if (*p != NUL) /* past first group name */
10125 {
10126 xp->xp_pattern = skipwhite(p);
10127 p = skiptowhite(xp->xp_pattern);
10128 }
10129 }
10130 if (*p != NUL) /* past group name(s) */
10131 xp->xp_context = EXPAND_NOTHING;
10132 }
10133 }
10134 }
10135}
10136
10137/*
10138 * List highlighting matches in a nice way.
10139 */
10140 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010141highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010142{
10143 int i;
10144
10145 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010146 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010147 for (i = 40; --i >= 0; )
10148 highlight_list_two(99, 0);
10149}
10150
10151 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010152highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010153{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010154 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010155 msg_clr_eos();
10156 out_flush();
10157 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10158}
10159
10160#endif /* FEAT_CMDL_COMPL */
10161
10162#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10163 || defined(FEAT_SIGNS) || defined(PROTO)
10164/*
10165 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010166 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010167 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010168get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010169{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010170 return get_highlight_name_ext(xp, idx, TRUE);
10171}
10172
10173/*
10174 * Obtain a highlight group name.
10175 * When "skip_cleared" is TRUE don't return a cleared entry.
10176 */
10177 char_u *
10178get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10179{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010180 if (idx < 0)
10181 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010182
10183 /* Items are never removed from the table, skip the ones that were
10184 * cleared. */
10185 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10186 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010187
Bram Moolenaar071d4272004-06-13 20:20:40 +000010188#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010189 if (idx == highlight_ga.ga_len && include_none != 0)
10190 return (char_u *)"none";
10191 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010192 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010193 if (idx == highlight_ga.ga_len + include_none + include_default
10194 && include_link != 0)
10195 return (char_u *)"link";
10196 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10197 && include_link != 0)
10198 return (char_u *)"clear";
10199#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010200 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010201 return NULL;
10202 return HL_TABLE()[idx].sg_name;
10203}
10204#endif
10205
Bram Moolenaar4f688582007-07-24 12:34:30 +000010206#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010207/*
10208 * Free all the highlight group fonts.
10209 * Used when quitting for systems which need it.
10210 */
10211 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010212free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010213{
10214 int idx;
10215
10216 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10217 {
10218 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10219 HL_TABLE()[idx].sg_font = NOFONT;
10220# ifdef FEAT_XFONTSET
10221 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10222 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10223# endif
10224 }
10225
10226 gui_mch_free_font(gui.norm_font);
10227# ifdef FEAT_XFONTSET
10228 gui_mch_free_fontset(gui.fontset);
10229# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010230# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010231 gui_mch_free_font(gui.bold_font);
10232 gui_mch_free_font(gui.ital_font);
10233 gui_mch_free_font(gui.boldital_font);
10234# endif
10235}
10236#endif
10237
10238/**************************************
10239 * End of Highlighting stuff *
10240 **************************************/