blob: 8fb4e20e5052f717642528fc268017692f8acf80 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010025 int sg_cleared; /* "hi clear" was used */
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/* for normal terminals */
27 int sg_term; /* "term=" highlighting attributes */
28 char_u *sg_start; /* terminal string for start highl */
29 char_u *sg_stop; /* terminal string for stop highl */
30 int sg_term_attr; /* Screen attr for term mode */
31/* for color terminals */
32 int sg_cterm; /* "cterm=" highlighting attr */
33 int sg_cterm_bold; /* bold attr was set for light color */
34 int sg_cterm_fg; /* terminal fg color number + 1 */
35 int sg_cterm_bg; /* terminal bg color number + 1 */
36 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000037/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000040 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020041#endif
42#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 GuiFont sg_font; /* GUI font handle */
45#ifdef FEAT_XFONTSET
46 GuiFontset sg_fontset; /* GUI fontset handle */
47#endif
48 char_u *sg_font_name; /* GUI font or fontset name */
49 int sg_gui_attr; /* Screen attr for GUI mode */
50#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020051#if defined(FEAT_GUI) || defined(FEAT_EVAL)
52/* Store the sp color name for the GUI or synIDattr() */
53 int sg_gui; /* "gui=" highlighting attributes */
54 char_u *sg_gui_fg_name;/* GUI foreground color name */
55 char_u *sg_gui_bg_name;/* GUI background color name */
56 char_u *sg_gui_sp_name;/* GUI special color name */
57#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 int sg_link; /* link to this highlight group ID */
59 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000060#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +020061 sctx_T sg_script_ctx; /* script in which the group was last set */
Bram Moolenaar661b1822005-07-28 22:36:45 +000062#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000063};
64
65#define SG_TERM 1 /* term has been set */
66#define SG_CTERM 2 /* cterm has been set */
67#define SG_GUI 4 /* gui has been set */
68#define SG_LINK 8 /* link has been set */
69
70static garray_T highlight_ga; /* highlight groups for 'highlight' option */
71
72#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
73
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020074#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000077/* Flags to indicate an additional string for highlight name completion. */
78static int include_none = 0; /* when 1 include "None" */
79static int include_default = 0; /* when 1 include "default" */
80static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000081#endif
82
83/*
84 * The "term", "cterm" and "gui" arguments can be any combination of the
85 * following names, separated by commas (but no spaces!).
86 */
87static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {"bold", "standout", "underline", "undercurl",
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020089 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
Bram Moolenaar0cd2a942017-08-12 15:12:30 +020092#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
Bram Moolenaar071d4272004-06-13 20:20:40 +000093
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010094static 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).
Bram Moolenaar36f92302018-02-24 21:36:34 +0100148 *
149 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 */
151typedef struct syn_pattern
152{
153 char sp_type; /* see SPTYPE_ defines below */
154 char sp_syncing; /* this item used for syncing */
Bram Moolenaar36f92302018-02-24 21:36:34 +0100155 short sp_syn_match_id; /* highlight group ID of pattern */
156 short sp_off_flags; /* see below */
157 int sp_offsets[SPO_COUNT]; /* offsets */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200158 int sp_flags; /* see HL_ defines below */
159#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200160 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200161#endif
Bram Moolenaar36f92302018-02-24 21:36:34 +0100162 int sp_ic; /* ignore-case flag for sp_prog */
163 int sp_sync_idx; /* sync item index (syncing only) */
164 int sp_line_id; /* ID of last line where tried */
165 int sp_startcol; /* next match in sp_line_id line */
166 short *sp_cont_list; /* cont. group IDs, if non-zero */
167 short *sp_next_list; /* next group IDs, if non-zero */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 struct sp_syn sp_syn; /* struct passed to in_id_list() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169 char_u *sp_pattern; /* regexp to match, pattern */
170 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200171#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200172 syn_time_T sp_time;
173#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174} synpat_T;
175
176/* The sp_off_flags are computed like this:
177 * offset from the start of the matched text: (1 << SPO_XX_OFF)
178 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
179 * When both are present, only one is used.
180 */
181
182#define SPTYPE_MATCH 1 /* match keyword with this group ID */
183#define SPTYPE_START 2 /* match a regexp, start of item */
184#define SPTYPE_END 3 /* match a regexp, end of item */
185#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
186
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187
188#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
189
190#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
191
192/*
193 * Flags for b_syn_sync_flags:
194 */
195#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
196#define SF_MATCH 0x02 /* sync by matching a pattern */
197
198#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
199
Bram Moolenaar071d4272004-06-13 20:20:40 +0000200#define MAXKEYWLEN 80 /* maximum length of a keyword */
201
202/*
203 * The attributes of the syntax item that has been recognized.
204 */
205static int current_attr = 0; /* attr of current syntax word */
206#ifdef FEAT_EVAL
207static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000208static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200210#ifdef FEAT_CONCEAL
211static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200212static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200213static int current_sub_char = 0;
214#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215
Bram Moolenaar217ad922005-03-20 22:37:15 +0000216typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217{
218 char_u *scl_name; /* syntax cluster name */
219 char_u *scl_name_u; /* uppercase of scl_name */
220 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000221} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222
223/*
224 * Methods of combining two clusters
225 */
226#define CLUSTER_REPLACE 1 /* replace first list with second */
227#define CLUSTER_ADD 2 /* add second list to first */
228#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
229
Bram Moolenaar217ad922005-03-20 22:37:15 +0000230#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231
232/*
233 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200234 * 0 - 19999 normal syntax groups
235 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
236 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
237 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
238 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200240#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200241#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
242#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
243#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
244
Bram Moolenaar42431a72011-04-01 14:44:59 +0200245#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
246#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247
248/*
249 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
250 * expand_filename(). Most of the other syntax commands don't need it, so
251 * instead of passing it to them, we stow it here.
252 */
253static char_u **syn_cmdlinep;
254
255/*
256 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200257 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258 * rules in each ":syn include"'d file.
259 */
260static int current_syn_inc_tag = 0;
261static int running_syn_inc_tag = 0;
262
263/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000264 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
265 * This avoids adding a pointer to the hashtable item.
266 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
267 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
268 * HI2KE() converts a hashitem pointer to a var pointer.
269 */
270static keyentry_T dumkey;
271#define KE2HIKEY(kp) ((kp)->keyword)
272#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
273#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
274
275/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276 * To reduce the time spent in keepend(), remember at which level in the state
277 * stack the first item with "keepend" is present. When "-1", there is no
278 * "keepend" on the stack.
279 */
280static int keepend_level = -1;
281
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200282static char msg_no_items[] = N_("No Syntax items defined for this buffer");
283
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284/*
285 * For the current state we need to remember more than just the idx.
286 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
287 * (The end positions have the column number of the next char)
288 */
289typedef struct state_item
290{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000291 int si_idx; /* index of syntax pattern or
292 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000294 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295 int si_m_lnum; /* lnum of the match */
296 int si_m_startcol; /* starting column of the match */
297 lpos_T si_m_endpos; /* just after end posn of the match */
298 lpos_T si_h_startpos; /* start position of the highlighting */
299 lpos_T si_h_endpos; /* end position of the highlighting */
300 lpos_T si_eoe_pos; /* end position of end pattern */
301 int si_end_idx; /* group ID for end pattern or zero */
302 int si_ends; /* if match ends before si_m_endpos */
303 int si_attr; /* attributes in this state */
304 long si_flags; /* HL_HAS_EOL flag in this state, and
305 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200306#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200307 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200308 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200309#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000310 short *si_cont_list; /* list of contained groups */
311 short *si_next_list; /* nextgroup IDs after this item ends */
312 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
313 * pattern */
314} stateitem_T;
315
316#define KEYWORD_IDX -1 /* value of si_idx for keywords */
317#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
318 but contained groups */
319
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100321static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200322#endif
323
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000325 * Struct to reduce the number of arguments to get_syn_options(), it's used
326 * very often.
327 */
328typedef struct
329{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000330 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000331 int keyword; /* TRUE for ":syn keyword" */
332 int *sync_idx; /* syntax item for "grouphere" argument, NULL
333 if not allowed */
334 char has_cont_list; /* TRUE if "cont_list" can be used */
335 short *cont_list; /* group IDs for "contains" argument */
336 short *cont_in_list; /* group IDs for "containedin" argument */
337 short *next_list; /* group IDs for "nextgroup" argument */
338} syn_opt_arg_T;
339
340/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 * The next possible match in the current line for any pattern is remembered,
342 * to avoid having to try for a match in each column.
343 * If next_match_idx == -1, not tried (in this line) yet.
344 * If next_match_col == MAXCOL, no match found in this line.
345 * (All end positions have the column of the char after the end)
346 */
347static int next_match_col; /* column for start of next match */
348static lpos_T next_match_m_endpos; /* position for end of next match */
349static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
350static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
351static int next_match_idx; /* index of matched item */
352static long next_match_flags; /* flags for next match */
353static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
354static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
355static int next_match_end_idx; /* ID of group for end pattn or zero */
356static reg_extmatch_T *next_match_extmatch = NULL;
357
358/*
359 * A state stack is an array of integers or stateitem_T, stored in a
360 * garray_T. A state stack is invalid if it's itemsize entry is zero.
361 */
362#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
363#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
364
365/*
366 * The current state (within the line) of the recognition engine.
367 * When current_state.ga_itemsize is 0 the current state is invalid.
368 */
369static win_T *syn_win; /* current window for highlighting */
370static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200371static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200372#ifdef FEAT_RELTIME
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200373static proftime_T *syn_tm; /* timeout limit */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200374#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375static linenr_T current_lnum = 0; /* lnum of current state */
376static colnr_T current_col = 0; /* column of current state */
377static int current_state_stored = 0; /* TRUE if stored current state
378 * after setting current_finished */
379static int current_finished = 0; /* current line has been finished */
380static garray_T current_state /* current stack of state_items */
381 = {0, 0, 0, 0, NULL};
382static short *current_next_list = NULL; /* when non-zero, nextgroup list */
383static int current_next_flags = 0; /* flags for current_next_list */
384static int current_line_id = 0; /* unique number for current line */
385
386#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
387
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100388static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100389static void save_chartab(char_u *chartab);
390static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100391static int syn_match_linecont(linenr_T lnum);
392static void syn_start_line(void);
393static void syn_update_ends(int startofline);
394static void syn_stack_alloc(void);
395static int syn_stack_cleanup(void);
396static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
397static synstate_T *syn_stack_find_entry(linenr_T lnum);
398static synstate_T *store_current_state(void);
399static void load_current_state(synstate_T *from);
400static void invalidate_current_state(void);
401static int syn_stack_equal(synstate_T *sp);
402static void validate_current_state(void);
403static int syn_finish_line(int syncing);
404static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
405static int did_match_already(int idx, garray_T *gap);
406static stateitem_T *push_next_match(stateitem_T *cur_si);
407static void check_state_ends(void);
408static void update_si_attr(int idx);
409static void check_keepend(void);
410static void update_si_end(stateitem_T *sip, int startcol, int force);
411static short *copy_id_list(short *list);
412static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
413static int push_current_state(int idx);
414static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200415#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100416static void syn_clear_time(syn_time_T *tt);
417static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200420#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200422#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100423static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200424static int syn_time_on = FALSE;
425# define IF_SYN_TIME(p) (p)
426#else
427# define IF_SYN_TIME(p) NULL
428typedef int syn_time_T;
429#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100431static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
432static 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);
433static void clear_syn_state(synstate_T *p);
434static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100436static void limit_pos(lpos_T *pos, lpos_T *limit);
437static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
438static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
439static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
440static char_u *syn_getcurline(void);
441static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
442static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
443static void syn_cmd_case(exarg_T *eap, int syncing);
444static void syn_cmd_spell(exarg_T *eap, int syncing);
445static void syntax_sync_clear(void);
446static void syn_remove_pattern(synblock_T *block, int idx);
447static void syn_clear_pattern(synblock_T *block, int i);
448static void syn_clear_cluster(synblock_T *block, int i);
449static void syn_cmd_clear(exarg_T *eap, int syncing);
450static void syn_cmd_conceal(exarg_T *eap, int syncing);
451static void syn_clear_one(int id, int syncing);
452static void syn_cmd_on(exarg_T *eap, int syncing);
453static void syn_cmd_enable(exarg_T *eap, int syncing);
454static void syn_cmd_reset(exarg_T *eap, int syncing);
455static void syn_cmd_manual(exarg_T *eap, int syncing);
456static void syn_cmd_off(exarg_T *eap, int syncing);
457static void syn_cmd_onoff(exarg_T *eap, char *name);
458static void syn_cmd_list(exarg_T *eap, int syncing);
459static void syn_lines_msg(void);
460static void syn_match_msg(void);
461static void syn_stack_free_block(synblock_T *block);
462static void syn_list_one(int id, int syncing, int link_only);
463static void syn_list_cluster(int id);
464static void put_id_list(char_u *name, short *list, int attr);
465static void put_pattern(char *s, int c, synpat_T *spp, int attr);
466static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
467static void syn_clear_keyword(int id, hashtab_T *ht);
468static void clear_keywtab(hashtab_T *ht);
469static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
470static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100471static 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 +0100472static void syn_cmd_include(exarg_T *eap, int syncing);
473static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
474static void syn_cmd_keyword(exarg_T *eap, int syncing);
475static void syn_cmd_match(exarg_T *eap, int syncing);
476static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100478static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100480static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100482static void syn_cmd_cluster(exarg_T *eap, int syncing);
483static int syn_scl_name2id(char_u *name);
484static int syn_scl_namen2id(char_u *linep, int len);
485static int syn_check_cluster(char_u *pp, int len);
486static int syn_add_cluster(char_u *name);
487static void init_syn_patterns(void);
488static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
489static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100490static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100491static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
492static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200494#if defined(FEAT_RELTIME) || defined(PROTO)
495/*
496 * Set the timeout used for syntax highlighting.
497 * Use NULL to reset, no timeout.
498 */
499 void
500syn_set_timeout(proftime_T *tm)
501{
502 syn_tm = tm;
503}
504#endif
505
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506/*
507 * Start the syntax recognition for a line. This function is normally called
508 * from the screen updating, once for each displayed line.
509 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
510 * it. Careful: curbuf and curwin are likely to point to another buffer and
511 * window.
512 */
513 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200514syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515{
516 synstate_T *p;
517 synstate_T *last_valid = NULL;
518 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000519 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 linenr_T parsed_lnum;
521 linenr_T first_stored;
522 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100523 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200525#ifdef FEAT_CONCEAL
526 current_sub_char = NUL;
527#endif
528
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 /*
530 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000531 * Also do this when a change was made, the current state may be invalid
532 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200534 if (syn_block != wp->w_s
535 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100536 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537 {
538 invalidate_current_state();
539 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100542 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 syn_win = wp;
544
545 /*
546 * Allocate syntax stack when needed.
547 */
548 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200549 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000550 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200551 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000552
553 /*
554 * If the state of the end of the previous line is useful, store it.
555 */
556 if (VALID_STATE(&current_state)
557 && current_lnum < lnum
558 && current_lnum < syn_buf->b_ml.ml_line_count)
559 {
560 (void)syn_finish_line(FALSE);
561 if (!current_state_stored)
562 {
563 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000564 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565 }
566
567 /*
568 * If the current_lnum is now the same as "lnum", keep the current
569 * state (this happens very often!). Otherwise invalidate
570 * current_state and figure it out below.
571 */
572 if (current_lnum != lnum)
573 invalidate_current_state();
574 }
575 else
576 invalidate_current_state();
577
578 /*
579 * Try to synchronize from a saved state in b_sst_array[].
580 * Only do this if lnum is not before and not to far beyond a saved state.
581 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200582 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 {
584 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200585 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 {
587 if (p->sst_lnum > lnum)
588 break;
589 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
590 {
591 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200592 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593 last_min_valid = p;
594 }
595 }
596 if (last_min_valid != NULL)
597 load_current_state(last_min_valid);
598 }
599
600 /*
601 * If "lnum" is before or far beyond a line with a saved state, need to
602 * re-synchronize.
603 */
604 if (INVALID_STATE(&current_state))
605 {
606 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200607 if (current_lnum == 1)
608 /* First line is always valid, no matter "minlines". */
609 first_stored = 1;
610 else
611 /* Need to parse "minlines" lines before state can be considered
612 * valid to store. */
613 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 }
615 else
616 first_stored = current_lnum;
617
618 /*
619 * Advance from the sync point or saved state until the current line.
620 * Save some entries for syncing with later on.
621 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200622 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000623 dist = 999999;
624 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200625 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626 while (current_lnum < lnum)
627 {
628 syn_start_line();
629 (void)syn_finish_line(FALSE);
630 ++current_lnum;
631
632 /* If we parsed at least "minlines" lines or started at a valid
633 * state, the current state is considered valid. */
634 if (current_lnum >= first_stored)
635 {
636 /* Check if the saved state entry is for the current line and is
637 * equal to the current state. If so, then validate all saved
638 * states that depended on a change before the parsed line. */
639 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000640 prev = syn_stack_find_entry(current_lnum - 1);
641 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200642 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000644 sp = prev;
645 while (sp != NULL && sp->sst_lnum < current_lnum)
646 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 if (sp != NULL
648 && sp->sst_lnum == current_lnum
649 && syn_stack_equal(sp))
650 {
651 parsed_lnum = current_lnum;
652 prev = sp;
653 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
654 {
655 if (sp->sst_lnum <= lnum)
656 /* valid state before desired line, use this one */
657 prev = sp;
658 else if (sp->sst_change_lnum == 0)
659 /* past saved states depending on change, break here. */
660 break;
661 sp->sst_change_lnum = 0;
662 sp = sp->sst_next;
663 }
664 load_current_state(prev);
665 }
666 /* Store the state at this line when it's the first one, the line
667 * where we start parsing, or some distance from the previously
668 * saved state. But only when parsed at least 'minlines'. */
669 else if (prev == NULL
670 || current_lnum == lnum
671 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000672 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000673 }
674
675 /* This can take a long time: break when CTRL-C pressed. The current
676 * state will be wrong then. */
677 line_breakcheck();
678 if (got_int)
679 {
680 current_lnum = lnum;
681 break;
682 }
683 }
684
685 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686}
687
688/*
689 * We cannot simply discard growarrays full of state_items or buf_states; we
690 * have to manually release their extmatch pointers first.
691 */
692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100693clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694{
695 int i;
696 garray_T *gap;
697
698 if (p->sst_stacksize > SST_FIX_STATES)
699 {
700 gap = &(p->sst_union.sst_ga);
701 for (i = 0; i < gap->ga_len; i++)
702 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
703 ga_clear(gap);
704 }
705 else
706 {
707 for (i = 0; i < p->sst_stacksize; i++)
708 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
709 }
710}
711
712/*
713 * Cleanup the current_state stack.
714 */
715 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100716clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717{
718 int i;
719 stateitem_T *sip;
720
721 sip = (stateitem_T *)(current_state.ga_data);
722 for (i = 0; i < current_state.ga_len; i++)
723 unref_extmatch(sip[i].si_extmatch);
724 ga_clear(&current_state);
725}
726
727/*
728 * Try to find a synchronisation point for line "lnum".
729 *
730 * This sets current_lnum and the current state. One of three methods is
731 * used:
732 * 1. Search backwards for the end of a C-comment.
733 * 2. Search backwards for given sync patterns.
734 * 3. Simply start on a given number of lines above "lnum".
735 */
736 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100737syn_sync(
738 win_T *wp,
739 linenr_T start_lnum,
740 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741{
742 buf_T *curbuf_save;
743 win_T *curwin_save;
744 pos_T cursor_save;
745 int idx;
746 linenr_T lnum;
747 linenr_T end_lnum;
748 linenr_T break_lnum;
749 int had_sync_point;
750 stateitem_T *cur_si;
751 synpat_T *spp;
752 char_u *line;
753 int found_flags = 0;
754 int found_match_idx = 0;
755 linenr_T found_current_lnum = 0;
756 int found_current_col= 0;
757 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000758 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759
760 /*
761 * Clear any current state that might be hanging around.
762 */
763 invalidate_current_state();
764
765 /*
766 * Start at least "minlines" back. Default starting point for parsing is
767 * there.
768 * Start further back, to avoid that scrolling backwards will result in
769 * resyncing for every line. Now it resyncs only one out of N lines,
770 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
771 * Watch out for overflow when minlines is MAXLNUM.
772 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200773 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 start_lnum = 1;
775 else
776 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200777 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200779 else if (syn_block->b_syn_sync_minlines < 10)
780 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200782 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
783 if (syn_block->b_syn_sync_maxlines != 0
784 && lnum > syn_block->b_syn_sync_maxlines)
785 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 if (lnum >= start_lnum)
787 start_lnum = 1;
788 else
789 start_lnum -= lnum;
790 }
791 current_lnum = start_lnum;
792
793 /*
794 * 1. Search backwards for the end of a C-style comment.
795 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200796 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000797 {
798 /* Need to make syn_buf the current buffer for a moment, to be able to
799 * use find_start_comment(). */
800 curwin_save = curwin;
801 curwin = wp;
802 curbuf_save = curbuf;
803 curbuf = syn_buf;
804
805 /*
806 * Skip lines that end in a backslash.
807 */
808 for ( ; start_lnum > 1; --start_lnum)
809 {
810 line = ml_get(start_lnum - 1);
811 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
812 break;
813 }
814 current_lnum = start_lnum;
815
816 /* set cursor to start of search */
817 cursor_save = wp->w_cursor;
818 wp->w_cursor.lnum = start_lnum;
819 wp->w_cursor.col = 0;
820
821 /*
822 * If the line is inside a comment, need to find the syntax item that
823 * defines the comment.
824 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
825 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200826 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000827 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200828 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
829 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
830 == syn_block->b_syn_sync_id
831 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 {
833 validate_current_state();
834 if (push_current_state(idx) == OK)
835 update_si_attr(current_state.ga_len - 1);
836 break;
837 }
838 }
839
840 /* restore cursor and buffer */
841 wp->w_cursor = cursor_save;
842 curwin = curwin_save;
843 curbuf = curbuf_save;
844 }
845
846 /*
847 * 2. Search backwards for given sync patterns.
848 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200849 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000850 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200851 if (syn_block->b_syn_sync_maxlines != 0
852 && start_lnum > syn_block->b_syn_sync_maxlines)
853 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854 else
855 break_lnum = 0;
856
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000857 found_m_endpos.lnum = 0;
858 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000859 end_lnum = start_lnum;
860 lnum = start_lnum;
861 while (--lnum > break_lnum)
862 {
863 /* This can take a long time: break when CTRL-C pressed. */
864 line_breakcheck();
865 if (got_int)
866 {
867 invalidate_current_state();
868 current_lnum = start_lnum;
869 break;
870 }
871
872 /* Check if we have run into a valid saved state stack now. */
873 if (last_valid != NULL && lnum == last_valid->sst_lnum)
874 {
875 load_current_state(last_valid);
876 break;
877 }
878
879 /*
880 * Check if the previous line has the line-continuation pattern.
881 */
882 if (lnum > 1 && syn_match_linecont(lnum - 1))
883 continue;
884
885 /*
886 * Start with nothing on the state stack
887 */
888 validate_current_state();
889
890 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
891 {
892 syn_start_line();
893 for (;;)
894 {
895 had_sync_point = syn_finish_line(TRUE);
896 /*
897 * When a sync point has been found, remember where, and
898 * continue to look for another one, further on in the line.
899 */
900 if (had_sync_point && current_state.ga_len)
901 {
902 cur_si = &CUR_STATE(current_state.ga_len - 1);
903 if (cur_si->si_m_endpos.lnum > start_lnum)
904 {
905 /* ignore match that goes to after where started */
906 current_lnum = end_lnum;
907 break;
908 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000909 if (cur_si->si_idx < 0)
910 {
911 /* Cannot happen? */
912 found_flags = 0;
913 found_match_idx = KEYWORD_IDX;
914 }
915 else
916 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200917 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000918 found_flags = spp->sp_flags;
919 found_match_idx = spp->sp_sync_idx;
920 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921 found_current_lnum = current_lnum;
922 found_current_col = current_col;
923 found_m_endpos = cur_si->si_m_endpos;
924 /*
925 * Continue after the match (be aware of a zero-length
926 * match).
927 */
928 if (found_m_endpos.lnum > current_lnum)
929 {
930 current_lnum = found_m_endpos.lnum;
931 current_col = found_m_endpos.col;
932 if (current_lnum >= end_lnum)
933 break;
934 }
935 else if (found_m_endpos.col > current_col)
936 current_col = found_m_endpos.col;
937 else
938 ++current_col;
939
940 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000941 * an item that ends here, need to do that now. Be
942 * careful not to go past the NUL. */
943 prev_current_col = current_col;
944 if (syn_getcurline()[current_col] != NUL)
945 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000947 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000948 }
949 else
950 break;
951 }
952 }
953
954 /*
955 * If a sync point was encountered, break here.
956 */
957 if (found_flags)
958 {
959 /*
960 * Put the item that was specified by the sync point on the
961 * state stack. If there was no item specified, make the
962 * state stack empty.
963 */
964 clear_current_state();
965 if (found_match_idx >= 0
966 && push_current_state(found_match_idx) == OK)
967 update_si_attr(current_state.ga_len - 1);
968
969 /*
970 * When using "grouphere", continue from the sync point
971 * match, until the end of the line. Parsing starts at
972 * the next line.
973 * For "groupthere" the parsing starts at start_lnum.
974 */
975 if (found_flags & HL_SYNC_HERE)
976 {
977 if (current_state.ga_len)
978 {
979 cur_si = &CUR_STATE(current_state.ga_len - 1);
980 cur_si->si_h_startpos.lnum = found_current_lnum;
981 cur_si->si_h_startpos.col = found_current_col;
982 update_si_end(cur_si, (int)current_col, TRUE);
983 check_keepend();
984 }
985 current_col = found_m_endpos.col;
986 current_lnum = found_m_endpos.lnum;
987 (void)syn_finish_line(FALSE);
988 ++current_lnum;
989 }
990 else
991 current_lnum = start_lnum;
992
993 break;
994 }
995
996 end_lnum = lnum;
997 invalidate_current_state();
998 }
999
1000 /* Ran into start of the file or exceeded maximum number of lines */
1001 if (lnum <= break_lnum)
1002 {
1003 invalidate_current_state();
1004 current_lnum = break_lnum + 1;
1005 }
1006 }
1007
1008 validate_current_state();
1009}
1010
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001011 static void
1012save_chartab(char_u *chartab)
1013{
1014 if (syn_block->b_syn_isk != empty_option)
1015 {
1016 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1017 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1018 (size_t)32);
1019 }
1020}
1021
1022 static void
1023restore_chartab(char_u *chartab)
1024{
1025 if (syn_win->w_s->b_syn_isk != empty_option)
1026 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1027}
1028
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029/*
1030 * Return TRUE if the line-continuation pattern matches in line "lnum".
1031 */
1032 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001033syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034{
1035 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001036 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001037 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038
Bram Moolenaar860cae12010-06-05 23:22:07 +02001039 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001041 /* use syntax iskeyword option */
1042 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001043 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1044 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001045 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001046 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001047 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001048 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001049 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 }
1051 return FALSE;
1052}
1053
1054/*
1055 * Prepare the current state for the start of a line.
1056 */
1057 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001058syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059{
1060 current_finished = FALSE;
1061 current_col = 0;
1062
1063 /*
1064 * Need to update the end of a start/skip/end that continues from the
1065 * previous line and regions that have "keepend".
1066 */
1067 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001068 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001070 check_state_ends();
1071 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072
1073 next_match_idx = -1;
1074 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001075#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001076 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001077#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001078}
1079
1080/*
1081 * Check for items in the stack that need their end updated.
1082 * When "startofline" is TRUE the last item is always updated.
1083 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1084 */
1085 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001086syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087{
1088 stateitem_T *cur_si;
1089 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001090 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091
1092 if (startofline)
1093 {
1094 /* Check for a match carried over from a previous line with a
1095 * contained region. The match ends as soon as the region ends. */
1096 for (i = 0; i < current_state.ga_len; ++i)
1097 {
1098 cur_si = &CUR_STATE(i);
1099 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001100 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 == SPTYPE_MATCH
1102 && cur_si->si_m_endpos.lnum < current_lnum)
1103 {
1104 cur_si->si_flags |= HL_MATCHCONT;
1105 cur_si->si_m_endpos.lnum = 0;
1106 cur_si->si_m_endpos.col = 0;
1107 cur_si->si_h_endpos = cur_si->si_m_endpos;
1108 cur_si->si_ends = TRUE;
1109 }
1110 }
1111 }
1112
1113 /*
1114 * Need to update the end of a start/skip/end that continues from the
1115 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001116 * influence contained items. If we've just removed "extend"
1117 * (startofline == 0) then we should update ends of normal regions
1118 * contained inside "keepend" because "extend" could have extended
1119 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 * Then check for items ending in column 0.
1121 */
1122 i = current_state.ga_len - 1;
1123 if (keepend_level >= 0)
1124 for ( ; i > keepend_level; --i)
1125 if (CUR_STATE(i).si_flags & HL_EXTEND)
1126 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001127
1128 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 for ( ; i < current_state.ga_len; ++i)
1130 {
1131 cur_si = &CUR_STATE(i);
1132 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001133 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 || (i == current_state.ga_len - 1 && startofline))
1135 {
1136 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1137 cur_si->si_h_startpos.lnum = current_lnum;
1138
1139 if (!(cur_si->si_flags & HL_MATCHCONT))
1140 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001141
1142 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1143 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 }
1145 }
1146 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147}
1148
1149/****************************************
1150 * Handling of the state stack cache.
1151 */
1152
1153/*
1154 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1155 *
1156 * To speed up syntax highlighting, the state stack for the start of some
1157 * lines is cached. These entries can be used to start parsing at that point.
1158 *
1159 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1160 * valid entries. b_sst_first points to the first one, then follow sst_next.
1161 * The entries are sorted on line number. The first entry is often for line 2
1162 * (line 1 always starts with an empty stack).
1163 * There is also a list for free entries. This construction is used to avoid
1164 * having to allocate and free memory blocks too often.
1165 *
1166 * When making changes to the buffer, this is logged in b_mod_*. When calling
1167 * update_screen() to update the display, it will call
1168 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1169 * entries. The entries which are inside the changed area are removed,
1170 * because they must be recomputed. Entries below the changed have their line
1171 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1172 * set to indicate that a check must be made if the changed lines would change
1173 * the cached entry.
1174 *
1175 * When later displaying lines, an entry is stored for each line. Displayed
1176 * lines are likely to be displayed again, in which case the state at the
1177 * start of the line is needed.
1178 * For not displayed lines, an entry is stored for every so many lines. These
1179 * entries will be used e.g., when scrolling backwards. The distance between
1180 * entries depends on the number of lines in the buffer. For small buffers
1181 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1182 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1183 */
1184
Bram Moolenaar860cae12010-06-05 23:22:07 +02001185 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001186syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187{
1188 synstate_T *p;
1189
1190 if (block->b_sst_array != NULL)
1191 {
1192 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1193 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001194 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001195 block->b_sst_len = 0;
1196 }
1197}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198/*
1199 * Free b_sst_array[] for buffer "buf".
1200 * Used when syntax items changed to force resyncing everywhere.
1201 */
1202 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001203syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001205#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001207#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208
Bram Moolenaar860cae12010-06-05 23:22:07 +02001209 syn_stack_free_block(block);
1210
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211#ifdef FEAT_FOLDING
1212 /* When using "syntax" fold method, must update all folds. */
1213 FOR_ALL_WINDOWS(wp)
1214 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001215 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216 foldUpdateAll(wp);
1217 }
1218#endif
1219}
1220
1221/*
1222 * Allocate the syntax state stack for syn_buf when needed.
1223 * If the number of entries in b_sst_array[] is much too big or a bit too
1224 * small, reallocate it.
1225 * Also used to allocate b_sst_array[] for the first time.
1226 */
1227 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001228syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229{
1230 long len;
1231 synstate_T *to, *from;
1232 synstate_T *sstp;
1233
1234 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1235 if (len < SST_MIN_ENTRIES)
1236 len = SST_MIN_ENTRIES;
1237 else if (len > SST_MAX_ENTRIES)
1238 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001239 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240 {
1241 /* Allocate 50% too much, to avoid reallocating too often. */
1242 len = syn_buf->b_ml.ml_line_count;
1243 len = (len + len / 2) / SST_DIST + Rows * 2;
1244 if (len < SST_MIN_ENTRIES)
1245 len = SST_MIN_ENTRIES;
1246 else if (len > SST_MAX_ENTRIES)
1247 len = SST_MAX_ENTRIES;
1248
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 {
1251 /* When shrinking the array, cleanup the existing stack.
1252 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 && syn_stack_cleanup())
1255 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1257 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 }
1259
1260 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1261 if (sstp == NULL) /* out of memory! */
1262 return;
1263
1264 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 {
1267 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 from = from->sst_next)
1270 {
1271 ++to;
1272 *to = *from;
1273 to->sst_next = to + 1;
1274 }
1275 }
1276 if (to != sstp - 1)
1277 {
1278 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 syn_block->b_sst_first = sstp;
1280 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 }
1282 else
1283 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 syn_block->b_sst_first = NULL;
1285 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 }
1287
1288 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001289 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 while (++to < sstp + len)
1291 to->sst_next = to + 1;
1292 (sstp + len - 1)->sst_next = NULL;
1293
Bram Moolenaar860cae12010-06-05 23:22:07 +02001294 vim_free(syn_block->b_sst_array);
1295 syn_block->b_sst_array = sstp;
1296 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 }
1298}
1299
1300/*
1301 * Check for changes in a buffer to affect stored syntax states. Uses the
1302 * b_mod_* fields.
1303 * Called from update_screen(), before screen is being updated, once for each
1304 * displayed buffer.
1305 */
1306 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001307syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 win_T *wp;
1310
1311 syn_stack_apply_changes_block(&buf->b_s, buf);
1312
1313 FOR_ALL_WINDOWS(wp)
1314 {
1315 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1316 syn_stack_apply_changes_block(wp->w_s, buf);
1317 }
1318}
1319
1320 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001321syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 synstate_T *p, *prev, *np;
1324 linenr_T n;
1325
Bram Moolenaar860cae12010-06-05 23:22:07 +02001326 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 return;
1328
1329 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001332 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 {
1334 n = p->sst_lnum + buf->b_mod_xlines;
1335 if (n <= buf->b_mod_bot)
1336 {
1337 /* this state is inside the changed area, remove it */
1338 np = p->sst_next;
1339 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 else
1342 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 p = np;
1345 continue;
1346 }
1347 /* This state is below the changed area. Remember the line
1348 * that needs to be parsed before this entry can be made valid
1349 * again. */
1350 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1351 {
1352 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1353 p->sst_change_lnum += buf->b_mod_xlines;
1354 else
1355 p->sst_change_lnum = buf->b_mod_top;
1356 }
1357 if (p->sst_change_lnum == 0
1358 || p->sst_change_lnum < buf->b_mod_bot)
1359 p->sst_change_lnum = buf->b_mod_bot;
1360
1361 p->sst_lnum = n;
1362 }
1363 prev = p;
1364 p = p->sst_next;
1365 }
1366}
1367
1368/*
1369 * Reduce the number of entries in the state stack for syn_buf.
1370 * Returns TRUE if at least one entry was freed.
1371 */
1372 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001373syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374{
1375 synstate_T *p, *prev;
1376 disptick_T tick;
1377 int above;
1378 int dist;
1379 int retval = FALSE;
1380
Bram Moolenaar860cae12010-06-05 23:22:07 +02001381 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 return retval;
1383
1384 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001386 dist = 999999;
1387 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001388 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389
1390 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001391 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001392 * be removed. Set "above" when the "tick" for the oldest entry is above
1393 * "b_sst_lasttick" (the display tick wraps around).
1394 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001397 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1399 {
1400 if (prev->sst_lnum + dist > p->sst_lnum)
1401 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001402 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 {
1404 if (!above || p->sst_tick < tick)
1405 tick = p->sst_tick;
1406 above = TRUE;
1407 }
1408 else if (!above && p->sst_tick < tick)
1409 tick = p->sst_tick;
1410 }
1411 }
1412
1413 /*
1414 * Go through the list to make the entries for the oldest tick at an
1415 * interval of several lines.
1416 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001417 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1419 {
1420 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1421 {
1422 /* Move this entry from used list to free list */
1423 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001424 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001425 p = prev;
1426 retval = TRUE;
1427 }
1428 }
1429 return retval;
1430}
1431
1432/*
1433 * Free the allocated memory for a syn_state item.
1434 * Move the entry into the free list.
1435 */
1436 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001437syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438{
1439 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 p->sst_next = block->b_sst_firstfree;
1441 block->b_sst_firstfree = p;
1442 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443}
1444
1445/*
1446 * Find an entry in the list of state stacks at or before "lnum".
1447 * Returns NULL when there is no entry or the first entry is after "lnum".
1448 */
1449 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001450syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451{
1452 synstate_T *p, *prev;
1453
1454 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001455 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 {
1457 if (p->sst_lnum == lnum)
1458 return p;
1459 if (p->sst_lnum > lnum)
1460 break;
1461 }
1462 return prev;
1463}
1464
1465/*
1466 * Try saving the current state in b_sst_array[].
1467 * The current state must be valid for the start of the current_lnum line!
1468 */
1469 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001470store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471{
1472 int i;
1473 synstate_T *p;
1474 bufstate_T *bp;
1475 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001476 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477
1478 /*
1479 * If the current state contains a start or end pattern that continues
1480 * from the previous line, we can't use it. Don't store it then.
1481 */
1482 for (i = current_state.ga_len - 1; i >= 0; --i)
1483 {
1484 cur_si = &CUR_STATE(i);
1485 if (cur_si->si_h_startpos.lnum >= current_lnum
1486 || cur_si->si_m_endpos.lnum >= current_lnum
1487 || cur_si->si_h_endpos.lnum >= current_lnum
1488 || (cur_si->si_end_idx
1489 && cur_si->si_eoe_pos.lnum >= current_lnum))
1490 break;
1491 }
1492 if (i >= 0)
1493 {
1494 if (sp != NULL)
1495 {
1496 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001497 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 else
1501 {
1502 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001503 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 if (p->sst_next == sp)
1505 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001506 if (p != NULL) /* just in case */
1507 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001509 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 sp = NULL;
1511 }
1512 }
1513 else if (sp == NULL || sp->sst_lnum != current_lnum)
1514 {
1515 /*
1516 * Add a new entry
1517 */
1518 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001519 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001520 {
1521 (void)syn_stack_cleanup();
1522 /* "sp" may have been moved to the freelist now */
1523 sp = syn_stack_find_entry(current_lnum);
1524 }
1525 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001526 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 sp = NULL;
1528 else
1529 {
1530 /* Take the first item from the free list and put it in the used
1531 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001532 p = syn_block->b_sst_firstfree;
1533 syn_block->b_sst_firstfree = p->sst_next;
1534 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535 if (sp == NULL)
1536 {
1537 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001538 p->sst_next = syn_block->b_sst_first;
1539 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540 }
1541 else
1542 {
1543 /* insert in list after *sp */
1544 p->sst_next = sp->sst_next;
1545 sp->sst_next = p;
1546 }
1547 sp = p;
1548 sp->sst_stacksize = 0;
1549 sp->sst_lnum = current_lnum;
1550 }
1551 }
1552 if (sp != NULL)
1553 {
1554 /* When overwriting an existing state stack, clear it first */
1555 clear_syn_state(sp);
1556 sp->sst_stacksize = current_state.ga_len;
1557 if (current_state.ga_len > SST_FIX_STATES)
1558 {
1559 /* Need to clear it, might be something remaining from when the
1560 * length was less than SST_FIX_STATES. */
1561 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1562 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1563 sp->sst_stacksize = 0;
1564 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1567 }
1568 else
1569 bp = sp->sst_union.sst_stack;
1570 for (i = 0; i < sp->sst_stacksize; ++i)
1571 {
1572 bp[i].bs_idx = CUR_STATE(i).si_idx;
1573 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001574#ifdef FEAT_CONCEAL
1575 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1576 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1579 }
1580 sp->sst_next_flags = current_next_flags;
1581 sp->sst_next_list = current_next_list;
1582 sp->sst_tick = display_tick;
1583 sp->sst_change_lnum = 0;
1584 }
1585 current_state_stored = TRUE;
1586 return sp;
1587}
1588
1589/*
1590 * Copy a state stack from "from" in b_sst_array[] to current_state;
1591 */
1592 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001593load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001594{
1595 int i;
1596 bufstate_T *bp;
1597
1598 clear_current_state();
1599 validate_current_state();
1600 keepend_level = -1;
1601 if (from->sst_stacksize
1602 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1603 {
1604 if (from->sst_stacksize > SST_FIX_STATES)
1605 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1606 else
1607 bp = from->sst_union.sst_stack;
1608 for (i = 0; i < from->sst_stacksize; ++i)
1609 {
1610 CUR_STATE(i).si_idx = bp[i].bs_idx;
1611 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001612#ifdef FEAT_CONCEAL
1613 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1614 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1615#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1617 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1618 keepend_level = i;
1619 CUR_STATE(i).si_ends = FALSE;
1620 CUR_STATE(i).si_m_lnum = 0;
1621 if (CUR_STATE(i).si_idx >= 0)
1622 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001623 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 else
1625 CUR_STATE(i).si_next_list = NULL;
1626 update_si_attr(i);
1627 }
1628 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 }
1630 current_next_list = from->sst_next_list;
1631 current_next_flags = from->sst_next_flags;
1632 current_lnum = from->sst_lnum;
1633}
1634
1635/*
1636 * Compare saved state stack "*sp" with the current state.
1637 * Return TRUE when they are equal.
1638 */
1639 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001640syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641{
1642 int i, j;
1643 bufstate_T *bp;
1644 reg_extmatch_T *six, *bsx;
1645
1646 /* First a quick check if the stacks have the same size end nextlist. */
1647 if (sp->sst_stacksize == current_state.ga_len
1648 && sp->sst_next_list == current_next_list)
1649 {
1650 /* Need to compare all states on both stacks. */
1651 if (sp->sst_stacksize > SST_FIX_STATES)
1652 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1653 else
1654 bp = sp->sst_union.sst_stack;
1655
1656 for (i = current_state.ga_len; --i >= 0; )
1657 {
1658 /* If the item has another index the state is different. */
1659 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1660 break;
1661 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1662 {
1663 /* When the extmatch pointers are different, the strings in
1664 * them can still be the same. Check if the extmatch
1665 * references are equal. */
1666 bsx = bp[i].bs_extmatch;
1667 six = CUR_STATE(i).si_extmatch;
1668 /* If one of the extmatch pointers is NULL the states are
1669 * different. */
1670 if (bsx == NULL || six == NULL)
1671 break;
1672 for (j = 0; j < NSUBEXP; ++j)
1673 {
1674 /* Check each referenced match string. They must all be
1675 * equal. */
1676 if (bsx->matches[j] != six->matches[j])
1677 {
1678 /* If the pointer is different it can still be the
1679 * same text. Compare the strings, ignore case when
1680 * the start item has the sp_ic flag set. */
1681 if (bsx->matches[j] == NULL
1682 || six->matches[j] == NULL)
1683 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001684 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 ? MB_STRICMP(bsx->matches[j],
1686 six->matches[j]) != 0
1687 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1688 break;
1689 }
1690 }
1691 if (j != NSUBEXP)
1692 break;
1693 }
1694 }
1695 if (i < 0)
1696 return TRUE;
1697 }
1698 return FALSE;
1699}
1700
1701/*
1702 * We stop parsing syntax above line "lnum". If the stored state at or below
1703 * this line depended on a change before it, it now depends on the line below
1704 * the last parsed line.
1705 * The window looks like this:
1706 * line which changed
1707 * displayed line
1708 * displayed line
1709 * lnum -> line below window
1710 */
1711 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001712syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713{
1714 synstate_T *sp;
1715
1716 sp = syn_stack_find_entry(lnum);
1717 if (sp != NULL && sp->sst_lnum < lnum)
1718 sp = sp->sst_next;
1719
1720 if (sp != NULL && sp->sst_change_lnum != 0)
1721 sp->sst_change_lnum = lnum;
1722}
1723
1724/*
1725 * End of handling of the state stack.
1726 ****************************************/
1727
1728 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731 clear_current_state();
1732 current_state.ga_itemsize = 0; /* mark current_state invalid */
1733 current_next_list = NULL;
1734 keepend_level = -1;
1735}
1736
1737 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001738validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739{
1740 current_state.ga_itemsize = sizeof(stateitem_T);
1741 current_state.ga_growsize = 3;
1742}
1743
1744/*
1745 * Return TRUE if the syntax at start of lnum changed since last time.
1746 * This will only be called just after get_syntax_attr() for the previous
1747 * line, to check if the next line needs to be redrawn too.
1748 */
1749 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001750syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751{
1752 int retval = TRUE;
1753 synstate_T *sp;
1754
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 /*
1756 * Check the state stack when:
1757 * - lnum is just below the previously syntaxed line.
1758 * - lnum is not before the lines with saved states.
1759 * - lnum is not past the lines with saved states.
1760 * - lnum is at or before the last changed line.
1761 */
1762 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1763 {
1764 sp = syn_stack_find_entry(lnum);
1765 if (sp != NULL && sp->sst_lnum == lnum)
1766 {
1767 /*
1768 * finish the previous line (needed when not all of the line was
1769 * drawn)
1770 */
1771 (void)syn_finish_line(FALSE);
1772
1773 /*
1774 * Compare the current state with the previously saved state of
1775 * the line.
1776 */
1777 if (syn_stack_equal(sp))
1778 retval = FALSE;
1779
1780 /*
1781 * Store the current state in b_sst_array[] for later use.
1782 */
1783 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001784 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 }
1786 }
1787
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 return retval;
1789}
1790
1791/*
1792 * Finish the current line.
1793 * This doesn't return any attributes, it only gets the state at the end of
1794 * the line. It can start anywhere in the line, as long as the current state
1795 * is valid.
1796 */
1797 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001798syn_finish_line(
1799 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800{
1801 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001802 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001804 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001806 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1807 /*
1808 * When syncing, and found some item, need to check the item.
1809 */
1810 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001813 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001815 cur_si = &CUR_STATE(current_state.ga_len - 1);
1816 if (cur_si->si_idx >= 0
1817 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1818 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1819 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001821 /* syn_current_attr() will have skipped the check for an item
1822 * that ends here, need to do that now. Be careful not to go
1823 * past the NUL. */
1824 prev_current_col = current_col;
1825 if (syn_getcurline()[current_col] != NUL)
1826 ++current_col;
1827 check_state_ends();
1828 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001830 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 }
1832 return FALSE;
1833}
1834
1835/*
1836 * Return highlight attributes for next character.
1837 * Must first call syntax_start() once for the line.
1838 * "col" is normally 0 for the first use in a line, and increments by one each
1839 * time. It's allowed to skip characters and to stop before the end of the
1840 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001841 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1842 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 */
1844 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001845get_syntax_attr(
1846 colnr_T col,
1847 int *can_spell,
1848 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849{
1850 int attr = 0;
1851
Bram Moolenaar349955a2007-08-14 21:07:36 +00001852 if (can_spell != NULL)
1853 /* Default: Only do spelling when there is no @Spell cluster or when
1854 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001855 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1856 ? (syn_block->b_spell_cluster_id == 0)
1857 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001858
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001860 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 return 0;
1862
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001863 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001864 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001865 {
1866 clear_current_state();
1867#ifdef FEAT_EVAL
1868 current_id = 0;
1869 current_trans_id = 0;
1870#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001871#ifdef FEAT_CONCEAL
1872 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001873 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001874#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001875 return 0;
1876 }
1877
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 /* Make sure current_state is valid */
1879 if (INVALID_STATE(&current_state))
1880 validate_current_state();
1881
1882 /*
1883 * Skip from the current column to "col", get the attributes for "col".
1884 */
1885 while (current_col <= col)
1886 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001887 attr = syn_current_attr(FALSE, TRUE, can_spell,
1888 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001889 ++current_col;
1890 }
1891
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 return attr;
1893}
1894
1895/*
1896 * Get syntax attributes for current_lnum, current_col.
1897 */
1898 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001899syn_current_attr(
1900 int syncing, /* When 1: called for syncing */
1901 int displaying, /* result will be displayed */
1902 int *can_spell, /* return: do spell checking */
1903 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001904{
1905 int syn_id;
1906 lpos_T endpos; /* was: char_u *endp; */
1907 lpos_T hl_startpos; /* was: int hl_startcol; */
1908 lpos_T hl_endpos;
1909 lpos_T eos_pos; /* end-of-start match (start region) */
1910 lpos_T eoe_pos; /* end-of-end pattern */
1911 int end_idx; /* group ID for end pattern */
1912 int idx;
1913 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001914 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 int startcol;
1916 int endcol;
1917 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001918 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 short *next_list;
1920 int found_match; /* found usable match */
1921 static int try_next_column = FALSE; /* must try in next col */
1922 int do_keywords;
1923 regmmatch_T regmatch;
1924 lpos_T pos;
1925 int lc_col;
1926 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001927 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928 char_u *line; /* current line. NOTE: becomes invalid after
1929 looking for a pattern match! */
1930
1931 /* variables for zero-width matches that have a "nextgroup" argument */
1932 int keep_next_list;
1933 int zero_width_next_list = FALSE;
1934 garray_T zero_width_next_ga;
1935
1936 /*
1937 * No character, no attributes! Past end of line?
1938 * Do try matching with an empty line (could be the start of a region).
1939 */
1940 line = syn_getcurline();
1941 if (line[current_col] == NUL && current_col != 0)
1942 {
1943 /*
1944 * If we found a match after the last column, use it.
1945 */
1946 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1947 && next_match_col != MAXCOL)
1948 (void)push_next_match(NULL);
1949
1950 current_finished = TRUE;
1951 current_state_stored = FALSE;
1952 return 0;
1953 }
1954
1955 /* if the current or next character is NUL, we will finish the line now */
1956 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1957 {
1958 current_finished = TRUE;
1959 current_state_stored = FALSE;
1960 }
1961
1962 /*
1963 * When in the previous column there was a match but it could not be used
1964 * (empty match or already matched in this column) need to try again in
1965 * the next column.
1966 */
1967 if (try_next_column)
1968 {
1969 next_match_idx = -1;
1970 try_next_column = FALSE;
1971 }
1972
1973 /* Only check for keywords when not syncing and there are some. */
1974 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001975 && (syn_block->b_keywtab.ht_used > 0
1976 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977
1978 /* Init the list of zero-width matches with a nextlist. This is used to
1979 * avoid matching the same item in the same position twice. */
1980 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1981
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001982 /* use syntax iskeyword option */
1983 save_chartab(buf_chartab);
1984
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 /*
1986 * Repeat matching keywords and patterns, to find contained items at the
1987 * same column. This stops when there are no extra matches at the current
1988 * column.
1989 */
1990 do
1991 {
1992 found_match = FALSE;
1993 keep_next_list = FALSE;
1994 syn_id = 0;
1995
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001996
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 /*
1998 * 1. Check for a current state.
1999 * Only when there is no current state, or if the current state may
2000 * contain other things, we need to check for keywords and patterns.
2001 * Always need to check for contained items if some item has the
2002 * "containedin" argument (takes extra time!).
2003 */
2004 if (current_state.ga_len)
2005 cur_si = &CUR_STATE(current_state.ga_len - 1);
2006 else
2007 cur_si = NULL;
2008
Bram Moolenaar860cae12010-06-05 23:22:07 +02002009 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 || cur_si->si_cont_list != NULL)
2011 {
2012 /*
2013 * 2. Check for keywords, if on a keyword char after a non-keyword
2014 * char. Don't do this when syncing.
2015 */
2016 if (do_keywords)
2017 {
2018 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002019 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002021 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022#ifdef FEAT_MBYTE
2023 - (has_mbyte
2024 ? (*mb_head_off)(line, line + current_col - 1)
2025 : 0)
2026#endif
2027 , syn_buf)))
2028 {
2029 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002030 &endcol, &flags, &next_list, cur_si,
2031 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002032 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 {
2034 if (push_current_state(KEYWORD_IDX) == OK)
2035 {
2036 cur_si = &CUR_STATE(current_state.ga_len - 1);
2037 cur_si->si_m_startcol = current_col;
2038 cur_si->si_h_startpos.lnum = current_lnum;
2039 cur_si->si_h_startpos.col = 0; /* starts right away */
2040 cur_si->si_m_endpos.lnum = current_lnum;
2041 cur_si->si_m_endpos.col = endcol;
2042 cur_si->si_h_endpos.lnum = current_lnum;
2043 cur_si->si_h_endpos.col = endcol;
2044 cur_si->si_ends = TRUE;
2045 cur_si->si_end_idx = 0;
2046 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002047#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002048 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002049 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002050 if (current_state.ga_len > 1)
2051 cur_si->si_flags |=
2052 CUR_STATE(current_state.ga_len - 2).si_flags
2053 & HL_CONCEAL;
2054#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055 cur_si->si_id = syn_id;
2056 cur_si->si_trans_id = syn_id;
2057 if (flags & HL_TRANSP)
2058 {
2059 if (current_state.ga_len < 2)
2060 {
2061 cur_si->si_attr = 0;
2062 cur_si->si_trans_id = 0;
2063 }
2064 else
2065 {
2066 cur_si->si_attr = CUR_STATE(
2067 current_state.ga_len - 2).si_attr;
2068 cur_si->si_trans_id = CUR_STATE(
2069 current_state.ga_len - 2).si_trans_id;
2070 }
2071 }
2072 else
2073 cur_si->si_attr = syn_id2attr(syn_id);
2074 cur_si->si_cont_list = NULL;
2075 cur_si->si_next_list = next_list;
2076 check_keepend();
2077 }
2078 else
2079 vim_free(next_list);
2080 }
2081 }
2082 }
2083
2084 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002085 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002087 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002088 {
2089 /*
2090 * If we didn't check for a match yet, or we are past it, check
2091 * for any match with a pattern.
2092 */
2093 if (next_match_idx < 0 || next_match_col < (int)current_col)
2094 {
2095 /*
2096 * Check all relevant patterns for a match at this
2097 * position. This is complicated, because matching with a
2098 * pattern takes quite a bit of time, thus we want to
2099 * avoid doing it when it's not needed.
2100 */
2101 next_match_idx = 0; /* no match in this line yet */
2102 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002103 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002105 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 if ( spp->sp_syncing == syncing
2107 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2108 && (spp->sp_type == SPTYPE_MATCH
2109 || spp->sp_type == SPTYPE_START)
2110 && (current_next_list != NULL
2111 ? in_id_list(NULL, current_next_list,
2112 &spp->sp_syn, 0)
2113 : (cur_si == NULL
2114 ? !(spp->sp_flags & HL_CONTAINED)
2115 : in_id_list(cur_si,
2116 cur_si->si_cont_list, &spp->sp_syn,
2117 spp->sp_flags & HL_CONTAINED))))
2118 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002119 int r;
2120
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121 /* If we already tried matching in this line, and
2122 * there isn't a match before next_match_col, skip
2123 * this item. */
2124 if (spp->sp_line_id == current_line_id
2125 && spp->sp_startcol >= next_match_col)
2126 continue;
2127 spp->sp_line_id = current_line_id;
2128
2129 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2130 if (lc_col < 0)
2131 lc_col = 0;
2132
2133 regmatch.rmm_ic = spp->sp_ic;
2134 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002135 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002136 current_lnum,
2137 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002138 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002139 spp->sp_prog = regmatch.regprog;
2140 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 {
2142 /* no match in this line, try another one */
2143 spp->sp_startcol = MAXCOL;
2144 continue;
2145 }
2146
2147 /*
2148 * Compute the first column of the match.
2149 */
2150 syn_add_start_off(&pos, &regmatch,
2151 spp, SPO_MS_OFF, -1);
2152 if (pos.lnum > current_lnum)
2153 {
2154 /* must have used end of match in a next line,
2155 * we can't handle that */
2156 spp->sp_startcol = MAXCOL;
2157 continue;
2158 }
2159 startcol = pos.col;
2160
2161 /* remember the next column where this pattern
2162 * matches in the current line */
2163 spp->sp_startcol = startcol;
2164
2165 /*
2166 * If a previously found match starts at a lower
2167 * column number, don't use this one.
2168 */
2169 if (startcol >= next_match_col)
2170 continue;
2171
2172 /*
2173 * If we matched this pattern at this position
2174 * before, skip it. Must retry in the next
2175 * column, because it may match from there.
2176 */
2177 if (did_match_already(idx, &zero_width_next_ga))
2178 {
2179 try_next_column = TRUE;
2180 continue;
2181 }
2182
2183 endpos.lnum = regmatch.endpos[0].lnum;
2184 endpos.col = regmatch.endpos[0].col;
2185
2186 /* Compute the highlight start. */
2187 syn_add_start_off(&hl_startpos, &regmatch,
2188 spp, SPO_HS_OFF, -1);
2189
2190 /* Compute the region start. */
2191 /* Default is to use the end of the match. */
2192 syn_add_end_off(&eos_pos, &regmatch,
2193 spp, SPO_RS_OFF, 0);
2194
2195 /*
2196 * Grab the external submatches before they get
2197 * overwritten. Reference count doesn't change.
2198 */
2199 unref_extmatch(cur_extmatch);
2200 cur_extmatch = re_extmatch_out;
2201 re_extmatch_out = NULL;
2202
2203 flags = 0;
2204 eoe_pos.lnum = 0; /* avoid warning */
2205 eoe_pos.col = 0;
2206 end_idx = 0;
2207 hl_endpos.lnum = 0;
2208
2209 /*
2210 * For a "oneline" the end must be found in the
2211 * same line too. Search for it after the end of
2212 * the match with the start pattern. Set the
2213 * resulting end positions at the same time.
2214 */
2215 if (spp->sp_type == SPTYPE_START
2216 && (spp->sp_flags & HL_ONELINE))
2217 {
2218 lpos_T startpos;
2219
2220 startpos = endpos;
2221 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2222 &flags, &eoe_pos, &end_idx, cur_extmatch);
2223 if (endpos.lnum == 0)
2224 continue; /* not found */
2225 }
2226
2227 /*
2228 * For a "match" the size must be > 0 after the
2229 * end offset needs has been added. Except when
2230 * syncing.
2231 */
2232 else if (spp->sp_type == SPTYPE_MATCH)
2233 {
2234 syn_add_end_off(&hl_endpos, &regmatch, spp,
2235 SPO_HE_OFF, 0);
2236 syn_add_end_off(&endpos, &regmatch, spp,
2237 SPO_ME_OFF, 0);
2238 if (endpos.lnum == current_lnum
2239 && (int)endpos.col + syncing < startcol)
2240 {
2241 /*
2242 * If an empty string is matched, may need
2243 * to try matching again at next column.
2244 */
2245 if (regmatch.startpos[0].col
2246 == regmatch.endpos[0].col)
2247 try_next_column = TRUE;
2248 continue;
2249 }
2250 }
2251
2252 /*
2253 * keep the best match so far in next_match_*
2254 */
2255 /* Highlighting must start after startpos and end
2256 * before endpos. */
2257 if (hl_startpos.lnum == current_lnum
2258 && (int)hl_startpos.col < startcol)
2259 hl_startpos.col = startcol;
2260 limit_pos_zero(&hl_endpos, &endpos);
2261
2262 next_match_idx = idx;
2263 next_match_col = startcol;
2264 next_match_m_endpos = endpos;
2265 next_match_h_endpos = hl_endpos;
2266 next_match_h_startpos = hl_startpos;
2267 next_match_flags = flags;
2268 next_match_eos_pos = eos_pos;
2269 next_match_eoe_pos = eoe_pos;
2270 next_match_end_idx = end_idx;
2271 unref_extmatch(next_match_extmatch);
2272 next_match_extmatch = cur_extmatch;
2273 cur_extmatch = NULL;
2274 }
2275 }
2276 }
2277
2278 /*
2279 * If we found a match at the current column, use it.
2280 */
2281 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2282 {
2283 synpat_T *lspp;
2284
2285 /* When a zero-width item matched which has a nextgroup,
2286 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002287 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 if (next_match_m_endpos.lnum == current_lnum
2289 && next_match_m_endpos.col == current_col
2290 && lspp->sp_next_list != NULL)
2291 {
2292 current_next_list = lspp->sp_next_list;
2293 current_next_flags = lspp->sp_flags;
2294 keep_next_list = TRUE;
2295 zero_width_next_list = TRUE;
2296
2297 /* Add the index to a list, so that we can check
2298 * later that we don't match it again (and cause an
2299 * endless loop). */
2300 if (ga_grow(&zero_width_next_ga, 1) == OK)
2301 {
2302 ((int *)(zero_width_next_ga.ga_data))
2303 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 }
2305 next_match_idx = -1;
2306 }
2307 else
2308 cur_si = push_next_match(cur_si);
2309 found_match = TRUE;
2310 }
2311 }
2312 }
2313
2314 /*
2315 * Handle searching for nextgroup match.
2316 */
2317 if (current_next_list != NULL && !keep_next_list)
2318 {
2319 /*
2320 * If a nextgroup was not found, continue looking for one if:
2321 * - this is an empty line and the "skipempty" option was given
2322 * - we are on white space and the "skipwhite" option was given
2323 */
2324 if (!found_match)
2325 {
2326 line = syn_getcurline();
2327 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002328 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 || ((current_next_flags & HL_SKIPEMPTY)
2330 && *line == NUL))
2331 break;
2332 }
2333
2334 /*
2335 * If a nextgroup was found: Use it, and continue looking for
2336 * contained matches.
2337 * If a nextgroup was not found: Continue looking for a normal
2338 * match.
2339 * When did set current_next_list for a zero-width item and no
2340 * match was found don't loop (would get stuck).
2341 */
2342 current_next_list = NULL;
2343 next_match_idx = -1;
2344 if (!zero_width_next_list)
2345 found_match = TRUE;
2346 }
2347
2348 } while (found_match);
2349
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002350 restore_chartab(buf_chartab);
2351
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 /*
2353 * Use attributes from the current state, if within its highlighting.
2354 * If not, use attributes from the current-but-one state, etc.
2355 */
2356 current_attr = 0;
2357#ifdef FEAT_EVAL
2358 current_id = 0;
2359 current_trans_id = 0;
2360#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002361#ifdef FEAT_CONCEAL
2362 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002363 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002364#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 if (cur_si != NULL)
2366 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002367#ifndef FEAT_EVAL
2368 int current_trans_id = 0;
2369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002370 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2371 {
2372 sip = &CUR_STATE(idx);
2373 if ((current_lnum > sip->si_h_startpos.lnum
2374 || (current_lnum == sip->si_h_startpos.lnum
2375 && current_col >= sip->si_h_startpos.col))
2376 && (sip->si_h_endpos.lnum == 0
2377 || current_lnum < sip->si_h_endpos.lnum
2378 || (current_lnum == sip->si_h_endpos.lnum
2379 && current_col < sip->si_h_endpos.col)))
2380 {
2381 current_attr = sip->si_attr;
2382#ifdef FEAT_EVAL
2383 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002386#ifdef FEAT_CONCEAL
2387 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002388 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002389 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002390#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391 break;
2392 }
2393 }
2394
Bram Moolenaar217ad922005-03-20 22:37:15 +00002395 if (can_spell != NULL)
2396 {
2397 struct sp_syn sps;
2398
2399 /*
2400 * set "can_spell" to TRUE if spell checking is supposed to be
2401 * done in the current item.
2402 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002403 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002404 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002405 /* There is no @Spell cluster: Do spelling for items without
2406 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002407 if (syn_block->b_nospell_cluster_id == 0
2408 || current_trans_id == 0)
2409 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002410 else
2411 {
2412 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002413 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002414 sps.cont_in_list = NULL;
2415 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2416 }
2417 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002418 else
2419 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002420 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002421 * the @Spell cluster. But not when @NoSpell is also there.
2422 * At the toplevel only spell check when ":syn spell toplevel"
2423 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002425 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002426 else
2427 {
2428 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002429 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002430 sps.cont_in_list = NULL;
2431 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2432
Bram Moolenaar860cae12010-06-05 23:22:07 +02002433 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002434 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002435 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002436 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2437 *can_spell = FALSE;
2438 }
2439 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002440 }
2441 }
2442
2443
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 /*
2445 * Check for end of current state (and the states before it) at the
2446 * next column. Don't do this for syncing, because we would miss a
2447 * single character match.
2448 * First check if the current state ends at the current column. It
2449 * may be for an empty match and a containing item might end in the
2450 * current column.
2451 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002452 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002453 {
2454 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002455 if (current_state.ga_len > 0
2456 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 {
2458 ++current_col;
2459 check_state_ends();
2460 --current_col;
2461 }
2462 }
2463 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002464 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002465 /* Default: Only do spelling when there is no @Spell cluster or when
2466 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002467 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2468 ? (syn_block->b_spell_cluster_id == 0)
2469 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002471 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002473 && (line = syn_getcurline())[current_col] != NUL
2474 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2476 current_next_list = NULL;
2477
2478 if (zero_width_next_ga.ga_len > 0)
2479 ga_clear(&zero_width_next_ga);
2480
2481 /* No longer need external matches. But keep next_match_extmatch. */
2482 unref_extmatch(re_extmatch_out);
2483 re_extmatch_out = NULL;
2484 unref_extmatch(cur_extmatch);
2485
2486 return current_attr;
2487}
2488
2489
2490/*
2491 * Check if we already matched pattern "idx" at the current column.
2492 */
2493 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002494did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002495{
2496 int i;
2497
2498 for (i = current_state.ga_len; --i >= 0; )
2499 if (CUR_STATE(i).si_m_startcol == (int)current_col
2500 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2501 && CUR_STATE(i).si_idx == idx)
2502 return TRUE;
2503
2504 /* Zero-width matches with a nextgroup argument are not put on the syntax
2505 * stack, and can only be matched once anyway. */
2506 for (i = gap->ga_len; --i >= 0; )
2507 if (((int *)(gap->ga_data))[i] == idx)
2508 return TRUE;
2509
2510 return FALSE;
2511}
2512
2513/*
2514 * Push the next match onto the stack.
2515 */
2516 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002517push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518{
2519 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002520#ifdef FEAT_CONCEAL
2521 int save_flags;
2522#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523
Bram Moolenaar860cae12010-06-05 23:22:07 +02002524 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002525
2526 /*
2527 * Push the item in current_state stack;
2528 */
2529 if (push_current_state(next_match_idx) == OK)
2530 {
2531 /*
2532 * If it's a start-skip-end type that crosses lines, figure out how
2533 * much it continues in this line. Otherwise just fill in the length.
2534 */
2535 cur_si = &CUR_STATE(current_state.ga_len - 1);
2536 cur_si->si_h_startpos = next_match_h_startpos;
2537 cur_si->si_m_startcol = current_col;
2538 cur_si->si_m_lnum = current_lnum;
2539 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002540#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002541 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002542 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002543 if (current_state.ga_len > 1)
2544 cur_si->si_flags |=
2545 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2546#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 cur_si->si_next_list = spp->sp_next_list;
2548 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2549 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2550 {
2551 /* Try to find the end pattern in the current line */
2552 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2553 check_keepend();
2554 }
2555 else
2556 {
2557 cur_si->si_m_endpos = next_match_m_endpos;
2558 cur_si->si_h_endpos = next_match_h_endpos;
2559 cur_si->si_ends = TRUE;
2560 cur_si->si_flags |= next_match_flags;
2561 cur_si->si_eoe_pos = next_match_eoe_pos;
2562 cur_si->si_end_idx = next_match_end_idx;
2563 }
2564 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2565 keepend_level = current_state.ga_len - 1;
2566 check_keepend();
2567 update_si_attr(current_state.ga_len - 1);
2568
Bram Moolenaar860cae12010-06-05 23:22:07 +02002569#ifdef FEAT_CONCEAL
2570 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2571#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 /*
2573 * If the start pattern has another highlight group, push another item
2574 * on the stack for the start pattern.
2575 */
2576 if ( spp->sp_type == SPTYPE_START
2577 && spp->sp_syn_match_id != 0
2578 && push_current_state(next_match_idx) == OK)
2579 {
2580 cur_si = &CUR_STATE(current_state.ga_len - 1);
2581 cur_si->si_h_startpos = next_match_h_startpos;
2582 cur_si->si_m_startcol = current_col;
2583 cur_si->si_m_lnum = current_lnum;
2584 cur_si->si_m_endpos = next_match_eos_pos;
2585 cur_si->si_h_endpos = next_match_eos_pos;
2586 cur_si->si_ends = TRUE;
2587 cur_si->si_end_idx = 0;
2588 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002589#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002590 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002591 cur_si->si_flags |= save_flags;
2592 if (cur_si->si_flags & HL_CONCEALENDS)
2593 cur_si->si_flags |= HL_CONCEAL;
2594#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595 cur_si->si_next_list = NULL;
2596 check_keepend();
2597 update_si_attr(current_state.ga_len - 1);
2598 }
2599 }
2600
2601 next_match_idx = -1; /* try other match next time */
2602
2603 return cur_si;
2604}
2605
2606/*
2607 * Check for end of current state (and the states before it).
2608 */
2609 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002610check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002611{
2612 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002613 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614
2615 cur_si = &CUR_STATE(current_state.ga_len - 1);
2616 for (;;)
2617 {
2618 if (cur_si->si_ends
2619 && (cur_si->si_m_endpos.lnum < current_lnum
2620 || (cur_si->si_m_endpos.lnum == current_lnum
2621 && cur_si->si_m_endpos.col <= current_col)))
2622 {
2623 /*
2624 * If there is an end pattern group ID, highlight the end pattern
2625 * now. No need to pop the current item from the stack.
2626 * Only do this if the end pattern continues beyond the current
2627 * position.
2628 */
2629 if (cur_si->si_end_idx
2630 && (cur_si->si_eoe_pos.lnum > current_lnum
2631 || (cur_si->si_eoe_pos.lnum == current_lnum
2632 && cur_si->si_eoe_pos.col > current_col)))
2633 {
2634 cur_si->si_idx = cur_si->si_end_idx;
2635 cur_si->si_end_idx = 0;
2636 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2637 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2638 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002639#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002640 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002641 if (cur_si->si_flags & HL_CONCEALENDS)
2642 cur_si->si_flags |= HL_CONCEAL;
2643#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002644 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002645
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002646 /* nextgroup= should not match in the end pattern */
2647 current_next_list = NULL;
2648
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002649 /* what matches next may be different now, clear it */
2650 next_match_idx = 0;
2651 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652 break;
2653 }
2654 else
2655 {
2656 /* handle next_list, unless at end of line and no "skipnl" or
2657 * "skipempty" */
2658 current_next_list = cur_si->si_next_list;
2659 current_next_flags = cur_si->si_flags;
2660 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2661 && syn_getcurline()[current_col] == NUL)
2662 current_next_list = NULL;
2663
2664 /* When the ended item has "extend", another item with
2665 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002666 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002667
2668 pop_current_state();
2669
2670 if (current_state.ga_len == 0)
2671 break;
2672
Bram Moolenaar81993f42008-01-11 20:27:45 +00002673 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 {
2675 syn_update_ends(FALSE);
2676 if (current_state.ga_len == 0)
2677 break;
2678 }
2679
2680 cur_si = &CUR_STATE(current_state.ga_len - 1);
2681
2682 /*
2683 * Only for a region the search for the end continues after
2684 * the end of the contained item. If the contained match
2685 * included the end-of-line, break here, the region continues.
2686 * Don't do this when:
2687 * - "keepend" is used for the contained item
2688 * - not at the end of the line (could be end="x$"me=e-1).
2689 * - "excludenl" is used (HL_HAS_EOL won't be set)
2690 */
2691 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002692 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002693 == SPTYPE_START
2694 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2695 {
2696 update_si_end(cur_si, (int)current_col, TRUE);
2697 check_keepend();
2698 if ((current_next_flags & HL_HAS_EOL)
2699 && keepend_level < 0
2700 && syn_getcurline()[current_col] == NUL)
2701 break;
2702 }
2703 }
2704 }
2705 else
2706 break;
2707 }
2708}
2709
2710/*
2711 * Update an entry in the current_state stack for a match or region. This
2712 * fills in si_attr, si_next_list and si_cont_list.
2713 */
2714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002715update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002716{
2717 stateitem_T *sip = &CUR_STATE(idx);
2718 synpat_T *spp;
2719
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002720 /* This should not happen... */
2721 if (sip->si_idx < 0)
2722 return;
2723
Bram Moolenaar860cae12010-06-05 23:22:07 +02002724 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725 if (sip->si_flags & HL_MATCH)
2726 sip->si_id = spp->sp_syn_match_id;
2727 else
2728 sip->si_id = spp->sp_syn.id;
2729 sip->si_attr = syn_id2attr(sip->si_id);
2730 sip->si_trans_id = sip->si_id;
2731 if (sip->si_flags & HL_MATCH)
2732 sip->si_cont_list = NULL;
2733 else
2734 sip->si_cont_list = spp->sp_cont_list;
2735
2736 /*
2737 * For transparent items, take attr from outer item.
2738 * Also take cont_list, if there is none.
2739 * Don't do this for the matchgroup of a start or end pattern.
2740 */
2741 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2742 {
2743 if (idx == 0)
2744 {
2745 sip->si_attr = 0;
2746 sip->si_trans_id = 0;
2747 if (sip->si_cont_list == NULL)
2748 sip->si_cont_list = ID_LIST_ALL;
2749 }
2750 else
2751 {
2752 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2753 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002754 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2755 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 if (sip->si_cont_list == NULL)
2757 {
2758 sip->si_flags |= HL_TRANS_CONT;
2759 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2760 }
2761 }
2762 }
2763}
2764
2765/*
2766 * Check the current stack for patterns with "keepend" flag.
2767 * Propagate the match-end to contained items, until a "skipend" item is found.
2768 */
2769 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002770check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771{
2772 int i;
2773 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002774 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 stateitem_T *sip;
2776
2777 /*
2778 * This check can consume a lot of time; only do it from the level where
2779 * there really is a keepend.
2780 */
2781 if (keepend_level < 0)
2782 return;
2783
2784 /*
2785 * Find the last index of an "extend" item. "keepend" items before that
2786 * won't do anything. If there is no "extend" item "i" will be
2787 * "keepend_level" and all "keepend" items will work normally.
2788 */
2789 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2790 if (CUR_STATE(i).si_flags & HL_EXTEND)
2791 break;
2792
2793 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002794 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002795 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002796 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 for ( ; i < current_state.ga_len; ++i)
2798 {
2799 sip = &CUR_STATE(i);
2800 if (maxpos.lnum != 0)
2801 {
2802 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002803 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2805 sip->si_ends = TRUE;
2806 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002807 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2808 {
2809 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810 || maxpos.lnum > sip->si_m_endpos.lnum
2811 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002812 && maxpos.col > sip->si_m_endpos.col))
2813 maxpos = sip->si_m_endpos;
2814 if (maxpos_h.lnum == 0
2815 || maxpos_h.lnum > sip->si_h_endpos.lnum
2816 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2817 && maxpos_h.col > sip->si_h_endpos.col))
2818 maxpos_h = sip->si_h_endpos;
2819 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 }
2821}
2822
2823/*
2824 * Update an entry in the current_state stack for a start-skip-end pattern.
2825 * This finds the end of the current item, if it's in the current line.
2826 *
2827 * Return the flags for the matched END.
2828 */
2829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002830update_si_end(
2831 stateitem_T *sip,
2832 int startcol, /* where to start searching for the end */
2833 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834{
2835 lpos_T startpos;
2836 lpos_T endpos;
2837 lpos_T hl_endpos;
2838 lpos_T end_endpos;
2839 int end_idx;
2840
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002841 /* return quickly for a keyword */
2842 if (sip->si_idx < 0)
2843 return;
2844
Bram Moolenaar071d4272004-06-13 20:20:40 +00002845 /* Don't update when it's already done. Can be a match of an end pattern
2846 * that started in a previous line. Watch out: can also be a "keepend"
2847 * from a containing item. */
2848 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2849 return;
2850
2851 /*
2852 * We need to find the end of the region. It may continue in the next
2853 * line.
2854 */
2855 end_idx = 0;
2856 startpos.lnum = current_lnum;
2857 startpos.col = startcol;
2858 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2859 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2860
2861 if (endpos.lnum == 0)
2862 {
2863 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002864 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 {
2866 /* a "oneline" never continues in the next line */
2867 sip->si_ends = TRUE;
2868 sip->si_m_endpos.lnum = current_lnum;
2869 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2870 }
2871 else
2872 {
2873 /* continues in the next line */
2874 sip->si_ends = FALSE;
2875 sip->si_m_endpos.lnum = 0;
2876 }
2877 sip->si_h_endpos = sip->si_m_endpos;
2878 }
2879 else
2880 {
2881 /* match within this line */
2882 sip->si_m_endpos = endpos;
2883 sip->si_h_endpos = hl_endpos;
2884 sip->si_eoe_pos = end_endpos;
2885 sip->si_ends = TRUE;
2886 sip->si_end_idx = end_idx;
2887 }
2888}
2889
2890/*
2891 * Add a new state to the current state stack.
2892 * It is cleared and the index set to "idx".
2893 * Return FAIL if it's not possible (out of memory).
2894 */
2895 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002896push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897{
2898 if (ga_grow(&current_state, 1) == FAIL)
2899 return FAIL;
2900 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2901 CUR_STATE(current_state.ga_len).si_idx = idx;
2902 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 return OK;
2904}
2905
2906/*
2907 * Remove a state from the current_state stack.
2908 */
2909 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002910pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911{
2912 if (current_state.ga_len)
2913 {
2914 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2915 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 }
2917 /* after the end of a pattern, try matching a keyword or pattern */
2918 next_match_idx = -1;
2919
2920 /* if first state with "keepend" is popped, reset keepend_level */
2921 if (keepend_level >= current_state.ga_len)
2922 keepend_level = -1;
2923}
2924
2925/*
2926 * Find the end of a start/skip/end syntax region after "startpos".
2927 * Only checks one line.
2928 * Also handles a match item that continued from a previous line.
2929 * If not found, the syntax item continues in the next line. m_endpos->lnum
2930 * will be 0.
2931 * If found, the end of the region and the end of the highlighting is
2932 * computed.
2933 */
2934 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002935find_endpos(
2936 int idx, /* index of the pattern */
2937 lpos_T *startpos, /* where to start looking for an END match */
2938 lpos_T *m_endpos, /* return: end of match */
2939 lpos_T *hl_endpos, /* return: end of highlighting */
2940 long *flagsp, /* return: flags of matching END */
2941 lpos_T *end_endpos, /* return: end of end pattern match */
2942 int *end_idx, /* return: group ID for end pat. match, or 0 */
2943 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944{
2945 colnr_T matchcol;
2946 synpat_T *spp, *spp_skip;
2947 int start_idx;
2948 int best_idx;
2949 regmmatch_T regmatch;
2950 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2951 lpos_T pos;
2952 char_u *line;
2953 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002954 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002956 /* just in case we are invoked for a keyword */
2957 if (idx < 0)
2958 return;
2959
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 /*
2961 * Check for being called with a START pattern.
2962 * Can happen with a match that continues to the next line, because it
2963 * contained a region.
2964 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002965 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002966 if (spp->sp_type != SPTYPE_START)
2967 {
2968 *hl_endpos = *startpos;
2969 return;
2970 }
2971
2972 /*
2973 * Find the SKIP or first END pattern after the last START pattern.
2974 */
2975 for (;;)
2976 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002977 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 if (spp->sp_type != SPTYPE_START)
2979 break;
2980 ++idx;
2981 }
2982
2983 /*
2984 * Lookup the SKIP pattern (if present)
2985 */
2986 if (spp->sp_type == SPTYPE_SKIP)
2987 {
2988 spp_skip = spp;
2989 ++idx;
2990 }
2991 else
2992 spp_skip = NULL;
2993
2994 /* Setup external matches for syn_regexec(). */
2995 unref_extmatch(re_extmatch_in);
2996 re_extmatch_in = ref_extmatch(start_ext);
2997
2998 matchcol = startpos->col; /* start looking for a match at sstart */
2999 start_idx = idx; /* remember the first END pattern. */
3000 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003001
3002 /* use syntax iskeyword option */
3003 save_chartab(buf_chartab);
3004
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005 for (;;)
3006 {
3007 /*
3008 * Find end pattern that matches first after "matchcol".
3009 */
3010 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003011 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 {
3013 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003014 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015
Bram Moolenaar860cae12010-06-05 23:22:07 +02003016 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003017 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3018 break;
3019 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3020 if (lc_col < 0)
3021 lc_col = 0;
3022
3023 regmatch.rmm_ic = spp->sp_ic;
3024 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003025 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3026 IF_SYN_TIME(&spp->sp_time));
3027 spp->sp_prog = regmatch.regprog;
3028 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029 {
3030 if (best_idx == -1 || regmatch.startpos[0].col
3031 < best_regmatch.startpos[0].col)
3032 {
3033 best_idx = idx;
3034 best_regmatch.startpos[0] = regmatch.startpos[0];
3035 best_regmatch.endpos[0] = regmatch.endpos[0];
3036 }
3037 }
3038 }
3039
3040 /*
3041 * If all end patterns have been tried, and there is no match, the
3042 * item continues until end-of-line.
3043 */
3044 if (best_idx == -1)
3045 break;
3046
3047 /*
3048 * If the skip pattern matches before the end pattern,
3049 * continue searching after the skip pattern.
3050 */
3051 if (spp_skip != NULL)
3052 {
3053 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003054 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003055
3056 if (lc_col < 0)
3057 lc_col = 0;
3058 regmatch.rmm_ic = spp_skip->sp_ic;
3059 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003060 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3061 IF_SYN_TIME(&spp_skip->sp_time));
3062 spp_skip->sp_prog = regmatch.regprog;
3063 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 <= best_regmatch.startpos[0].col)
3065 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003066 int line_len;
3067
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068 /* Add offset to skip pattern match */
3069 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3070
3071 /* If the skip pattern goes on to the next line, there is no
3072 * match with an end pattern in this line. */
3073 if (pos.lnum > startpos->lnum)
3074 break;
3075
3076 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003077 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078
3079 /* take care of an empty match or negative offset */
3080 if (pos.col <= matchcol)
3081 ++matchcol;
3082 else if (pos.col <= regmatch.endpos[0].col)
3083 matchcol = pos.col;
3084 else
3085 /* Be careful not to jump over the NUL at the end-of-line */
3086 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003087 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088 ++matchcol)
3089 ;
3090
3091 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003092 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 break;
3094
3095 continue; /* start with first end pattern again */
3096 }
3097 }
3098
3099 /*
3100 * Match from start pattern to end pattern.
3101 * Correct for match and highlight offset of end pattern.
3102 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003103 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3105 /* can't end before the start */
3106 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3107 m_endpos->col = startpos->col;
3108
3109 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3110 /* can't end before the start */
3111 if (end_endpos->lnum == startpos->lnum
3112 && end_endpos->col < startpos->col)
3113 end_endpos->col = startpos->col;
3114 /* can't end after the match */
3115 limit_pos(end_endpos, m_endpos);
3116
3117 /*
3118 * If the end group is highlighted differently, adjust the pointers.
3119 */
3120 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3121 {
3122 *end_idx = best_idx;
3123 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3124 {
3125 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3126 hl_endpos->col = best_regmatch.endpos[0].col;
3127 }
3128 else
3129 {
3130 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3131 hl_endpos->col = best_regmatch.startpos[0].col;
3132 }
3133 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3134
3135 /* can't end before the start */
3136 if (hl_endpos->lnum == startpos->lnum
3137 && hl_endpos->col < startpos->col)
3138 hl_endpos->col = startpos->col;
3139 limit_pos(hl_endpos, m_endpos);
3140
3141 /* now the match ends where the highlighting ends, it is turned
3142 * into the matchgroup for the end */
3143 *m_endpos = *hl_endpos;
3144 }
3145 else
3146 {
3147 *end_idx = 0;
3148 *hl_endpos = *end_endpos;
3149 }
3150
3151 *flagsp = spp->sp_flags;
3152
3153 had_match = TRUE;
3154 break;
3155 }
3156
3157 /* no match for an END pattern in this line */
3158 if (!had_match)
3159 m_endpos->lnum = 0;
3160
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003161 restore_chartab(buf_chartab);
3162
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163 /* Remove external matches. */
3164 unref_extmatch(re_extmatch_in);
3165 re_extmatch_in = NULL;
3166}
3167
3168/*
3169 * Limit "pos" not to be after "limit".
3170 */
3171 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003172limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173{
3174 if (pos->lnum > limit->lnum)
3175 *pos = *limit;
3176 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3177 pos->col = limit->col;
3178}
3179
3180/*
3181 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3182 */
3183 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003184limit_pos_zero(
3185 lpos_T *pos,
3186 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187{
3188 if (pos->lnum == 0)
3189 *pos = *limit;
3190 else
3191 limit_pos(pos, limit);
3192}
3193
3194/*
3195 * Add offset to matched text for end of match or highlight.
3196 */
3197 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003198syn_add_end_off(
3199 lpos_T *result, /* returned position */
3200 regmmatch_T *regmatch, /* start/end of match */
3201 synpat_T *spp, /* matched pattern */
3202 int idx, /* index of offset */
3203 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204{
3205 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003206 int off;
3207 char_u *base;
3208 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003209
3210 if (spp->sp_off_flags & (1 << idx))
3211 {
3212 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003213 col = regmatch->startpos[0].col;
3214 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 }
3216 else
3217 {
3218 result->lnum = regmatch->endpos[0].lnum;
3219 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003222 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3223 * is a matchgroup. Watch out for match with last NL in the buffer. */
3224 if (result->lnum > syn_buf->b_ml.ml_line_count)
3225 col = 0;
3226 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003227 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003228 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3229 p = base + col;
3230 if (off > 0)
3231 {
3232 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003233 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003234 }
3235 else if (off < 0)
3236 {
3237 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003238 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003239 }
3240 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003241 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003242 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243}
3244
3245/*
3246 * Add offset to matched text for start of match or highlight.
3247 * Avoid resulting column to become negative.
3248 */
3249 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003250syn_add_start_off(
3251 lpos_T *result, /* returned position */
3252 regmmatch_T *regmatch, /* start/end of match */
3253 synpat_T *spp,
3254 int idx,
3255 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256{
3257 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003258 int off;
3259 char_u *base;
3260 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261
3262 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3263 {
3264 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003265 col = regmatch->endpos[0].col;
3266 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 }
3268 else
3269 {
3270 result->lnum = regmatch->startpos[0].lnum;
3271 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003272 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003274 if (result->lnum > syn_buf->b_ml.ml_line_count)
3275 {
3276 /* a "\n" at the end of the pattern may take us below the last line */
3277 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003278 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003279 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003280 if (off != 0)
3281 {
3282 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3283 p = base + col;
3284 if (off > 0)
3285 {
3286 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003287 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003288 }
3289 else if (off < 0)
3290 {
3291 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003292 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003293 }
3294 col = (int)(p - base);
3295 }
3296 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297}
3298
3299/*
3300 * Get current line in syntax buffer.
3301 */
3302 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003303syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003304{
3305 return ml_get_buf(syn_buf, current_lnum, FALSE);
3306}
3307
3308/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003309 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310 * Returns TRUE when there is a match.
3311 */
3312 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003313syn_regexec(
3314 regmmatch_T *rmp,
3315 linenr_T lnum,
3316 colnr_T col,
3317 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003319 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003320#ifdef FEAT_RELTIME
3321 int timed_out = FALSE;
3322#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003323#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003324 proftime_T pt;
3325
3326 if (syn_time_on)
3327 profile_start(&pt);
3328#endif
3329
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003330 if (rmp->regprog == NULL)
3331 // This can happen if a previous call to vim_regexec_multi() tried to
3332 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3333 // compiling the pattern with the other engine fails.
3334 return FALSE;
3335
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003336 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003337 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3338#ifdef FEAT_RELTIME
3339 syn_tm, &timed_out
3340#else
3341 NULL, NULL
3342#endif
3343 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003344
Bram Moolenaarf7512552013-06-06 14:55:19 +02003345#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003346 if (syn_time_on)
3347 {
3348 profile_end(&pt);
3349 profile_add(&st->total, &pt);
3350 if (profile_cmp(&pt, &st->slowest) < 0)
3351 st->slowest = pt;
3352 ++st->count;
3353 if (r > 0)
3354 ++st->match;
3355 }
3356#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003357#ifdef FEAT_RELTIME
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003358 if (timed_out && !syn_win->w_s->b_syn_slow)
3359 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003360 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003361 MSG(_("'redrawtime' exceeded, syntax highlighting disabled"));
3362 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003363#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003364
3365 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 {
3367 rmp->startpos[0].lnum += lnum;
3368 rmp->endpos[0].lnum += lnum;
3369 return TRUE;
3370 }
3371 return FALSE;
3372}
3373
3374/*
3375 * Check one position in a line for a matching keyword.
3376 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003377 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378 */
3379 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003380check_keyword_id(
3381 char_u *line,
3382 int startcol, /* position in line to check for keyword */
3383 int *endcolp, /* return: character after found keyword */
3384 long *flagsp, /* return: flags of matching keyword */
3385 short **next_listp, /* return: next_list of matching keyword */
3386 stateitem_T *cur_si, /* item at the top of the stack */
3387 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003389 keyentry_T *kp;
3390 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003393 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003394 hashtab_T *ht;
3395 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396
3397 /* Find first character after the keyword. First character was already
3398 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003399 kwp = line + startcol;
3400 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401 do
3402 {
3403#ifdef FEAT_MBYTE
3404 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003405 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003406 else
3407#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003408 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003410 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411
Bram Moolenaardad6b692005-01-25 22:14:34 +00003412 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413 return 0;
3414
3415 /*
3416 * Must make a copy of the keyword, so we can add a NUL and make it
3417 * lowercase.
3418 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003419 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420
3421 /*
3422 * Try twice:
3423 * 1. matching case
3424 * 2. ignoring case
3425 */
3426 for (round = 1; round <= 2; ++round)
3427 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003428 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003429 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003431 if (round == 2) /* ignore case */
3432 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433
3434 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003435 * Find keywords that match. There can be several with different
3436 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003437 * When current_next_list is non-zero accept only that group, otherwise:
3438 * Accept a not-contained keyword at toplevel.
3439 * Accept a keyword at other levels only if it is in the contains list.
3440 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003441 hi = hash_find(ht, keyword);
3442 if (!HASHITEM_EMPTY(hi))
3443 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003444 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003445 if (current_next_list != 0
3446 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3447 : (cur_si == NULL
3448 ? !(kp->flags & HL_CONTAINED)
3449 : in_id_list(cur_si, cur_si->si_cont_list,
3450 &kp->k_syn, kp->flags & HL_CONTAINED)))
3451 {
3452 *endcolp = startcol + kwlen;
3453 *flagsp = kp->flags;
3454 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003455#ifdef FEAT_CONCEAL
3456 *ccharp = kp->k_char;
3457#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003458 return kp->k_syn.id;
3459 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460 }
3461 }
3462 return 0;
3463}
3464
3465/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003466 * Handle ":syntax conceal" command.
3467 */
3468 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003469syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003470{
3471#ifdef FEAT_CONCEAL
3472 char_u *arg = eap->arg;
3473 char_u *next;
3474
3475 eap->nextcmd = find_nextcmd(arg);
3476 if (eap->skip)
3477 return;
3478
3479 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003480 if (*arg == NUL)
3481 {
3482 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003483 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003484 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003485 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003486 }
3487 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488 curwin->w_s->b_syn_conceal = TRUE;
3489 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3490 curwin->w_s->b_syn_conceal = FALSE;
3491 else
3492 EMSG2(_("E390: Illegal argument: %s"), arg);
3493#endif
3494}
3495
3496/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497 * Handle ":syntax case" command.
3498 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003500syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501{
3502 char_u *arg = eap->arg;
3503 char_u *next;
3504
3505 eap->nextcmd = find_nextcmd(arg);
3506 if (eap->skip)
3507 return;
3508
3509 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003510 if (*arg == NUL)
3511 {
3512 if (curwin->w_s->b_syn_ic)
3513 MSG(_("syntax case ignore"));
3514 else
3515 MSG(_("syntax case match"));
3516 }
3517 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003518 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003520 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003521 else
3522 EMSG2(_("E390: Illegal argument: %s"), arg);
3523}
3524
3525/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003526 * Handle ":syntax spell" command.
3527 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003528 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003529syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003530{
3531 char_u *arg = eap->arg;
3532 char_u *next;
3533
3534 eap->nextcmd = find_nextcmd(arg);
3535 if (eap->skip)
3536 return;
3537
3538 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003539 if (*arg == NUL)
3540 {
3541 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3542 MSG(_("syntax spell toplevel"));
3543 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3544 MSG(_("syntax spell notoplevel"));
3545 else
3546 MSG(_("syntax spell default"));
3547 }
3548 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003549 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003550 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003552 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003553 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003554 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003555 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003556 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003557 return;
3558 }
3559
3560 /* assume spell checking changed, force a redraw */
3561 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003562}
3563
3564/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003565 * Handle ":syntax iskeyword" command.
3566 */
3567 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003568syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003569{
3570 char_u *arg = eap->arg;
3571 char_u save_chartab[32];
3572 char_u *save_isk;
3573
3574 if (eap->skip)
3575 return;
3576
3577 arg = skipwhite(arg);
3578 if (*arg == NUL)
3579 {
3580 MSG_PUTS("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003581 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003582 {
3583 MSG_PUTS(_("syntax iskeyword "));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003584 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003585 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003586 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003587 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003588 }
3589 else
3590 {
3591 if (STRNICMP(arg, "clear", 5) == 0)
3592 {
3593 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3594 (size_t)32);
3595 clear_string_option(&curwin->w_s->b_syn_isk);
3596 }
3597 else
3598 {
3599 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3600 save_isk = curbuf->b_p_isk;
3601 curbuf->b_p_isk = vim_strsave(arg);
3602
3603 buf_init_chartab(curbuf, FALSE);
3604 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3605 (size_t)32);
3606 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3607 clear_string_option(&curwin->w_s->b_syn_isk);
3608 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3609 curbuf->b_p_isk = save_isk;
3610 }
3611 }
3612 redraw_win_later(curwin, NOT_VALID);
3613}
3614
3615/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616 * Clear all syntax info for one buffer.
3617 */
3618 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003619syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620{
3621 int i;
3622
Bram Moolenaar860cae12010-06-05 23:22:07 +02003623 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003624#ifdef FEAT_RELTIME
3625 block->b_syn_slow = FALSE; /* clear previous timeout */
3626#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003627 block->b_syn_ic = FALSE; /* Use case, by default */
3628 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3629 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003630#ifdef FEAT_CONCEAL
3631 block->b_syn_conceal = FALSE;
3632#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633
3634 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003635 clear_keywtab(&block->b_keywtab);
3636 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637
3638 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003639 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3640 syn_clear_pattern(block, i);
3641 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642
3643 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003644 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3645 syn_clear_cluster(block, i);
3646 ga_clear(&block->b_syn_clusters);
3647 block->b_spell_cluster_id = 0;
3648 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 block->b_syn_sync_flags = 0;
3651 block->b_syn_sync_minlines = 0;
3652 block->b_syn_sync_maxlines = 0;
3653 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654
Bram Moolenaar473de612013-06-08 18:19:48 +02003655 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003656 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003657 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003659 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003661 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662
3663 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003664 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003666
3667 /* Reset the counter for ":syn include" */
3668 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669}
3670
3671/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003672 * Get rid of ownsyntax for window "wp".
3673 */
3674 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003675reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003676{
3677 if (wp->w_s != &wp->w_buffer->b_s)
3678 {
3679 syntax_clear(wp->w_s);
3680 vim_free(wp->w_s);
3681 wp->w_s = &wp->w_buffer->b_s;
3682 }
3683}
3684
3685/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686 * Clear syncing info for one buffer.
3687 */
3688 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003689syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690{
3691 int i;
3692
3693 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003694 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3695 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3696 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697
Bram Moolenaar860cae12010-06-05 23:22:07 +02003698 curwin->w_s->b_syn_sync_flags = 0;
3699 curwin->w_s->b_syn_sync_minlines = 0;
3700 curwin->w_s->b_syn_sync_maxlines = 0;
3701 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702
Bram Moolenaar473de612013-06-08 18:19:48 +02003703 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003704 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003705 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003706 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003708 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709}
3710
3711/*
3712 * Remove one pattern from the buffer's pattern list.
3713 */
3714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003715syn_remove_pattern(
3716 synblock_T *block,
3717 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718{
3719 synpat_T *spp;
3720
Bram Moolenaar860cae12010-06-05 23:22:07 +02003721 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722#ifdef FEAT_FOLDING
3723 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003724 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003726 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3729 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730}
3731
3732/*
3733 * Clear and free one syntax pattern. When clearing all, must be called from
3734 * last to first!
3735 */
3736 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003737syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003739 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003740 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003742 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003744 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3745 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3746 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747 }
3748}
3749
3750/*
3751 * Clear and free one syntax cluster.
3752 */
3753 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003754syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003756 vim_free(SYN_CLSTR(block)[i].scl_name);
3757 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3758 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003759}
3760
3761/*
3762 * Handle ":syntax clear" command.
3763 */
3764 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003765syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003766{
3767 char_u *arg = eap->arg;
3768 char_u *arg_end;
3769 int id;
3770
3771 eap->nextcmd = find_nextcmd(arg);
3772 if (eap->skip)
3773 return;
3774
3775 /*
3776 * We have to disable this within ":syn include @group filename",
3777 * because otherwise @group would get deleted.
3778 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3779 * clear".
3780 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003781 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 return;
3783
3784 if (ends_excmd(*arg))
3785 {
3786 /*
3787 * No argument: Clear all syntax items.
3788 */
3789 if (syncing)
3790 syntax_sync_clear();
3791 else
3792 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003793 syntax_clear(curwin->w_s);
3794 if (curwin->w_s == &curwin->w_buffer->b_s)
3795 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003796 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797 }
3798 }
3799 else
3800 {
3801 /*
3802 * Clear the group IDs that are in the argument.
3803 */
3804 while (!ends_excmd(*arg))
3805 {
3806 arg_end = skiptowhite(arg);
3807 if (*arg == '@')
3808 {
3809 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3810 if (id == 0)
3811 {
3812 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3813 break;
3814 }
3815 else
3816 {
3817 /*
3818 * We can't physically delete a cluster without changing
3819 * the IDs of other clusters, so we do the next best thing
3820 * and make it empty.
3821 */
3822 short scl_id = id - SYNID_CLUSTER;
3823
Bram Moolenaard23a8232018-02-10 18:45:26 +01003824 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 }
3826 }
3827 else
3828 {
3829 id = syn_namen2id(arg, (int)(arg_end - arg));
3830 if (id == 0)
3831 {
3832 EMSG2(_(e_nogroup), arg);
3833 break;
3834 }
3835 else
3836 syn_clear_one(id, syncing);
3837 }
3838 arg = skipwhite(arg_end);
3839 }
3840 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003841 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003842 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843}
3844
3845/*
3846 * Clear one syntax group for the current buffer.
3847 */
3848 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003849syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850{
3851 synpat_T *spp;
3852 int idx;
3853
3854 /* Clear keywords only when not ":syn sync clear group-name" */
3855 if (!syncing)
3856 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003857 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3858 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 }
3860
3861 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003862 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003864 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3866 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003867 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868 }
3869}
3870
3871/*
3872 * Handle ":syntax on" command.
3873 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003875syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876{
3877 syn_cmd_onoff(eap, "syntax");
3878}
3879
3880/*
3881 * Handle ":syntax enable" command.
3882 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003884syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885{
3886 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3887 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003888 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889}
3890
3891/*
3892 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003893 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003896syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003897{
3898 eap->nextcmd = check_nextcmd(eap->arg);
3899 if (!eap->skip)
3900 {
3901 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3902 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003903 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003904 }
3905}
3906
3907/*
3908 * Handle ":syntax manual" command.
3909 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003911syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912{
3913 syn_cmd_onoff(eap, "manual");
3914}
3915
3916/*
3917 * Handle ":syntax off" command.
3918 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003920syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921{
3922 syn_cmd_onoff(eap, "nosyntax");
3923}
3924
3925 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003926syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927{
3928 char_u buf[100];
3929
3930 eap->nextcmd = check_nextcmd(eap->arg);
3931 if (!eap->skip)
3932 {
3933 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003934 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 do_cmdline_cmd(buf);
3936 }
3937}
3938
3939/*
3940 * Handle ":syntax [list]" command: list current syntax words.
3941 */
3942 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003943syn_cmd_list(
3944 exarg_T *eap,
3945 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003946{
3947 char_u *arg = eap->arg;
3948 int id;
3949 char_u *arg_end;
3950
3951 eap->nextcmd = find_nextcmd(arg);
3952 if (eap->skip)
3953 return;
3954
Bram Moolenaar860cae12010-06-05 23:22:07 +02003955 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003957 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958 return;
3959 }
3960
3961 if (syncing)
3962 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003963 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003964 {
3965 MSG_PUTS(_("syncing on C-style comments"));
3966 syn_lines_msg();
3967 syn_match_msg();
3968 return;
3969 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003970 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003971 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003972 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 MSG_PUTS(_("no syncing"));
3974 else
3975 {
3976 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003977 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978 MSG_PUTS(_(" lines before top line"));
3979 syn_match_msg();
3980 }
3981 return;
3982 }
3983 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003984 if (curwin->w_s->b_syn_sync_minlines > 0
3985 || curwin->w_s->b_syn_sync_maxlines > 0
3986 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 {
3988 MSG_PUTS(_("\nsyncing on items"));
3989 syn_lines_msg();
3990 syn_match_msg();
3991 }
3992 }
3993 else
3994 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3995 if (ends_excmd(*arg))
3996 {
3997 /*
3998 * No argument: List all group IDs and all syntax clusters.
3999 */
4000 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
4001 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004002 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003 syn_list_cluster(id);
4004 }
4005 else
4006 {
4007 /*
4008 * List the group IDs and syntax clusters that are in the argument.
4009 */
4010 while (!ends_excmd(*arg) && !got_int)
4011 {
4012 arg_end = skiptowhite(arg);
4013 if (*arg == '@')
4014 {
4015 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
4016 if (id == 0)
4017 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4018 else
4019 syn_list_cluster(id - SYNID_CLUSTER);
4020 }
4021 else
4022 {
4023 id = syn_namen2id(arg, (int)(arg_end - arg));
4024 if (id == 0)
4025 EMSG2(_(e_nogroup), arg);
4026 else
4027 syn_list_one(id, syncing, TRUE);
4028 }
4029 arg = skipwhite(arg_end);
4030 }
4031 }
4032 eap->nextcmd = check_nextcmd(arg);
4033}
4034
4035 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004036syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004038 if (curwin->w_s->b_syn_sync_maxlines > 0
4039 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 {
4041 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004042 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 {
4044 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004045 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4046 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004047 MSG_PUTS(", ");
4048 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004049 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 {
4051 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004052 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 }
4054 MSG_PUTS(_(" lines before top line"));
4055 }
4056}
4057
4058 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004059syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004061 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 {
4063 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004064 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004065 MSG_PUTS(_(" line breaks"));
4066 }
4067}
4068
4069static int last_matchgroup;
4070
4071struct name_list
4072{
4073 int flag;
4074 char *name;
4075};
4076
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004077static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078
4079/*
4080 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4081 */
4082 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004083syn_list_one(
4084 int id,
4085 int syncing, /* when TRUE: list syncing items */
4086 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087{
4088 int attr;
4089 int idx;
4090 int did_header = FALSE;
4091 synpat_T *spp;
4092 static struct name_list namelist1[] =
4093 {
4094 {HL_DISPLAY, "display"},
4095 {HL_CONTAINED, "contained"},
4096 {HL_ONELINE, "oneline"},
4097 {HL_KEEPEND, "keepend"},
4098 {HL_EXTEND, "extend"},
4099 {HL_EXCLUDENL, "excludenl"},
4100 {HL_TRANSP, "transparent"},
4101 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004102#ifdef FEAT_CONCEAL
4103 {HL_CONCEAL, "conceal"},
4104 {HL_CONCEALENDS, "concealends"},
4105#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106 {0, NULL}
4107 };
4108 static struct name_list namelist2[] =
4109 {
4110 {HL_SKIPWHITE, "skipwhite"},
4111 {HL_SKIPNL, "skipnl"},
4112 {HL_SKIPEMPTY, "skipempty"},
4113 {0, NULL}
4114 };
4115
Bram Moolenaar8820b482017-03-16 17:23:31 +01004116 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117
4118 /* list the keywords for "id" */
4119 if (!syncing)
4120 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004121 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4122 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 did_header, attr);
4124 }
4125
4126 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004127 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004129 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4131 continue;
4132
4133 (void)syn_list_header(did_header, 999, id);
4134 did_header = TRUE;
4135 last_matchgroup = 0;
4136 if (spp->sp_type == SPTYPE_MATCH)
4137 {
4138 put_pattern("match", ' ', spp, attr);
4139 msg_putchar(' ');
4140 }
4141 else if (spp->sp_type == SPTYPE_START)
4142 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004143 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4144 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4145 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4146 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4147 while (idx < curwin->w_s->b_syn_patterns.ga_len
4148 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4149 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 --idx;
4151 msg_putchar(' ');
4152 }
4153 syn_list_flags(namelist1, spp->sp_flags, attr);
4154
4155 if (spp->sp_cont_list != NULL)
4156 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4157
4158 if (spp->sp_syn.cont_in_list != NULL)
4159 put_id_list((char_u *)"containedin",
4160 spp->sp_syn.cont_in_list, attr);
4161
4162 if (spp->sp_next_list != NULL)
4163 {
4164 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4165 syn_list_flags(namelist2, spp->sp_flags, attr);
4166 }
4167 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4168 {
4169 if (spp->sp_flags & HL_SYNC_HERE)
4170 msg_puts_attr((char_u *)"grouphere", attr);
4171 else
4172 msg_puts_attr((char_u *)"groupthere", attr);
4173 msg_putchar(' ');
4174 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004175 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4177 else
4178 MSG_PUTS("NONE");
4179 msg_putchar(' ');
4180 }
4181 }
4182
4183 /* list the link, if there is one */
4184 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4185 {
4186 (void)syn_list_header(did_header, 999, id);
4187 msg_puts_attr((char_u *)"links to", attr);
4188 msg_putchar(' ');
4189 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4190 }
4191}
4192
4193 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004194syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195{
4196 int i;
4197
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004198 for (i = 0; nlist[i].flag != 0; ++i)
4199 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004201 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202 msg_putchar(' ');
4203 }
4204}
4205
4206/*
4207 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4208 */
4209 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004210syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211{
4212 int endcol = 15;
4213
4214 /* slight hack: roughly duplicate the guts of syn_list_header() */
4215 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004216 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217
4218 if (msg_col >= endcol) /* output at least one space */
4219 endcol = msg_col + 1;
4220 if (Columns <= endcol) /* avoid hang for tiny window */
4221 endcol = Columns - 1;
4222
4223 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004224 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004226 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004227 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228 }
4229 else
4230 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004231 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004232 msg_puts((char_u *)"=NONE");
4233 }
4234}
4235
4236 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004237put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004238{
4239 short *p;
4240
4241 msg_puts_attr(name, attr);
4242 msg_putchar('=');
4243 for (p = list; *p; ++p)
4244 {
4245 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4246 {
4247 if (p[1])
4248 MSG_PUTS("ALLBUT");
4249 else
4250 MSG_PUTS("ALL");
4251 }
4252 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4253 {
4254 MSG_PUTS("TOP");
4255 }
4256 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4257 {
4258 MSG_PUTS("CONTAINED");
4259 }
4260 else if (*p >= SYNID_CLUSTER)
4261 {
4262 short scl_id = *p - SYNID_CLUSTER;
4263
4264 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004265 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 }
4267 else
4268 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4269 if (p[1])
4270 msg_putchar(',');
4271 }
4272 msg_putchar(' ');
4273}
4274
4275 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004276put_pattern(
4277 char *s,
4278 int c,
4279 synpat_T *spp,
4280 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281{
4282 long n;
4283 int mask;
4284 int first;
4285 static char *sepchars = "/+=-#@\"|'^&";
4286 int i;
4287
4288 /* May have to write "matchgroup=group" */
4289 if (last_matchgroup != spp->sp_syn_match_id)
4290 {
4291 last_matchgroup = spp->sp_syn_match_id;
4292 msg_puts_attr((char_u *)"matchgroup", attr);
4293 msg_putchar('=');
4294 if (last_matchgroup == 0)
4295 msg_outtrans((char_u *)"NONE");
4296 else
4297 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4298 msg_putchar(' ');
4299 }
4300
4301 /* output the name of the pattern and an '=' or ' ' */
4302 msg_puts_attr((char_u *)s, attr);
4303 msg_putchar(c);
4304
4305 /* output the pattern, in between a char that is not in the pattern */
4306 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4307 if (sepchars[++i] == NUL)
4308 {
4309 i = 0; /* no good char found, just use the first one */
4310 break;
4311 }
4312 msg_putchar(sepchars[i]);
4313 msg_outtrans(spp->sp_pattern);
4314 msg_putchar(sepchars[i]);
4315
4316 /* output any pattern options */
4317 first = TRUE;
4318 for (i = 0; i < SPO_COUNT; ++i)
4319 {
4320 mask = (1 << i);
4321 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4322 {
4323 if (!first)
4324 msg_putchar(','); /* separate with commas */
4325 msg_puts((char_u *)spo_name_tab[i]);
4326 n = spp->sp_offsets[i];
4327 if (i != SPO_LC_OFF)
4328 {
4329 if (spp->sp_off_flags & mask)
4330 msg_putchar('s');
4331 else
4332 msg_putchar('e');
4333 if (n > 0)
4334 msg_putchar('+');
4335 }
4336 if (n || i == SPO_LC_OFF)
4337 msg_outnum(n);
4338 first = FALSE;
4339 }
4340 }
4341 msg_putchar(' ');
4342}
4343
4344/*
4345 * List or clear the keywords for one syntax group.
4346 * Return TRUE if the header has been printed.
4347 */
4348 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004349syn_list_keywords(
4350 int id,
4351 hashtab_T *ht,
4352 int did_header, /* header has already been printed */
4353 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 hashitem_T *hi;
4357 keyentry_T *kp;
4358 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 int prev_contained = 0;
4360 short *prev_next_list = NULL;
4361 short *prev_cont_in_list = NULL;
4362 int prev_skipnl = 0;
4363 int prev_skipwhite = 0;
4364 int prev_skipempty = 0;
4365
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366 /*
4367 * Unfortunately, this list of keywords is not sorted on alphabet but on
4368 * hash value...
4369 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004370 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004371 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004375 --todo;
4376 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 if (prev_contained != (kp->flags & HL_CONTAINED)
4381 || prev_skipnl != (kp->flags & HL_SKIPNL)
4382 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4383 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4384 || prev_cont_in_list != kp->k_syn.cont_in_list
4385 || prev_next_list != kp->next_list)
4386 outlen = 9999;
4387 else
4388 outlen = (int)STRLEN(kp->keyword);
4389 /* output "contained" and "nextgroup" on each line */
4390 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 prev_contained = 0;
4393 prev_next_list = NULL;
4394 prev_cont_in_list = NULL;
4395 prev_skipnl = 0;
4396 prev_skipwhite = 0;
4397 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004399 did_header = TRUE;
4400 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004402 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004406 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004407 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004408 put_id_list((char_u *)"containedin",
4409 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004411 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004412 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004413 if (kp->next_list != prev_next_list)
4414 {
4415 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4416 msg_putchar(' ');
4417 prev_next_list = kp->next_list;
4418 if (kp->flags & HL_SKIPNL)
4419 {
4420 msg_puts_attr((char_u *)"skipnl", attr);
4421 msg_putchar(' ');
4422 prev_skipnl = (kp->flags & HL_SKIPNL);
4423 }
4424 if (kp->flags & HL_SKIPWHITE)
4425 {
4426 msg_puts_attr((char_u *)"skipwhite", attr);
4427 msg_putchar(' ');
4428 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4429 }
4430 if (kp->flags & HL_SKIPEMPTY)
4431 {
4432 msg_puts_attr((char_u *)"skipempty", attr);
4433 msg_putchar(' ');
4434 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4435 }
4436 }
4437 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 }
4440 }
4441 }
4442
4443 return did_header;
4444}
4445
4446 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004447syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 hashitem_T *hi;
4450 keyentry_T *kp;
4451 keyentry_T *kp_prev;
4452 keyentry_T *kp_next;
4453 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454
Bram Moolenaardad6b692005-01-25 22:14:34 +00004455 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004456 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004457 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004459 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004461 --todo;
4462 kp_prev = NULL;
4463 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004465 if (kp->k_syn.id == id)
4466 {
4467 kp_next = kp->ke_next;
4468 if (kp_prev == NULL)
4469 {
4470 if (kp_next == NULL)
4471 hash_remove(ht, hi);
4472 else
4473 hi->hi_key = KE2HIKEY(kp_next);
4474 }
4475 else
4476 kp_prev->ke_next = kp_next;
4477 vim_free(kp->next_list);
4478 vim_free(kp->k_syn.cont_in_list);
4479 vim_free(kp);
4480 kp = kp_next;
4481 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004483 {
4484 kp_prev = kp;
4485 kp = kp->ke_next;
4486 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 }
4488 }
4489 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004490 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491}
4492
4493/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 */
4496 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004497clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004499 hashitem_T *hi;
4500 int todo;
4501 keyentry_T *kp;
4502 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004504 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004507 if (!HASHITEM_EMPTY(hi))
4508 {
4509 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004510 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004512 kp_next = kp->ke_next;
4513 vim_free(kp->next_list);
4514 vim_free(kp->k_syn.cont_in_list);
4515 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004517 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004519 hash_clear(ht);
4520 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521}
4522
4523/*
4524 * Add a keyword to the list of keywords.
4525 */
4526 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004527add_keyword(
4528 char_u *name, /* name of keyword */
4529 int id, /* group ID for this keyword */
4530 int flags, /* flags for this keyword */
4531 short *cont_in_list, /* containedin for this keyword */
4532 short *next_list, /* nextgroup for this keyword */
4533 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004535 keyentry_T *kp;
4536 hashtab_T *ht;
4537 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004538 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004539 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004540 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541
Bram Moolenaar860cae12010-06-05 23:22:07 +02004542 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004543 name_ic = str_foldcase(name, (int)STRLEN(name),
4544 name_folded, MAXKEYWLEN + 1);
4545 else
4546 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004547 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4548 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004550 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004551 kp->k_syn.id = id;
4552 kp->k_syn.inc_tag = current_syn_inc_tag;
4553 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004554 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004555 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004557 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004558 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559
Bram Moolenaar860cae12010-06-05 23:22:07 +02004560 if (curwin->w_s->b_syn_ic)
4561 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004563 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564
Bram Moolenaardad6b692005-01-25 22:14:34 +00004565 hash = hash_hash(kp->keyword);
4566 hi = hash_lookup(ht, kp->keyword, hash);
4567 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004569 /* new keyword, add to hashtable */
4570 kp->ke_next = NULL;
4571 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004573 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004575 /* keyword already exists, prepend to list */
4576 kp->ke_next = HI2KE(hi);
4577 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579}
4580
4581/*
4582 * Get the start and end of the group name argument.
4583 * Return a pointer to the first argument.
4584 * Return NULL if the end of the command was found instead of further args.
4585 */
4586 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004587get_group_name(
4588 char_u *arg, /* start of the argument */
4589 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004590{
4591 char_u *rest;
4592
4593 *name_end = skiptowhite(arg);
4594 rest = skipwhite(*name_end);
4595
4596 /*
4597 * Check if there are enough arguments. The first argument may be a
4598 * pattern, where '|' is allowed, so only check for NUL.
4599 */
4600 if (ends_excmd(*arg) || *rest == NUL)
4601 return NULL;
4602 return rest;
4603}
4604
4605/*
4606 * Check for syntax command option arguments.
4607 * This can be called at any place in the list of arguments, and just picks
4608 * out the arguments that are known. Can be called several times in a row to
4609 * collect all options in between other arguments.
4610 * Return a pointer to the next argument (which isn't an option).
4611 * Return NULL for any error;
4612 */
4613 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004614get_syn_options(
4615 char_u *arg, /* next argument to be checked */
4616 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004617 int *conceal_char UNUSED,
4618 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004619{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004620 char_u *gname_start, *gname;
4621 int syn_id;
4622 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004623 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004624 int i;
4625 int fidx;
4626 static struct flag
4627 {
4628 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004629 int argtype;
4630 int flags;
4631 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4632 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4633 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4634 {"eExXtTeEnNdD", 0, HL_EXTEND},
4635 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4636 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4637 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4638 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4639 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4640 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4641 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4642 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4643 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004644 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4645 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4646 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004647 {"cCoOnNtTaAiInNsS", 1, 0},
4648 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4649 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004651 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652
4653 if (arg == NULL) /* already detected error */
4654 return NULL;
4655
Bram Moolenaar860cae12010-06-05 23:22:07 +02004656#ifdef FEAT_CONCEAL
4657 if (curwin->w_s->b_syn_conceal)
4658 opt->flags |= HL_CONCEAL;
4659#endif
4660
Bram Moolenaar071d4272004-06-13 20:20:40 +00004661 for (;;)
4662 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004663 /*
4664 * This is used very often when a large number of keywords is defined.
4665 * Need to skip quickly when no option name is found.
4666 * Also avoid tolower(), it's slow.
4667 */
4668 if (strchr(first_letters, *arg) == NULL)
4669 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670
4671 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4672 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004673 p = flagtab[fidx].name;
4674 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4675 if (arg[len] != p[i] && arg[len] != p[i + 1])
4676 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004677 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004678 || (flagtab[fidx].argtype > 0
4679 ? arg[len] == '='
4680 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004682 if (opt->keyword
4683 && (flagtab[fidx].flags == HL_DISPLAY
4684 || flagtab[fidx].flags == HL_FOLD
4685 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 /* treat "display", "fold" and "extend" as a keyword */
4687 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 break;
4689 }
4690 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004691 if (fidx < 0) /* no match found */
4692 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004694 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004696 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 {
4698 EMSG(_("E395: contains argument not accepted here"));
4699 return NULL;
4700 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004701 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702 return NULL;
4703 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004704 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004706 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707 return NULL;
4708 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004709 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004711 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004712 return NULL;
4713 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004714 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4715 {
4716#ifdef FEAT_MBYTE
4717 /* cchar=? */
4718 if (has_mbyte)
4719 {
4720# ifdef FEAT_CONCEAL
4721 *conceal_char = mb_ptr2char(arg + 6);
4722# endif
4723 arg += mb_ptr2len(arg + 6) - 1;
4724 }
4725 else
4726#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004727 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004728#ifdef FEAT_CONCEAL
4729 *conceal_char = arg[6];
4730#else
4731 ;
4732#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004733 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004734#ifdef FEAT_CONCEAL
4735 if (!vim_isprintc_strict(*conceal_char))
4736 {
4737 EMSG(_("E844: invalid cchar value"));
4738 return NULL;
4739 }
4740#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004741 arg = skipwhite(arg + 7);
4742 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004743 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004744 {
4745 opt->flags |= flagtab[fidx].flags;
4746 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004748 if (flagtab[fidx].flags == HL_SYNC_HERE
4749 || flagtab[fidx].flags == HL_SYNC_THERE)
4750 {
4751 if (opt->sync_idx == NULL)
4752 {
4753 EMSG(_("E393: group[t]here not accepted here"));
4754 return NULL;
4755 }
4756 gname_start = arg;
4757 arg = skiptowhite(arg);
4758 if (gname_start == arg)
4759 return NULL;
4760 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4761 if (gname == NULL)
4762 return NULL;
4763 if (STRCMP(gname, "NONE") == 0)
4764 *opt->sync_idx = NONE_IDX;
4765 else
4766 {
4767 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004768 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4769 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4770 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004771 {
4772 *opt->sync_idx = i;
4773 break;
4774 }
4775 if (i < 0)
4776 {
4777 EMSG2(_("E394: Didn't find region item for %s"), gname);
4778 vim_free(gname);
4779 return NULL;
4780 }
4781 }
4782
4783 vim_free(gname);
4784 arg = skipwhite(arg);
4785 }
4786#ifdef FEAT_FOLDING
4787 else if (flagtab[fidx].flags == HL_FOLD
4788 && foldmethodIsSyntax(curwin))
4789 /* Need to update folds later. */
4790 foldUpdateAll(curwin);
4791#endif
4792 }
4793 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794
4795 return arg;
4796}
4797
4798/*
4799 * Adjustments to syntax item when declared in a ":syn include"'d file.
4800 * Set the contained flag, and if the item is not already contained, add it
4801 * to the specified top-level group, if any.
4802 */
4803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004804syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004806 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807 return;
4808 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004809 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 {
4811 /* We have to alloc this, because syn_combine_list() will free it. */
4812 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004813 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004814
4815 if (grp_list != NULL)
4816 {
4817 grp_list[0] = id;
4818 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004819 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820 CLUSTER_ADD);
4821 }
4822 }
4823}
4824
4825/*
4826 * Handle ":syntax include [@{group-name}] filename" command.
4827 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004829syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004830{
4831 char_u *arg = eap->arg;
4832 int sgl_id = 1;
4833 char_u *group_name_end;
4834 char_u *rest;
4835 char_u *errormsg = NULL;
4836 int prev_toplvl_grp;
4837 int prev_syn_inc_tag;
4838 int source = FALSE;
4839
4840 eap->nextcmd = find_nextcmd(arg);
4841 if (eap->skip)
4842 return;
4843
4844 if (arg[0] == '@')
4845 {
4846 ++arg;
4847 rest = get_group_name(arg, &group_name_end);
4848 if (rest == NULL)
4849 {
4850 EMSG((char_u *)_("E397: Filename required"));
4851 return;
4852 }
4853 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004854 if (sgl_id == 0)
4855 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004856 /* separate_nextcmd() and expand_filename() depend on this */
4857 eap->arg = rest;
4858 }
4859
4860 /*
4861 * Everything that's left, up to the next command, should be the
4862 * filename to include.
4863 */
4864 eap->argt |= (XFILE | NOSPC);
4865 separate_nextcmd(eap);
4866 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4867 {
4868 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4869 * file. Need to expand the file name first. In other cases
4870 * ":runtime!" is used. */
4871 source = TRUE;
4872 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4873 {
4874 if (errormsg != NULL)
4875 EMSG(errormsg);
4876 return;
4877 }
4878 }
4879
4880 /*
4881 * Save and restore the existing top-level grouplist id and ":syn
4882 * include" tag around the actual inclusion.
4883 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004884 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4885 {
4886 EMSG((char_u *)_("E847: Too many syntax includes"));
4887 return;
4888 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889 prev_syn_inc_tag = current_syn_inc_tag;
4890 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4892 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004893 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004894 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004896 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 current_syn_inc_tag = prev_syn_inc_tag;
4898}
4899
4900/*
4901 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4902 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004903 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004904syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905{
4906 char_u *arg = eap->arg;
4907 char_u *group_name_end;
4908 int syn_id;
4909 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004910 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004911 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004912 char_u *kw;
4913 syn_opt_arg_T syn_opt_arg;
4914 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004915 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916
4917 rest = get_group_name(arg, &group_name_end);
4918
4919 if (rest != NULL)
4920 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004921 if (eap->skip)
4922 syn_id = -1;
4923 else
4924 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004925 if (syn_id != 0)
4926 /* allocate a buffer, for removing backslashes in the keyword */
4927 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928 if (keyword_copy != NULL)
4929 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 syn_opt_arg.flags = 0;
4931 syn_opt_arg.keyword = TRUE;
4932 syn_opt_arg.sync_idx = NULL;
4933 syn_opt_arg.has_cont_list = FALSE;
4934 syn_opt_arg.cont_in_list = NULL;
4935 syn_opt_arg.next_list = NULL;
4936
Bram Moolenaar071d4272004-06-13 20:20:40 +00004937 /*
4938 * The options given apply to ALL keywords, so all options must be
4939 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004940 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004942 cnt = 0;
4943 p = keyword_copy;
4944 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004946 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4947 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004948 if (rest == NULL || ends_excmd(*rest))
4949 break;
4950 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004951 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004952 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004953 if (*rest == '\\' && rest[1] != NUL)
4954 ++rest;
4955 *p++ = *rest++;
4956 }
4957 *p++ = NUL;
4958 ++cnt;
4959 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004961 if (!eap->skip)
4962 {
4963 /* Adjust flags for use of ":syn include". */
4964 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4965
4966 /*
4967 * 2: Add an entry for each keyword.
4968 */
4969 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4970 {
4971 for (p = vim_strchr(kw, '['); ; )
4972 {
4973 if (p != NULL)
4974 *p = NUL;
4975 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004976 syn_opt_arg.cont_in_list,
4977 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004978 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004979 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004980 if (p[1] == NUL)
4981 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004982 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004983 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004984 }
4985 if (p[1] == ']')
4986 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004987 if (p[2] != NUL)
4988 {
4989 EMSG3(_("E890: trailing char after ']': %s]%s"),
4990 kw, &p[2]);
4991 goto error;
4992 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004993 kw = p + 1; /* skip over the "]" */
4994 break;
4995 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004996#ifdef FEAT_MBYTE
4997 if (has_mbyte)
4998 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004999 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005000
5001 mch_memmove(p, p + 1, l);
5002 p += l;
5003 }
5004 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005006 {
5007 p[0] = p[1];
5008 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009 }
5010 }
5011 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005012 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02005013error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005015 vim_free(syn_opt_arg.cont_in_list);
5016 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017 }
5018 }
5019
5020 if (rest != NULL)
5021 eap->nextcmd = check_nextcmd(rest);
5022 else
5023 EMSG2(_(e_invarg2), arg);
5024
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005025 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005026 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027}
5028
5029/*
5030 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5031 *
5032 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5033 */
5034 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005035syn_cmd_match(
5036 exarg_T *eap,
5037 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038{
5039 char_u *arg = eap->arg;
5040 char_u *group_name_end;
5041 char_u *rest;
5042 synpat_T item; /* the item found in the line */
5043 int syn_id;
5044 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005045 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005047 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048
5049 /* Isolate the group name, check for validity */
5050 rest = get_group_name(arg, &group_name_end);
5051
5052 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005053 syn_opt_arg.flags = 0;
5054 syn_opt_arg.keyword = FALSE;
5055 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5056 syn_opt_arg.has_cont_list = TRUE;
5057 syn_opt_arg.cont_list = NULL;
5058 syn_opt_arg.cont_in_list = NULL;
5059 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005060 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061
5062 /* get the pattern. */
5063 init_syn_patterns();
5064 vim_memset(&item, 0, sizeof(item));
5065 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5067 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068
5069 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005070 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071
5072 if (rest != NULL) /* all arguments are valid */
5073 {
5074 /*
5075 * Check for trailing command and illegal trailing arguments.
5076 */
5077 eap->nextcmd = check_nextcmd(rest);
5078 if (!ends_excmd(*rest) || eap->skip)
5079 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005080 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081 && (syn_id = syn_check_group(arg,
5082 (int)(group_name_end - arg))) != 0)
5083 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005084 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085 /*
5086 * Store the pattern in the syn_items list
5087 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005088 idx = curwin->w_s->b_syn_patterns.ga_len;
5089 SYN_ITEMS(curwin->w_s)[idx] = item;
5090 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5091 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5092 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5093 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5094 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5095 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5096 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5097 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005098 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005099#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005100 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005101#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005102 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005103 curwin->w_s->b_syn_containedin = TRUE;
5104 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5105 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106
5107 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005108 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005109 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005111 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005112 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113#endif
5114
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005115 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005116 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 return; /* don't free the progs and patterns now */
5118 }
5119 }
5120
5121 /*
5122 * Something failed, free the allocated memory.
5123 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005124 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005126 vim_free(syn_opt_arg.cont_list);
5127 vim_free(syn_opt_arg.cont_in_list);
5128 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005129
5130 if (rest == NULL)
5131 EMSG2(_(e_invarg2), arg);
5132}
5133
5134/*
5135 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5136 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5137 */
5138 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005139syn_cmd_region(
5140 exarg_T *eap,
5141 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142{
5143 char_u *arg = eap->arg;
5144 char_u *group_name_end;
5145 char_u *rest; /* next arg, NULL on error */
5146 char_u *key_end;
5147 char_u *key = NULL;
5148 char_u *p;
5149 int item;
5150#define ITEM_START 0
5151#define ITEM_SKIP 1
5152#define ITEM_END 2
5153#define ITEM_MATCHGROUP 3
5154 struct pat_ptr
5155 {
5156 synpat_T *pp_synp; /* pointer to syn_pattern */
5157 int pp_matchgroup_id; /* matchgroup ID */
5158 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5159 } *(pat_ptrs[3]);
5160 /* patterns found in the line */
5161 struct pat_ptr *ppp;
5162 struct pat_ptr *ppp_next;
5163 int pat_count = 0; /* nr of syn_patterns found */
5164 int syn_id;
5165 int matchgroup_id = 0;
5166 int not_enough = FALSE; /* not enough arguments */
5167 int illegal = FALSE; /* illegal arguments */
5168 int success = FALSE;
5169 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005170 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005171 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172
5173 /* Isolate the group name, check for validity */
5174 rest = get_group_name(arg, &group_name_end);
5175
5176 pat_ptrs[0] = NULL;
5177 pat_ptrs[1] = NULL;
5178 pat_ptrs[2] = NULL;
5179
5180 init_syn_patterns();
5181
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005182 syn_opt_arg.flags = 0;
5183 syn_opt_arg.keyword = FALSE;
5184 syn_opt_arg.sync_idx = NULL;
5185 syn_opt_arg.has_cont_list = TRUE;
5186 syn_opt_arg.cont_list = NULL;
5187 syn_opt_arg.cont_in_list = NULL;
5188 syn_opt_arg.next_list = NULL;
5189
Bram Moolenaar071d4272004-06-13 20:20:40 +00005190 /*
5191 * get the options, patterns and matchgroup.
5192 */
5193 while (rest != NULL && !ends_excmd(*rest))
5194 {
5195 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005196 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005197 if (rest == NULL || ends_excmd(*rest))
5198 break;
5199
5200 /* must be a pattern or matchgroup then */
5201 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005202 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005203 ++key_end;
5204 vim_free(key);
5205 key = vim_strnsave_up(rest, (int)(key_end - rest));
5206 if (key == NULL) /* out of memory */
5207 {
5208 rest = NULL;
5209 break;
5210 }
5211 if (STRCMP(key, "MATCHGROUP") == 0)
5212 item = ITEM_MATCHGROUP;
5213 else if (STRCMP(key, "START") == 0)
5214 item = ITEM_START;
5215 else if (STRCMP(key, "END") == 0)
5216 item = ITEM_END;
5217 else if (STRCMP(key, "SKIP") == 0)
5218 {
5219 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5220 {
5221 illegal = TRUE;
5222 break;
5223 }
5224 item = ITEM_SKIP;
5225 }
5226 else
5227 break;
5228 rest = skipwhite(key_end);
5229 if (*rest != '=')
5230 {
5231 rest = NULL;
5232 EMSG2(_("E398: Missing '=': %s"), arg);
5233 break;
5234 }
5235 rest = skipwhite(rest + 1);
5236 if (*rest == NUL)
5237 {
5238 not_enough = TRUE;
5239 break;
5240 }
5241
5242 if (item == ITEM_MATCHGROUP)
5243 {
5244 p = skiptowhite(rest);
5245 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5246 matchgroup_id = 0;
5247 else
5248 {
5249 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5250 if (matchgroup_id == 0)
5251 {
5252 illegal = TRUE;
5253 break;
5254 }
5255 }
5256 rest = skipwhite(p);
5257 }
5258 else
5259 {
5260 /*
5261 * Allocate room for a syn_pattern, and link it in the list of
5262 * syn_patterns for this item, at the start (because the list is
5263 * used from end to start).
5264 */
5265 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5266 if (ppp == NULL)
5267 {
5268 rest = NULL;
5269 break;
5270 }
5271 ppp->pp_next = pat_ptrs[item];
5272 pat_ptrs[item] = ppp;
5273 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5274 if (ppp->pp_synp == NULL)
5275 {
5276 rest = NULL;
5277 break;
5278 }
5279
5280 /*
5281 * Get the syntax pattern and the following offset(s).
5282 */
5283 /* Enable the appropriate \z specials. */
5284 if (item == ITEM_START)
5285 reg_do_extmatch = REX_SET;
5286 else if (item == ITEM_SKIP || item == ITEM_END)
5287 reg_do_extmatch = REX_USE;
5288 rest = get_syn_pattern(rest, ppp->pp_synp);
5289 reg_do_extmatch = 0;
5290 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005291 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005292 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5293 ppp->pp_matchgroup_id = matchgroup_id;
5294 ++pat_count;
5295 }
5296 }
5297 vim_free(key);
5298 if (illegal || not_enough)
5299 rest = NULL;
5300
5301 /*
5302 * Must have a "start" and "end" pattern.
5303 */
5304 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5305 pat_ptrs[ITEM_END] == NULL))
5306 {
5307 not_enough = TRUE;
5308 rest = NULL;
5309 }
5310
5311 if (rest != NULL)
5312 {
5313 /*
5314 * Check for trailing garbage or command.
5315 * If OK, add the item.
5316 */
5317 eap->nextcmd = check_nextcmd(rest);
5318 if (!ends_excmd(*rest) || eap->skip)
5319 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005320 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005321 && (syn_id = syn_check_group(arg,
5322 (int)(group_name_end - arg))) != 0)
5323 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005324 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005325 /*
5326 * Store the start/skip/end in the syn_items list
5327 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005328 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 for (item = ITEM_START; item <= ITEM_END; ++item)
5330 {
5331 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5332 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005333 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5334 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5335 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 (item == ITEM_START) ? SPTYPE_START :
5337 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005338 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5339 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005340 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5341 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005342 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005343 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005344#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005345 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005346#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347 if (item == ITEM_START)
5348 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005349 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005350 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005351 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005352 syn_opt_arg.cont_in_list;
5353 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005354 curwin->w_s->b_syn_containedin = TRUE;
5355 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005356 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005358 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005359 ++idx;
5360#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005361 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005362 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005363#endif
5364 }
5365 }
5366
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005367 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005368 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005369 success = TRUE; /* don't free the progs and patterns now */
5370 }
5371 }
5372
5373 /*
5374 * Free the allocated memory.
5375 */
5376 for (item = ITEM_START; item <= ITEM_END; ++item)
5377 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5378 {
5379 if (!success)
5380 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005381 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005382 vim_free(ppp->pp_synp->sp_pattern);
5383 }
5384 vim_free(ppp->pp_synp);
5385 ppp_next = ppp->pp_next;
5386 vim_free(ppp);
5387 }
5388
5389 if (!success)
5390 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005391 vim_free(syn_opt_arg.cont_list);
5392 vim_free(syn_opt_arg.cont_in_list);
5393 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 if (not_enough)
5395 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5396 else if (illegal || rest == NULL)
5397 EMSG2(_(e_invarg2), arg);
5398 }
5399}
5400
5401/*
5402 * A simple syntax group ID comparison function suitable for use in qsort()
5403 */
5404 static int
5405#ifdef __BORLANDC__
5406_RTLENTRYF
5407#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005408syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005409{
5410 const short *s1 = v1;
5411 const short *s2 = v2;
5412
5413 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5414}
5415
5416/*
5417 * Combines lists of syntax clusters.
5418 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5419 */
5420 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005421syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005422{
5423 int count1 = 0;
5424 int count2 = 0;
5425 short *g1;
5426 short *g2;
5427 short *clstr = NULL;
5428 int count;
5429 int round;
5430
5431 /*
5432 * Handle degenerate cases.
5433 */
5434 if (*clstr2 == NULL)
5435 return;
5436 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5437 {
5438 if (list_op == CLUSTER_REPLACE)
5439 vim_free(*clstr1);
5440 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5441 *clstr1 = *clstr2;
5442 else
5443 vim_free(*clstr2);
5444 return;
5445 }
5446
5447 for (g1 = *clstr1; *g1; g1++)
5448 ++count1;
5449 for (g2 = *clstr2; *g2; g2++)
5450 ++count2;
5451
5452 /*
5453 * For speed purposes, sort both lists.
5454 */
5455 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5456 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5457
5458 /*
5459 * We proceed in two passes; in round 1, we count the elements to place
5460 * in the new list, and in round 2, we allocate and populate the new
5461 * list. For speed, we use a mergesort-like method, adding the smaller
5462 * of the current elements in each list to the new list.
5463 */
5464 for (round = 1; round <= 2; round++)
5465 {
5466 g1 = *clstr1;
5467 g2 = *clstr2;
5468 count = 0;
5469
5470 /*
5471 * First, loop through the lists until one of them is empty.
5472 */
5473 while (*g1 && *g2)
5474 {
5475 /*
5476 * We always want to add from the first list.
5477 */
5478 if (*g1 < *g2)
5479 {
5480 if (round == 2)
5481 clstr[count] = *g1;
5482 count++;
5483 g1++;
5484 continue;
5485 }
5486 /*
5487 * We only want to add from the second list if we're adding the
5488 * lists.
5489 */
5490 if (list_op == CLUSTER_ADD)
5491 {
5492 if (round == 2)
5493 clstr[count] = *g2;
5494 count++;
5495 }
5496 if (*g1 == *g2)
5497 g1++;
5498 g2++;
5499 }
5500
5501 /*
5502 * Now add the leftovers from whichever list didn't get finished
5503 * first. As before, we only want to add from the second list if
5504 * we're adding the lists.
5505 */
5506 for (; *g1; g1++, count++)
5507 if (round == 2)
5508 clstr[count] = *g1;
5509 if (list_op == CLUSTER_ADD)
5510 for (; *g2; g2++, count++)
5511 if (round == 2)
5512 clstr[count] = *g2;
5513
5514 if (round == 1)
5515 {
5516 /*
5517 * If the group ended up empty, we don't need to allocate any
5518 * space for it.
5519 */
5520 if (count == 0)
5521 {
5522 clstr = NULL;
5523 break;
5524 }
5525 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5526 if (clstr == NULL)
5527 break;
5528 clstr[count] = 0;
5529 }
5530 }
5531
5532 /*
5533 * Finally, put the new list in place.
5534 */
5535 vim_free(*clstr1);
5536 vim_free(*clstr2);
5537 *clstr1 = clstr;
5538}
5539
5540/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005541 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542 * If it is not found, 0 is returned.
5543 */
5544 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005545syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546{
5547 int i;
5548 char_u *name_u;
5549
5550 /* Avoid using stricmp() too much, it's slow on some systems */
5551 name_u = vim_strsave_up(name);
5552 if (name_u == NULL)
5553 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005554 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5555 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5556 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557 break;
5558 vim_free(name_u);
5559 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5560}
5561
5562/*
5563 * Like syn_scl_name2id(), but take a pointer + length argument.
5564 */
5565 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005566syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567{
5568 char_u *name;
5569 int id = 0;
5570
5571 name = vim_strnsave(linep, len);
5572 if (name != NULL)
5573 {
5574 id = syn_scl_name2id(name);
5575 vim_free(name);
5576 }
5577 return id;
5578}
5579
5580/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005581 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 * The argument is a pointer to the name and the length of the name.
5583 * If it doesn't exist yet, a new entry is created.
5584 * Return 0 for failure.
5585 */
5586 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005587syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588{
5589 int id;
5590 char_u *name;
5591
5592 name = vim_strnsave(pp, len);
5593 if (name == NULL)
5594 return 0;
5595
5596 id = syn_scl_name2id(name);
5597 if (id == 0) /* doesn't exist yet */
5598 id = syn_add_cluster(name);
5599 else
5600 vim_free(name);
5601 return id;
5602}
5603
5604/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005605 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 * "name" must be an allocated string, it will be consumed.
5607 * Return 0 for failure.
5608 */
5609 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005610syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005611{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005612 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613
5614 /*
5615 * First call for this growarray: init growing array.
5616 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005617 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005619 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5620 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 }
5622
Bram Moolenaar42431a72011-04-01 14:44:59 +02005623 len = curwin->w_s->b_syn_clusters.ga_len;
5624 if (len >= MAX_CLUSTER_ID)
5625 {
5626 EMSG((char_u *)_("E848: Too many syntax clusters"));
5627 vim_free(name);
5628 return 0;
5629 }
5630
Bram Moolenaar071d4272004-06-13 20:20:40 +00005631 /*
5632 * Make room for at least one other cluster entry.
5633 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005634 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005635 {
5636 vim_free(name);
5637 return 0;
5638 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639
Bram Moolenaar860cae12010-06-05 23:22:07 +02005640 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5641 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5642 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5643 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5644 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005645
Bram Moolenaar217ad922005-03-20 22:37:15 +00005646 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005647 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005648 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005649 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005650
Bram Moolenaar071d4272004-06-13 20:20:40 +00005651 return len + SYNID_CLUSTER;
5652}
5653
5654/*
5655 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5656 * [add={groupname},..] [remove={groupname},..]".
5657 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005659syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005660{
5661 char_u *arg = eap->arg;
5662 char_u *group_name_end;
5663 char_u *rest;
5664 int scl_id;
5665 short *clstr_list;
5666 int got_clstr = FALSE;
5667 int opt_len;
5668 int list_op;
5669
5670 eap->nextcmd = find_nextcmd(arg);
5671 if (eap->skip)
5672 return;
5673
5674 rest = get_group_name(arg, &group_name_end);
5675
5676 if (rest != NULL)
5677 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005678 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5679 if (scl_id == 0)
5680 return;
5681 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682
5683 for (;;)
5684 {
5685 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005686 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687 {
5688 opt_len = 3;
5689 list_op = CLUSTER_ADD;
5690 }
5691 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005692 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 {
5694 opt_len = 6;
5695 list_op = CLUSTER_SUBTRACT;
5696 }
5697 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005698 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699 {
5700 opt_len = 8;
5701 list_op = CLUSTER_REPLACE;
5702 }
5703 else
5704 break;
5705
5706 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005707 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005708 {
5709 EMSG2(_(e_invarg2), rest);
5710 break;
5711 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005712 if (scl_id >= 0)
5713 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005715 else
5716 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005717 got_clstr = TRUE;
5718 }
5719
5720 if (got_clstr)
5721 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005722 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005723 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724 }
5725 }
5726
5727 if (!got_clstr)
5728 EMSG(_("E400: No cluster specified"));
5729 if (rest == NULL || !ends_excmd(*rest))
5730 EMSG2(_(e_invarg2), arg);
5731}
5732
5733/*
5734 * On first call for current buffer: Init growing array.
5735 */
5736 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005737init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005739 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5740 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005741}
5742
5743/*
5744 * Get one pattern for a ":syntax match" or ":syntax region" command.
5745 * Stores the pattern and program in a synpat_T.
5746 * Returns a pointer to the next argument, or NULL in case of an error.
5747 */
5748 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005749get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005750{
5751 char_u *end;
5752 int *p;
5753 int idx;
5754 char_u *cpo_save;
5755
5756 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005757 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005758 return NULL;
5759
5760 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5761 if (*end != *arg) /* end delimiter not found */
5762 {
5763 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5764 return NULL;
5765 }
5766 /* store the pattern and compiled regexp program */
5767 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5768 return NULL;
5769
5770 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5771 cpo_save = p_cpo;
5772 p_cpo = (char_u *)"";
5773 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5774 p_cpo = cpo_save;
5775
5776 if (ci->sp_prog == NULL)
5777 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005778 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005779#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005780 syn_clear_time(&ci->sp_time);
5781#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782
5783 /*
5784 * Check for a match, highlight or region offset.
5785 */
5786 ++end;
5787 do
5788 {
5789 for (idx = SPO_COUNT; --idx >= 0; )
5790 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5791 break;
5792 if (idx >= 0)
5793 {
5794 p = &(ci->sp_offsets[idx]);
5795 if (idx != SPO_LC_OFF)
5796 switch (end[3])
5797 {
5798 case 's': break;
5799 case 'b': break;
5800 case 'e': idx += SPO_COUNT; break;
5801 default: idx = -1; break;
5802 }
5803 if (idx >= 0)
5804 {
5805 ci->sp_off_flags |= (1 << idx);
5806 if (idx == SPO_LC_OFF) /* lc=99 */
5807 {
5808 end += 3;
5809 *p = getdigits(&end);
5810
5811 /* "lc=" offset automatically sets "ms=" offset */
5812 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5813 {
5814 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5815 ci->sp_offsets[SPO_MS_OFF] = *p;
5816 }
5817 }
5818 else /* yy=x+99 */
5819 {
5820 end += 4;
5821 if (*end == '+')
5822 {
5823 ++end;
5824 *p = getdigits(&end); /* positive offset */
5825 }
5826 else if (*end == '-')
5827 {
5828 ++end;
5829 *p = -getdigits(&end); /* negative offset */
5830 }
5831 }
5832 if (*end != ',')
5833 break;
5834 ++end;
5835 }
5836 }
5837 } while (idx >= 0);
5838
Bram Moolenaar1c465442017-03-12 20:10:05 +01005839 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 {
5841 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5842 return NULL;
5843 }
5844 return skipwhite(end);
5845}
5846
5847/*
5848 * Handle ":syntax sync .." command.
5849 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005851syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005852{
5853 char_u *arg_start = eap->arg;
5854 char_u *arg_end;
5855 char_u *key = NULL;
5856 char_u *next_arg;
5857 int illegal = FALSE;
5858 int finished = FALSE;
5859 long n;
5860 char_u *cpo_save;
5861
5862 if (ends_excmd(*arg_start))
5863 {
5864 syn_cmd_list(eap, TRUE);
5865 return;
5866 }
5867
5868 while (!ends_excmd(*arg_start))
5869 {
5870 arg_end = skiptowhite(arg_start);
5871 next_arg = skipwhite(arg_end);
5872 vim_free(key);
5873 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5874 if (STRCMP(key, "CCOMMENT") == 0)
5875 {
5876 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005877 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005878 if (!ends_excmd(*next_arg))
5879 {
5880 arg_end = skiptowhite(next_arg);
5881 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005882 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005883 (int)(arg_end - next_arg));
5884 next_arg = skipwhite(arg_end);
5885 }
5886 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005887 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005888 }
5889 else if ( STRNCMP(key, "LINES", 5) == 0
5890 || STRNCMP(key, "MINLINES", 8) == 0
5891 || STRNCMP(key, "MAXLINES", 8) == 0
5892 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5893 {
5894 if (key[4] == 'S')
5895 arg_end = key + 6;
5896 else if (key[0] == 'L')
5897 arg_end = key + 11;
5898 else
5899 arg_end = key + 9;
5900 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5901 {
5902 illegal = TRUE;
5903 break;
5904 }
5905 n = getdigits(&arg_end);
5906 if (!eap->skip)
5907 {
5908 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005909 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005910 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005911 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005913 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914 }
5915 }
5916 else if (STRCMP(key, "FROMSTART") == 0)
5917 {
5918 if (!eap->skip)
5919 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005920 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5921 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005922 }
5923 }
5924 else if (STRCMP(key, "LINECONT") == 0)
5925 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005926 if (*next_arg == NUL) /* missing pattern */
5927 {
5928 illegal = TRUE;
5929 break;
5930 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005931 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005932 {
5933 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5934 finished = TRUE;
5935 break;
5936 }
5937 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5938 if (*arg_end != *next_arg) /* end delimiter not found */
5939 {
5940 illegal = TRUE;
5941 break;
5942 }
5943
5944 if (!eap->skip)
5945 {
5946 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005947 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005948 (int)(arg_end - next_arg - 1))) == NULL)
5949 {
5950 finished = TRUE;
5951 break;
5952 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005953 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005954
5955 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5956 cpo_save = p_cpo;
5957 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005958 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005959 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005961#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005962 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5963#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005964
Bram Moolenaar860cae12010-06-05 23:22:07 +02005965 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005966 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005967 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005968 finished = TRUE;
5969 break;
5970 }
5971 }
5972 next_arg = skipwhite(arg_end + 1);
5973 }
5974 else
5975 {
5976 eap->arg = next_arg;
5977 if (STRCMP(key, "MATCH") == 0)
5978 syn_cmd_match(eap, TRUE);
5979 else if (STRCMP(key, "REGION") == 0)
5980 syn_cmd_region(eap, TRUE);
5981 else if (STRCMP(key, "CLEAR") == 0)
5982 syn_cmd_clear(eap, TRUE);
5983 else
5984 illegal = TRUE;
5985 finished = TRUE;
5986 break;
5987 }
5988 arg_start = next_arg;
5989 }
5990 vim_free(key);
5991 if (illegal)
5992 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5993 else if (!finished)
5994 {
5995 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005996 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005997 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005998 }
5999}
6000
6001/*
6002 * Convert a line of highlight group names into a list of group ID numbers.
6003 * "arg" should point to the "contains" or "nextgroup" keyword.
6004 * "arg" is advanced to after the last group name.
6005 * Careful: the argument is modified (NULs added).
6006 * returns FAIL for some error, OK for success.
6007 */
6008 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006009get_id_list(
6010 char_u **arg,
6011 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006012 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00006013 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006014 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006015{
6016 char_u *p = NULL;
6017 char_u *end;
6018 int round;
6019 int count;
6020 int total_count = 0;
6021 short *retval = NULL;
6022 char_u *name;
6023 regmatch_T regmatch;
6024 int id;
6025 int i;
6026 int failed = FALSE;
6027
6028 /*
6029 * We parse the list twice:
6030 * round == 1: count the number of items, allocate the array.
6031 * round == 2: fill the array with the items.
6032 * In round 1 new groups may be added, causing the number of items to
6033 * grow when a regexp is used. In that case round 1 is done once again.
6034 */
6035 for (round = 1; round <= 2; ++round)
6036 {
6037 /*
6038 * skip "contains"
6039 */
6040 p = skipwhite(*arg + keylen);
6041 if (*p != '=')
6042 {
6043 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6044 break;
6045 }
6046 p = skipwhite(p + 1);
6047 if (ends_excmd(*p))
6048 {
6049 EMSG2(_("E406: Empty argument: %s"), *arg);
6050 break;
6051 }
6052
6053 /*
6054 * parse the arguments after "contains"
6055 */
6056 count = 0;
6057 while (!ends_excmd(*p))
6058 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006059 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006060 ;
6061 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6062 if (name == NULL)
6063 {
6064 failed = TRUE;
6065 break;
6066 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006067 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006068 if ( STRCMP(name + 1, "ALLBUT") == 0
6069 || STRCMP(name + 1, "ALL") == 0
6070 || STRCMP(name + 1, "TOP") == 0
6071 || STRCMP(name + 1, "CONTAINED") == 0)
6072 {
6073 if (TOUPPER_ASC(**arg) != 'C')
6074 {
6075 EMSG2(_("E407: %s not allowed here"), name + 1);
6076 failed = TRUE;
6077 vim_free(name);
6078 break;
6079 }
6080 if (count != 0)
6081 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006082 EMSG2(_("E408: %s must be first in contains list"),
6083 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006084 failed = TRUE;
6085 vim_free(name);
6086 break;
6087 }
6088 if (name[1] == 'A')
6089 id = SYNID_ALLBUT;
6090 else if (name[1] == 'T')
6091 id = SYNID_TOP;
6092 else
6093 id = SYNID_CONTAINED;
6094 id += current_syn_inc_tag;
6095 }
6096 else if (name[1] == '@')
6097 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006098 if (skip)
6099 id = -1;
6100 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006101 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006102 }
6103 else
6104 {
6105 /*
6106 * Handle full group name.
6107 */
6108 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6109 id = syn_check_group(name + 1, (int)(end - p));
6110 else
6111 {
6112 /*
6113 * Handle match of regexp with group names.
6114 */
6115 *name = '^';
6116 STRCAT(name, "$");
6117 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6118 if (regmatch.regprog == NULL)
6119 {
6120 failed = TRUE;
6121 vim_free(name);
6122 break;
6123 }
6124
6125 regmatch.rm_ic = TRUE;
6126 id = 0;
6127 for (i = highlight_ga.ga_len; --i >= 0; )
6128 {
6129 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6130 (colnr_T)0))
6131 {
6132 if (round == 2)
6133 {
6134 /* Got more items than expected; can happen
6135 * when adding items that match:
6136 * "contains=a.*b,axb".
6137 * Go back to first round */
6138 if (count >= total_count)
6139 {
6140 vim_free(retval);
6141 round = 1;
6142 }
6143 else
6144 retval[count] = i + 1;
6145 }
6146 ++count;
6147 id = -1; /* remember that we found one */
6148 }
6149 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006150 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006151 }
6152 }
6153 vim_free(name);
6154 if (id == 0)
6155 {
6156 EMSG2(_("E409: Unknown group name: %s"), p);
6157 failed = TRUE;
6158 break;
6159 }
6160 if (id > 0)
6161 {
6162 if (round == 2)
6163 {
6164 /* Got more items than expected, go back to first round */
6165 if (count >= total_count)
6166 {
6167 vim_free(retval);
6168 round = 1;
6169 }
6170 else
6171 retval[count] = id;
6172 }
6173 ++count;
6174 }
6175 p = skipwhite(end);
6176 if (*p != ',')
6177 break;
6178 p = skipwhite(p + 1); /* skip comma in between arguments */
6179 }
6180 if (failed)
6181 break;
6182 if (round == 1)
6183 {
6184 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6185 if (retval == NULL)
6186 break;
6187 retval[count] = 0; /* zero means end of the list */
6188 total_count = count;
6189 }
6190 }
6191
6192 *arg = p;
6193 if (failed || retval == NULL)
6194 {
6195 vim_free(retval);
6196 return FAIL;
6197 }
6198
6199 if (*list == NULL)
6200 *list = retval;
6201 else
6202 vim_free(retval); /* list already found, don't overwrite it */
6203
6204 return OK;
6205}
6206
6207/*
6208 * Make a copy of an ID list.
6209 */
6210 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006211copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212{
6213 int len;
6214 int count;
6215 short *retval;
6216
6217 if (list == NULL)
6218 return NULL;
6219
6220 for (count = 0; list[count]; ++count)
6221 ;
6222 len = (count + 1) * sizeof(short);
6223 retval = (short *)alloc((unsigned)len);
6224 if (retval != NULL)
6225 mch_memmove(retval, list, (size_t)len);
6226
6227 return retval;
6228}
6229
6230/*
6231 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6232 * "cur_si" can be NULL if not checking the "containedin" list.
6233 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6234 * the current item.
6235 * This function is called very often, keep it fast!!
6236 */
6237 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006238in_id_list(
6239 stateitem_T *cur_si, /* current item or NULL */
6240 short *list, /* id list */
6241 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6242 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243{
6244 int retval;
6245 short *scl_list;
6246 short item;
6247 short id = ssp->id;
6248 static int depth = 0;
6249 int r;
6250
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006251 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006252 if (cur_si != NULL && ssp->cont_in_list != NULL
6253 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006254 {
6255 /* Ignore transparent items without a contains argument. Double check
6256 * that we don't go back past the first one. */
6257 while ((cur_si->si_flags & HL_TRANS_CONT)
6258 && cur_si > (stateitem_T *)(current_state.ga_data))
6259 --cur_si;
6260 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6261 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006262 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6263 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 return TRUE;
6265 }
6266
6267 if (list == NULL)
6268 return FALSE;
6269
6270 /*
6271 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6272 * inside anything. Only allow not-contained groups.
6273 */
6274 if (list == ID_LIST_ALL)
6275 return !contained;
6276
6277 /*
6278 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6279 * contains list. We also require that "id" is at the same ":syn include"
6280 * level as the list.
6281 */
6282 item = *list;
6283 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6284 {
6285 if (item < SYNID_TOP)
6286 {
6287 /* ALL or ALLBUT: accept all groups in the same file */
6288 if (item - SYNID_ALLBUT != ssp->inc_tag)
6289 return FALSE;
6290 }
6291 else if (item < SYNID_CONTAINED)
6292 {
6293 /* TOP: accept all not-contained groups in the same file */
6294 if (item - SYNID_TOP != ssp->inc_tag || contained)
6295 return FALSE;
6296 }
6297 else
6298 {
6299 /* CONTAINED: accept all contained groups in the same file */
6300 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6301 return FALSE;
6302 }
6303 item = *++list;
6304 retval = FALSE;
6305 }
6306 else
6307 retval = TRUE;
6308
6309 /*
6310 * Return "retval" if id is in the contains list.
6311 */
6312 while (item != 0)
6313 {
6314 if (item == id)
6315 return retval;
6316 if (item >= SYNID_CLUSTER)
6317 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006318 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006319 /* restrict recursiveness to 30 to avoid an endless loop for a
6320 * cluster that includes itself (indirectly) */
6321 if (scl_list != NULL && depth < 30)
6322 {
6323 ++depth;
6324 r = in_id_list(NULL, scl_list, ssp, contained);
6325 --depth;
6326 if (r)
6327 return retval;
6328 }
6329 }
6330 item = *++list;
6331 }
6332 return !retval;
6333}
6334
6335struct subcommand
6336{
Bram Moolenaard99df422016-01-29 23:20:40 +01006337 char *name; /* subcommand name */
6338 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006339};
6340
6341static struct subcommand subcommands[] =
6342{
6343 {"case", syn_cmd_case},
6344 {"clear", syn_cmd_clear},
6345 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006346 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347 {"enable", syn_cmd_enable},
6348 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006349 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350 {"keyword", syn_cmd_keyword},
6351 {"list", syn_cmd_list},
6352 {"manual", syn_cmd_manual},
6353 {"match", syn_cmd_match},
6354 {"on", syn_cmd_on},
6355 {"off", syn_cmd_off},
6356 {"region", syn_cmd_region},
6357 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006358 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006359 {"sync", syn_cmd_sync},
6360 {"", syn_cmd_list},
6361 {NULL, NULL}
6362};
6363
6364/*
6365 * ":syntax".
6366 * This searches the subcommands[] table for the subcommand name, and calls a
6367 * syntax_subcommand() function to do the rest.
6368 */
6369 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006370ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006371{
6372 char_u *arg = eap->arg;
6373 char_u *subcmd_end;
6374 char_u *subcmd_name;
6375 int i;
6376
6377 syn_cmdlinep = eap->cmdlinep;
6378
6379 /* isolate subcommand name */
6380 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6381 ;
6382 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6383 if (subcmd_name != NULL)
6384 {
6385 if (eap->skip) /* skip error messages for all subcommands */
6386 ++emsg_skip;
6387 for (i = 0; ; ++i)
6388 {
6389 if (subcommands[i].name == NULL)
6390 {
6391 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6392 break;
6393 }
6394 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6395 {
6396 eap->arg = skipwhite(subcmd_end);
6397 (subcommands[i].func)(eap, FALSE);
6398 break;
6399 }
6400 }
6401 vim_free(subcmd_name);
6402 if (eap->skip)
6403 --emsg_skip;
6404 }
6405}
6406
Bram Moolenaar860cae12010-06-05 23:22:07 +02006407 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006408ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006410 char_u *old_value;
6411 char_u *new_value;
6412
Bram Moolenaar860cae12010-06-05 23:22:07 +02006413 if (curwin->w_s == &curwin->w_buffer->b_s)
6414 {
6415 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6416 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006417 hash_init(&curwin->w_s->b_keywtab);
6418 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006419#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006420 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006421 curwin->w_p_spell = FALSE; /* No spell checking */
6422 clear_string_option(&curwin->w_s->b_p_spc);
6423 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006424 clear_string_option(&curwin->w_s->b_p_spl);
6425#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006426 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006427 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006428
6429 /* save value of b:current_syntax */
6430 old_value = get_var_value((char_u *)"b:current_syntax");
6431 if (old_value != NULL)
6432 old_value = vim_strsave(old_value);
6433
6434 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6435 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006436 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006437
6438 /* move value of b:current_syntax to w:current_syntax */
6439 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006440 if (new_value != NULL)
6441 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006442
6443 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006444 if (old_value == NULL)
6445 do_unlet((char_u *)"b:current_syntax", TRUE);
6446 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006447 {
6448 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6449 vim_free(old_value);
6450 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006451}
6452
6453 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006454syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006455{
6456 return (win->w_s->b_syn_patterns.ga_len != 0
6457 || win->w_s->b_syn_clusters.ga_len != 0
6458 || win->w_s->b_keywtab.ht_used > 0
6459 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460}
6461
6462#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6463
6464static enum
6465{
6466 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006467 EXP_CASE, /* expand ":syn case" arguments */
6468 EXP_SPELL, /* expand ":syn spell" arguments */
6469 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470} expand_what;
6471
Bram Moolenaar4f688582007-07-24 12:34:30 +00006472/*
6473 * Reset include_link, include_default, include_none to 0.
6474 * Called when we are done expanding.
6475 */
6476 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006477reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006478{
6479 include_link = include_default = include_none = 0;
6480}
6481
6482/*
6483 * Handle command line completion for :match and :echohl command: Add "None"
6484 * as highlight group.
6485 */
6486 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006487set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006488{
6489 xp->xp_context = EXPAND_HIGHLIGHT;
6490 xp->xp_pattern = arg;
6491 include_none = 1;
6492}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006493
6494/*
6495 * Handle command line completion for :syntax command.
6496 */
6497 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006498set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499{
6500 char_u *p;
6501
6502 /* Default: expand subcommands */
6503 xp->xp_context = EXPAND_SYNTAX;
6504 expand_what = EXP_SUBCMD;
6505 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006506 include_link = 0;
6507 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508
6509 /* (part of) subcommand already typed */
6510 if (*arg != NUL)
6511 {
6512 p = skiptowhite(arg);
6513 if (*p != NUL) /* past first word */
6514 {
6515 xp->xp_pattern = skipwhite(p);
6516 if (*skiptowhite(xp->xp_pattern) != NUL)
6517 xp->xp_context = EXPAND_NOTHING;
6518 else if (STRNICMP(arg, "case", p - arg) == 0)
6519 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006520 else if (STRNICMP(arg, "spell", p - arg) == 0)
6521 expand_what = EXP_SPELL;
6522 else if (STRNICMP(arg, "sync", p - arg) == 0)
6523 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6525 || STRNICMP(arg, "region", p - arg) == 0
6526 || STRNICMP(arg, "match", p - arg) == 0
6527 || STRNICMP(arg, "list", p - arg) == 0)
6528 xp->xp_context = EXPAND_HIGHLIGHT;
6529 else
6530 xp->xp_context = EXPAND_NOTHING;
6531 }
6532 }
6533}
6534
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535/*
6536 * Function given to ExpandGeneric() to obtain the list syntax names for
6537 * expansion.
6538 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006540get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006542 switch (expand_what)
6543 {
6544 case EXP_SUBCMD:
6545 return (char_u *)subcommands[idx].name;
6546 case EXP_CASE:
6547 {
6548 static char *case_args[] = {"match", "ignore", NULL};
6549 return (char_u *)case_args[idx];
6550 }
6551 case EXP_SPELL:
6552 {
6553 static char *spell_args[] =
6554 {"toplevel", "notoplevel", "default", NULL};
6555 return (char_u *)spell_args[idx];
6556 }
6557 case EXP_SYNC:
6558 {
6559 static char *sync_args[] =
6560 {"ccomment", "clear", "fromstart",
6561 "linebreaks=", "linecont", "lines=", "match",
6562 "maxlines=", "minlines=", "region", NULL};
6563 return (char_u *)sync_args[idx];
6564 }
6565 }
6566 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006567}
6568
6569#endif /* FEAT_CMDL_COMPL */
6570
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571/*
6572 * Function called for expression evaluation: get syntax ID at file position.
6573 */
6574 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006575syn_get_id(
6576 win_T *wp,
6577 long lnum,
6578 colnr_T col,
6579 int trans, /* remove transparency */
6580 int *spellp, /* return: can do spell checking */
6581 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582{
6583 /* When the position is not after the current position and in the same
6584 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006585 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006586 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006587 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006588 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006589 else if (wp->w_buffer == syn_buf
6590 && lnum == current_lnum
6591 && col > current_col)
6592 /* next_match may not be correct when moving around, e.g. with the
6593 * "skip" expression in searchpair() */
6594 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006595
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006596 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006597
6598 return (trans ? current_trans_id : current_id);
6599}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006600
Bram Moolenaar860cae12010-06-05 23:22:07 +02006601#if defined(FEAT_CONCEAL) || defined(PROTO)
6602/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006603 * Get extra information about the syntax item. Must be called right after
6604 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006605 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006606 * Returns the current flags.
6607 */
6608 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006609get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006610{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006611 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006612 return current_flags;
6613}
6614
6615/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006616 * Return conceal substitution character
6617 */
6618 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006619syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006620{
6621 return current_sub_char;
6622}
6623#endif
6624
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006625#if defined(FEAT_EVAL) || defined(PROTO)
6626/*
6627 * Return the syntax ID at position "i" in the current stack.
6628 * The caller must have called syn_get_id() before to fill the stack.
6629 * Returns -1 when "i" is out of range.
6630 */
6631 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006632syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006633{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006634 if (i >= current_state.ga_len)
6635 {
6636 /* Need to invalidate the state, because we didn't properly finish it
6637 * for the last character, "keep_state" was TRUE. */
6638 invalidate_current_state();
6639 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006640 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006641 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006642 return CUR_STATE(i).si_id;
6643}
6644#endif
6645
Bram Moolenaar071d4272004-06-13 20:20:40 +00006646#if defined(FEAT_FOLDING) || defined(PROTO)
6647/*
6648 * Function called to get folding level for line "lnum" in window "wp".
6649 */
6650 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006651syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006652{
6653 int level = 0;
6654 int i;
6655
6656 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006657 if (wp->w_s->b_syn_folditems != 0
6658 && !wp->w_s->b_syn_error
6659# ifdef SYN_TIME_LIMIT
6660 && !wp->w_s->b_syn_slow
6661# endif
6662 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006663 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006664 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006665
6666 for (i = 0; i < current_state.ga_len; ++i)
6667 if (CUR_STATE(i).si_flags & HL_FOLD)
6668 ++level;
6669 }
6670 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006671 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006672 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006673 if (level < 0)
6674 level = 0;
6675 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006676 return level;
6677}
6678#endif
6679
Bram Moolenaar01615492015-02-03 13:00:38 +01006680#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006681/*
6682 * ":syntime".
6683 */
6684 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006685ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006686{
6687 if (STRCMP(eap->arg, "on") == 0)
6688 syn_time_on = TRUE;
6689 else if (STRCMP(eap->arg, "off") == 0)
6690 syn_time_on = FALSE;
6691 else if (STRCMP(eap->arg, "clear") == 0)
6692 syntime_clear();
6693 else if (STRCMP(eap->arg, "report") == 0)
6694 syntime_report();
6695 else
6696 EMSG2(_(e_invarg2), eap->arg);
6697}
6698
6699 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006700syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006701{
6702 profile_zero(&st->total);
6703 profile_zero(&st->slowest);
6704 st->count = 0;
6705 st->match = 0;
6706}
6707
6708/*
6709 * Clear the syntax timing for the current buffer.
6710 */
6711 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006712syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006713{
6714 int idx;
6715 synpat_T *spp;
6716
6717 if (!syntax_present(curwin))
6718 {
6719 MSG(_(msg_no_items));
6720 return;
6721 }
6722 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6723 {
6724 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6725 syn_clear_time(&spp->sp_time);
6726 }
6727}
6728
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006729#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6730/*
6731 * Function given to ExpandGeneric() to obtain the possible arguments of the
6732 * ":syntime {on,off,clear,report}" command.
6733 */
6734 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006735get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006736{
6737 switch (idx)
6738 {
6739 case 0: return (char_u *)"on";
6740 case 1: return (char_u *)"off";
6741 case 2: return (char_u *)"clear";
6742 case 3: return (char_u *)"report";
6743 }
6744 return NULL;
6745}
6746#endif
6747
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006748typedef struct
6749{
6750 proftime_T total;
6751 int count;
6752 int match;
6753 proftime_T slowest;
6754 proftime_T average;
6755 int id;
6756 char_u *pattern;
6757} time_entry_T;
6758
6759 static int
6760#ifdef __BORLANDC__
6761_RTLENTRYF
6762#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006763syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006764{
6765 const time_entry_T *s1 = v1;
6766 const time_entry_T *s2 = v2;
6767
6768 return profile_cmp(&s1->total, &s2->total);
6769}
6770
6771/*
6772 * Clear the syntax timing for the current buffer.
6773 */
6774 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006775syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006776{
6777 int idx;
6778 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006779# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006780 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006781# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006782 int len;
6783 proftime_T total_total;
6784 int total_count = 0;
6785 garray_T ga;
6786 time_entry_T *p;
6787
6788 if (!syntax_present(curwin))
6789 {
6790 MSG(_(msg_no_items));
6791 return;
6792 }
6793
6794 ga_init2(&ga, sizeof(time_entry_T), 50);
6795 profile_zero(&total_total);
6796 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6797 {
6798 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6799 if (spp->sp_time.count > 0)
6800 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006801 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006802 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6803 p->total = spp->sp_time.total;
6804 profile_add(&total_total, &spp->sp_time.total);
6805 p->count = spp->sp_time.count;
6806 p->match = spp->sp_time.match;
6807 total_count += spp->sp_time.count;
6808 p->slowest = spp->sp_time.slowest;
6809# ifdef FEAT_FLOAT
6810 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6811 p->average = tm;
6812# endif
6813 p->id = spp->sp_syn.id;
6814 p->pattern = spp->sp_pattern;
6815 ++ga.ga_len;
6816 }
6817 }
6818
Bram Moolenaara2162552017-01-08 17:46:20 +01006819 /* Sort on total time. Skip if there are no items to avoid passing NULL
6820 * pointer to qsort(). */
6821 if (ga.ga_len > 1)
6822 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006823 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006824
6825 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6826 MSG_PUTS("\n");
6827 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6828 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006829 p = ((time_entry_T *)ga.ga_data) + idx;
6830
6831 MSG_PUTS(profile_msg(&p->total));
6832 MSG_PUTS(" "); /* make sure there is always a separating space */
6833 msg_advance(13);
6834 msg_outnum(p->count);
6835 MSG_PUTS(" ");
6836 msg_advance(20);
6837 msg_outnum(p->match);
6838 MSG_PUTS(" ");
6839 msg_advance(26);
6840 MSG_PUTS(profile_msg(&p->slowest));
6841 MSG_PUTS(" ");
6842 msg_advance(38);
6843# ifdef FEAT_FLOAT
6844 MSG_PUTS(profile_msg(&p->average));
6845 MSG_PUTS(" ");
6846# endif
6847 msg_advance(50);
6848 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6849 MSG_PUTS(" ");
6850
6851 msg_advance(69);
6852 if (Columns < 80)
6853 len = 20; /* will wrap anyway */
6854 else
6855 len = Columns - 70;
6856 if (len > (int)STRLEN(p->pattern))
6857 len = (int)STRLEN(p->pattern);
6858 msg_outtrans_len(p->pattern, len);
6859 MSG_PUTS("\n");
6860 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006861 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006862 if (!got_int)
6863 {
6864 MSG_PUTS("\n");
6865 MSG_PUTS(profile_msg(&total_total));
6866 msg_advance(13);
6867 msg_outnum(total_count);
6868 MSG_PUTS("\n");
6869 }
6870}
6871#endif
6872
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873#endif /* FEAT_SYN_HL */
6874
Bram Moolenaar071d4272004-06-13 20:20:40 +00006875/**************************************
6876 * Highlighting stuff *
6877 **************************************/
6878
6879/*
6880 * The default highlight groups. These are compiled-in for fast startup and
6881 * they still work when the runtime files can't be found.
6882 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006883 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6884 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006885 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006886#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006887# define CENT(a, b) b
6888#else
6889# define CENT(a, b) a
6890#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006891static char *(highlight_init_both[]) = {
6892 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6893 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6894 CENT("IncSearch term=reverse cterm=reverse",
6895 "IncSearch term=reverse cterm=reverse gui=reverse"),
6896 CENT("ModeMsg term=bold cterm=bold",
6897 "ModeMsg term=bold cterm=bold gui=bold"),
6898 CENT("NonText term=bold ctermfg=Blue",
6899 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6900 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6901 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6902 CENT("StatusLineNC term=reverse cterm=reverse",
6903 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6904 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006905 CENT("VertSplit term=reverse cterm=reverse",
6906 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006907#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006908 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6909 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006910#endif
6911#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006912 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6913 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006914#endif
6915#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006916 CENT("PmenuSbar ctermbg=Grey",
6917 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006918#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006919 CENT("TabLineSel term=bold cterm=bold",
6920 "TabLineSel term=bold cterm=bold gui=bold"),
6921 CENT("TabLineFill term=reverse cterm=reverse",
6922 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006923#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006924 "Cursor guibg=fg guifg=bg",
6925 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006926#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006927 "default link QuickFixLine Search",
6928 NULL
6929};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006930
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006931/* Default colors only used with a light background. */
6932static char *(highlight_init_light[]) = {
6933 CENT("Directory term=bold ctermfg=DarkBlue",
6934 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6935 CENT("LineNr term=underline ctermfg=Brown",
6936 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6937 CENT("CursorLineNr term=bold ctermfg=Brown",
6938 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6939 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6940 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6941 CENT("Question term=standout ctermfg=DarkGreen",
6942 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6943 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6944 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006945#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006946 CENT("SpellBad term=reverse ctermbg=LightRed",
6947 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6948 CENT("SpellCap term=reverse ctermbg=LightBlue",
6949 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6950 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6951 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6952 CENT("SpellLocal term=underline ctermbg=Cyan",
6953 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006954#endif
6955#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006956 CENT("PmenuThumb ctermbg=Black",
6957 "PmenuThumb ctermbg=Black guibg=Black"),
6958 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6959 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6960 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6961 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006962#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006963 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6964 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6965 CENT("Title term=bold ctermfg=DarkMagenta",
6966 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6967 CENT("WarningMsg term=standout ctermfg=DarkRed",
6968 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006969#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006970 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6971 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006972#endif
6973#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006974 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6975 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6976 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6977 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006978#endif
6979#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006980 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6981 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006982#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006983 CENT("Visual term=reverse",
6984 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006985#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006986 CENT("DiffAdd term=bold ctermbg=LightBlue",
6987 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6988 CENT("DiffChange term=bold ctermbg=LightMagenta",
6989 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6990 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6991 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006992#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006993 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6994 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006995#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006996 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6997 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6998 CENT("CursorLine term=underline cterm=underline",
6999 "CursorLine term=underline cterm=underline guibg=Grey90"),
7000 CENT("ColorColumn term=reverse ctermbg=LightRed",
7001 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007002#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007003#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007004 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7005 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007006#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007007 CENT("MatchParen term=reverse ctermbg=Cyan",
7008 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007009#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007010 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007011#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007012#ifdef FEAT_TERMINAL
7013 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7014 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7015 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7016 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7017#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007018#ifdef FEAT_MENU
7019 CENT("ToolbarLine term=underline ctermbg=LightGrey",
7020 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
7021 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007022 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007023#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007024 NULL
7025};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007026
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007027/* Default colors only used with a dark background. */
7028static char *(highlight_init_dark[]) = {
7029 CENT("Directory term=bold ctermfg=LightCyan",
7030 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7031 CENT("LineNr term=underline ctermfg=Yellow",
7032 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7033 CENT("CursorLineNr term=bold ctermfg=Yellow",
7034 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7035 CENT("MoreMsg term=bold ctermfg=LightGreen",
7036 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7037 CENT("Question term=standout ctermfg=LightGreen",
7038 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7039 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7040 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7041 CENT("SpecialKey term=bold ctermfg=LightBlue",
7042 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007043#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007044 CENT("SpellBad term=reverse ctermbg=Red",
7045 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7046 CENT("SpellCap term=reverse ctermbg=Blue",
7047 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7048 CENT("SpellRare term=reverse ctermbg=Magenta",
7049 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7050 CENT("SpellLocal term=underline ctermbg=Cyan",
7051 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007052#endif
7053#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007054 CENT("PmenuThumb ctermbg=White",
7055 "PmenuThumb ctermbg=White guibg=White"),
7056 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7057 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7058 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7059 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007060#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007061 CENT("Title term=bold ctermfg=LightMagenta",
7062 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7063 CENT("WarningMsg term=standout ctermfg=LightRed",
7064 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007065#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007066 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7067 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007068#endif
7069#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007070 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7071 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7072 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7073 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007074#endif
7075#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007076 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7077 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007078#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007079 CENT("Visual term=reverse",
7080 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007081#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007082 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7083 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7084 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7085 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7086 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7087 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007088#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007089 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7090 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007091#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007092 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7093 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7094 CENT("CursorLine term=underline cterm=underline",
7095 "CursorLine term=underline cterm=underline guibg=Grey40"),
7096 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7097 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007098#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007099 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7100 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007101#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007102 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7103 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007104#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007105#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007106 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007107#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007108#ifdef FEAT_TERMINAL
7109 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7110 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7111 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7112 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7113#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007114#ifdef FEAT_MENU
7115 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007116 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007117 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7118 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7119#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007120 NULL
7121};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007122
7123 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007124init_highlight(
7125 int both, /* include groups where 'bg' doesn't matter */
7126 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007127{
7128 int i;
7129 char **pp;
7130 static int had_both = FALSE;
7131#ifdef FEAT_EVAL
7132 char_u *p;
7133
7134 /*
7135 * Try finding the color scheme file. Used when a color file was loaded
7136 * and 'background' or 't_Co' is changed.
7137 */
7138 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007139 if (p != NULL)
7140 {
7141 /* The value of g:colors_name could be freed when sourcing the script,
7142 * making "p" invalid, so copy it. */
7143 char_u *copy_p = vim_strsave(p);
7144 int r;
7145
7146 if (copy_p != NULL)
7147 {
7148 r = load_colors(copy_p);
7149 vim_free(copy_p);
7150 if (r == OK)
7151 return;
7152 }
7153 }
7154
Bram Moolenaar071d4272004-06-13 20:20:40 +00007155#endif
7156
7157 /*
7158 * Didn't use a color file, use the compiled-in colors.
7159 */
7160 if (both)
7161 {
7162 had_both = TRUE;
7163 pp = highlight_init_both;
7164 for (i = 0; pp[i] != NULL; ++i)
7165 do_highlight((char_u *)pp[i], reset, TRUE);
7166 }
7167 else if (!had_both)
7168 /* Don't do anything before the call with both == TRUE from main().
7169 * Not everything has been setup then, and that call will overrule
7170 * everything anyway. */
7171 return;
7172
7173 if (*p_bg == 'l')
7174 pp = highlight_init_light;
7175 else
7176 pp = highlight_init_dark;
7177 for (i = 0; pp[i] != NULL; ++i)
7178 do_highlight((char_u *)pp[i], reset, TRUE);
7179
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007180 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007181 * depend on the number of colors available.
7182 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007183 * to avoid Statement highlighted text disappears.
7184 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007185 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007186 do_highlight((char_u *)(*p_bg == 'l'
7187 ? "Visual cterm=NONE ctermbg=LightGrey"
7188 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007189 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007190 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007191 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7192 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007193 if (*p_bg == 'l')
7194 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7195 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007196
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197#ifdef FEAT_SYN_HL
7198 /*
7199 * If syntax highlighting is enabled load the highlighting for it.
7200 */
7201 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007202 {
7203 static int recursive = 0;
7204
7205 if (recursive >= 5)
7206 EMSG(_("E679: recursive loop loading syncolor.vim"));
7207 else
7208 {
7209 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007210 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007211 --recursive;
7212 }
7213 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007214#endif
7215}
7216
7217/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007218 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007219 * Return OK for success, FAIL for failure.
7220 */
7221 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007222load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007223{
7224 char_u *buf;
7225 int retval = FAIL;
7226 static int recursive = FALSE;
7227
7228 /* When being called recursively, this is probably because setting
7229 * 'background' caused the highlighting to be reloaded. This means it is
7230 * working, thus we should return OK. */
7231 if (recursive)
7232 return OK;
7233
7234 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007235 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007236 if (buf != NULL)
7237 {
Bram Moolenaar60a68362018-04-30 15:40:48 +02007238 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
7239 curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007240 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007241 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242 vim_free(buf);
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007243 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007244 }
7245 recursive = FALSE;
7246
7247 return retval;
7248}
7249
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007250static char *(color_names[28]) = {
7251 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7252 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7253 "Gray", "Grey", "LightGray", "LightGrey",
7254 "DarkGray", "DarkGrey",
7255 "Blue", "LightBlue", "Green", "LightGreen",
7256 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7257 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7258 /* indices:
7259 * 0, 1, 2, 3,
7260 * 4, 5, 6, 7,
7261 * 8, 9, 10, 11,
7262 * 12, 13,
7263 * 14, 15, 16, 17,
7264 * 18, 19, 20, 21, 22,
7265 * 23, 24, 25, 26, 27 */
7266static int color_numbers_16[28] = {0, 1, 2, 3,
7267 4, 5, 6, 6,
7268 7, 7, 7, 7,
7269 8, 8,
7270 9, 9, 10, 10,
7271 11, 11, 12, 12, 13,
7272 13, 14, 14, 15, -1};
7273/* for xterm with 88 colors... */
7274static int color_numbers_88[28] = {0, 4, 2, 6,
7275 1, 5, 32, 72,
7276 84, 84, 7, 7,
7277 82, 82,
7278 12, 43, 10, 61,
7279 14, 63, 9, 74, 13,
7280 75, 11, 78, 15, -1};
7281/* for xterm with 256 colors... */
7282static int color_numbers_256[28] = {0, 4, 2, 6,
7283 1, 5, 130, 130,
7284 248, 248, 7, 7,
7285 242, 242,
7286 12, 81, 10, 121,
7287 14, 159, 9, 224, 13,
7288 225, 11, 229, 15, -1};
7289/* for terminals with less than 16 colors... */
7290static int color_numbers_8[28] = {0, 4, 2, 6,
7291 1, 5, 3, 3,
7292 7, 7, 7, 7,
7293 0+8, 0+8,
7294 4+8, 4+8, 2+8, 2+8,
7295 6+8, 6+8, 1+8, 1+8, 5+8,
7296 5+8, 3+8, 3+8, 7+8, -1};
7297
7298/*
7299 * Lookup the "cterm" value to be used for color with index "idx" in
7300 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007301 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7302 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007303 */
7304 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007305lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007306{
7307 int color = color_numbers_16[idx];
7308 char_u *p;
7309
7310 /* Use the _16 table to check if it's a valid color name. */
7311 if (color < 0)
7312 return -1;
7313
7314 if (t_colors == 8)
7315 {
7316 /* t_Co is 8: use the 8 colors table */
7317#if defined(__QNXNTO__)
7318 color = color_numbers_8_qansi[idx];
7319#else
7320 color = color_numbers_8[idx];
7321#endif
7322 if (foreground)
7323 {
7324 /* set/reset bold attribute to get light foreground
7325 * colors (on some terminals, e.g. "linux") */
7326 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007327 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007328 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007329 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007330 }
7331 color &= 7; /* truncate to 8 colors */
7332 }
7333 else if (t_colors == 16 || t_colors == 88
7334 || t_colors >= 256)
7335 {
7336 /*
7337 * Guess: if the termcap entry ends in 'm', it is
7338 * probably an xterm-like terminal. Use the changed
7339 * order for colors.
7340 */
7341 if (*T_CAF != NUL)
7342 p = T_CAF;
7343 else
7344 p = T_CSF;
7345 if (*p != NUL && (t_colors > 256
7346 || *(p + STRLEN(p) - 1) == 'm'))
7347 {
7348 if (t_colors == 88)
7349 color = color_numbers_88[idx];
7350 else if (t_colors >= 256)
7351 color = color_numbers_256[idx];
7352 else
7353 color = color_numbers_8[idx];
7354 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007355#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007356 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7357 /* Terminal.app has a bug: 15 is light grey. Use white
7358 * from the color cube instead. */
7359 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007360#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007361 }
7362 return color;
7363}
7364
Bram Moolenaar071d4272004-06-13 20:20:40 +00007365/*
7366 * Handle the ":highlight .." command.
7367 * When using ":hi clear" this is called recursively for each group with
7368 * "forceit" and "init" both TRUE.
7369 */
7370 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007371do_highlight(
7372 char_u *line,
7373 int forceit,
7374 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007375{
7376 char_u *name_end;
7377 char_u *p;
7378 char_u *linep;
7379 char_u *key_start;
7380 char_u *arg_start;
7381 char_u *key = NULL, *arg = NULL;
7382 long i;
7383 int off;
7384 int len;
7385 int attr;
7386 int id;
7387 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007388 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007389 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007390 int dodefault = FALSE;
7391 int doclear = FALSE;
7392 int dolink = FALSE;
7393 int error = FALSE;
7394 int color;
7395 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007396#ifdef FEAT_TERMINAL
7397 int is_terminal_group = FALSE; /* "Terminal" group */
7398#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007399#ifdef FEAT_GUI_X11
7400 int is_menu_group = FALSE; /* "Menu" group */
7401 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7402 int is_tooltip_group = FALSE; /* "Tooltip" group */
7403 int do_colors = FALSE; /* need to update colors? */
7404#else
7405# define is_menu_group 0
7406# define is_tooltip_group 0
7407#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007408#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7409 int did_highlight_changed = FALSE;
7410#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007411
7412 /*
7413 * If no argument, list current highlighting.
7414 */
7415 if (ends_excmd(*line))
7416 {
7417 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7418 /* TODO: only call when the group has attributes set */
7419 highlight_list_one((int)i);
7420 return;
7421 }
7422
7423 /*
7424 * Isolate the name.
7425 */
7426 name_end = skiptowhite(line);
7427 linep = skipwhite(name_end);
7428
7429 /*
7430 * Check for "default" argument.
7431 */
7432 if (STRNCMP(line, "default", name_end - line) == 0)
7433 {
7434 dodefault = TRUE;
7435 line = linep;
7436 name_end = skiptowhite(line);
7437 linep = skipwhite(name_end);
7438 }
7439
7440 /*
7441 * Check for "clear" or "link" argument.
7442 */
7443 if (STRNCMP(line, "clear", name_end - line) == 0)
7444 doclear = TRUE;
7445 if (STRNCMP(line, "link", name_end - line) == 0)
7446 dolink = TRUE;
7447
7448 /*
7449 * ":highlight {group-name}": list highlighting for one group.
7450 */
7451 if (!doclear && !dolink && ends_excmd(*linep))
7452 {
7453 id = syn_namen2id(line, (int)(name_end - line));
7454 if (id == 0)
7455 EMSG2(_("E411: highlight group not found: %s"), line);
7456 else
7457 highlight_list_one(id);
7458 return;
7459 }
7460
7461 /*
7462 * Handle ":highlight link {from} {to}" command.
7463 */
7464 if (dolink)
7465 {
7466 char_u *from_start = linep;
7467 char_u *from_end;
7468 char_u *to_start;
7469 char_u *to_end;
7470 int from_id;
7471 int to_id;
7472
7473 from_end = skiptowhite(from_start);
7474 to_start = skipwhite(from_end);
7475 to_end = skiptowhite(to_start);
7476
7477 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7478 {
7479 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7480 from_start);
7481 return;
7482 }
7483
7484 if (!ends_excmd(*skipwhite(to_end)))
7485 {
7486 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7487 return;
7488 }
7489
7490 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7491 if (STRNCMP(to_start, "NONE", 4) == 0)
7492 to_id = 0;
7493 else
7494 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7495
Bram Moolenaar414168d2017-09-10 15:21:55 +02007496 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007497 {
7498 /*
7499 * Don't allow a link when there already is some highlighting
7500 * for the group, unless '!' is used
7501 */
7502 if (to_id > 0 && !forceit && !init
7503 && hl_has_settings(from_id - 1, dodefault))
7504 {
7505 if (sourcing_name == NULL && !dodefault)
7506 EMSG(_("E414: group has settings, highlight link ignored"));
7507 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007508 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007509#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007510 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
7511 != current_sctx.sc_sid
Bram Moolenaar99433292017-09-08 12:37:47 +02007512#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007513 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007514 {
7515 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007516 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7517 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007518#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007519 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
7520 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007521#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007522 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007523 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007524
7525 /* Only call highlight_changed() once after multiple changes. */
7526 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007527 }
7528 }
7529
Bram Moolenaar071d4272004-06-13 20:20:40 +00007530 return;
7531 }
7532
7533 if (doclear)
7534 {
7535 /*
7536 * ":highlight clear [group]" command.
7537 */
7538 line = linep;
7539 if (ends_excmd(*line))
7540 {
7541#ifdef FEAT_GUI
7542 /* First, we do not destroy the old values, but allocate the new
7543 * ones and update the display. THEN we destroy the old values.
7544 * If we destroy the old values first, then the old values
7545 * (such as GuiFont's or GuiFontset's) will still be displayed but
7546 * invalid because they were free'd.
7547 */
7548 if (gui.in_use)
7549 {
7550# ifdef FEAT_BEVAL_TIP
7551 gui_init_tooltip_font();
7552# endif
7553# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7554 gui_init_menu_font();
7555# endif
7556 }
7557# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7558 gui_mch_def_colors();
7559# endif
7560# ifdef FEAT_GUI_X11
7561# ifdef FEAT_MENU
7562
7563 /* This only needs to be done when there is no Menu highlight
7564 * group defined by default, which IS currently the case.
7565 */
7566 gui_mch_new_menu_colors();
7567# endif
7568 if (gui.in_use)
7569 {
7570 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007571# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007572 gui_mch_new_tooltip_colors();
7573# endif
7574# ifdef FEAT_MENU
7575 gui_mch_new_menu_font();
7576# endif
7577 }
7578# endif
7579
7580 /* Ok, we're done allocating the new default graphics items.
7581 * The screen should already be refreshed at this point.
7582 * It is now Ok to clear out the old data.
7583 */
7584#endif
7585#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007586 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007587#endif
7588 restore_cterm_colors();
7589
7590 /*
7591 * Clear all default highlight groups and load the defaults.
7592 */
7593 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7594 highlight_clear(idx);
7595 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007596#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007597 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007598 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007599 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007600#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007601 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007602 redraw_later_clear();
7603 return;
7604 }
7605 name_end = skiptowhite(line);
7606 linep = skipwhite(name_end);
7607 }
7608
7609 /*
7610 * Find the group name in the table. If it does not exist yet, add it.
7611 */
7612 id = syn_check_group(line, (int)(name_end - line));
7613 if (id == 0) /* failed (out of memory) */
7614 return;
7615 idx = id - 1; /* index is ID minus one */
7616
7617 /* Return if "default" was used and the group already has settings. */
7618 if (dodefault && hl_has_settings(idx, TRUE))
7619 return;
7620
Bram Moolenaar99433292017-09-08 12:37:47 +02007621 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007622 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007623
Bram Moolenaar414168d2017-09-10 15:21:55 +02007624 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007625 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007626#ifdef FEAT_TERMINAL
7627 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7628 is_terminal_group = TRUE;
7629#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007630#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007631 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007632 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007633 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007634 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007635 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007636 is_tooltip_group = TRUE;
7637#endif
7638
7639 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7640 if (doclear || (forceit && init))
7641 {
7642 highlight_clear(idx);
7643 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007644 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007645 }
7646
7647 if (!doclear)
7648 while (!ends_excmd(*linep))
7649 {
7650 key_start = linep;
7651 if (*linep == '=')
7652 {
7653 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7654 error = TRUE;
7655 break;
7656 }
7657
7658 /*
7659 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7660 * "guibg").
7661 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007662 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007663 ++linep;
7664 vim_free(key);
7665 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7666 if (key == NULL)
7667 {
7668 error = TRUE;
7669 break;
7670 }
7671 linep = skipwhite(linep);
7672
7673 if (STRCMP(key, "NONE") == 0)
7674 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007675 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007676 {
7677 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007678 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007679 highlight_clear(idx);
7680 }
7681 continue;
7682 }
7683
7684 /*
7685 * Check for the equal sign.
7686 */
7687 if (*linep != '=')
7688 {
7689 EMSG2(_("E416: missing equal sign: %s"), key_start);
7690 error = TRUE;
7691 break;
7692 }
7693 ++linep;
7694
7695 /*
7696 * Isolate the argument.
7697 */
7698 linep = skipwhite(linep);
7699 if (*linep == '\'') /* guifg='color name' */
7700 {
7701 arg_start = ++linep;
7702 linep = vim_strchr(linep, '\'');
7703 if (linep == NULL)
7704 {
7705 EMSG2(_(e_invarg2), key_start);
7706 error = TRUE;
7707 break;
7708 }
7709 }
7710 else
7711 {
7712 arg_start = linep;
7713 linep = skiptowhite(linep);
7714 }
7715 if (linep == arg_start)
7716 {
7717 EMSG2(_("E417: missing argument: %s"), key_start);
7718 error = TRUE;
7719 break;
7720 }
7721 vim_free(arg);
7722 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7723 if (arg == NULL)
7724 {
7725 error = TRUE;
7726 break;
7727 }
7728 if (*linep == '\'')
7729 ++linep;
7730
7731 /*
7732 * Store the argument.
7733 */
7734 if ( STRCMP(key, "TERM") == 0
7735 || STRCMP(key, "CTERM") == 0
7736 || STRCMP(key, "GUI") == 0)
7737 {
7738 attr = 0;
7739 off = 0;
7740 while (arg[off] != NUL)
7741 {
7742 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7743 {
7744 len = (int)STRLEN(hl_name_table[i]);
7745 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7746 {
7747 attr |= hl_attr_table[i];
7748 off += len;
7749 break;
7750 }
7751 }
7752 if (i < 0)
7753 {
7754 EMSG2(_("E418: Illegal value: %s"), arg);
7755 error = TRUE;
7756 break;
7757 }
7758 if (arg[off] == ',') /* another one follows */
7759 ++off;
7760 }
7761 if (error)
7762 break;
7763 if (*key == 'T')
7764 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007765 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007766 {
7767 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007768 HL_TABLE()[idx].sg_set |= SG_TERM;
7769 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007770 }
7771 }
7772 else if (*key == 'C')
7773 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007774 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007775 {
7776 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007777 HL_TABLE()[idx].sg_set |= SG_CTERM;
7778 HL_TABLE()[idx].sg_cterm = attr;
7779 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780 }
7781 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007782#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007783 else
7784 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007785 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007786 {
7787 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007788 HL_TABLE()[idx].sg_set |= SG_GUI;
7789 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007790 }
7791 }
7792#endif
7793 }
7794 else if (STRCMP(key, "FONT") == 0)
7795 {
7796 /* in non-GUI fonts are simply ignored */
7797#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007798 if (HL_TABLE()[idx].sg_font_name != NULL
7799 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007800 {
7801 /* Font name didn't change, ignore. */
7802 }
7803 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804 {
7805 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007806 vim_free(HL_TABLE()[idx].sg_font_name);
7807 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007808 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809 }
7810 else
7811 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007812 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007814 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815# endif
7816 /* First, save the current font/fontset.
7817 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007818 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007819 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7820 */
7821
Bram Moolenaar414168d2017-09-10 15:21:55 +02007822 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007824 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007825# endif
7826 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007827 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828
7829# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007830 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007831 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007832 /* New fontset was accepted. Free the old one, if there
7833 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007834 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007835 vim_free(HL_TABLE()[idx].sg_font_name);
7836 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007837 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007838 }
7839 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007840 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007841# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007842 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007843 {
7844 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007845 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007846 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007847 vim_free(HL_TABLE()[idx].sg_font_name);
7848 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007849 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 }
7851 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007852 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007853 }
7854#endif
7855 }
7856 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7857 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007858 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007859 {
7860 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007861 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007862
7863 /* When setting the foreground color, and previously the "bold"
7864 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007865 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007866 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007867 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7868 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007869 }
7870
7871 if (VIM_ISDIGIT(*arg))
7872 color = atoi((char *)arg);
7873 else if (STRICMP(arg, "fg") == 0)
7874 {
7875 if (cterm_normal_fg_color)
7876 color = cterm_normal_fg_color - 1;
7877 else
7878 {
7879 EMSG(_("E419: FG color unknown"));
7880 error = TRUE;
7881 break;
7882 }
7883 }
7884 else if (STRICMP(arg, "bg") == 0)
7885 {
7886 if (cterm_normal_bg_color > 0)
7887 color = cterm_normal_bg_color - 1;
7888 else
7889 {
7890 EMSG(_("E420: BG color unknown"));
7891 error = TRUE;
7892 break;
7893 }
7894 }
7895 else
7896 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007897 int bold = MAYBE;
7898
Bram Moolenaar071d4272004-06-13 20:20:40 +00007899#if defined(__QNXNTO__)
7900 static int *color_numbers_8_qansi = color_numbers_8;
7901 /* On qnx, the 8 & 16 color arrays are the same */
7902 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7903 color_numbers_8_qansi = color_numbers_16;
7904#endif
7905
7906 /* reduce calls to STRICMP a bit, it can be slow */
7907 off = TOUPPER_ASC(*arg);
7908 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7909 if (off == color_names[i][0]
7910 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7911 break;
7912 if (i < 0)
7913 {
7914 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7915 error = TRUE;
7916 break;
7917 }
7918
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007919 color = lookup_color(i, key[5] == 'F', &bold);
7920
7921 /* set/reset bold attribute to get light foreground
7922 * colors (on some terminals, e.g. "linux") */
7923 if (bold == TRUE)
7924 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007925 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7926 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007927 }
7928 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007929 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007930 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007931
Bram Moolenaarccbab932010-05-13 15:40:30 +02007932 /* Add one to the argument, to avoid zero. Zero is used for
7933 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007934 if (key[5] == 'F')
7935 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007936 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007937 if (is_normal_group)
7938 {
7939 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007940 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007941#ifdef FEAT_GUI
7942 /* Don't do this if the GUI is used. */
7943 if (!gui.in_use && !gui.starting)
7944#endif
7945 {
7946 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007947 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007948 term_fg_color(color);
7949 }
7950 }
7951 }
7952 else
7953 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007954 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007955 if (is_normal_group)
7956 {
7957 cterm_normal_bg_color = color + 1;
7958#ifdef FEAT_GUI
7959 /* Don't mess with 'background' if the GUI is used. */
7960 if (!gui.in_use && !gui.starting)
7961#endif
7962 {
7963 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007964 if (color >= 0)
7965 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007966 int dark = -1;
7967
Bram Moolenaarccbab932010-05-13 15:40:30 +02007968 if (termcap_active)
7969 term_bg_color(color);
7970 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007971 dark = (color == 0 || color == 4);
7972 /* Limit the heuristic to the standard 16 colors */
7973 else if (color < 16)
7974 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007975 /* Set the 'background' option if the value is
7976 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007977 if (dark != -1
7978 && dark != (*p_bg == 'd')
7979 && !option_was_set((char_u *)"bg"))
7980 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007981 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007982 (char_u *)(dark ? "dark" : "light"), 0);
7983 reset_option_was_set((char_u *)"bg");
7984 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007985 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007986 }
7987 }
7988 }
7989 }
7990 }
7991 else if (STRCMP(key, "GUIFG") == 0)
7992 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007993#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007994 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7995
Bram Moolenaar414168d2017-09-10 15:21:55 +02007996 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007997 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007998 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007999 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008000
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008001# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008002 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008003 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02008004 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008005 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008006 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008007# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008008 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8009 {
8010 vim_free(*namep);
8011 if (STRCMP(arg, "NONE") != 0)
8012 *namep = vim_strsave(arg);
8013 else
8014 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008015 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008016 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008017# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008018# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008019 if (is_menu_group && gui.menu_fg_pixel != i)
8020 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008021 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008022 do_colors = TRUE;
8023 }
8024 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8025 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008026 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008027 do_colors = TRUE;
8028 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008029# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008030 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8031 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008032 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008033 do_colors = TRUE;
8034 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008035# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008036# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008037 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008038# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008039 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008040#endif
8041 }
8042 else if (STRCMP(key, "GUIBG") == 0)
8043 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008044#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008045 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8046
Bram Moolenaar414168d2017-09-10 15:21:55 +02008047 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008048 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008049 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008050 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008051
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008052# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008053 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008054 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008055 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008056 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008057 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008058# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008059 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8060 {
8061 vim_free(*namep);
8062 if (STRCMP(arg, "NONE") != 0)
8063 *namep = vim_strsave(arg);
8064 else
8065 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008066 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008067 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008068# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008069# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008070 if (is_menu_group && gui.menu_bg_pixel != i)
8071 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008072 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008073 do_colors = TRUE;
8074 }
8075 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8076 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008077 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008078 do_colors = TRUE;
8079 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008080# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008081 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8082 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008083 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008084 do_colors = TRUE;
8085 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008086# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008087# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008088 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008089# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008090 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008091#endif
8092 }
8093 else if (STRCMP(key, "GUISP") == 0)
8094 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008095#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008096 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8097
Bram Moolenaar414168d2017-09-10 15:21:55 +02008098 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008099 {
8100 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008101 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008102
Bram Moolenaar61623362010-07-14 22:04:22 +02008103# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008104 i = color_name2handle(arg);
8105 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8106 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008107 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008108# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008109 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8110 {
8111 vim_free(*namep);
8112 if (STRCMP(arg, "NONE") != 0)
8113 *namep = vim_strsave(arg);
8114 else
8115 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008116 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008117 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008118# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008119 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008120# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008121 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008122#endif
8123 }
8124 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8125 {
8126 char_u buf[100];
8127 char_u *tname;
8128
8129 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008130 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008131
8132 /*
8133 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008134 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008135 */
8136 if (STRNCMP(arg, "t_", 2) == 0)
8137 {
8138 off = 0;
8139 buf[0] = 0;
8140 while (arg[off] != NUL)
8141 {
8142 /* Isolate one termcap name */
8143 for (len = 0; arg[off + len] &&
8144 arg[off + len] != ','; ++len)
8145 ;
8146 tname = vim_strnsave(arg + off, len);
8147 if (tname == NULL) /* out of memory */
8148 {
8149 error = TRUE;
8150 break;
8151 }
8152 /* lookup the escape sequence for the item */
8153 p = get_term_code(tname);
8154 vim_free(tname);
8155 if (p == NULL) /* ignore non-existing things */
8156 p = (char_u *)"";
8157
8158 /* Append it to the already found stuff */
8159 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8160 {
8161 EMSG2(_("E422: terminal code too long: %s"), arg);
8162 error = TRUE;
8163 break;
8164 }
8165 STRCAT(buf, p);
8166
8167 /* Advance to the next item */
8168 off += len;
8169 if (arg[off] == ',') /* another one follows */
8170 ++off;
8171 }
8172 }
8173 else
8174 {
8175 /*
8176 * Copy characters from arg[] to buf[], translating <> codes.
8177 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008178 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008180 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008181 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182 off += len;
8183 else /* copy as normal char */
8184 buf[off++] = *p++;
8185 }
8186 buf[off] = NUL;
8187 }
8188 if (error)
8189 break;
8190
8191 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8192 p = NULL;
8193 else
8194 p = vim_strsave(buf);
8195 if (key[2] == 'A')
8196 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008197 vim_free(HL_TABLE()[idx].sg_start);
8198 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199 }
8200 else
8201 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008202 vim_free(HL_TABLE()[idx].sg_stop);
8203 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008204 }
8205 }
8206 else
8207 {
8208 EMSG2(_("E423: Illegal argument: %s"), key_start);
8209 error = TRUE;
8210 break;
8211 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008212 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008213
8214 /*
8215 * When highlighting has been given for a group, don't link it.
8216 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008217 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8218 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219
8220 /*
8221 * Continue with next argument.
8222 */
8223 linep = skipwhite(linep);
8224 }
8225
8226 /*
8227 * If there is an error, and it's a new entry, remove it from the table.
8228 */
8229 if (error && idx == highlight_ga.ga_len)
8230 syn_unadd_group();
8231 else
8232 {
8233 if (is_normal_group)
8234 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008235 HL_TABLE()[idx].sg_term_attr = 0;
8236 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008238 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008239 /*
8240 * Need to update all groups, because they might be using "bg"
8241 * and/or "fg", which have been changed now.
8242 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008243#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008244#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008245 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008246 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008247 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008248 did_highlight_changed = TRUE;
8249 redraw_all_later(NOT_VALID);
8250 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008251#endif
8252 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008253#ifdef FEAT_TERMINAL
8254 else if (is_terminal_group)
8255 set_terminal_default_colors(
8256 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8257#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008258#ifdef FEAT_GUI_X11
8259# ifdef FEAT_MENU
8260 else if (is_menu_group)
8261 {
8262 if (gui.in_use && do_colors)
8263 gui_mch_new_menu_colors();
8264 }
8265# endif
8266 else if (is_scrollbar_group)
8267 {
8268 if (gui.in_use && do_colors)
8269 gui_new_scrollbar_colors();
8270 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008271# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008272 else if (is_tooltip_group)
8273 {
8274 if (gui.in_use && do_colors)
8275 gui_mch_new_tooltip_colors();
8276 }
8277# endif
8278#endif
8279 else
8280 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008281#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008282 HL_TABLE()[idx].sg_script_ctx = current_sctx;
8283 HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008284#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008285 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008286
Bram Moolenaar071d4272004-06-13 20:20:40 +00008287 vim_free(key);
8288 vim_free(arg);
8289
Bram Moolenaar99433292017-09-08 12:37:47 +02008290 /* Only call highlight_changed() once, after a sequence of highlight
8291 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008292 if ((did_change
8293 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008294#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8295 && !did_highlight_changed
8296#endif
8297 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008298 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008299 /* Do not trigger a redraw when highlighting is changed while
8300 * redrawing. This may happen when evaluating 'statusline' changes the
8301 * StatusLine group. */
8302 if (!updating_screen)
8303 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008304 need_highlight_changed = TRUE;
8305 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008306}
8307
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008308#if defined(EXITFREE) || defined(PROTO)
8309 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008310free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008311{
8312 int i;
8313
8314 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008315 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008316 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008317 vim_free(HL_TABLE()[i].sg_name);
8318 vim_free(HL_TABLE()[i].sg_name_u);
8319 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008320 ga_clear(&highlight_ga);
8321}
8322#endif
8323
Bram Moolenaar071d4272004-06-13 20:20:40 +00008324/*
8325 * Reset the cterm colors to what they were before Vim was started, if
8326 * possible. Otherwise reset them to zero.
8327 */
8328 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008329restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008330{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008331#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008332 /* Since t_me has been set, this probably means that the user
8333 * wants to use this as default colors. Need to reset default
8334 * background/foreground colors. */
8335 mch_set_normal_colors();
8336#else
8337 cterm_normal_fg_color = 0;
8338 cterm_normal_fg_bold = 0;
8339 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008340# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008341 cterm_normal_fg_gui_color = INVALCOLOR;
8342 cterm_normal_bg_gui_color = INVALCOLOR;
8343# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008344#endif
8345}
8346
8347/*
8348 * Return TRUE if highlight group "idx" has any settings.
8349 * When "check_link" is TRUE also check for an existing link.
8350 */
8351 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008352hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353{
8354 return ( HL_TABLE()[idx].sg_term_attr != 0
8355 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008356 || HL_TABLE()[idx].sg_cterm_fg != 0
8357 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008358#ifdef FEAT_GUI
8359 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008360 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8361 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8362 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008363 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364#endif
8365 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8366}
8367
8368/*
8369 * Clear highlighting for one group.
8370 */
8371 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008372highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008373{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008374 HL_TABLE()[idx].sg_cleared = TRUE;
8375
Bram Moolenaar071d4272004-06-13 20:20:40 +00008376 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008377 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8378 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008379 HL_TABLE()[idx].sg_term_attr = 0;
8380 HL_TABLE()[idx].sg_cterm = 0;
8381 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8382 HL_TABLE()[idx].sg_cterm_fg = 0;
8383 HL_TABLE()[idx].sg_cterm_bg = 0;
8384 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008385#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008387 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8388 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8389 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008390#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008391#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008392 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8393 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008394#endif
8395#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008396 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008397 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8398 HL_TABLE()[idx].sg_font = NOFONT;
8399# ifdef FEAT_XFONTSET
8400 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8401 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8402# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008403 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008404 HL_TABLE()[idx].sg_gui_attr = 0;
8405#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008406#ifdef FEAT_EVAL
8407 /* Clear the script ID only when there is no link, since that is not
8408 * cleared. */
8409 if (HL_TABLE()[idx].sg_link == 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008410 {
8411 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
8412 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
8413 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008414#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415}
8416
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008417#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008418/*
8419 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008420 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008421 * "Tooltip" colors.
8422 */
8423 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008424set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008425{
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008426# ifdef FEAT_GUI
8427# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008428 if (gui.in_use)
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008429# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008430 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008431 if (set_group_colors((char_u *)"Normal",
8432 &gui.norm_pixel, &gui.back_pixel,
8433 FALSE, TRUE, FALSE))
8434 {
8435 gui_mch_new_colors();
8436 must_redraw = CLEAR;
8437 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008438# ifdef FEAT_GUI_X11
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008439 if (set_group_colors((char_u *)"Menu",
8440 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8441 TRUE, FALSE, FALSE))
8442 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008443# ifdef FEAT_MENU
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008444 gui_mch_new_menu_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008445# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008446 must_redraw = CLEAR;
8447 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008448# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008449 if (set_group_colors((char_u *)"Tooltip",
8450 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8451 FALSE, FALSE, TRUE))
8452 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008453# ifdef FEAT_TOOLBAR
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008454 gui_mch_new_tooltip_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008455# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008456 must_redraw = CLEAR;
8457 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008458# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008459 if (set_group_colors((char_u *)"Scrollbar",
8460 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8461 FALSE, FALSE, FALSE))
8462 {
8463 gui_new_scrollbar_colors();
8464 must_redraw = CLEAR;
8465 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008466# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008467 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008468# endif
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008469# ifdef FEAT_TERMGUICOLORS
8470# ifdef FEAT_GUI
8471 else
8472# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008473 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008474 int idx;
8475
8476 idx = syn_name2id((char_u *)"Normal") - 1;
8477 if (idx >= 0)
8478 {
8479 gui_do_one_color(idx, FALSE, FALSE);
8480
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008481 /* If the normal fg or bg color changed a complete redraw is
8482 * required. */
8483 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
8484 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008485 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008486 /* if the GUI color is INVALCOLOR then we use the default cterm
8487 * color */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008488 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008489 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8490 must_redraw = CLEAR;
8491 }
8492 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008493 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008494# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008495}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008497
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008498#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008499/*
8500 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8501 */
8502 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008503set_group_colors(
8504 char_u *name,
8505 guicolor_T *fgp,
8506 guicolor_T *bgp,
8507 int do_menu,
8508 int use_norm,
8509 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008510{
8511 int idx;
8512
8513 idx = syn_name2id(name) - 1;
8514 if (idx >= 0)
8515 {
8516 gui_do_one_color(idx, do_menu, do_tooltip);
8517
8518 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8519 *fgp = HL_TABLE()[idx].sg_gui_fg;
8520 else if (use_norm)
8521 *fgp = gui.def_norm_pixel;
8522 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8523 *bgp = HL_TABLE()[idx].sg_gui_bg;
8524 else if (use_norm)
8525 *bgp = gui.def_back_pixel;
8526 return TRUE;
8527 }
8528 return FALSE;
8529}
8530
8531/*
8532 * Get the font of the "Normal" group.
8533 * Returns "" when it's not found or not set.
8534 */
8535 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008536hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537{
8538 int id;
8539 char_u *s;
8540
8541 id = syn_name2id((char_u *)"Normal");
8542 if (id > 0)
8543 {
8544 s = HL_TABLE()[id - 1].sg_font_name;
8545 if (s != NULL)
8546 return s;
8547 }
8548 return (char_u *)"";
8549}
8550
8551/*
8552 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8553 * actually chosen to be used.
8554 */
8555 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008556hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008557{
8558 int id;
8559
8560 id = syn_name2id((char_u *)"Normal");
8561 if (id > 0)
8562 {
8563 vim_free(HL_TABLE()[id - 1].sg_font_name);
8564 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8565 }
8566}
8567
8568/*
8569 * Set background color for "Normal" group. Called by gui_set_bg_color()
8570 * when the color is known.
8571 */
8572 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008573hl_set_bg_color_name(
8574 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008575{
8576 int id;
8577
8578 if (name != NULL)
8579 {
8580 id = syn_name2id((char_u *)"Normal");
8581 if (id > 0)
8582 {
8583 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8584 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8585 }
8586 }
8587}
8588
8589/*
8590 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8591 * when the color is known.
8592 */
8593 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008594hl_set_fg_color_name(
8595 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008596{
8597 int id;
8598
8599 if (name != NULL)
8600 {
8601 id = syn_name2id((char_u *)"Normal");
8602 if (id > 0)
8603 {
8604 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8605 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8606 }
8607 }
8608}
8609
8610/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008611 * Return the handle for a font name.
8612 * Returns NOFONT when failed.
8613 */
8614 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008615font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008616{
8617 if (STRCMP(name, "NONE") == 0)
8618 return NOFONT;
8619
8620 return gui_mch_get_font(name, TRUE);
8621}
8622
8623# ifdef FEAT_XFONTSET
8624/*
8625 * Return the handle for a fontset name.
8626 * Returns NOFONTSET when failed.
8627 */
8628 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008629fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008630{
8631 if (STRCMP(name, "NONE") == 0)
8632 return NOFONTSET;
8633
8634 return gui_mch_get_fontset(name, TRUE, fixed_width);
8635}
8636# endif
8637
8638/*
8639 * Get the font or fontset for one highlight group.
8640 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008641 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008642hl_do_font(
8643 int idx,
8644 char_u *arg,
8645 int do_normal, /* set normal font */
8646 int do_menu UNUSED, /* set menu font */
8647 int do_tooltip UNUSED, /* set tooltip font */
8648 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008649{
8650# ifdef FEAT_XFONTSET
8651 /* If 'guifontset' is not empty, first try using the name as a
8652 * fontset. If that doesn't work, use it as a font name. */
8653 if (*p_guifontset != NUL
8654# ifdef FONTSET_ALWAYS
8655 || do_menu
8656# endif
8657# ifdef FEAT_BEVAL_TIP
8658 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8659 || do_tooltip
8660# endif
8661 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008662 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008663 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008664 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8666# ifdef FONTSET_ALWAYS
8667 || do_menu
8668# endif
8669# ifdef FEAT_BEVAL_TIP
8670 || do_tooltip
8671# endif
8672 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008673 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008674 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8675 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008676 /* If it worked and it's the Normal group, use it as the normal
8677 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008678 if (do_normal)
8679 gui_init_font(arg, TRUE);
8680# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8681 if (do_menu)
8682 {
8683# ifdef FONTSET_ALWAYS
8684 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8685# else
8686 /* YIKES! This is a bug waiting to crash the program */
8687 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8688# endif
8689 gui_mch_new_menu_font();
8690 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008691# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008692 if (do_tooltip)
8693 {
8694 /* The Athena widget set cannot currently handle switching between
8695 * displaying a single font and a fontset.
8696 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008697 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008698 * XFontStruct is used.
8699 */
8700 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8701 gui_mch_new_tooltip_font();
8702 }
8703# endif
8704# endif
8705 }
8706 else
8707# endif
8708 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008709 if (free_font)
8710 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008711 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8712 /* If it worked and it's the Normal group, use it as the
8713 * normal font. Same for the Menu group. */
8714 if (HL_TABLE()[idx].sg_font != NOFONT)
8715 {
8716 if (do_normal)
8717 gui_init_font(arg, FALSE);
8718#ifndef FONTSET_ALWAYS
8719# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8720 if (do_menu)
8721 {
8722 gui.menu_font = HL_TABLE()[idx].sg_font;
8723 gui_mch_new_menu_font();
8724 }
8725# endif
8726#endif
8727 }
8728 }
8729}
8730
8731#endif /* FEAT_GUI */
8732
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008733#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008734/*
8735 * Return the handle for a color name.
8736 * Returns INVALCOLOR when failed.
8737 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008738 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008739color_name2handle(char_u *name)
8740{
8741 if (STRCMP(name, "NONE") == 0)
8742 return INVALCOLOR;
8743
8744 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8745 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008746#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008747 if (gui.in_use)
8748#endif
8749#ifdef FEAT_GUI
8750 return gui.norm_pixel;
8751#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008752#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008753 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008754 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008755 /* Guess that the foreground is black or white. */
8756 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008757#endif
8758 }
8759 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8760 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008761#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008762 if (gui.in_use)
8763#endif
8764#ifdef FEAT_GUI
8765 return gui.back_pixel;
8766#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008767#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008768 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008769 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008770 /* Guess that the background is white or black. */
8771 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008772#endif
8773 }
8774
8775 return GUI_GET_COLOR(name);
8776}
8777#endif
8778
Bram Moolenaar071d4272004-06-13 20:20:40 +00008779/*
8780 * Table with the specifications for an attribute number.
8781 * Note that this table is used by ALL buffers. This is required because the
8782 * GUI can redraw at any time for any buffer.
8783 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008784static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008785
8786#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8787
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008788static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008789
8790#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8791
8792#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008793static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008794
8795#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8796#endif
8797
8798/*
8799 * Return the attr number for a set of colors and font.
8800 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8801 * if the combination is new.
8802 * Return 0 for error (no more room).
8803 */
8804 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008805get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008806{
8807 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008808 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008809 static int recursive = FALSE;
8810
8811 /*
8812 * Init the table, in case it wasn't done yet.
8813 */
8814 table->ga_itemsize = sizeof(attrentry_T);
8815 table->ga_growsize = 7;
8816
8817 /*
8818 * Try to find an entry with the same specifications.
8819 */
8820 for (i = 0; i < table->ga_len; ++i)
8821 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008822 taep = &(((attrentry_T *)table->ga_data)[i]);
8823 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008824 && (
8825#ifdef FEAT_GUI
8826 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008827 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8828 && aep->ae_u.gui.bg_color
8829 == taep->ae_u.gui.bg_color
8830 && aep->ae_u.gui.sp_color
8831 == taep->ae_u.gui.sp_color
8832 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008833# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008834 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008835# endif
8836 ))
8837 ||
8838#endif
8839 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008840 && (aep->ae_u.term.start == NULL)
8841 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008842 && (aep->ae_u.term.start == NULL
8843 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008844 taep->ae_u.term.start) == 0)
8845 && (aep->ae_u.term.stop == NULL)
8846 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008847 && (aep->ae_u.term.stop == NULL
8848 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008849 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008850 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008851 && aep->ae_u.cterm.fg_color
8852 == taep->ae_u.cterm.fg_color
8853 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008854 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008855#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008856 && aep->ae_u.cterm.fg_rgb
8857 == taep->ae_u.cterm.fg_rgb
8858 && aep->ae_u.cterm.bg_rgb
8859 == taep->ae_u.cterm.bg_rgb
8860#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008861 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008862
8863 return i + ATTR_OFF;
8864 }
8865
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008866 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008867 {
8868 /*
8869 * Running out of attribute entries! remove all attributes, and
8870 * compute new ones for all groups.
8871 * When called recursively, we are really out of numbers.
8872 */
8873 if (recursive)
8874 {
8875 EMSG(_("E424: Too many different highlighting attributes in use"));
8876 return 0;
8877 }
8878 recursive = TRUE;
8879
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008880 clear_hl_tables();
8881
Bram Moolenaar071d4272004-06-13 20:20:40 +00008882 must_redraw = CLEAR;
8883
8884 for (i = 0; i < highlight_ga.ga_len; ++i)
8885 set_hl_attr(i);
8886
8887 recursive = FALSE;
8888 }
8889
8890 /*
8891 * This is a new combination of colors and font, add an entry.
8892 */
8893 if (ga_grow(table, 1) == FAIL)
8894 return 0;
8895
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008896 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8897 vim_memset(taep, 0, sizeof(attrentry_T));
8898 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008899#ifdef FEAT_GUI
8900 if (table == &gui_attr_table)
8901 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008902 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8903 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8904 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8905 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008906# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008907 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008908# endif
8909 }
8910#endif
8911 if (table == &term_attr_table)
8912 {
8913 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008914 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008915 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008916 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008917 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008918 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008919 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008920 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008921 }
8922 else if (table == &cterm_attr_table)
8923 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008924 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8925 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008926#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008927 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8928 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8929#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008930 }
8931 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008932 return (table->ga_len - 1 + ATTR_OFF);
8933}
8934
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008935/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008936 * Get an attribute index for a cterm entry.
8937 * Uses an existing entry when possible or adds one when needed.
8938 */
8939 int
8940get_cterm_attr_idx(int attr, int fg, int bg)
8941{
8942 attrentry_T at_en;
8943
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008944 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008945#ifdef FEAT_TERMGUICOLORS
8946 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8947 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8948#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008949 at_en.ae_attr = attr;
8950 at_en.ae_u.cterm.fg_color = fg;
8951 at_en.ae_u.cterm.bg_color = bg;
8952 return get_attr_entry(&cterm_attr_table, &at_en);
8953}
8954
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008955#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8956/*
8957 * Get an attribute index for a 'termguicolors' entry.
8958 * Uses an existing entry when possible or adds one when needed.
8959 */
8960 int
8961get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8962{
8963 attrentry_T at_en;
8964
8965 vim_memset(&at_en, 0, sizeof(attrentry_T));
8966 at_en.ae_attr = attr;
Bram Moolenaard4fc5772018-02-27 14:39:03 +01008967 if (fg == INVALCOLOR && bg == INVALCOLOR)
8968 {
8969 /* If both GUI colors are not set fall back to the cterm colors. Helps
8970 * if the GUI only has an attribute, such as undercurl. */
8971 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
8972 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
8973 }
8974 else
8975 {
8976 at_en.ae_u.cterm.fg_rgb = fg;
8977 at_en.ae_u.cterm.bg_rgb = bg;
8978 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008979 return get_attr_entry(&cterm_attr_table, &at_en);
8980}
8981#endif
8982
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008983#if defined(FEAT_GUI) || defined(PROTO)
8984/*
8985 * Get an attribute index for a cterm entry.
8986 * Uses an existing entry when possible or adds one when needed.
8987 */
8988 int
8989get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8990{
8991 attrentry_T at_en;
8992
8993 vim_memset(&at_en, 0, sizeof(attrentry_T));
8994 at_en.ae_attr = attr;
8995 at_en.ae_u.gui.fg_color = fg;
8996 at_en.ae_u.gui.bg_color = bg;
8997 return get_attr_entry(&gui_attr_table, &at_en);
8998}
8999#endif
9000
Bram Moolenaareeac6772017-07-23 15:48:37 +02009001/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00009002 * Clear all highlight tables.
9003 */
9004 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009005clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00009006{
9007 int i;
9008 attrentry_T *taep;
9009
9010#ifdef FEAT_GUI
9011 ga_clear(&gui_attr_table);
9012#endif
9013 for (i = 0; i < term_attr_table.ga_len; ++i)
9014 {
9015 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
9016 vim_free(taep->ae_u.term.start);
9017 vim_free(taep->ae_u.term.stop);
9018 }
9019 ga_clear(&term_attr_table);
9020 ga_clear(&cterm_attr_table);
9021}
9022
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009023#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009024/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00009025 * Combine special attributes (e.g., for spelling) with other attributes
9026 * (e.g., for syntax highlighting).
9027 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00009028 * This creates a new group when required.
9029 * Since we expect there to be few spelling mistakes we don't cache the
9030 * result.
9031 * Return the resulting attributes.
9032 */
9033 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009034hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009035{
9036 attrentry_T *char_aep = NULL;
9037 attrentry_T *spell_aep;
9038 attrentry_T new_en;
9039
9040 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00009041 return prim_attr;
9042 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009043 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009044#ifdef FEAT_GUI
9045 if (gui.in_use)
9046 {
9047 if (char_attr > HL_ALL)
9048 char_aep = syn_gui_attr2entry(char_attr);
9049 if (char_aep != NULL)
9050 new_en = *char_aep;
9051 else
9052 {
9053 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009054 new_en.ae_u.gui.fg_color = INVALCOLOR;
9055 new_en.ae_u.gui.bg_color = INVALCOLOR;
9056 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009057 if (char_attr <= HL_ALL)
9058 new_en.ae_attr = char_attr;
9059 }
9060
Bram Moolenaar30abd282005-06-22 22:35:10 +00009061 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009062 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009063 else
9064 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009065 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009066 if (spell_aep != NULL)
9067 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009068 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9069 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009070 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9071 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9072 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9073 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9074 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9075 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9076 if (spell_aep->ae_u.gui.font != NOFONT)
9077 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9078# ifdef FEAT_XFONTSET
9079 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9080 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9081# endif
9082 }
9083 }
9084 return get_attr_entry(&gui_attr_table, &new_en);
9085 }
9086#endif
9087
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009088 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009089 {
9090 if (char_attr > HL_ALL)
9091 char_aep = syn_cterm_attr2entry(char_attr);
9092 if (char_aep != NULL)
9093 new_en = *char_aep;
9094 else
9095 {
9096 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009097#ifdef FEAT_TERMGUICOLORS
9098 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9099 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9100#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009101 if (char_attr <= HL_ALL)
9102 new_en.ae_attr = char_attr;
9103 }
9104
Bram Moolenaar30abd282005-06-22 22:35:10 +00009105 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009106 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009107 else
9108 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009109 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009110 if (spell_aep != NULL)
9111 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009112 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9113 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009114 if (spell_aep->ae_u.cterm.fg_color > 0)
9115 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9116 if (spell_aep->ae_u.cterm.bg_color > 0)
9117 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009118#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009119 /* If both fg and bg are not set fall back to cterm colors.
9120 * Helps for SpellBad which uses undercurl in the GUI. */
9121 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
9122 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
9123 {
9124 if (spell_aep->ae_u.cterm.fg_color > 0)
9125 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9126 if (spell_aep->ae_u.cterm.bg_color > 0)
9127 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9128 }
9129 else
9130 {
9131 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
9132 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
9133 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
9134 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9135 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009136#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009137 }
9138 }
9139 return get_attr_entry(&cterm_attr_table, &new_en);
9140 }
9141
9142 if (char_attr > HL_ALL)
9143 char_aep = syn_term_attr2entry(char_attr);
9144 if (char_aep != NULL)
9145 new_en = *char_aep;
9146 else
9147 {
9148 vim_memset(&new_en, 0, sizeof(new_en));
9149 if (char_attr <= HL_ALL)
9150 new_en.ae_attr = char_attr;
9151 }
9152
Bram Moolenaar30abd282005-06-22 22:35:10 +00009153 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009154 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009155 else
9156 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009157 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009158 if (spell_aep != NULL)
9159 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009160 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009161 if (spell_aep->ae_u.term.start != NULL)
9162 {
9163 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9164 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9165 }
9166 }
9167 }
9168 return get_attr_entry(&term_attr_table, &new_en);
9169}
9170#endif
9171
Bram Moolenaar071d4272004-06-13 20:20:40 +00009172#ifdef FEAT_GUI
9173
9174 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009175syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009176{
9177 attr -= ATTR_OFF;
9178 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9179 return NULL;
9180 return &(GUI_ATTR_ENTRY(attr));
9181}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009182#endif /* FEAT_GUI */
9183
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009184/*
9185 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9186 * Only to be used when "attr" > HL_ALL.
9187 */
9188 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009189syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009190{
9191 attrentry_T *aep;
9192
9193#ifdef FEAT_GUI
9194 if (gui.in_use)
9195 aep = syn_gui_attr2entry(attr);
9196 else
9197#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009198 if (IS_CTERM)
9199 aep = syn_cterm_attr2entry(attr);
9200 else
9201 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009202
9203 if (aep == NULL) /* highlighting not set */
9204 return 0;
9205 return aep->ae_attr;
9206}
9207
9208
Bram Moolenaar071d4272004-06-13 20:20:40 +00009209 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009210syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009211{
9212 attr -= ATTR_OFF;
9213 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9214 return NULL;
9215 return &(TERM_ATTR_ENTRY(attr));
9216}
9217
9218 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009219syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009220{
9221 attr -= ATTR_OFF;
9222 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9223 return NULL;
9224 return &(CTERM_ATTR_ENTRY(attr));
9225}
9226
9227#define LIST_ATTR 1
9228#define LIST_STRING 2
9229#define LIST_INT 3
9230
9231 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009232highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009233{
9234 struct hl_group *sgp;
9235 int didh = FALSE;
9236
9237 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9238
9239 didh = highlight_list_arg(id, didh, LIST_ATTR,
9240 sgp->sg_term, NULL, "term");
9241 didh = highlight_list_arg(id, didh, LIST_STRING,
9242 0, sgp->sg_start, "start");
9243 didh = highlight_list_arg(id, didh, LIST_STRING,
9244 0, sgp->sg_stop, "stop");
9245
9246 didh = highlight_list_arg(id, didh, LIST_ATTR,
9247 sgp->sg_cterm, NULL, "cterm");
9248 didh = highlight_list_arg(id, didh, LIST_INT,
9249 sgp->sg_cterm_fg, NULL, "ctermfg");
9250 didh = highlight_list_arg(id, didh, LIST_INT,
9251 sgp->sg_cterm_bg, NULL, "ctermbg");
9252
Bram Moolenaar61623362010-07-14 22:04:22 +02009253#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009254 didh = highlight_list_arg(id, didh, LIST_ATTR,
9255 sgp->sg_gui, NULL, "gui");
9256 didh = highlight_list_arg(id, didh, LIST_STRING,
9257 0, sgp->sg_gui_fg_name, "guifg");
9258 didh = highlight_list_arg(id, didh, LIST_STRING,
9259 0, sgp->sg_gui_bg_name, "guibg");
9260 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009261 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009262#endif
9263#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009264 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009265 0, sgp->sg_font_name, "font");
9266#endif
9267
Bram Moolenaar661b1822005-07-28 22:36:45 +00009268 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009269 {
9270 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009271 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009272 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009273 msg_putchar(' ');
9274 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9275 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009276
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009277 if (!didh)
9278 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009279#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009280 if (p_verbose > 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02009281 last_set_msg(sgp->sg_script_ctx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009282#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009283}
9284
9285 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009286highlight_list_arg(
9287 int id,
9288 int didh,
9289 int type,
9290 int iarg,
9291 char_u *sarg,
9292 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009293{
9294 char_u buf[100];
9295 char_u *ts;
9296 int i;
9297
Bram Moolenaar661b1822005-07-28 22:36:45 +00009298 if (got_int)
9299 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009300 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9301 {
9302 ts = buf;
9303 if (type == LIST_INT)
9304 sprintf((char *)buf, "%d", iarg - 1);
9305 else if (type == LIST_STRING)
9306 ts = sarg;
9307 else /* type == LIST_ATTR */
9308 {
9309 buf[0] = NUL;
9310 for (i = 0; hl_attr_table[i] != 0; ++i)
9311 {
9312 if (iarg & hl_attr_table[i])
9313 {
9314 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009315 vim_strcat(buf, (char_u *)",", 100);
9316 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9318 }
9319 }
9320 }
9321
9322 (void)syn_list_header(didh,
9323 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9324 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009325 if (!got_int)
9326 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009327 if (*name != NUL)
9328 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009329 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9330 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009331 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009332 msg_outtrans(ts);
9333 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009334 }
9335 return didh;
9336}
9337
9338#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9339/*
9340 * Return "1" if highlight group "id" has attribute "flag".
9341 * Return NULL otherwise.
9342 */
9343 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009344highlight_has_attr(
9345 int id,
9346 int flag,
9347 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009348{
9349 int attr;
9350
9351 if (id <= 0 || id > highlight_ga.ga_len)
9352 return NULL;
9353
Bram Moolenaar61623362010-07-14 22:04:22 +02009354#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 if (modec == 'g')
9356 attr = HL_TABLE()[id - 1].sg_gui;
9357 else
9358#endif
9359 if (modec == 'c')
9360 attr = HL_TABLE()[id - 1].sg_cterm;
9361 else
9362 attr = HL_TABLE()[id - 1].sg_term;
9363
9364 if (attr & flag)
9365 return (char_u *)"1";
9366 return NULL;
9367}
9368#endif
9369
9370#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9371/*
9372 * Return color name of highlight group "id".
9373 */
9374 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009375highlight_color(
9376 int id,
9377 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9378 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009379{
9380 static char_u name[20];
9381 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009382 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009383 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009384 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009385
9386 if (id <= 0 || id > highlight_ga.ga_len)
9387 return NULL;
9388
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009389 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009390 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009391 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009392 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009393 font = TRUE;
9394 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009395 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009396 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9397 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009398 if (modec == 'g')
9399 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009400# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009401# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009402 /* return font name */
9403 if (font)
9404 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009405# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009406
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009408 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009409 {
9410 guicolor_T color;
9411 long_u rgb;
9412 static char_u buf[10];
9413
9414 if (fg)
9415 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009416 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009417# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009418 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009419# else
9420 color = INVALCOLOR;
9421# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009422 else
9423 color = HL_TABLE()[id - 1].sg_gui_bg;
9424 if (color == INVALCOLOR)
9425 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009426 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009427 sprintf((char *)buf, "#%02x%02x%02x",
9428 (unsigned)(rgb >> 16),
9429 (unsigned)(rgb >> 8) & 255,
9430 (unsigned)rgb & 255);
9431 return buf;
9432 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009433# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009434 if (fg)
9435 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009436 if (sp)
9437 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009438 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9439 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009440 if (font || sp)
9441 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009442 if (modec == 'c')
9443 {
9444 if (fg)
9445 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9446 else
9447 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009448 if (n < 0)
9449 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009450 sprintf((char *)name, "%d", n);
9451 return name;
9452 }
9453 /* term doesn't have color */
9454 return NULL;
9455}
9456#endif
9457
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009458#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009459 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009460 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009461/*
9462 * Return color name of highlight group "id" as RGB value.
9463 */
9464 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009465highlight_gui_color_rgb(
9466 int id,
9467 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009468{
9469 guicolor_T color;
9470
9471 if (id <= 0 || id > highlight_ga.ga_len)
9472 return 0L;
9473
9474 if (fg)
9475 color = HL_TABLE()[id - 1].sg_gui_fg;
9476 else
9477 color = HL_TABLE()[id - 1].sg_gui_bg;
9478
9479 if (color == INVALCOLOR)
9480 return 0L;
9481
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009482 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009483}
9484#endif
9485
9486/*
9487 * Output the syntax list header.
9488 * Return TRUE when started a new line.
9489 */
9490 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009491syn_list_header(
9492 int did_header, /* did header already */
9493 int outlen, /* length of string that comes */
9494 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009495{
9496 int endcol = 19;
9497 int newline = TRUE;
9498
9499 if (!did_header)
9500 {
9501 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009502 if (got_int)
9503 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009504 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9505 endcol = 15;
9506 }
9507 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009508 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009509 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009510 if (got_int)
9511 return TRUE;
9512 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009513 else
9514 {
9515 if (msg_col >= endcol) /* wrap around is like starting a new line */
9516 newline = FALSE;
9517 }
9518
9519 if (msg_col >= endcol) /* output at least one space */
9520 endcol = msg_col + 1;
9521 if (Columns <= endcol) /* avoid hang for tiny window */
9522 endcol = Columns - 1;
9523
9524 msg_advance(endcol);
9525
9526 /* Show "xxx" with the attributes. */
9527 if (!did_header)
9528 {
9529 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9530 msg_putchar(' ');
9531 }
9532
9533 return newline;
9534}
9535
9536/*
9537 * Set the attribute numbers for a highlight group.
9538 * Called after one of the attributes has changed.
9539 */
9540 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009541set_hl_attr(
9542 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009543{
9544 attrentry_T at_en;
9545 struct hl_group *sgp = HL_TABLE() + idx;
9546
9547 /* The "Normal" group doesn't need an attribute number */
9548 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9549 return;
9550
9551#ifdef FEAT_GUI
9552 /*
9553 * For the GUI mode: If there are other than "normal" highlighting
9554 * attributes, need to allocate an attr number.
9555 */
9556 if (sgp->sg_gui_fg == INVALCOLOR
9557 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009558 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009559 && sgp->sg_font == NOFONT
9560# ifdef FEAT_XFONTSET
9561 && sgp->sg_fontset == NOFONTSET
9562# endif
9563 )
9564 {
9565 sgp->sg_gui_attr = sgp->sg_gui;
9566 }
9567 else
9568 {
9569 at_en.ae_attr = sgp->sg_gui;
9570 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9571 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009572 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009573 at_en.ae_u.gui.font = sgp->sg_font;
9574# ifdef FEAT_XFONTSET
9575 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9576# endif
9577 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9578 }
9579#endif
9580 /*
9581 * For the term mode: If there are other than "normal" highlighting
9582 * attributes, need to allocate an attr number.
9583 */
9584 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9585 sgp->sg_term_attr = sgp->sg_term;
9586 else
9587 {
9588 at_en.ae_attr = sgp->sg_term;
9589 at_en.ae_u.term.start = sgp->sg_start;
9590 at_en.ae_u.term.stop = sgp->sg_stop;
9591 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9592 }
9593
9594 /*
9595 * For the color term mode: If there are other than "normal"
9596 * highlighting attributes, need to allocate an attr number.
9597 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009598 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009599# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009600 && sgp->sg_gui_fg == INVALCOLOR
9601 && sgp->sg_gui_bg == INVALCOLOR
9602# endif
9603 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009604 sgp->sg_cterm_attr = sgp->sg_cterm;
9605 else
9606 {
9607 at_en.ae_attr = sgp->sg_cterm;
9608 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9609 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009610# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009611# ifdef WIN3264
9612 {
9613 int id;
9614 guicolor_T fg, bg;
9615
9616 id = syn_name2id((char_u *)"Normal");
9617 if (id > 0)
9618 {
9619 syn_id2colors(id, &fg, &bg);
9620 if (sgp->sg_gui_fg == INVALCOLOR)
9621 sgp->sg_gui_fg = fg;
9622 if (sgp->sg_gui_bg == INVALCOLOR)
9623 sgp->sg_gui_bg = bg;
9624 }
9625
9626 }
9627# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009628 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9629 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009630 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
9631 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
9632 {
9633 /* If both fg and bg are invalid fall back to the cterm colors.
9634 * Helps when the GUI only uses an attribute, e.g. undercurl. */
9635 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9636 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9637 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009638# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009639 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9640 }
9641}
9642
9643/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009644 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009645 * If it is not found, 0 is returned.
9646 */
9647 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009648syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009649{
9650 int i;
9651 char_u name_u[200];
9652
9653 /* Avoid using stricmp() too much, it's slow on some systems */
9654 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9655 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009656 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009657 vim_strup(name_u);
9658 for (i = highlight_ga.ga_len; --i >= 0; )
9659 if (HL_TABLE()[i].sg_name_u != NULL
9660 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9661 break;
9662 return i + 1;
9663}
9664
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009665/*
9666 * Lookup a highlight group name and return its attributes.
9667 * Return zero if not found.
9668 */
9669 int
9670syn_name2attr(char_u *name)
9671{
9672 int id = syn_name2id(name);
9673
9674 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009675 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009676 return 0;
9677}
9678
Bram Moolenaar071d4272004-06-13 20:20:40 +00009679#if defined(FEAT_EVAL) || defined(PROTO)
9680/*
9681 * Return TRUE if highlight group "name" exists.
9682 */
9683 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009684highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009685{
9686 return (syn_name2id(name) > 0);
9687}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009688
9689# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9690/*
9691 * Return the name of highlight group "id".
9692 * When not a valid ID return an empty string.
9693 */
9694 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009695syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009696{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009697 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009698 return (char_u *)"";
9699 return HL_TABLE()[id - 1].sg_name;
9700}
9701# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009702#endif
9703
9704/*
9705 * Like syn_name2id(), but take a pointer + length argument.
9706 */
9707 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009708syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009709{
9710 char_u *name;
9711 int id = 0;
9712
9713 name = vim_strnsave(linep, len);
9714 if (name != NULL)
9715 {
9716 id = syn_name2id(name);
9717 vim_free(name);
9718 }
9719 return id;
9720}
9721
9722/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009723 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009724 * The argument is a pointer to the name and the length of the name.
9725 * If it doesn't exist yet, a new entry is created.
9726 * Return 0 for failure.
9727 */
9728 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009729syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009730{
9731 int id;
9732 char_u *name;
9733
9734 name = vim_strnsave(pp, len);
9735 if (name == NULL)
9736 return 0;
9737
9738 id = syn_name2id(name);
9739 if (id == 0) /* doesn't exist yet */
9740 id = syn_add_group(name);
9741 else
9742 vim_free(name);
9743 return id;
9744}
9745
9746/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009747 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009748 * "name" must be an allocated string, it will be consumed.
9749 * Return 0 for failure.
9750 */
9751 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009752syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009753{
9754 char_u *p;
9755
9756 /* Check that the name is ASCII letters, digits and underscore. */
9757 for (p = name; *p != NUL; ++p)
9758 {
9759 if (!vim_isprintc(*p))
9760 {
9761 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009762 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009763 return 0;
9764 }
9765 else if (!ASCII_ISALNUM(*p) && *p != '_')
9766 {
9767 /* This is an error, but since there previously was no check only
9768 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009769 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009770 MSG(_("W18: Invalid character in group name"));
9771 break;
9772 }
9773 }
9774
9775 /*
9776 * First call for this growarray: init growing array.
9777 */
9778 if (highlight_ga.ga_data == NULL)
9779 {
9780 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9781 highlight_ga.ga_growsize = 10;
9782 }
9783
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009784 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009785 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009786 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009787 vim_free(name);
9788 return 0;
9789 }
9790
Bram Moolenaar071d4272004-06-13 20:20:40 +00009791 /*
9792 * Make room for at least one other syntax_highlight entry.
9793 */
9794 if (ga_grow(&highlight_ga, 1) == FAIL)
9795 {
9796 vim_free(name);
9797 return 0;
9798 }
9799
9800 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9801 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9802 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009803#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009804 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9805 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009806# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009807 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009808# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009809#endif
9810 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009811
9812 return highlight_ga.ga_len; /* ID is index plus one */
9813}
9814
9815/*
9816 * When, just after calling syn_add_group(), an error is discovered, this
9817 * function deletes the new name.
9818 */
9819 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009820syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009821{
9822 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009823 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9824 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9825}
9826
9827/*
9828 * Translate a group ID to highlight attributes.
9829 */
9830 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009831syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009832{
9833 int attr;
9834 struct hl_group *sgp;
9835
9836 hl_id = syn_get_final_id(hl_id);
9837 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9838
9839#ifdef FEAT_GUI
9840 /*
9841 * Only use GUI attr when the GUI is being used.
9842 */
9843 if (gui.in_use)
9844 attr = sgp->sg_gui_attr;
9845 else
9846#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009847 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009848 attr = sgp->sg_cterm_attr;
9849 else
9850 attr = sgp->sg_term_attr;
9851
9852 return attr;
9853}
9854
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009855#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009856/*
9857 * Get the GUI colors and attributes for a group ID.
9858 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9859 */
9860 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009861syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009862{
9863 struct hl_group *sgp;
9864
9865 hl_id = syn_get_final_id(hl_id);
9866 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9867
9868 *fgp = sgp->sg_gui_fg;
9869 *bgp = sgp->sg_gui_bg;
9870 return sgp->sg_gui;
9871}
9872#endif
9873
Bram Moolenaara772baf2018-05-20 13:35:44 +02009874#if (defined(WIN3264) \
9875 && !defined(FEAT_GUI_W32) \
9876 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009877 void
9878syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9879{
9880 struct hl_group *sgp;
9881
9882 hl_id = syn_get_final_id(hl_id);
9883 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9884 *fgp = sgp->sg_cterm_fg - 1;
9885 *bgp = sgp->sg_cterm_bg - 1;
9886}
9887#endif
9888
Bram Moolenaar071d4272004-06-13 20:20:40 +00009889/*
9890 * Translate a group ID to the final group ID (following links).
9891 */
9892 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009893syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009894{
9895 int count;
9896 struct hl_group *sgp;
9897
9898 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9899 return 0; /* Can be called from eval!! */
9900
9901 /*
9902 * Follow links until there is no more.
9903 * Look out for loops! Break after 100 links.
9904 */
9905 for (count = 100; --count >= 0; )
9906 {
9907 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9908 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9909 break;
9910 hl_id = sgp->sg_link;
9911 }
9912
9913 return hl_id;
9914}
9915
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009916#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009917/*
9918 * Call this function just after the GUI has started.
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009919 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009920 * It finds the font and color handles for the highlighting groups.
9921 */
9922 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009923highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009924{
9925 int idx;
9926
9927 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009928 if (USE_24BIT)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009929 set_normal_colors();
Bram Moolenaar071d4272004-06-13 20:20:40 +00009930
9931 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9932 gui_do_one_color(idx, FALSE, FALSE);
9933
9934 highlight_changed();
9935}
9936
9937 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009938gui_do_one_color(
9939 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009940 int do_menu UNUSED, /* TRUE: might set the menu font */
9941 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009942{
9943 int didit = FALSE;
9944
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009945# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009946# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009947 if (gui.in_use)
9948# endif
9949 if (HL_TABLE()[idx].sg_font_name != NULL)
9950 {
9951 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009952 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009953 didit = TRUE;
9954 }
9955# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009956 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9957 {
9958 HL_TABLE()[idx].sg_gui_fg =
9959 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9960 didit = TRUE;
9961 }
9962 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9963 {
9964 HL_TABLE()[idx].sg_gui_bg =
9965 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9966 didit = TRUE;
9967 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009968# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009969 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9970 {
9971 HL_TABLE()[idx].sg_gui_sp =
9972 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9973 didit = TRUE;
9974 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009975# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009976 if (didit) /* need to get a new attr number */
9977 set_hl_attr(idx);
9978}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009979#endif
9980
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009981#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9982/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009983 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009984 */
9985 static void
9986combine_stl_hlt(
9987 int id,
9988 int id_S,
9989 int id_alt,
9990 int hlcnt,
9991 int i,
9992 int hlf,
9993 int *table)
9994{
9995 struct hl_group *hlt = HL_TABLE();
9996
9997 if (id_alt == 0)
9998 {
9999 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
10000 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
10001 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
10002# if defined(FEAT_GUI) || defined(FEAT_EVAL)
10003 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
10004# endif
10005 }
10006 else
10007 mch_memmove(&hlt[hlcnt + i],
10008 &hlt[id_alt - 1],
10009 sizeof(struct hl_group));
10010 hlt[hlcnt + i].sg_link = 0;
10011
10012 hlt[hlcnt + i].sg_term ^=
10013 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
10014 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
10015 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
10016 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
10017 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
10018 hlt[hlcnt + i].sg_cterm ^=
10019 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
10020 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
10021 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
10022 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
10023 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
10024# if defined(FEAT_GUI) || defined(FEAT_EVAL)
10025 hlt[hlcnt + i].sg_gui ^=
10026 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
10027# endif
10028# ifdef FEAT_GUI
10029 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
10030 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
10031 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
10032 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
10033 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
10034 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
10035 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
10036 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
10037# ifdef FEAT_XFONTSET
10038 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
10039 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
10040# endif
10041# endif
10042 highlight_ga.ga_len = hlcnt + i + 1;
10043 set_hl_attr(hlcnt + i); /* At long last we can apply */
10044 table[i] = syn_id2attr(hlcnt + i + 1);
10045}
10046#endif
10047
Bram Moolenaar071d4272004-06-13 20:20:40 +000010048/*
10049 * Translate the 'highlight' option into attributes in highlight_attr[] and
10050 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
10051 * corresponding highlights to use on top of HLF_SNC is computed.
10052 * Called only when the 'highlight' option has been changed and upon first
10053 * screen redraw after any :highlight command.
10054 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10055 */
10056 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010057highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010058{
10059 int hlf;
10060 int i;
10061 char_u *p;
10062 int attr;
10063 char_u *end;
10064 int id;
10065#ifdef USER_HIGHLIGHT
10066 char_u userhl[10];
10067# ifdef FEAT_STL_OPT
Bram Moolenaar071d4272004-06-13 20:20:40 +000010068 int id_S = -1;
Bram Moolenaar61859032018-03-20 13:00:25 +010010069 int id_SNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010070# ifdef FEAT_TERMINAL
Bram Moolenaar61859032018-03-20 13:00:25 +010010071 int id_ST = 0;
10072 int id_STNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010073# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010074 int hlcnt;
10075# endif
10076#endif
10077 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10078
10079 need_highlight_changed = FALSE;
10080
10081 /*
10082 * Clear all attributes.
10083 */
10084 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10085 highlight_attr[hlf] = 0;
10086
10087 /*
10088 * First set all attributes to their default value.
10089 * Then use the attributes from the 'highlight' option.
10090 */
10091 for (i = 0; i < 2; ++i)
10092 {
10093 if (i)
10094 p = p_hl;
10095 else
10096 p = get_highlight_default();
10097 if (p == NULL) /* just in case */
10098 continue;
10099
10100 while (*p)
10101 {
10102 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10103 if (hl_flags[hlf] == *p)
10104 break;
10105 ++p;
10106 if (hlf == (int)HLF_COUNT || *p == NUL)
10107 return FAIL;
10108
10109 /*
10110 * Allow several hl_flags to be combined, like "bu" for
10111 * bold-underlined.
10112 */
10113 attr = 0;
10114 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10115 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010116 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010117 continue;
10118
10119 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10120 return FAIL;
10121
10122 switch (*p)
10123 {
10124 case 'b': attr |= HL_BOLD;
10125 break;
10126 case 'i': attr |= HL_ITALIC;
10127 break;
10128 case '-':
10129 case 'n': /* no highlighting */
10130 break;
10131 case 'r': attr |= HL_INVERSE;
10132 break;
10133 case 's': attr |= HL_STANDOUT;
10134 break;
10135 case 'u': attr |= HL_UNDERLINE;
10136 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010137 case 'c': attr |= HL_UNDERCURL;
10138 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010139 case 't': attr |= HL_STRIKETHROUGH;
10140 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010141 case ':': ++p; /* highlight group name */
10142 if (attr || *p == NUL) /* no combinations */
10143 return FAIL;
10144 end = vim_strchr(p, ',');
10145 if (end == NULL)
10146 end = p + STRLEN(p);
10147 id = syn_check_group(p, (int)(end - p));
10148 if (id == 0)
10149 return FAIL;
10150 attr = syn_id2attr(id);
10151 p = end - 1;
10152#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10153 if (hlf == (int)HLF_SNC)
10154 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010155# ifdef FEAT_TERMINAL
10156 else if (hlf == (int)HLF_ST)
10157 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010158 else if (hlf == (int)HLF_STNC)
10159 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010160# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010161 else if (hlf == (int)HLF_S)
10162 id_S = syn_get_final_id(id);
10163#endif
10164 break;
10165 default: return FAIL;
10166 }
10167 }
10168 highlight_attr[hlf] = attr;
10169
10170 p = skip_to_option_part(p); /* skip comma and spaces */
10171 }
10172 }
10173
10174#ifdef USER_HIGHLIGHT
10175 /* Setup the user highlights
10176 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010177 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010178 * 9 for User1-User9 combined with StatusLineNC
10179 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010180 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010181 * 1 for StatusLine default
10182 * Have to be in there simultaneously in case of table overflows in
10183 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010184 */
10185# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010186 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010187 return FAIL;
10188 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010189 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010190 {
10191 /* Make sure id_S is always valid to simplify code below. Use the last
10192 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010193 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010194 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10195 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010196 }
10197# endif
10198 for (i = 0; i < 9; i++)
10199 {
10200 sprintf((char *)userhl, "User%d", i + 1);
10201 id = syn_name2id(userhl);
10202 if (id == 0)
10203 {
10204 highlight_user[i] = 0;
10205# ifdef FEAT_STL_OPT
10206 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010207# ifdef FEAT_TERMINAL
10208 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010209 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010210# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010211# endif
10212 }
10213 else
10214 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010215 highlight_user[i] = syn_id2attr(id);
10216# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010217 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10218 HLF_SNC, highlight_stlnc);
10219# ifdef FEAT_TERMINAL
10220 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10221 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010222 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10223 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010224# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010225# endif
10226 }
10227 }
10228# ifdef FEAT_STL_OPT
10229 highlight_ga.ga_len = hlcnt;
10230# endif
10231
10232#endif /* USER_HIGHLIGHT */
10233
10234 return OK;
10235}
10236
Bram Moolenaar4f688582007-07-24 12:34:30 +000010237#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010238
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010239static void highlight_list(void);
10240static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010241
10242/*
10243 * Handle command line completion for :highlight command.
10244 */
10245 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010246set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010247{
10248 char_u *p;
10249
10250 /* Default: expand group names */
10251 xp->xp_context = EXPAND_HIGHLIGHT;
10252 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010253 include_link = 2;
10254 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010255
10256 /* (part of) subcommand already typed */
10257 if (*arg != NUL)
10258 {
10259 p = skiptowhite(arg);
10260 if (*p != NUL) /* past "default" or group name */
10261 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010262 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010263 if (STRNCMP("default", arg, p - arg) == 0)
10264 {
10265 arg = skipwhite(p);
10266 xp->xp_pattern = arg;
10267 p = skiptowhite(arg);
10268 }
10269 if (*p != NUL) /* past group name */
10270 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010271 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010272 if (arg[1] == 'i' && arg[0] == 'N')
10273 highlight_list();
10274 if (STRNCMP("link", arg, p - arg) == 0
10275 || STRNCMP("clear", arg, p - arg) == 0)
10276 {
10277 xp->xp_pattern = skipwhite(p);
10278 p = skiptowhite(xp->xp_pattern);
10279 if (*p != NUL) /* past first group name */
10280 {
10281 xp->xp_pattern = skipwhite(p);
10282 p = skiptowhite(xp->xp_pattern);
10283 }
10284 }
10285 if (*p != NUL) /* past group name(s) */
10286 xp->xp_context = EXPAND_NOTHING;
10287 }
10288 }
10289 }
10290}
10291
10292/*
10293 * List highlighting matches in a nice way.
10294 */
10295 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010296highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010297{
10298 int i;
10299
10300 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010301 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010302 for (i = 40; --i >= 0; )
10303 highlight_list_two(99, 0);
10304}
10305
10306 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010307highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010308{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010309 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010310 msg_clr_eos();
10311 out_flush();
10312 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10313}
10314
10315#endif /* FEAT_CMDL_COMPL */
10316
10317#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10318 || defined(FEAT_SIGNS) || defined(PROTO)
10319/*
10320 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010321 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010322 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010323get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010324{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010325 return get_highlight_name_ext(xp, idx, TRUE);
10326}
10327
10328/*
10329 * Obtain a highlight group name.
10330 * When "skip_cleared" is TRUE don't return a cleared entry.
10331 */
10332 char_u *
10333get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10334{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010335 if (idx < 0)
10336 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010337
10338 /* Items are never removed from the table, skip the ones that were
10339 * cleared. */
10340 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10341 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010342
Bram Moolenaar071d4272004-06-13 20:20:40 +000010343#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010344 if (idx == highlight_ga.ga_len && include_none != 0)
10345 return (char_u *)"none";
10346 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010347 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010348 if (idx == highlight_ga.ga_len + include_none + include_default
10349 && include_link != 0)
10350 return (char_u *)"link";
10351 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10352 && include_link != 0)
10353 return (char_u *)"clear";
10354#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010355 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010356 return NULL;
10357 return HL_TABLE()[idx].sg_name;
10358}
10359#endif
10360
Bram Moolenaar4f688582007-07-24 12:34:30 +000010361#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010362/*
10363 * Free all the highlight group fonts.
10364 * Used when quitting for systems which need it.
10365 */
10366 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010367free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010368{
10369 int idx;
10370
10371 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10372 {
10373 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10374 HL_TABLE()[idx].sg_font = NOFONT;
10375# ifdef FEAT_XFONTSET
10376 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10377 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10378# endif
10379 }
10380
10381 gui_mch_free_font(gui.norm_font);
10382# ifdef FEAT_XFONTSET
10383 gui_mch_free_fontset(gui.fontset);
10384# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010385# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010386 gui_mch_free_font(gui.bold_font);
10387 gui_mch_free_font(gui.ital_font);
10388 gui_mch_free_font(gui.boldital_font);
10389# endif
10390}
10391#endif
10392
10393/**************************************
10394 * End of Highlighting stuff *
10395 **************************************/