blob: e54ec9a82630c276ae6f7cec47f37d1e55f22ad1 [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 Moolenaar95892c22018-09-28 22:26:54 +02001195 block->b_sst_first = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001196 block->b_sst_len = 0;
1197 }
1198}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199/*
1200 * Free b_sst_array[] for buffer "buf".
1201 * Used when syntax items changed to force resyncing everywhere.
1202 */
1203 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001204syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001206#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001208#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209
Bram Moolenaar860cae12010-06-05 23:22:07 +02001210 syn_stack_free_block(block);
1211
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212#ifdef FEAT_FOLDING
1213 /* When using "syntax" fold method, must update all folds. */
1214 FOR_ALL_WINDOWS(wp)
1215 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001216 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 foldUpdateAll(wp);
1218 }
1219#endif
1220}
1221
1222/*
1223 * Allocate the syntax state stack for syn_buf when needed.
1224 * If the number of entries in b_sst_array[] is much too big or a bit too
1225 * small, reallocate it.
1226 * Also used to allocate b_sst_array[] for the first time.
1227 */
1228 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001229syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230{
1231 long len;
1232 synstate_T *to, *from;
1233 synstate_T *sstp;
1234
1235 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1236 if (len < SST_MIN_ENTRIES)
1237 len = SST_MIN_ENTRIES;
1238 else if (len > SST_MAX_ENTRIES)
1239 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 {
1242 /* Allocate 50% too much, to avoid reallocating too often. */
1243 len = syn_buf->b_ml.ml_line_count;
1244 len = (len + len / 2) / SST_DIST + Rows * 2;
1245 if (len < SST_MIN_ENTRIES)
1246 len = SST_MIN_ENTRIES;
1247 else if (len > SST_MAX_ENTRIES)
1248 len = SST_MAX_ENTRIES;
1249
Bram Moolenaar860cae12010-06-05 23:22:07 +02001250 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001251 {
1252 /* When shrinking the array, cleanup the existing stack.
1253 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001254 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 && syn_stack_cleanup())
1256 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001257 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1258 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 }
1260
1261 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1262 if (sstp == NULL) /* out of memory! */
1263 return;
1264
1265 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001266 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 {
1268 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 from = from->sst_next)
1271 {
1272 ++to;
1273 *to = *from;
1274 to->sst_next = to + 1;
1275 }
1276 }
1277 if (to != sstp - 1)
1278 {
1279 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001280 syn_block->b_sst_first = sstp;
1281 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282 }
1283 else
1284 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001285 syn_block->b_sst_first = NULL;
1286 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 }
1288
1289 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001290 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 while (++to < sstp + len)
1292 to->sst_next = to + 1;
1293 (sstp + len - 1)->sst_next = NULL;
1294
Bram Moolenaar860cae12010-06-05 23:22:07 +02001295 vim_free(syn_block->b_sst_array);
1296 syn_block->b_sst_array = sstp;
1297 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 }
1299}
1300
1301/*
1302 * Check for changes in a buffer to affect stored syntax states. Uses the
1303 * b_mod_* fields.
1304 * Called from update_screen(), before screen is being updated, once for each
1305 * displayed buffer.
1306 */
1307 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001308syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001310 win_T *wp;
1311
1312 syn_stack_apply_changes_block(&buf->b_s, buf);
1313
1314 FOR_ALL_WINDOWS(wp)
1315 {
1316 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1317 syn_stack_apply_changes_block(wp->w_s, buf);
1318 }
1319}
1320
1321 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001322syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 synstate_T *p, *prev, *np;
1325 linenr_T n;
1326
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
1332 n = p->sst_lnum + buf->b_mod_xlines;
1333 if (n <= buf->b_mod_bot)
1334 {
1335 /* this state is inside the changed area, remove it */
1336 np = p->sst_next;
1337 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 else
1340 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001341 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001342 p = np;
1343 continue;
1344 }
1345 /* This state is below the changed area. Remember the line
1346 * that needs to be parsed before this entry can be made valid
1347 * again. */
1348 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1349 {
1350 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1351 p->sst_change_lnum += buf->b_mod_xlines;
1352 else
1353 p->sst_change_lnum = buf->b_mod_top;
1354 }
1355 if (p->sst_change_lnum == 0
1356 || p->sst_change_lnum < buf->b_mod_bot)
1357 p->sst_change_lnum = buf->b_mod_bot;
1358
1359 p->sst_lnum = n;
1360 }
1361 prev = p;
1362 p = p->sst_next;
1363 }
1364}
1365
1366/*
1367 * Reduce the number of entries in the state stack for syn_buf.
1368 * Returns TRUE if at least one entry was freed.
1369 */
1370 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001371syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372{
1373 synstate_T *p, *prev;
1374 disptick_T tick;
1375 int above;
1376 int dist;
1377 int retval = FALSE;
1378
Bram Moolenaar95892c22018-09-28 22:26:54 +02001379 if (syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 return retval;
1381
1382 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001383 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001384 dist = 999999;
1385 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001386 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387
1388 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001389 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 * be removed. Set "above" when the "tick" for the oldest entry is above
1391 * "b_sst_lasttick" (the display tick wraps around).
1392 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001393 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1397 {
1398 if (prev->sst_lnum + dist > p->sst_lnum)
1399 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001400 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 {
1402 if (!above || p->sst_tick < tick)
1403 tick = p->sst_tick;
1404 above = TRUE;
1405 }
1406 else if (!above && p->sst_tick < tick)
1407 tick = p->sst_tick;
1408 }
1409 }
1410
1411 /*
1412 * Go through the list to make the entries for the oldest tick at an
1413 * interval of several lines.
1414 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001415 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1417 {
1418 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1419 {
1420 /* Move this entry from used list to free list */
1421 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001422 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001423 p = prev;
1424 retval = TRUE;
1425 }
1426 }
1427 return retval;
1428}
1429
1430/*
1431 * Free the allocated memory for a syn_state item.
1432 * Move the entry into the free list.
1433 */
1434 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001435syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001436{
1437 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001438 p->sst_next = block->b_sst_firstfree;
1439 block->b_sst_firstfree = p;
1440 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441}
1442
1443/*
1444 * Find an entry in the list of state stacks at or before "lnum".
1445 * Returns NULL when there is no entry or the first entry is after "lnum".
1446 */
1447 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001448syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449{
1450 synstate_T *p, *prev;
1451
1452 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001453 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454 {
1455 if (p->sst_lnum == lnum)
1456 return p;
1457 if (p->sst_lnum > lnum)
1458 break;
1459 }
1460 return prev;
1461}
1462
1463/*
1464 * Try saving the current state in b_sst_array[].
1465 * The current state must be valid for the start of the current_lnum line!
1466 */
1467 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001468store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469{
1470 int i;
1471 synstate_T *p;
1472 bufstate_T *bp;
1473 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001474 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475
1476 /*
1477 * If the current state contains a start or end pattern that continues
1478 * from the previous line, we can't use it. Don't store it then.
1479 */
1480 for (i = current_state.ga_len - 1; i >= 0; --i)
1481 {
1482 cur_si = &CUR_STATE(i);
1483 if (cur_si->si_h_startpos.lnum >= current_lnum
1484 || cur_si->si_m_endpos.lnum >= current_lnum
1485 || cur_si->si_h_endpos.lnum >= current_lnum
1486 || (cur_si->si_end_idx
1487 && cur_si->si_eoe_pos.lnum >= current_lnum))
1488 break;
1489 }
1490 if (i >= 0)
1491 {
1492 if (sp != NULL)
1493 {
1494 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001495 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001497 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 else
1499 {
1500 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001501 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 if (p->sst_next == sp)
1503 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001504 if (p != NULL) /* just in case */
1505 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001507 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 sp = NULL;
1509 }
1510 }
1511 else if (sp == NULL || sp->sst_lnum != current_lnum)
1512 {
1513 /*
1514 * Add a new entry
1515 */
1516 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001517 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001518 {
1519 (void)syn_stack_cleanup();
1520 /* "sp" may have been moved to the freelist now */
1521 sp = syn_stack_find_entry(current_lnum);
1522 }
1523 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001524 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 sp = NULL;
1526 else
1527 {
1528 /* Take the first item from the free list and put it in the used
1529 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001530 p = syn_block->b_sst_firstfree;
1531 syn_block->b_sst_firstfree = p->sst_next;
1532 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533 if (sp == NULL)
1534 {
1535 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001536 p->sst_next = syn_block->b_sst_first;
1537 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538 }
1539 else
1540 {
1541 /* insert in list after *sp */
1542 p->sst_next = sp->sst_next;
1543 sp->sst_next = p;
1544 }
1545 sp = p;
1546 sp->sst_stacksize = 0;
1547 sp->sst_lnum = current_lnum;
1548 }
1549 }
1550 if (sp != NULL)
1551 {
1552 /* When overwriting an existing state stack, clear it first */
1553 clear_syn_state(sp);
1554 sp->sst_stacksize = current_state.ga_len;
1555 if (current_state.ga_len > SST_FIX_STATES)
1556 {
1557 /* Need to clear it, might be something remaining from when the
1558 * length was less than SST_FIX_STATES. */
1559 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1560 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1561 sp->sst_stacksize = 0;
1562 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001563 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1565 }
1566 else
1567 bp = sp->sst_union.sst_stack;
1568 for (i = 0; i < sp->sst_stacksize; ++i)
1569 {
1570 bp[i].bs_idx = CUR_STATE(i).si_idx;
1571 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001572#ifdef FEAT_CONCEAL
1573 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1574 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1575#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1577 }
1578 sp->sst_next_flags = current_next_flags;
1579 sp->sst_next_list = current_next_list;
1580 sp->sst_tick = display_tick;
1581 sp->sst_change_lnum = 0;
1582 }
1583 current_state_stored = TRUE;
1584 return sp;
1585}
1586
1587/*
1588 * Copy a state stack from "from" in b_sst_array[] to current_state;
1589 */
1590 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001591load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001592{
1593 int i;
1594 bufstate_T *bp;
1595
1596 clear_current_state();
1597 validate_current_state();
1598 keepend_level = -1;
1599 if (from->sst_stacksize
1600 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1601 {
1602 if (from->sst_stacksize > SST_FIX_STATES)
1603 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1604 else
1605 bp = from->sst_union.sst_stack;
1606 for (i = 0; i < from->sst_stacksize; ++i)
1607 {
1608 CUR_STATE(i).si_idx = bp[i].bs_idx;
1609 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001610#ifdef FEAT_CONCEAL
1611 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1612 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1613#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1615 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1616 keepend_level = i;
1617 CUR_STATE(i).si_ends = FALSE;
1618 CUR_STATE(i).si_m_lnum = 0;
1619 if (CUR_STATE(i).si_idx >= 0)
1620 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001621 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622 else
1623 CUR_STATE(i).si_next_list = NULL;
1624 update_si_attr(i);
1625 }
1626 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001627 }
1628 current_next_list = from->sst_next_list;
1629 current_next_flags = from->sst_next_flags;
1630 current_lnum = from->sst_lnum;
1631}
1632
1633/*
1634 * Compare saved state stack "*sp" with the current state.
1635 * Return TRUE when they are equal.
1636 */
1637 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001638syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001639{
1640 int i, j;
1641 bufstate_T *bp;
1642 reg_extmatch_T *six, *bsx;
1643
1644 /* First a quick check if the stacks have the same size end nextlist. */
1645 if (sp->sst_stacksize == current_state.ga_len
1646 && sp->sst_next_list == current_next_list)
1647 {
1648 /* Need to compare all states on both stacks. */
1649 if (sp->sst_stacksize > SST_FIX_STATES)
1650 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1651 else
1652 bp = sp->sst_union.sst_stack;
1653
1654 for (i = current_state.ga_len; --i >= 0; )
1655 {
1656 /* If the item has another index the state is different. */
1657 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1658 break;
1659 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1660 {
1661 /* When the extmatch pointers are different, the strings in
1662 * them can still be the same. Check if the extmatch
1663 * references are equal. */
1664 bsx = bp[i].bs_extmatch;
1665 six = CUR_STATE(i).si_extmatch;
1666 /* If one of the extmatch pointers is NULL the states are
1667 * different. */
1668 if (bsx == NULL || six == NULL)
1669 break;
1670 for (j = 0; j < NSUBEXP; ++j)
1671 {
1672 /* Check each referenced match string. They must all be
1673 * equal. */
1674 if (bsx->matches[j] != six->matches[j])
1675 {
1676 /* If the pointer is different it can still be the
1677 * same text. Compare the strings, ignore case when
1678 * the start item has the sp_ic flag set. */
1679 if (bsx->matches[j] == NULL
1680 || six->matches[j] == NULL)
1681 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001682 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 ? MB_STRICMP(bsx->matches[j],
1684 six->matches[j]) != 0
1685 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1686 break;
1687 }
1688 }
1689 if (j != NSUBEXP)
1690 break;
1691 }
1692 }
1693 if (i < 0)
1694 return TRUE;
1695 }
1696 return FALSE;
1697}
1698
1699/*
1700 * We stop parsing syntax above line "lnum". If the stored state at or below
1701 * this line depended on a change before it, it now depends on the line below
1702 * the last parsed line.
1703 * The window looks like this:
1704 * line which changed
1705 * displayed line
1706 * displayed line
1707 * lnum -> line below window
1708 */
1709 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001710syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711{
1712 synstate_T *sp;
1713
1714 sp = syn_stack_find_entry(lnum);
1715 if (sp != NULL && sp->sst_lnum < lnum)
1716 sp = sp->sst_next;
1717
1718 if (sp != NULL && sp->sst_change_lnum != 0)
1719 sp->sst_change_lnum = lnum;
1720}
1721
1722/*
1723 * End of handling of the state stack.
1724 ****************************************/
1725
1726 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001727invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001728{
1729 clear_current_state();
1730 current_state.ga_itemsize = 0; /* mark current_state invalid */
1731 current_next_list = NULL;
1732 keepend_level = -1;
1733}
1734
1735 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001736validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737{
1738 current_state.ga_itemsize = sizeof(stateitem_T);
1739 current_state.ga_growsize = 3;
1740}
1741
1742/*
1743 * Return TRUE if the syntax at start of lnum changed since last time.
1744 * This will only be called just after get_syntax_attr() for the previous
1745 * line, to check if the next line needs to be redrawn too.
1746 */
1747 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001748syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001749{
1750 int retval = TRUE;
1751 synstate_T *sp;
1752
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 /*
1754 * Check the state stack when:
1755 * - lnum is just below the previously syntaxed line.
1756 * - lnum is not before the lines with saved states.
1757 * - lnum is not past the lines with saved states.
1758 * - lnum is at or before the last changed line.
1759 */
1760 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1761 {
1762 sp = syn_stack_find_entry(lnum);
1763 if (sp != NULL && sp->sst_lnum == lnum)
1764 {
1765 /*
1766 * finish the previous line (needed when not all of the line was
1767 * drawn)
1768 */
1769 (void)syn_finish_line(FALSE);
1770
1771 /*
1772 * Compare the current state with the previously saved state of
1773 * the line.
1774 */
1775 if (syn_stack_equal(sp))
1776 retval = FALSE;
1777
1778 /*
1779 * Store the current state in b_sst_array[] for later use.
1780 */
1781 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001782 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783 }
1784 }
1785
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 return retval;
1787}
1788
1789/*
1790 * Finish the current line.
1791 * This doesn't return any attributes, it only gets the state at the end of
1792 * the line. It can start anywhere in the line, as long as the current state
1793 * is valid.
1794 */
1795 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001796syn_finish_line(
1797 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798{
1799 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001800 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001802 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001804 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1805 /*
1806 * When syncing, and found some item, need to check the item.
1807 */
1808 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001811 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001813 cur_si = &CUR_STATE(current_state.ga_len - 1);
1814 if (cur_si->si_idx >= 0
1815 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1816 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1817 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001819 /* syn_current_attr() will have skipped the check for an item
1820 * that ends here, need to do that now. Be careful not to go
1821 * past the NUL. */
1822 prev_current_col = current_col;
1823 if (syn_getcurline()[current_col] != NUL)
1824 ++current_col;
1825 check_state_ends();
1826 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001828 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 }
1830 return FALSE;
1831}
1832
1833/*
1834 * Return highlight attributes for next character.
1835 * Must first call syntax_start() once for the line.
1836 * "col" is normally 0 for the first use in a line, and increments by one each
1837 * time. It's allowed to skip characters and to stop before the end of the
1838 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001839 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1840 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 */
1842 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001843get_syntax_attr(
1844 colnr_T col,
1845 int *can_spell,
1846 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847{
1848 int attr = 0;
1849
Bram Moolenaar349955a2007-08-14 21:07:36 +00001850 if (can_spell != NULL)
1851 /* Default: Only do spelling when there is no @Spell cluster or when
1852 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001853 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1854 ? (syn_block->b_spell_cluster_id == 0)
1855 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001856
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001858 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 return 0;
1860
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001861 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001862 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001863 {
1864 clear_current_state();
1865#ifdef FEAT_EVAL
1866 current_id = 0;
1867 current_trans_id = 0;
1868#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001869#ifdef FEAT_CONCEAL
1870 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001871 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001872#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001873 return 0;
1874 }
1875
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876 /* Make sure current_state is valid */
1877 if (INVALID_STATE(&current_state))
1878 validate_current_state();
1879
1880 /*
1881 * Skip from the current column to "col", get the attributes for "col".
1882 */
1883 while (current_col <= col)
1884 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001885 attr = syn_current_attr(FALSE, TRUE, can_spell,
1886 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 ++current_col;
1888 }
1889
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 return attr;
1891}
1892
1893/*
1894 * Get syntax attributes for current_lnum, current_col.
1895 */
1896 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001897syn_current_attr(
1898 int syncing, /* When 1: called for syncing */
1899 int displaying, /* result will be displayed */
1900 int *can_spell, /* return: do spell checking */
1901 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902{
1903 int syn_id;
1904 lpos_T endpos; /* was: char_u *endp; */
1905 lpos_T hl_startpos; /* was: int hl_startcol; */
1906 lpos_T hl_endpos;
1907 lpos_T eos_pos; /* end-of-start match (start region) */
1908 lpos_T eoe_pos; /* end-of-end pattern */
1909 int end_idx; /* group ID for end pattern */
1910 int idx;
1911 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001912 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913 int startcol;
1914 int endcol;
1915 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001916 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001917 short *next_list;
1918 int found_match; /* found usable match */
1919 static int try_next_column = FALSE; /* must try in next col */
1920 int do_keywords;
1921 regmmatch_T regmatch;
1922 lpos_T pos;
1923 int lc_col;
1924 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001925 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 char_u *line; /* current line. NOTE: becomes invalid after
1927 looking for a pattern match! */
1928
1929 /* variables for zero-width matches that have a "nextgroup" argument */
1930 int keep_next_list;
1931 int zero_width_next_list = FALSE;
1932 garray_T zero_width_next_ga;
1933
1934 /*
1935 * No character, no attributes! Past end of line?
1936 * Do try matching with an empty line (could be the start of a region).
1937 */
1938 line = syn_getcurline();
1939 if (line[current_col] == NUL && current_col != 0)
1940 {
1941 /*
1942 * If we found a match after the last column, use it.
1943 */
1944 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1945 && next_match_col != MAXCOL)
1946 (void)push_next_match(NULL);
1947
1948 current_finished = TRUE;
1949 current_state_stored = FALSE;
1950 return 0;
1951 }
1952
1953 /* if the current or next character is NUL, we will finish the line now */
1954 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1955 {
1956 current_finished = TRUE;
1957 current_state_stored = FALSE;
1958 }
1959
1960 /*
1961 * When in the previous column there was a match but it could not be used
1962 * (empty match or already matched in this column) need to try again in
1963 * the next column.
1964 */
1965 if (try_next_column)
1966 {
1967 next_match_idx = -1;
1968 try_next_column = FALSE;
1969 }
1970
1971 /* Only check for keywords when not syncing and there are some. */
1972 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001973 && (syn_block->b_keywtab.ht_used > 0
1974 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975
1976 /* Init the list of zero-width matches with a nextlist. This is used to
1977 * avoid matching the same item in the same position twice. */
1978 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1979
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001980 /* use syntax iskeyword option */
1981 save_chartab(buf_chartab);
1982
Bram Moolenaar071d4272004-06-13 20:20:40 +00001983 /*
1984 * Repeat matching keywords and patterns, to find contained items at the
1985 * same column. This stops when there are no extra matches at the current
1986 * column.
1987 */
1988 do
1989 {
1990 found_match = FALSE;
1991 keep_next_list = FALSE;
1992 syn_id = 0;
1993
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001994
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 /*
1996 * 1. Check for a current state.
1997 * Only when there is no current state, or if the current state may
1998 * contain other things, we need to check for keywords and patterns.
1999 * Always need to check for contained items if some item has the
2000 * "containedin" argument (takes extra time!).
2001 */
2002 if (current_state.ga_len)
2003 cur_si = &CUR_STATE(current_state.ga_len - 1);
2004 else
2005 cur_si = NULL;
2006
Bram Moolenaar860cae12010-06-05 23:22:07 +02002007 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 || cur_si->si_cont_list != NULL)
2009 {
2010 /*
2011 * 2. Check for keywords, if on a keyword char after a non-keyword
2012 * char. Don't do this when syncing.
2013 */
2014 if (do_keywords)
2015 {
2016 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002017 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002019 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020#ifdef FEAT_MBYTE
2021 - (has_mbyte
2022 ? (*mb_head_off)(line, line + current_col - 1)
2023 : 0)
2024#endif
2025 , syn_buf)))
2026 {
2027 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002028 &endcol, &flags, &next_list, cur_si,
2029 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002030 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 {
2032 if (push_current_state(KEYWORD_IDX) == OK)
2033 {
2034 cur_si = &CUR_STATE(current_state.ga_len - 1);
2035 cur_si->si_m_startcol = current_col;
2036 cur_si->si_h_startpos.lnum = current_lnum;
2037 cur_si->si_h_startpos.col = 0; /* starts right away */
2038 cur_si->si_m_endpos.lnum = current_lnum;
2039 cur_si->si_m_endpos.col = endcol;
2040 cur_si->si_h_endpos.lnum = current_lnum;
2041 cur_si->si_h_endpos.col = endcol;
2042 cur_si->si_ends = TRUE;
2043 cur_si->si_end_idx = 0;
2044 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002045#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002046 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002047 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002048 if (current_state.ga_len > 1)
2049 cur_si->si_flags |=
2050 CUR_STATE(current_state.ga_len - 2).si_flags
2051 & HL_CONCEAL;
2052#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053 cur_si->si_id = syn_id;
2054 cur_si->si_trans_id = syn_id;
2055 if (flags & HL_TRANSP)
2056 {
2057 if (current_state.ga_len < 2)
2058 {
2059 cur_si->si_attr = 0;
2060 cur_si->si_trans_id = 0;
2061 }
2062 else
2063 {
2064 cur_si->si_attr = CUR_STATE(
2065 current_state.ga_len - 2).si_attr;
2066 cur_si->si_trans_id = CUR_STATE(
2067 current_state.ga_len - 2).si_trans_id;
2068 }
2069 }
2070 else
2071 cur_si->si_attr = syn_id2attr(syn_id);
2072 cur_si->si_cont_list = NULL;
2073 cur_si->si_next_list = next_list;
2074 check_keepend();
2075 }
2076 else
2077 vim_free(next_list);
2078 }
2079 }
2080 }
2081
2082 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002083 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002085 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 {
2087 /*
2088 * If we didn't check for a match yet, or we are past it, check
2089 * for any match with a pattern.
2090 */
2091 if (next_match_idx < 0 || next_match_col < (int)current_col)
2092 {
2093 /*
2094 * Check all relevant patterns for a match at this
2095 * position. This is complicated, because matching with a
2096 * pattern takes quite a bit of time, thus we want to
2097 * avoid doing it when it's not needed.
2098 */
2099 next_match_idx = 0; /* no match in this line yet */
2100 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002101 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002103 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 if ( spp->sp_syncing == syncing
2105 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2106 && (spp->sp_type == SPTYPE_MATCH
2107 || spp->sp_type == SPTYPE_START)
2108 && (current_next_list != NULL
2109 ? in_id_list(NULL, current_next_list,
2110 &spp->sp_syn, 0)
2111 : (cur_si == NULL
2112 ? !(spp->sp_flags & HL_CONTAINED)
2113 : in_id_list(cur_si,
2114 cur_si->si_cont_list, &spp->sp_syn,
2115 spp->sp_flags & HL_CONTAINED))))
2116 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002117 int r;
2118
Bram Moolenaar071d4272004-06-13 20:20:40 +00002119 /* If we already tried matching in this line, and
2120 * there isn't a match before next_match_col, skip
2121 * this item. */
2122 if (spp->sp_line_id == current_line_id
2123 && spp->sp_startcol >= next_match_col)
2124 continue;
2125 spp->sp_line_id = current_line_id;
2126
2127 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2128 if (lc_col < 0)
2129 lc_col = 0;
2130
2131 regmatch.rmm_ic = spp->sp_ic;
2132 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002133 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002134 current_lnum,
2135 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002136 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002137 spp->sp_prog = regmatch.regprog;
2138 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 {
2140 /* no match in this line, try another one */
2141 spp->sp_startcol = MAXCOL;
2142 continue;
2143 }
2144
2145 /*
2146 * Compute the first column of the match.
2147 */
2148 syn_add_start_off(&pos, &regmatch,
2149 spp, SPO_MS_OFF, -1);
2150 if (pos.lnum > current_lnum)
2151 {
2152 /* must have used end of match in a next line,
2153 * we can't handle that */
2154 spp->sp_startcol = MAXCOL;
2155 continue;
2156 }
2157 startcol = pos.col;
2158
2159 /* remember the next column where this pattern
2160 * matches in the current line */
2161 spp->sp_startcol = startcol;
2162
2163 /*
2164 * If a previously found match starts at a lower
2165 * column number, don't use this one.
2166 */
2167 if (startcol >= next_match_col)
2168 continue;
2169
2170 /*
2171 * If we matched this pattern at this position
2172 * before, skip it. Must retry in the next
2173 * column, because it may match from there.
2174 */
2175 if (did_match_already(idx, &zero_width_next_ga))
2176 {
2177 try_next_column = TRUE;
2178 continue;
2179 }
2180
2181 endpos.lnum = regmatch.endpos[0].lnum;
2182 endpos.col = regmatch.endpos[0].col;
2183
2184 /* Compute the highlight start. */
2185 syn_add_start_off(&hl_startpos, &regmatch,
2186 spp, SPO_HS_OFF, -1);
2187
2188 /* Compute the region start. */
2189 /* Default is to use the end of the match. */
2190 syn_add_end_off(&eos_pos, &regmatch,
2191 spp, SPO_RS_OFF, 0);
2192
2193 /*
2194 * Grab the external submatches before they get
2195 * overwritten. Reference count doesn't change.
2196 */
2197 unref_extmatch(cur_extmatch);
2198 cur_extmatch = re_extmatch_out;
2199 re_extmatch_out = NULL;
2200
2201 flags = 0;
2202 eoe_pos.lnum = 0; /* avoid warning */
2203 eoe_pos.col = 0;
2204 end_idx = 0;
2205 hl_endpos.lnum = 0;
2206
2207 /*
2208 * For a "oneline" the end must be found in the
2209 * same line too. Search for it after the end of
2210 * the match with the start pattern. Set the
2211 * resulting end positions at the same time.
2212 */
2213 if (spp->sp_type == SPTYPE_START
2214 && (spp->sp_flags & HL_ONELINE))
2215 {
2216 lpos_T startpos;
2217
2218 startpos = endpos;
2219 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2220 &flags, &eoe_pos, &end_idx, cur_extmatch);
2221 if (endpos.lnum == 0)
2222 continue; /* not found */
2223 }
2224
2225 /*
2226 * For a "match" the size must be > 0 after the
2227 * end offset needs has been added. Except when
2228 * syncing.
2229 */
2230 else if (spp->sp_type == SPTYPE_MATCH)
2231 {
2232 syn_add_end_off(&hl_endpos, &regmatch, spp,
2233 SPO_HE_OFF, 0);
2234 syn_add_end_off(&endpos, &regmatch, spp,
2235 SPO_ME_OFF, 0);
2236 if (endpos.lnum == current_lnum
2237 && (int)endpos.col + syncing < startcol)
2238 {
2239 /*
2240 * If an empty string is matched, may need
2241 * to try matching again at next column.
2242 */
2243 if (regmatch.startpos[0].col
2244 == regmatch.endpos[0].col)
2245 try_next_column = TRUE;
2246 continue;
2247 }
2248 }
2249
2250 /*
2251 * keep the best match so far in next_match_*
2252 */
2253 /* Highlighting must start after startpos and end
2254 * before endpos. */
2255 if (hl_startpos.lnum == current_lnum
2256 && (int)hl_startpos.col < startcol)
2257 hl_startpos.col = startcol;
2258 limit_pos_zero(&hl_endpos, &endpos);
2259
2260 next_match_idx = idx;
2261 next_match_col = startcol;
2262 next_match_m_endpos = endpos;
2263 next_match_h_endpos = hl_endpos;
2264 next_match_h_startpos = hl_startpos;
2265 next_match_flags = flags;
2266 next_match_eos_pos = eos_pos;
2267 next_match_eoe_pos = eoe_pos;
2268 next_match_end_idx = end_idx;
2269 unref_extmatch(next_match_extmatch);
2270 next_match_extmatch = cur_extmatch;
2271 cur_extmatch = NULL;
2272 }
2273 }
2274 }
2275
2276 /*
2277 * If we found a match at the current column, use it.
2278 */
2279 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2280 {
2281 synpat_T *lspp;
2282
2283 /* When a zero-width item matched which has a nextgroup,
2284 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002285 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 if (next_match_m_endpos.lnum == current_lnum
2287 && next_match_m_endpos.col == current_col
2288 && lspp->sp_next_list != NULL)
2289 {
2290 current_next_list = lspp->sp_next_list;
2291 current_next_flags = lspp->sp_flags;
2292 keep_next_list = TRUE;
2293 zero_width_next_list = TRUE;
2294
2295 /* Add the index to a list, so that we can check
2296 * later that we don't match it again (and cause an
2297 * endless loop). */
2298 if (ga_grow(&zero_width_next_ga, 1) == OK)
2299 {
2300 ((int *)(zero_width_next_ga.ga_data))
2301 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002302 }
2303 next_match_idx = -1;
2304 }
2305 else
2306 cur_si = push_next_match(cur_si);
2307 found_match = TRUE;
2308 }
2309 }
2310 }
2311
2312 /*
2313 * Handle searching for nextgroup match.
2314 */
2315 if (current_next_list != NULL && !keep_next_list)
2316 {
2317 /*
2318 * If a nextgroup was not found, continue looking for one if:
2319 * - this is an empty line and the "skipempty" option was given
2320 * - we are on white space and the "skipwhite" option was given
2321 */
2322 if (!found_match)
2323 {
2324 line = syn_getcurline();
2325 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002326 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327 || ((current_next_flags & HL_SKIPEMPTY)
2328 && *line == NUL))
2329 break;
2330 }
2331
2332 /*
2333 * If a nextgroup was found: Use it, and continue looking for
2334 * contained matches.
2335 * If a nextgroup was not found: Continue looking for a normal
2336 * match.
2337 * When did set current_next_list for a zero-width item and no
2338 * match was found don't loop (would get stuck).
2339 */
2340 current_next_list = NULL;
2341 next_match_idx = -1;
2342 if (!zero_width_next_list)
2343 found_match = TRUE;
2344 }
2345
2346 } while (found_match);
2347
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002348 restore_chartab(buf_chartab);
2349
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 /*
2351 * Use attributes from the current state, if within its highlighting.
2352 * If not, use attributes from the current-but-one state, etc.
2353 */
2354 current_attr = 0;
2355#ifdef FEAT_EVAL
2356 current_id = 0;
2357 current_trans_id = 0;
2358#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002359#ifdef FEAT_CONCEAL
2360 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002361 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363 if (cur_si != NULL)
2364 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002365#ifndef FEAT_EVAL
2366 int current_trans_id = 0;
2367#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002368 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2369 {
2370 sip = &CUR_STATE(idx);
2371 if ((current_lnum > sip->si_h_startpos.lnum
2372 || (current_lnum == sip->si_h_startpos.lnum
2373 && current_col >= sip->si_h_startpos.col))
2374 && (sip->si_h_endpos.lnum == 0
2375 || current_lnum < sip->si_h_endpos.lnum
2376 || (current_lnum == sip->si_h_endpos.lnum
2377 && current_col < sip->si_h_endpos.col)))
2378 {
2379 current_attr = sip->si_attr;
2380#ifdef FEAT_EVAL
2381 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002383 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002384#ifdef FEAT_CONCEAL
2385 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002386 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002387 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 break;
2390 }
2391 }
2392
Bram Moolenaar217ad922005-03-20 22:37:15 +00002393 if (can_spell != NULL)
2394 {
2395 struct sp_syn sps;
2396
2397 /*
2398 * set "can_spell" to TRUE if spell checking is supposed to be
2399 * done in the current item.
2400 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002401 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002402 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002403 /* There is no @Spell cluster: Do spelling for items without
2404 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002405 if (syn_block->b_nospell_cluster_id == 0
2406 || current_trans_id == 0)
2407 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002408 else
2409 {
2410 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002411 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002412 sps.cont_in_list = NULL;
2413 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2414 }
2415 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002416 else
2417 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002418 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002419 * the @Spell cluster. But not when @NoSpell is also there.
2420 * At the toplevel only spell check when ":syn spell toplevel"
2421 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002422 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 else
2425 {
2426 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002427 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002428 sps.cont_in_list = NULL;
2429 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2430
Bram Moolenaar860cae12010-06-05 23:22:07 +02002431 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002432 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002433 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002434 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2435 *can_spell = FALSE;
2436 }
2437 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002438 }
2439 }
2440
2441
Bram Moolenaar071d4272004-06-13 20:20:40 +00002442 /*
2443 * Check for end of current state (and the states before it) at the
2444 * next column. Don't do this for syncing, because we would miss a
2445 * single character match.
2446 * First check if the current state ends at the current column. It
2447 * may be for an empty match and a containing item might end in the
2448 * current column.
2449 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002450 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 {
2452 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002453 if (current_state.ga_len > 0
2454 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455 {
2456 ++current_col;
2457 check_state_ends();
2458 --current_col;
2459 }
2460 }
2461 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002462 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002463 /* Default: Only do spelling when there is no @Spell cluster or when
2464 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002465 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2466 ? (syn_block->b_spell_cluster_id == 0)
2467 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002469 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002471 && (line = syn_getcurline())[current_col] != NUL
2472 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2474 current_next_list = NULL;
2475
2476 if (zero_width_next_ga.ga_len > 0)
2477 ga_clear(&zero_width_next_ga);
2478
2479 /* No longer need external matches. But keep next_match_extmatch. */
2480 unref_extmatch(re_extmatch_out);
2481 re_extmatch_out = NULL;
2482 unref_extmatch(cur_extmatch);
2483
2484 return current_attr;
2485}
2486
2487
2488/*
2489 * Check if we already matched pattern "idx" at the current column.
2490 */
2491 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002492did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493{
2494 int i;
2495
2496 for (i = current_state.ga_len; --i >= 0; )
2497 if (CUR_STATE(i).si_m_startcol == (int)current_col
2498 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2499 && CUR_STATE(i).si_idx == idx)
2500 return TRUE;
2501
2502 /* Zero-width matches with a nextgroup argument are not put on the syntax
2503 * stack, and can only be matched once anyway. */
2504 for (i = gap->ga_len; --i >= 0; )
2505 if (((int *)(gap->ga_data))[i] == idx)
2506 return TRUE;
2507
2508 return FALSE;
2509}
2510
2511/*
2512 * Push the next match onto the stack.
2513 */
2514 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002515push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002516{
2517 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002518#ifdef FEAT_CONCEAL
2519 int save_flags;
2520#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002521
Bram Moolenaar860cae12010-06-05 23:22:07 +02002522 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523
2524 /*
2525 * Push the item in current_state stack;
2526 */
2527 if (push_current_state(next_match_idx) == OK)
2528 {
2529 /*
2530 * If it's a start-skip-end type that crosses lines, figure out how
2531 * much it continues in this line. Otherwise just fill in the length.
2532 */
2533 cur_si = &CUR_STATE(current_state.ga_len - 1);
2534 cur_si->si_h_startpos = next_match_h_startpos;
2535 cur_si->si_m_startcol = current_col;
2536 cur_si->si_m_lnum = current_lnum;
2537 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002538#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002539 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002540 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002541 if (current_state.ga_len > 1)
2542 cur_si->si_flags |=
2543 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2544#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545 cur_si->si_next_list = spp->sp_next_list;
2546 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2547 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2548 {
2549 /* Try to find the end pattern in the current line */
2550 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2551 check_keepend();
2552 }
2553 else
2554 {
2555 cur_si->si_m_endpos = next_match_m_endpos;
2556 cur_si->si_h_endpos = next_match_h_endpos;
2557 cur_si->si_ends = TRUE;
2558 cur_si->si_flags |= next_match_flags;
2559 cur_si->si_eoe_pos = next_match_eoe_pos;
2560 cur_si->si_end_idx = next_match_end_idx;
2561 }
2562 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2563 keepend_level = current_state.ga_len - 1;
2564 check_keepend();
2565 update_si_attr(current_state.ga_len - 1);
2566
Bram Moolenaar860cae12010-06-05 23:22:07 +02002567#ifdef FEAT_CONCEAL
2568 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2569#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 /*
2571 * If the start pattern has another highlight group, push another item
2572 * on the stack for the start pattern.
2573 */
2574 if ( spp->sp_type == SPTYPE_START
2575 && spp->sp_syn_match_id != 0
2576 && push_current_state(next_match_idx) == OK)
2577 {
2578 cur_si = &CUR_STATE(current_state.ga_len - 1);
2579 cur_si->si_h_startpos = next_match_h_startpos;
2580 cur_si->si_m_startcol = current_col;
2581 cur_si->si_m_lnum = current_lnum;
2582 cur_si->si_m_endpos = next_match_eos_pos;
2583 cur_si->si_h_endpos = next_match_eos_pos;
2584 cur_si->si_ends = TRUE;
2585 cur_si->si_end_idx = 0;
2586 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002587#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002588 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002589 cur_si->si_flags |= save_flags;
2590 if (cur_si->si_flags & HL_CONCEALENDS)
2591 cur_si->si_flags |= HL_CONCEAL;
2592#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593 cur_si->si_next_list = NULL;
2594 check_keepend();
2595 update_si_attr(current_state.ga_len - 1);
2596 }
2597 }
2598
2599 next_match_idx = -1; /* try other match next time */
2600
2601 return cur_si;
2602}
2603
2604/*
2605 * Check for end of current state (and the states before it).
2606 */
2607 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002608check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609{
2610 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002611 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002612
2613 cur_si = &CUR_STATE(current_state.ga_len - 1);
2614 for (;;)
2615 {
2616 if (cur_si->si_ends
2617 && (cur_si->si_m_endpos.lnum < current_lnum
2618 || (cur_si->si_m_endpos.lnum == current_lnum
2619 && cur_si->si_m_endpos.col <= current_col)))
2620 {
2621 /*
2622 * If there is an end pattern group ID, highlight the end pattern
2623 * now. No need to pop the current item from the stack.
2624 * Only do this if the end pattern continues beyond the current
2625 * position.
2626 */
2627 if (cur_si->si_end_idx
2628 && (cur_si->si_eoe_pos.lnum > current_lnum
2629 || (cur_si->si_eoe_pos.lnum == current_lnum
2630 && cur_si->si_eoe_pos.col > current_col)))
2631 {
2632 cur_si->si_idx = cur_si->si_end_idx;
2633 cur_si->si_end_idx = 0;
2634 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2635 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2636 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002637#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002638 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002639 if (cur_si->si_flags & HL_CONCEALENDS)
2640 cur_si->si_flags |= HL_CONCEAL;
2641#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002643
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002644 /* nextgroup= should not match in the end pattern */
2645 current_next_list = NULL;
2646
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002647 /* what matches next may be different now, clear it */
2648 next_match_idx = 0;
2649 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 break;
2651 }
2652 else
2653 {
2654 /* handle next_list, unless at end of line and no "skipnl" or
2655 * "skipempty" */
2656 current_next_list = cur_si->si_next_list;
2657 current_next_flags = cur_si->si_flags;
2658 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2659 && syn_getcurline()[current_col] == NUL)
2660 current_next_list = NULL;
2661
2662 /* When the ended item has "extend", another item with
2663 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002664 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665
2666 pop_current_state();
2667
2668 if (current_state.ga_len == 0)
2669 break;
2670
Bram Moolenaar81993f42008-01-11 20:27:45 +00002671 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 {
2673 syn_update_ends(FALSE);
2674 if (current_state.ga_len == 0)
2675 break;
2676 }
2677
2678 cur_si = &CUR_STATE(current_state.ga_len - 1);
2679
2680 /*
2681 * Only for a region the search for the end continues after
2682 * the end of the contained item. If the contained match
2683 * included the end-of-line, break here, the region continues.
2684 * Don't do this when:
2685 * - "keepend" is used for the contained item
2686 * - not at the end of the line (could be end="x$"me=e-1).
2687 * - "excludenl" is used (HL_HAS_EOL won't be set)
2688 */
2689 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002690 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 == SPTYPE_START
2692 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2693 {
2694 update_si_end(cur_si, (int)current_col, TRUE);
2695 check_keepend();
2696 if ((current_next_flags & HL_HAS_EOL)
2697 && keepend_level < 0
2698 && syn_getcurline()[current_col] == NUL)
2699 break;
2700 }
2701 }
2702 }
2703 else
2704 break;
2705 }
2706}
2707
2708/*
2709 * Update an entry in the current_state stack for a match or region. This
2710 * fills in si_attr, si_next_list and si_cont_list.
2711 */
2712 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002713update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714{
2715 stateitem_T *sip = &CUR_STATE(idx);
2716 synpat_T *spp;
2717
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002718 /* This should not happen... */
2719 if (sip->si_idx < 0)
2720 return;
2721
Bram Moolenaar860cae12010-06-05 23:22:07 +02002722 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 if (sip->si_flags & HL_MATCH)
2724 sip->si_id = spp->sp_syn_match_id;
2725 else
2726 sip->si_id = spp->sp_syn.id;
2727 sip->si_attr = syn_id2attr(sip->si_id);
2728 sip->si_trans_id = sip->si_id;
2729 if (sip->si_flags & HL_MATCH)
2730 sip->si_cont_list = NULL;
2731 else
2732 sip->si_cont_list = spp->sp_cont_list;
2733
2734 /*
2735 * For transparent items, take attr from outer item.
2736 * Also take cont_list, if there is none.
2737 * Don't do this for the matchgroup of a start or end pattern.
2738 */
2739 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2740 {
2741 if (idx == 0)
2742 {
2743 sip->si_attr = 0;
2744 sip->si_trans_id = 0;
2745 if (sip->si_cont_list == NULL)
2746 sip->si_cont_list = ID_LIST_ALL;
2747 }
2748 else
2749 {
2750 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2751 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002752 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2753 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754 if (sip->si_cont_list == NULL)
2755 {
2756 sip->si_flags |= HL_TRANS_CONT;
2757 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2758 }
2759 }
2760 }
2761}
2762
2763/*
2764 * Check the current stack for patterns with "keepend" flag.
2765 * Propagate the match-end to contained items, until a "skipend" item is found.
2766 */
2767 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002768check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769{
2770 int i;
2771 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002772 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002773 stateitem_T *sip;
2774
2775 /*
2776 * This check can consume a lot of time; only do it from the level where
2777 * there really is a keepend.
2778 */
2779 if (keepend_level < 0)
2780 return;
2781
2782 /*
2783 * Find the last index of an "extend" item. "keepend" items before that
2784 * won't do anything. If there is no "extend" item "i" will be
2785 * "keepend_level" and all "keepend" items will work normally.
2786 */
2787 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2788 if (CUR_STATE(i).si_flags & HL_EXTEND)
2789 break;
2790
2791 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002792 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002793 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002794 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 for ( ; i < current_state.ga_len; ++i)
2796 {
2797 sip = &CUR_STATE(i);
2798 if (maxpos.lnum != 0)
2799 {
2800 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002801 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002802 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2803 sip->si_ends = TRUE;
2804 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002805 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2806 {
2807 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808 || maxpos.lnum > sip->si_m_endpos.lnum
2809 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002810 && maxpos.col > sip->si_m_endpos.col))
2811 maxpos = sip->si_m_endpos;
2812 if (maxpos_h.lnum == 0
2813 || maxpos_h.lnum > sip->si_h_endpos.lnum
2814 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2815 && maxpos_h.col > sip->si_h_endpos.col))
2816 maxpos_h = sip->si_h_endpos;
2817 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 }
2819}
2820
2821/*
2822 * Update an entry in the current_state stack for a start-skip-end pattern.
2823 * This finds the end of the current item, if it's in the current line.
2824 *
2825 * Return the flags for the matched END.
2826 */
2827 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002828update_si_end(
2829 stateitem_T *sip,
2830 int startcol, /* where to start searching for the end */
2831 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002832{
2833 lpos_T startpos;
2834 lpos_T endpos;
2835 lpos_T hl_endpos;
2836 lpos_T end_endpos;
2837 int end_idx;
2838
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002839 /* return quickly for a keyword */
2840 if (sip->si_idx < 0)
2841 return;
2842
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 /* Don't update when it's already done. Can be a match of an end pattern
2844 * that started in a previous line. Watch out: can also be a "keepend"
2845 * from a containing item. */
2846 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2847 return;
2848
2849 /*
2850 * We need to find the end of the region. It may continue in the next
2851 * line.
2852 */
2853 end_idx = 0;
2854 startpos.lnum = current_lnum;
2855 startpos.col = startcol;
2856 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2857 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2858
2859 if (endpos.lnum == 0)
2860 {
2861 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002862 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863 {
2864 /* a "oneline" never continues in the next line */
2865 sip->si_ends = TRUE;
2866 sip->si_m_endpos.lnum = current_lnum;
2867 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2868 }
2869 else
2870 {
2871 /* continues in the next line */
2872 sip->si_ends = FALSE;
2873 sip->si_m_endpos.lnum = 0;
2874 }
2875 sip->si_h_endpos = sip->si_m_endpos;
2876 }
2877 else
2878 {
2879 /* match within this line */
2880 sip->si_m_endpos = endpos;
2881 sip->si_h_endpos = hl_endpos;
2882 sip->si_eoe_pos = end_endpos;
2883 sip->si_ends = TRUE;
2884 sip->si_end_idx = end_idx;
2885 }
2886}
2887
2888/*
2889 * Add a new state to the current state stack.
2890 * It is cleared and the index set to "idx".
2891 * Return FAIL if it's not possible (out of memory).
2892 */
2893 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002894push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895{
2896 if (ga_grow(&current_state, 1) == FAIL)
2897 return FAIL;
2898 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2899 CUR_STATE(current_state.ga_len).si_idx = idx;
2900 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 return OK;
2902}
2903
2904/*
2905 * Remove a state from the current_state stack.
2906 */
2907 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002908pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909{
2910 if (current_state.ga_len)
2911 {
2912 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2913 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914 }
2915 /* after the end of a pattern, try matching a keyword or pattern */
2916 next_match_idx = -1;
2917
2918 /* if first state with "keepend" is popped, reset keepend_level */
2919 if (keepend_level >= current_state.ga_len)
2920 keepend_level = -1;
2921}
2922
2923/*
2924 * Find the end of a start/skip/end syntax region after "startpos".
2925 * Only checks one line.
2926 * Also handles a match item that continued from a previous line.
2927 * If not found, the syntax item continues in the next line. m_endpos->lnum
2928 * will be 0.
2929 * If found, the end of the region and the end of the highlighting is
2930 * computed.
2931 */
2932 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002933find_endpos(
2934 int idx, /* index of the pattern */
2935 lpos_T *startpos, /* where to start looking for an END match */
2936 lpos_T *m_endpos, /* return: end of match */
2937 lpos_T *hl_endpos, /* return: end of highlighting */
2938 long *flagsp, /* return: flags of matching END */
2939 lpos_T *end_endpos, /* return: end of end pattern match */
2940 int *end_idx, /* return: group ID for end pat. match, or 0 */
2941 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942{
2943 colnr_T matchcol;
2944 synpat_T *spp, *spp_skip;
2945 int start_idx;
2946 int best_idx;
2947 regmmatch_T regmatch;
2948 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2949 lpos_T pos;
2950 char_u *line;
2951 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002952 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002954 /* just in case we are invoked for a keyword */
2955 if (idx < 0)
2956 return;
2957
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958 /*
2959 * Check for being called with a START pattern.
2960 * Can happen with a match that continues to the next line, because it
2961 * contained a region.
2962 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002963 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 if (spp->sp_type != SPTYPE_START)
2965 {
2966 *hl_endpos = *startpos;
2967 return;
2968 }
2969
2970 /*
2971 * Find the SKIP or first END pattern after the last START pattern.
2972 */
2973 for (;;)
2974 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002975 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 if (spp->sp_type != SPTYPE_START)
2977 break;
2978 ++idx;
2979 }
2980
2981 /*
2982 * Lookup the SKIP pattern (if present)
2983 */
2984 if (spp->sp_type == SPTYPE_SKIP)
2985 {
2986 spp_skip = spp;
2987 ++idx;
2988 }
2989 else
2990 spp_skip = NULL;
2991
2992 /* Setup external matches for syn_regexec(). */
2993 unref_extmatch(re_extmatch_in);
2994 re_extmatch_in = ref_extmatch(start_ext);
2995
2996 matchcol = startpos->col; /* start looking for a match at sstart */
2997 start_idx = idx; /* remember the first END pattern. */
2998 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002999
3000 /* use syntax iskeyword option */
3001 save_chartab(buf_chartab);
3002
Bram Moolenaar071d4272004-06-13 20:20:40 +00003003 for (;;)
3004 {
3005 /*
3006 * Find end pattern that matches first after "matchcol".
3007 */
3008 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003009 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010 {
3011 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003012 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013
Bram Moolenaar860cae12010-06-05 23:22:07 +02003014 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3016 break;
3017 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3018 if (lc_col < 0)
3019 lc_col = 0;
3020
3021 regmatch.rmm_ic = spp->sp_ic;
3022 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003023 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3024 IF_SYN_TIME(&spp->sp_time));
3025 spp->sp_prog = regmatch.regprog;
3026 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027 {
3028 if (best_idx == -1 || regmatch.startpos[0].col
3029 < best_regmatch.startpos[0].col)
3030 {
3031 best_idx = idx;
3032 best_regmatch.startpos[0] = regmatch.startpos[0];
3033 best_regmatch.endpos[0] = regmatch.endpos[0];
3034 }
3035 }
3036 }
3037
3038 /*
3039 * If all end patterns have been tried, and there is no match, the
3040 * item continues until end-of-line.
3041 */
3042 if (best_idx == -1)
3043 break;
3044
3045 /*
3046 * If the skip pattern matches before the end pattern,
3047 * continue searching after the skip pattern.
3048 */
3049 if (spp_skip != NULL)
3050 {
3051 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003052 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053
3054 if (lc_col < 0)
3055 lc_col = 0;
3056 regmatch.rmm_ic = spp_skip->sp_ic;
3057 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003058 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3059 IF_SYN_TIME(&spp_skip->sp_time));
3060 spp_skip->sp_prog = regmatch.regprog;
3061 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062 <= best_regmatch.startpos[0].col)
3063 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003064 int line_len;
3065
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 /* Add offset to skip pattern match */
3067 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3068
3069 /* If the skip pattern goes on to the next line, there is no
3070 * match with an end pattern in this line. */
3071 if (pos.lnum > startpos->lnum)
3072 break;
3073
3074 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003075 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003076
3077 /* take care of an empty match or negative offset */
3078 if (pos.col <= matchcol)
3079 ++matchcol;
3080 else if (pos.col <= regmatch.endpos[0].col)
3081 matchcol = pos.col;
3082 else
3083 /* Be careful not to jump over the NUL at the end-of-line */
3084 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003085 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003086 ++matchcol)
3087 ;
3088
3089 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003090 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091 break;
3092
3093 continue; /* start with first end pattern again */
3094 }
3095 }
3096
3097 /*
3098 * Match from start pattern to end pattern.
3099 * Correct for match and highlight offset of end pattern.
3100 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003101 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003102 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3103 /* can't end before the start */
3104 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3105 m_endpos->col = startpos->col;
3106
3107 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3108 /* can't end before the start */
3109 if (end_endpos->lnum == startpos->lnum
3110 && end_endpos->col < startpos->col)
3111 end_endpos->col = startpos->col;
3112 /* can't end after the match */
3113 limit_pos(end_endpos, m_endpos);
3114
3115 /*
3116 * If the end group is highlighted differently, adjust the pointers.
3117 */
3118 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3119 {
3120 *end_idx = best_idx;
3121 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3122 {
3123 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3124 hl_endpos->col = best_regmatch.endpos[0].col;
3125 }
3126 else
3127 {
3128 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3129 hl_endpos->col = best_regmatch.startpos[0].col;
3130 }
3131 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3132
3133 /* can't end before the start */
3134 if (hl_endpos->lnum == startpos->lnum
3135 && hl_endpos->col < startpos->col)
3136 hl_endpos->col = startpos->col;
3137 limit_pos(hl_endpos, m_endpos);
3138
3139 /* now the match ends where the highlighting ends, it is turned
3140 * into the matchgroup for the end */
3141 *m_endpos = *hl_endpos;
3142 }
3143 else
3144 {
3145 *end_idx = 0;
3146 *hl_endpos = *end_endpos;
3147 }
3148
3149 *flagsp = spp->sp_flags;
3150
3151 had_match = TRUE;
3152 break;
3153 }
3154
3155 /* no match for an END pattern in this line */
3156 if (!had_match)
3157 m_endpos->lnum = 0;
3158
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003159 restore_chartab(buf_chartab);
3160
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161 /* Remove external matches. */
3162 unref_extmatch(re_extmatch_in);
3163 re_extmatch_in = NULL;
3164}
3165
3166/*
3167 * Limit "pos" not to be after "limit".
3168 */
3169 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003170limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171{
3172 if (pos->lnum > limit->lnum)
3173 *pos = *limit;
3174 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3175 pos->col = limit->col;
3176}
3177
3178/*
3179 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3180 */
3181 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003182limit_pos_zero(
3183 lpos_T *pos,
3184 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185{
3186 if (pos->lnum == 0)
3187 *pos = *limit;
3188 else
3189 limit_pos(pos, limit);
3190}
3191
3192/*
3193 * Add offset to matched text for end of match or highlight.
3194 */
3195 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003196syn_add_end_off(
3197 lpos_T *result, /* returned position */
3198 regmmatch_T *regmatch, /* start/end of match */
3199 synpat_T *spp, /* matched pattern */
3200 int idx, /* index of offset */
3201 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202{
3203 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003204 int off;
3205 char_u *base;
3206 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207
3208 if (spp->sp_off_flags & (1 << idx))
3209 {
3210 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003211 col = regmatch->startpos[0].col;
3212 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213 }
3214 else
3215 {
3216 result->lnum = regmatch->endpos[0].lnum;
3217 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003218 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3221 * is a matchgroup. Watch out for match with last NL in the buffer. */
3222 if (result->lnum > syn_buf->b_ml.ml_line_count)
3223 col = 0;
3224 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003225 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003226 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3227 p = base + col;
3228 if (off > 0)
3229 {
3230 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003231 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003232 }
3233 else if (off < 0)
3234 {
3235 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003236 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003237 }
3238 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003239 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003240 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241}
3242
3243/*
3244 * Add offset to matched text for start of match or highlight.
3245 * Avoid resulting column to become negative.
3246 */
3247 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003248syn_add_start_off(
3249 lpos_T *result, /* returned position */
3250 regmmatch_T *regmatch, /* start/end of match */
3251 synpat_T *spp,
3252 int idx,
3253 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254{
3255 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003256 int off;
3257 char_u *base;
3258 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259
3260 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3261 {
3262 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003263 col = regmatch->endpos[0].col;
3264 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265 }
3266 else
3267 {
3268 result->lnum = regmatch->startpos[0].lnum;
3269 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003270 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003272 if (result->lnum > syn_buf->b_ml.ml_line_count)
3273 {
3274 /* a "\n" at the end of the pattern may take us below the last line */
3275 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003276 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003277 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003278 if (off != 0)
3279 {
3280 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3281 p = base + col;
3282 if (off > 0)
3283 {
3284 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003285 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003286 }
3287 else if (off < 0)
3288 {
3289 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003290 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003291 }
3292 col = (int)(p - base);
3293 }
3294 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295}
3296
3297/*
3298 * Get current line in syntax buffer.
3299 */
3300 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003301syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302{
3303 return ml_get_buf(syn_buf, current_lnum, FALSE);
3304}
3305
3306/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003307 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308 * Returns TRUE when there is a match.
3309 */
3310 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003311syn_regexec(
3312 regmmatch_T *rmp,
3313 linenr_T lnum,
3314 colnr_T col,
3315 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003317 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003318#ifdef FEAT_RELTIME
3319 int timed_out = FALSE;
3320#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003321#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003322 proftime_T pt;
3323
3324 if (syn_time_on)
3325 profile_start(&pt);
3326#endif
3327
Bram Moolenaarbcf94422018-06-23 14:21:42 +02003328 if (rmp->regprog == NULL)
3329 // This can happen if a previous call to vim_regexec_multi() tried to
3330 // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and
3331 // compiling the pattern with the other engine fails.
3332 return FALSE;
3333
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003334 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003335 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3336#ifdef FEAT_RELTIME
3337 syn_tm, &timed_out
3338#else
3339 NULL, NULL
3340#endif
3341 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003342
Bram Moolenaarf7512552013-06-06 14:55:19 +02003343#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003344 if (syn_time_on)
3345 {
3346 profile_end(&pt);
3347 profile_add(&st->total, &pt);
3348 if (profile_cmp(&pt, &st->slowest) < 0)
3349 st->slowest = pt;
3350 ++st->count;
3351 if (r > 0)
3352 ++st->match;
3353 }
3354#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003355#ifdef FEAT_RELTIME
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003356 if (timed_out && !syn_win->w_s->b_syn_slow)
3357 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003358 syn_win->w_s->b_syn_slow = TRUE;
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003359 MSG(_("'redrawtime' exceeded, syntax highlighting disabled"));
3360 }
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003361#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003362
3363 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364 {
3365 rmp->startpos[0].lnum += lnum;
3366 rmp->endpos[0].lnum += lnum;
3367 return TRUE;
3368 }
3369 return FALSE;
3370}
3371
3372/*
3373 * Check one position in a line for a matching keyword.
3374 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003375 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376 */
3377 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003378check_keyword_id(
3379 char_u *line,
3380 int startcol, /* position in line to check for keyword */
3381 int *endcolp, /* return: character after found keyword */
3382 long *flagsp, /* return: flags of matching keyword */
3383 short **next_listp, /* return: next_list of matching keyword */
3384 stateitem_T *cur_si, /* item at the top of the stack */
3385 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003387 keyentry_T *kp;
3388 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003390 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 hashtab_T *ht;
3393 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394
3395 /* Find first character after the keyword. First character was already
3396 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003397 kwp = line + startcol;
3398 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399 do
3400 {
3401#ifdef FEAT_MBYTE
3402 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003403 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404 else
3405#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003406 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003408 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409
Bram Moolenaardad6b692005-01-25 22:14:34 +00003410 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 return 0;
3412
3413 /*
3414 * Must make a copy of the keyword, so we can add a NUL and make it
3415 * lowercase.
3416 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003417 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003418
3419 /*
3420 * Try twice:
3421 * 1. matching case
3422 * 2. ignoring case
3423 */
3424 for (round = 1; round <= 2; ++round)
3425 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003426 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003427 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003429 if (round == 2) /* ignore case */
3430 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003431
3432 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003433 * Find keywords that match. There can be several with different
3434 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003435 * When current_next_list is non-zero accept only that group, otherwise:
3436 * Accept a not-contained keyword at toplevel.
3437 * Accept a keyword at other levels only if it is in the contains list.
3438 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003439 hi = hash_find(ht, keyword);
3440 if (!HASHITEM_EMPTY(hi))
3441 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003442 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003443 if (current_next_list != 0
3444 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3445 : (cur_si == NULL
3446 ? !(kp->flags & HL_CONTAINED)
3447 : in_id_list(cur_si, cur_si->si_cont_list,
3448 &kp->k_syn, kp->flags & HL_CONTAINED)))
3449 {
3450 *endcolp = startcol + kwlen;
3451 *flagsp = kp->flags;
3452 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003453#ifdef FEAT_CONCEAL
3454 *ccharp = kp->k_char;
3455#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003456 return kp->k_syn.id;
3457 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458 }
3459 }
3460 return 0;
3461}
3462
3463/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003464 * Handle ":syntax conceal" command.
3465 */
3466 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003467syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468{
3469#ifdef FEAT_CONCEAL
3470 char_u *arg = eap->arg;
3471 char_u *next;
3472
3473 eap->nextcmd = find_nextcmd(arg);
3474 if (eap->skip)
3475 return;
3476
3477 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003478 if (*arg == NUL)
3479 {
3480 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003481 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003482 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003483 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003484 }
3485 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003486 curwin->w_s->b_syn_conceal = TRUE;
3487 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3488 curwin->w_s->b_syn_conceal = FALSE;
3489 else
3490 EMSG2(_("E390: Illegal argument: %s"), arg);
3491#endif
3492}
3493
3494/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003495 * Handle ":syntax case" command.
3496 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003498syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499{
3500 char_u *arg = eap->arg;
3501 char_u *next;
3502
3503 eap->nextcmd = find_nextcmd(arg);
3504 if (eap->skip)
3505 return;
3506
3507 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003508 if (*arg == NUL)
3509 {
3510 if (curwin->w_s->b_syn_ic)
3511 MSG(_("syntax case ignore"));
3512 else
3513 MSG(_("syntax case match"));
3514 }
3515 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003516 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003518 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519 else
3520 EMSG2(_("E390: Illegal argument: %s"), arg);
3521}
3522
3523/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003524 * Handle ":syntax spell" command.
3525 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003526 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003527syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003528{
3529 char_u *arg = eap->arg;
3530 char_u *next;
3531
3532 eap->nextcmd = find_nextcmd(arg);
3533 if (eap->skip)
3534 return;
3535
3536 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003537 if (*arg == NUL)
3538 {
3539 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3540 MSG(_("syntax spell toplevel"));
3541 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3542 MSG(_("syntax spell notoplevel"));
3543 else
3544 MSG(_("syntax spell default"));
3545 }
3546 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003547 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003548 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003549 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003550 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003552 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003553 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003554 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003555 return;
3556 }
3557
3558 /* assume spell checking changed, force a redraw */
3559 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003560}
3561
3562/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003563 * Handle ":syntax iskeyword" command.
3564 */
3565 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003566syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003567{
3568 char_u *arg = eap->arg;
3569 char_u save_chartab[32];
3570 char_u *save_isk;
3571
3572 if (eap->skip)
3573 return;
3574
3575 arg = skipwhite(arg);
3576 if (*arg == NUL)
3577 {
3578 MSG_PUTS("\n");
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003579 if (curwin->w_s->b_syn_isk != empty_option)
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003580 {
3581 MSG_PUTS(_("syntax iskeyword "));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003582 msg_outtrans(curwin->w_s->b_syn_isk);
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003583 }
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003584 else
Bram Moolenaar0a6efcd2018-07-20 19:56:10 +02003585 msg_outtrans((char_u *)_("syntax iskeyword not set"));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003586 }
3587 else
3588 {
3589 if (STRNICMP(arg, "clear", 5) == 0)
3590 {
3591 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3592 (size_t)32);
3593 clear_string_option(&curwin->w_s->b_syn_isk);
3594 }
3595 else
3596 {
3597 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3598 save_isk = curbuf->b_p_isk;
3599 curbuf->b_p_isk = vim_strsave(arg);
3600
3601 buf_init_chartab(curbuf, FALSE);
3602 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3603 (size_t)32);
3604 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3605 clear_string_option(&curwin->w_s->b_syn_isk);
3606 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3607 curbuf->b_p_isk = save_isk;
3608 }
3609 }
3610 redraw_win_later(curwin, NOT_VALID);
3611}
3612
3613/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 * Clear all syntax info for one buffer.
3615 */
3616 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003617syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618{
3619 int i;
3620
Bram Moolenaar860cae12010-06-05 23:22:07 +02003621 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003622#ifdef FEAT_RELTIME
3623 block->b_syn_slow = FALSE; /* clear previous timeout */
3624#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003625 block->b_syn_ic = FALSE; /* Use case, by default */
3626 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3627 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003628#ifdef FEAT_CONCEAL
3629 block->b_syn_conceal = FALSE;
3630#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631
3632 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003633 clear_keywtab(&block->b_keywtab);
3634 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635
3636 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003637 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3638 syn_clear_pattern(block, i);
3639 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640
3641 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003642 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3643 syn_clear_cluster(block, i);
3644 ga_clear(&block->b_syn_clusters);
3645 block->b_spell_cluster_id = 0;
3646 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 block->b_syn_sync_flags = 0;
3649 block->b_syn_sync_minlines = 0;
3650 block->b_syn_sync_maxlines = 0;
3651 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652
Bram Moolenaar473de612013-06-08 18:19:48 +02003653 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003654 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003655 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003657 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003659 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660
3661 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003662 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003664
3665 /* Reset the counter for ":syn include" */
3666 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667}
3668
3669/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003670 * Get rid of ownsyntax for window "wp".
3671 */
3672 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003673reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003674{
3675 if (wp->w_s != &wp->w_buffer->b_s)
3676 {
3677 syntax_clear(wp->w_s);
3678 vim_free(wp->w_s);
3679 wp->w_s = &wp->w_buffer->b_s;
3680 }
3681}
3682
3683/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684 * Clear syncing info for one buffer.
3685 */
3686 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003687syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688{
3689 int i;
3690
3691 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3693 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3694 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695
Bram Moolenaar860cae12010-06-05 23:22:07 +02003696 curwin->w_s->b_syn_sync_flags = 0;
3697 curwin->w_s->b_syn_sync_minlines = 0;
3698 curwin->w_s->b_syn_sync_maxlines = 0;
3699 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700
Bram Moolenaar473de612013-06-08 18:19:48 +02003701 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003702 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003703 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003704 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003706 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707}
3708
3709/*
3710 * Remove one pattern from the buffer's pattern list.
3711 */
3712 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003713syn_remove_pattern(
3714 synblock_T *block,
3715 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716{
3717 synpat_T *spp;
3718
Bram Moolenaar860cae12010-06-05 23:22:07 +02003719 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720#ifdef FEAT_FOLDING
3721 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003722 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003724 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003726 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3727 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003728}
3729
3730/*
3731 * Clear and free one syntax pattern. When clearing all, must be called from
3732 * last to first!
3733 */
3734 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003735syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003737 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003738 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003740 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003742 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3743 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3744 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 }
3746}
3747
3748/*
3749 * Clear and free one syntax cluster.
3750 */
3751 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003752syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003754 vim_free(SYN_CLSTR(block)[i].scl_name);
3755 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3756 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003757}
3758
3759/*
3760 * Handle ":syntax clear" command.
3761 */
3762 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003763syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003764{
3765 char_u *arg = eap->arg;
3766 char_u *arg_end;
3767 int id;
3768
3769 eap->nextcmd = find_nextcmd(arg);
3770 if (eap->skip)
3771 return;
3772
3773 /*
3774 * We have to disable this within ":syn include @group filename",
3775 * because otherwise @group would get deleted.
3776 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3777 * clear".
3778 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003779 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 return;
3781
3782 if (ends_excmd(*arg))
3783 {
3784 /*
3785 * No argument: Clear all syntax items.
3786 */
3787 if (syncing)
3788 syntax_sync_clear();
3789 else
3790 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003791 syntax_clear(curwin->w_s);
3792 if (curwin->w_s == &curwin->w_buffer->b_s)
3793 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003794 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003795 }
3796 }
3797 else
3798 {
3799 /*
3800 * Clear the group IDs that are in the argument.
3801 */
3802 while (!ends_excmd(*arg))
3803 {
3804 arg_end = skiptowhite(arg);
3805 if (*arg == '@')
3806 {
3807 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3808 if (id == 0)
3809 {
3810 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3811 break;
3812 }
3813 else
3814 {
3815 /*
3816 * We can't physically delete a cluster without changing
3817 * the IDs of other clusters, so we do the next best thing
3818 * and make it empty.
3819 */
3820 short scl_id = id - SYNID_CLUSTER;
3821
Bram Moolenaard23a8232018-02-10 18:45:26 +01003822 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 }
3824 }
3825 else
3826 {
3827 id = syn_namen2id(arg, (int)(arg_end - arg));
3828 if (id == 0)
3829 {
3830 EMSG2(_(e_nogroup), arg);
3831 break;
3832 }
3833 else
3834 syn_clear_one(id, syncing);
3835 }
3836 arg = skipwhite(arg_end);
3837 }
3838 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003839 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003840 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841}
3842
3843/*
3844 * Clear one syntax group for the current buffer.
3845 */
3846 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003847syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848{
3849 synpat_T *spp;
3850 int idx;
3851
3852 /* Clear keywords only when not ":syn sync clear group-name" */
3853 if (!syncing)
3854 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003855 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3856 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 }
3858
3859 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003860 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003861 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003862 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3864 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003865 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 }
3867}
3868
3869/*
3870 * Handle ":syntax on" command.
3871 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003872 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003873syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874{
3875 syn_cmd_onoff(eap, "syntax");
3876}
3877
3878/*
3879 * Handle ":syntax enable" command.
3880 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003882syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883{
3884 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3885 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003886 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003887}
3888
3889/*
3890 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003891 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003894syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895{
3896 eap->nextcmd = check_nextcmd(eap->arg);
3897 if (!eap->skip)
3898 {
3899 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3900 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003901 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 }
3903}
3904
3905/*
3906 * Handle ":syntax manual" command.
3907 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003909syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910{
3911 syn_cmd_onoff(eap, "manual");
3912}
3913
3914/*
3915 * Handle ":syntax off" command.
3916 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003918syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919{
3920 syn_cmd_onoff(eap, "nosyntax");
3921}
3922
3923 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003924syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925{
3926 char_u buf[100];
3927
3928 eap->nextcmd = check_nextcmd(eap->arg);
3929 if (!eap->skip)
3930 {
3931 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003932 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 do_cmdline_cmd(buf);
3934 }
3935}
3936
3937/*
3938 * Handle ":syntax [list]" command: list current syntax words.
3939 */
3940 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003941syn_cmd_list(
3942 exarg_T *eap,
3943 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944{
3945 char_u *arg = eap->arg;
3946 int id;
3947 char_u *arg_end;
3948
3949 eap->nextcmd = find_nextcmd(arg);
3950 if (eap->skip)
3951 return;
3952
Bram Moolenaar860cae12010-06-05 23:22:07 +02003953 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003955 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 return;
3957 }
3958
3959 if (syncing)
3960 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003961 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 {
3963 MSG_PUTS(_("syncing on C-style comments"));
3964 syn_lines_msg();
3965 syn_match_msg();
3966 return;
3967 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003968 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003969 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003970 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003971 MSG_PUTS(_("no syncing"));
3972 else
3973 {
3974 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003975 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 MSG_PUTS(_(" lines before top line"));
3977 syn_match_msg();
3978 }
3979 return;
3980 }
3981 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003982 if (curwin->w_s->b_syn_sync_minlines > 0
3983 || curwin->w_s->b_syn_sync_maxlines > 0
3984 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985 {
3986 MSG_PUTS(_("\nsyncing on items"));
3987 syn_lines_msg();
3988 syn_match_msg();
3989 }
3990 }
3991 else
3992 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3993 if (ends_excmd(*arg))
3994 {
3995 /*
3996 * No argument: List all group IDs and all syntax clusters.
3997 */
3998 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3999 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004000 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 syn_list_cluster(id);
4002 }
4003 else
4004 {
4005 /*
4006 * List the group IDs and syntax clusters that are in the argument.
4007 */
4008 while (!ends_excmd(*arg) && !got_int)
4009 {
4010 arg_end = skiptowhite(arg);
4011 if (*arg == '@')
4012 {
4013 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
4014 if (id == 0)
4015 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4016 else
4017 syn_list_cluster(id - SYNID_CLUSTER);
4018 }
4019 else
4020 {
4021 id = syn_namen2id(arg, (int)(arg_end - arg));
4022 if (id == 0)
4023 EMSG2(_(e_nogroup), arg);
4024 else
4025 syn_list_one(id, syncing, TRUE);
4026 }
4027 arg = skipwhite(arg_end);
4028 }
4029 }
4030 eap->nextcmd = check_nextcmd(arg);
4031}
4032
4033 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004034syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004036 if (curwin->w_s->b_syn_sync_maxlines > 0
4037 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 {
4039 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004040 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041 {
4042 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004043 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4044 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004045 MSG_PUTS(", ");
4046 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004047 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048 {
4049 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004050 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 }
4052 MSG_PUTS(_(" lines before top line"));
4053 }
4054}
4055
4056 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004057syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004058{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004059 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060 {
4061 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004062 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063 MSG_PUTS(_(" line breaks"));
4064 }
4065}
4066
4067static int last_matchgroup;
4068
4069struct name_list
4070{
4071 int flag;
4072 char *name;
4073};
4074
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004075static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004076
4077/*
4078 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4079 */
4080 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004081syn_list_one(
4082 int id,
4083 int syncing, /* when TRUE: list syncing items */
4084 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085{
4086 int attr;
4087 int idx;
4088 int did_header = FALSE;
4089 synpat_T *spp;
4090 static struct name_list namelist1[] =
4091 {
4092 {HL_DISPLAY, "display"},
4093 {HL_CONTAINED, "contained"},
4094 {HL_ONELINE, "oneline"},
4095 {HL_KEEPEND, "keepend"},
4096 {HL_EXTEND, "extend"},
4097 {HL_EXCLUDENL, "excludenl"},
4098 {HL_TRANSP, "transparent"},
4099 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004100#ifdef FEAT_CONCEAL
4101 {HL_CONCEAL, "conceal"},
4102 {HL_CONCEALENDS, "concealends"},
4103#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104 {0, NULL}
4105 };
4106 static struct name_list namelist2[] =
4107 {
4108 {HL_SKIPWHITE, "skipwhite"},
4109 {HL_SKIPNL, "skipnl"},
4110 {HL_SKIPEMPTY, "skipempty"},
4111 {0, NULL}
4112 };
4113
Bram Moolenaar8820b482017-03-16 17:23:31 +01004114 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004115
4116 /* list the keywords for "id" */
4117 if (!syncing)
4118 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004119 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4120 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121 did_header, attr);
4122 }
4123
4124 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004125 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004127 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4129 continue;
4130
4131 (void)syn_list_header(did_header, 999, id);
4132 did_header = TRUE;
4133 last_matchgroup = 0;
4134 if (spp->sp_type == SPTYPE_MATCH)
4135 {
4136 put_pattern("match", ' ', spp, attr);
4137 msg_putchar(' ');
4138 }
4139 else if (spp->sp_type == SPTYPE_START)
4140 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004141 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4142 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4143 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4144 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4145 while (idx < curwin->w_s->b_syn_patterns.ga_len
4146 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4147 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 --idx;
4149 msg_putchar(' ');
4150 }
4151 syn_list_flags(namelist1, spp->sp_flags, attr);
4152
4153 if (spp->sp_cont_list != NULL)
4154 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4155
4156 if (spp->sp_syn.cont_in_list != NULL)
4157 put_id_list((char_u *)"containedin",
4158 spp->sp_syn.cont_in_list, attr);
4159
4160 if (spp->sp_next_list != NULL)
4161 {
4162 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4163 syn_list_flags(namelist2, spp->sp_flags, attr);
4164 }
4165 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4166 {
4167 if (spp->sp_flags & HL_SYNC_HERE)
4168 msg_puts_attr((char_u *)"grouphere", attr);
4169 else
4170 msg_puts_attr((char_u *)"groupthere", attr);
4171 msg_putchar(' ');
4172 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004173 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4175 else
4176 MSG_PUTS("NONE");
4177 msg_putchar(' ');
4178 }
4179 }
4180
4181 /* list the link, if there is one */
4182 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4183 {
4184 (void)syn_list_header(did_header, 999, id);
4185 msg_puts_attr((char_u *)"links to", attr);
4186 msg_putchar(' ');
4187 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4188 }
4189}
4190
4191 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004192syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193{
4194 int i;
4195
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004196 for (i = 0; nlist[i].flag != 0; ++i)
4197 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004199 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200 msg_putchar(' ');
4201 }
4202}
4203
4204/*
4205 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4206 */
4207 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004208syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209{
4210 int endcol = 15;
4211
4212 /* slight hack: roughly duplicate the guts of syn_list_header() */
4213 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004214 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215
4216 if (msg_col >= endcol) /* output at least one space */
4217 endcol = msg_col + 1;
4218 if (Columns <= endcol) /* avoid hang for tiny window */
4219 endcol = Columns - 1;
4220
4221 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004222 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004224 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004225 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 }
4227 else
4228 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004229 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230 msg_puts((char_u *)"=NONE");
4231 }
4232}
4233
4234 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004235put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236{
4237 short *p;
4238
4239 msg_puts_attr(name, attr);
4240 msg_putchar('=');
4241 for (p = list; *p; ++p)
4242 {
4243 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4244 {
4245 if (p[1])
4246 MSG_PUTS("ALLBUT");
4247 else
4248 MSG_PUTS("ALL");
4249 }
4250 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4251 {
4252 MSG_PUTS("TOP");
4253 }
4254 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4255 {
4256 MSG_PUTS("CONTAINED");
4257 }
4258 else if (*p >= SYNID_CLUSTER)
4259 {
4260 short scl_id = *p - SYNID_CLUSTER;
4261
4262 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004263 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 }
4265 else
4266 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4267 if (p[1])
4268 msg_putchar(',');
4269 }
4270 msg_putchar(' ');
4271}
4272
4273 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004274put_pattern(
4275 char *s,
4276 int c,
4277 synpat_T *spp,
4278 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279{
4280 long n;
4281 int mask;
4282 int first;
4283 static char *sepchars = "/+=-#@\"|'^&";
4284 int i;
4285
4286 /* May have to write "matchgroup=group" */
4287 if (last_matchgroup != spp->sp_syn_match_id)
4288 {
4289 last_matchgroup = spp->sp_syn_match_id;
4290 msg_puts_attr((char_u *)"matchgroup", attr);
4291 msg_putchar('=');
4292 if (last_matchgroup == 0)
4293 msg_outtrans((char_u *)"NONE");
4294 else
4295 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4296 msg_putchar(' ');
4297 }
4298
4299 /* output the name of the pattern and an '=' or ' ' */
4300 msg_puts_attr((char_u *)s, attr);
4301 msg_putchar(c);
4302
4303 /* output the pattern, in between a char that is not in the pattern */
4304 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4305 if (sepchars[++i] == NUL)
4306 {
4307 i = 0; /* no good char found, just use the first one */
4308 break;
4309 }
4310 msg_putchar(sepchars[i]);
4311 msg_outtrans(spp->sp_pattern);
4312 msg_putchar(sepchars[i]);
4313
4314 /* output any pattern options */
4315 first = TRUE;
4316 for (i = 0; i < SPO_COUNT; ++i)
4317 {
4318 mask = (1 << i);
4319 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4320 {
4321 if (!first)
4322 msg_putchar(','); /* separate with commas */
4323 msg_puts((char_u *)spo_name_tab[i]);
4324 n = spp->sp_offsets[i];
4325 if (i != SPO_LC_OFF)
4326 {
4327 if (spp->sp_off_flags & mask)
4328 msg_putchar('s');
4329 else
4330 msg_putchar('e');
4331 if (n > 0)
4332 msg_putchar('+');
4333 }
4334 if (n || i == SPO_LC_OFF)
4335 msg_outnum(n);
4336 first = FALSE;
4337 }
4338 }
4339 msg_putchar(' ');
4340}
4341
4342/*
4343 * List or clear the keywords for one syntax group.
4344 * Return TRUE if the header has been printed.
4345 */
4346 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004347syn_list_keywords(
4348 int id,
4349 hashtab_T *ht,
4350 int did_header, /* header has already been printed */
4351 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 hashitem_T *hi;
4355 keyentry_T *kp;
4356 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357 int prev_contained = 0;
4358 short *prev_next_list = NULL;
4359 short *prev_cont_in_list = NULL;
4360 int prev_skipnl = 0;
4361 int prev_skipwhite = 0;
4362 int prev_skipempty = 0;
4363
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 /*
4365 * Unfortunately, this list of keywords is not sorted on alphabet but on
4366 * hash value...
4367 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004368 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004370 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004371 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 --todo;
4374 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004376 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 if (prev_contained != (kp->flags & HL_CONTAINED)
4379 || prev_skipnl != (kp->flags & HL_SKIPNL)
4380 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4381 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4382 || prev_cont_in_list != kp->k_syn.cont_in_list
4383 || prev_next_list != kp->next_list)
4384 outlen = 9999;
4385 else
4386 outlen = (int)STRLEN(kp->keyword);
4387 /* output "contained" and "nextgroup" on each line */
4388 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 prev_contained = 0;
4391 prev_next_list = NULL;
4392 prev_cont_in_list = NULL;
4393 prev_skipnl = 0;
4394 prev_skipwhite = 0;
4395 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004397 did_header = TRUE;
4398 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004400 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004402 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004406 put_id_list((char_u *)"containedin",
4407 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004409 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004411 if (kp->next_list != prev_next_list)
4412 {
4413 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4414 msg_putchar(' ');
4415 prev_next_list = kp->next_list;
4416 if (kp->flags & HL_SKIPNL)
4417 {
4418 msg_puts_attr((char_u *)"skipnl", attr);
4419 msg_putchar(' ');
4420 prev_skipnl = (kp->flags & HL_SKIPNL);
4421 }
4422 if (kp->flags & HL_SKIPWHITE)
4423 {
4424 msg_puts_attr((char_u *)"skipwhite", attr);
4425 msg_putchar(' ');
4426 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4427 }
4428 if (kp->flags & HL_SKIPEMPTY)
4429 {
4430 msg_puts_attr((char_u *)"skipempty", attr);
4431 msg_putchar(' ');
4432 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4433 }
4434 }
4435 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 }
4438 }
4439 }
4440
4441 return did_header;
4442}
4443
4444 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004445syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 hashitem_T *hi;
4448 keyentry_T *kp;
4449 keyentry_T *kp_prev;
4450 keyentry_T *kp_next;
4451 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452
Bram Moolenaardad6b692005-01-25 22:14:34 +00004453 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004454 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004455 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004457 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004459 --todo;
4460 kp_prev = NULL;
4461 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004462 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004463 if (kp->k_syn.id == id)
4464 {
4465 kp_next = kp->ke_next;
4466 if (kp_prev == NULL)
4467 {
4468 if (kp_next == NULL)
4469 hash_remove(ht, hi);
4470 else
4471 hi->hi_key = KE2HIKEY(kp_next);
4472 }
4473 else
4474 kp_prev->ke_next = kp_next;
4475 vim_free(kp->next_list);
4476 vim_free(kp->k_syn.cont_in_list);
4477 vim_free(kp);
4478 kp = kp_next;
4479 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004481 {
4482 kp_prev = kp;
4483 kp = kp->ke_next;
4484 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 }
4486 }
4487 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004488 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489}
4490
4491/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004492 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 */
4494 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004495clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004497 hashitem_T *hi;
4498 int todo;
4499 keyentry_T *kp;
4500 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004502 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004503 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 if (!HASHITEM_EMPTY(hi))
4506 {
4507 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004508 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004510 kp_next = kp->ke_next;
4511 vim_free(kp->next_list);
4512 vim_free(kp->k_syn.cont_in_list);
4513 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004515 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004517 hash_clear(ht);
4518 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519}
4520
4521/*
4522 * Add a keyword to the list of keywords.
4523 */
4524 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004525add_keyword(
4526 char_u *name, /* name of keyword */
4527 int id, /* group ID for this keyword */
4528 int flags, /* flags for this keyword */
4529 short *cont_in_list, /* containedin for this keyword */
4530 short *next_list, /* nextgroup for this keyword */
4531 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004533 keyentry_T *kp;
4534 hashtab_T *ht;
4535 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004536 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004537 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004538 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539
Bram Moolenaar860cae12010-06-05 23:22:07 +02004540 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004541 name_ic = str_foldcase(name, (int)STRLEN(name),
4542 name_folded, MAXKEYWLEN + 1);
4543 else
4544 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004545 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4546 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004548 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004549 kp->k_syn.id = id;
4550 kp->k_syn.inc_tag = current_syn_inc_tag;
4551 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004552 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004553 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004555 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004556 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557
Bram Moolenaar860cae12010-06-05 23:22:07 +02004558 if (curwin->w_s->b_syn_ic)
4559 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004561 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562
Bram Moolenaardad6b692005-01-25 22:14:34 +00004563 hash = hash_hash(kp->keyword);
4564 hi = hash_lookup(ht, kp->keyword, hash);
4565 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004567 /* new keyword, add to hashtable */
4568 kp->ke_next = NULL;
4569 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004571 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004573 /* keyword already exists, prepend to list */
4574 kp->ke_next = HI2KE(hi);
4575 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577}
4578
4579/*
4580 * Get the start and end of the group name argument.
4581 * Return a pointer to the first argument.
4582 * Return NULL if the end of the command was found instead of further args.
4583 */
4584 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004585get_group_name(
4586 char_u *arg, /* start of the argument */
4587 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004588{
4589 char_u *rest;
4590
4591 *name_end = skiptowhite(arg);
4592 rest = skipwhite(*name_end);
4593
4594 /*
4595 * Check if there are enough arguments. The first argument may be a
4596 * pattern, where '|' is allowed, so only check for NUL.
4597 */
4598 if (ends_excmd(*arg) || *rest == NUL)
4599 return NULL;
4600 return rest;
4601}
4602
4603/*
4604 * Check for syntax command option arguments.
4605 * This can be called at any place in the list of arguments, and just picks
4606 * out the arguments that are known. Can be called several times in a row to
4607 * collect all options in between other arguments.
4608 * Return a pointer to the next argument (which isn't an option).
4609 * Return NULL for any error;
4610 */
4611 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004612get_syn_options(
4613 char_u *arg, /* next argument to be checked */
4614 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004615 int *conceal_char UNUSED,
4616 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618 char_u *gname_start, *gname;
4619 int syn_id;
4620 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004621 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004622 int i;
4623 int fidx;
4624 static struct flag
4625 {
4626 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004627 int argtype;
4628 int flags;
4629 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4630 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4631 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4632 {"eExXtTeEnNdD", 0, HL_EXTEND},
4633 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4634 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4635 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4636 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4637 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4638 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4639 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4640 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4641 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004642 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4643 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4644 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004645 {"cCoOnNtTaAiInNsS", 1, 0},
4646 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4647 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004649 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650
4651 if (arg == NULL) /* already detected error */
4652 return NULL;
4653
Bram Moolenaar860cae12010-06-05 23:22:07 +02004654#ifdef FEAT_CONCEAL
4655 if (curwin->w_s->b_syn_conceal)
4656 opt->flags |= HL_CONCEAL;
4657#endif
4658
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659 for (;;)
4660 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004661 /*
4662 * This is used very often when a large number of keywords is defined.
4663 * Need to skip quickly when no option name is found.
4664 * Also avoid tolower(), it's slow.
4665 */
4666 if (strchr(first_letters, *arg) == NULL)
4667 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668
4669 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4670 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004671 p = flagtab[fidx].name;
4672 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4673 if (arg[len] != p[i] && arg[len] != p[i + 1])
4674 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004675 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004676 || (flagtab[fidx].argtype > 0
4677 ? arg[len] == '='
4678 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004679 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004680 if (opt->keyword
4681 && (flagtab[fidx].flags == HL_DISPLAY
4682 || flagtab[fidx].flags == HL_FOLD
4683 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 /* treat "display", "fold" and "extend" as a keyword */
4685 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 break;
4687 }
4688 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004689 if (fidx < 0) /* no match found */
4690 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004692 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004694 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 {
4696 EMSG(_("E395: contains argument not accepted here"));
4697 return NULL;
4698 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004699 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 return NULL;
4701 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004702 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004704 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004705 return NULL;
4706 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004707 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004708 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004709 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 return NULL;
4711 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004712 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4713 {
4714#ifdef FEAT_MBYTE
4715 /* cchar=? */
4716 if (has_mbyte)
4717 {
4718# ifdef FEAT_CONCEAL
4719 *conceal_char = mb_ptr2char(arg + 6);
4720# endif
4721 arg += mb_ptr2len(arg + 6) - 1;
4722 }
4723 else
4724#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004725 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004726#ifdef FEAT_CONCEAL
4727 *conceal_char = arg[6];
4728#else
4729 ;
4730#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004731 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004732#ifdef FEAT_CONCEAL
4733 if (!vim_isprintc_strict(*conceal_char))
4734 {
4735 EMSG(_("E844: invalid cchar value"));
4736 return NULL;
4737 }
4738#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004739 arg = skipwhite(arg + 7);
4740 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004741 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004742 {
4743 opt->flags |= flagtab[fidx].flags;
4744 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004746 if (flagtab[fidx].flags == HL_SYNC_HERE
4747 || flagtab[fidx].flags == HL_SYNC_THERE)
4748 {
4749 if (opt->sync_idx == NULL)
4750 {
4751 EMSG(_("E393: group[t]here not accepted here"));
4752 return NULL;
4753 }
4754 gname_start = arg;
4755 arg = skiptowhite(arg);
4756 if (gname_start == arg)
4757 return NULL;
4758 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4759 if (gname == NULL)
4760 return NULL;
4761 if (STRCMP(gname, "NONE") == 0)
4762 *opt->sync_idx = NONE_IDX;
4763 else
4764 {
4765 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004766 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4767 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4768 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004769 {
4770 *opt->sync_idx = i;
4771 break;
4772 }
4773 if (i < 0)
4774 {
4775 EMSG2(_("E394: Didn't find region item for %s"), gname);
4776 vim_free(gname);
4777 return NULL;
4778 }
4779 }
4780
4781 vim_free(gname);
4782 arg = skipwhite(arg);
4783 }
4784#ifdef FEAT_FOLDING
4785 else if (flagtab[fidx].flags == HL_FOLD
4786 && foldmethodIsSyntax(curwin))
4787 /* Need to update folds later. */
4788 foldUpdateAll(curwin);
4789#endif
4790 }
4791 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792
4793 return arg;
4794}
4795
4796/*
4797 * Adjustments to syntax item when declared in a ":syn include"'d file.
4798 * Set the contained flag, and if the item is not already contained, add it
4799 * to the specified top-level group, if any.
4800 */
4801 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004802syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004804 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805 return;
4806 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004807 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 {
4809 /* We have to alloc this, because syn_combine_list() will free it. */
4810 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004811 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812
4813 if (grp_list != NULL)
4814 {
4815 grp_list[0] = id;
4816 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004817 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818 CLUSTER_ADD);
4819 }
4820 }
4821}
4822
4823/*
4824 * Handle ":syntax include [@{group-name}] filename" command.
4825 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004826 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004827syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828{
4829 char_u *arg = eap->arg;
4830 int sgl_id = 1;
4831 char_u *group_name_end;
4832 char_u *rest;
4833 char_u *errormsg = NULL;
4834 int prev_toplvl_grp;
4835 int prev_syn_inc_tag;
4836 int source = FALSE;
4837
4838 eap->nextcmd = find_nextcmd(arg);
4839 if (eap->skip)
4840 return;
4841
4842 if (arg[0] == '@')
4843 {
4844 ++arg;
4845 rest = get_group_name(arg, &group_name_end);
4846 if (rest == NULL)
4847 {
4848 EMSG((char_u *)_("E397: Filename required"));
4849 return;
4850 }
4851 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004852 if (sgl_id == 0)
4853 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004854 /* separate_nextcmd() and expand_filename() depend on this */
4855 eap->arg = rest;
4856 }
4857
4858 /*
4859 * Everything that's left, up to the next command, should be the
4860 * filename to include.
4861 */
4862 eap->argt |= (XFILE | NOSPC);
4863 separate_nextcmd(eap);
4864 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4865 {
4866 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4867 * file. Need to expand the file name first. In other cases
4868 * ":runtime!" is used. */
4869 source = TRUE;
4870 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4871 {
4872 if (errormsg != NULL)
4873 EMSG(errormsg);
4874 return;
4875 }
4876 }
4877
4878 /*
4879 * Save and restore the existing top-level grouplist id and ":syn
4880 * include" tag around the actual inclusion.
4881 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004882 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4883 {
4884 EMSG((char_u *)_("E847: Too many syntax includes"));
4885 return;
4886 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 prev_syn_inc_tag = current_syn_inc_tag;
4888 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004889 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4890 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004891 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004892 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004894 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 current_syn_inc_tag = prev_syn_inc_tag;
4896}
4897
4898/*
4899 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4900 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004902syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004903{
4904 char_u *arg = eap->arg;
4905 char_u *group_name_end;
4906 int syn_id;
4907 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004908 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004910 char_u *kw;
4911 syn_opt_arg_T syn_opt_arg;
4912 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004913 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914
4915 rest = get_group_name(arg, &group_name_end);
4916
4917 if (rest != NULL)
4918 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004919 if (eap->skip)
4920 syn_id = -1;
4921 else
4922 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004923 if (syn_id != 0)
4924 /* allocate a buffer, for removing backslashes in the keyword */
4925 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926 if (keyword_copy != NULL)
4927 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004928 syn_opt_arg.flags = 0;
4929 syn_opt_arg.keyword = TRUE;
4930 syn_opt_arg.sync_idx = NULL;
4931 syn_opt_arg.has_cont_list = FALSE;
4932 syn_opt_arg.cont_in_list = NULL;
4933 syn_opt_arg.next_list = NULL;
4934
Bram Moolenaar071d4272004-06-13 20:20:40 +00004935 /*
4936 * The options given apply to ALL keywords, so all options must be
4937 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004938 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004940 cnt = 0;
4941 p = keyword_copy;
4942 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004944 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4945 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004946 if (rest == NULL || ends_excmd(*rest))
4947 break;
4948 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004949 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004951 if (*rest == '\\' && rest[1] != NUL)
4952 ++rest;
4953 *p++ = *rest++;
4954 }
4955 *p++ = NUL;
4956 ++cnt;
4957 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004959 if (!eap->skip)
4960 {
4961 /* Adjust flags for use of ":syn include". */
4962 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4963
4964 /*
4965 * 2: Add an entry for each keyword.
4966 */
4967 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4968 {
4969 for (p = vim_strchr(kw, '['); ; )
4970 {
4971 if (p != NULL)
4972 *p = NUL;
4973 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004974 syn_opt_arg.cont_in_list,
4975 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004976 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004977 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004978 if (p[1] == NUL)
4979 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004980 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004981 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004982 }
4983 if (p[1] == ']')
4984 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004985 if (p[2] != NUL)
4986 {
4987 EMSG3(_("E890: trailing char after ']': %s]%s"),
4988 kw, &p[2]);
4989 goto error;
4990 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004991 kw = p + 1; /* skip over the "]" */
4992 break;
4993 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004994#ifdef FEAT_MBYTE
4995 if (has_mbyte)
4996 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004997 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004998
4999 mch_memmove(p, p + 1, l);
5000 p += l;
5001 }
5002 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005004 {
5005 p[0] = p[1];
5006 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005007 }
5008 }
5009 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005010 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02005011error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005012 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005013 vim_free(syn_opt_arg.cont_in_list);
5014 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015 }
5016 }
5017
5018 if (rest != NULL)
5019 eap->nextcmd = check_nextcmd(rest);
5020 else
5021 EMSG2(_(e_invarg2), arg);
5022
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005023 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005024 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025}
5026
5027/*
5028 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5029 *
5030 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5031 */
5032 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005033syn_cmd_match(
5034 exarg_T *eap,
5035 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036{
5037 char_u *arg = eap->arg;
5038 char_u *group_name_end;
5039 char_u *rest;
5040 synpat_T item; /* the item found in the line */
5041 int syn_id;
5042 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005043 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005044 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005045 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046
5047 /* Isolate the group name, check for validity */
5048 rest = get_group_name(arg, &group_name_end);
5049
5050 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005051 syn_opt_arg.flags = 0;
5052 syn_opt_arg.keyword = FALSE;
5053 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5054 syn_opt_arg.has_cont_list = TRUE;
5055 syn_opt_arg.cont_list = NULL;
5056 syn_opt_arg.cont_in_list = NULL;
5057 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005058 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005059
5060 /* get the pattern. */
5061 init_syn_patterns();
5062 vim_memset(&item, 0, sizeof(item));
5063 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005064 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5065 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066
5067 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005068 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069
5070 if (rest != NULL) /* all arguments are valid */
5071 {
5072 /*
5073 * Check for trailing command and illegal trailing arguments.
5074 */
5075 eap->nextcmd = check_nextcmd(rest);
5076 if (!ends_excmd(*rest) || eap->skip)
5077 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005078 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079 && (syn_id = syn_check_group(arg,
5080 (int)(group_name_end - arg))) != 0)
5081 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005082 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005083 /*
5084 * Store the pattern in the syn_items list
5085 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005086 idx = curwin->w_s->b_syn_patterns.ga_len;
5087 SYN_ITEMS(curwin->w_s)[idx] = item;
5088 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5089 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5090 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5091 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5092 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5093 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5094 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5095 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005096 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005097#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005098 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005099#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005100 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005101 curwin->w_s->b_syn_containedin = TRUE;
5102 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5103 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104
5105 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005106 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005107 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005109 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005110 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111#endif
5112
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005113 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005114 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 return; /* don't free the progs and patterns now */
5116 }
5117 }
5118
5119 /*
5120 * Something failed, free the allocated memory.
5121 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005122 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005123 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005124 vim_free(syn_opt_arg.cont_list);
5125 vim_free(syn_opt_arg.cont_in_list);
5126 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127
5128 if (rest == NULL)
5129 EMSG2(_(e_invarg2), arg);
5130}
5131
5132/*
5133 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5134 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5135 */
5136 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005137syn_cmd_region(
5138 exarg_T *eap,
5139 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140{
5141 char_u *arg = eap->arg;
5142 char_u *group_name_end;
5143 char_u *rest; /* next arg, NULL on error */
5144 char_u *key_end;
5145 char_u *key = NULL;
5146 char_u *p;
5147 int item;
5148#define ITEM_START 0
5149#define ITEM_SKIP 1
5150#define ITEM_END 2
5151#define ITEM_MATCHGROUP 3
5152 struct pat_ptr
5153 {
5154 synpat_T *pp_synp; /* pointer to syn_pattern */
5155 int pp_matchgroup_id; /* matchgroup ID */
5156 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5157 } *(pat_ptrs[3]);
5158 /* patterns found in the line */
5159 struct pat_ptr *ppp;
5160 struct pat_ptr *ppp_next;
5161 int pat_count = 0; /* nr of syn_patterns found */
5162 int syn_id;
5163 int matchgroup_id = 0;
5164 int not_enough = FALSE; /* not enough arguments */
5165 int illegal = FALSE; /* illegal arguments */
5166 int success = FALSE;
5167 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005168 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005169 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005170
5171 /* Isolate the group name, check for validity */
5172 rest = get_group_name(arg, &group_name_end);
5173
5174 pat_ptrs[0] = NULL;
5175 pat_ptrs[1] = NULL;
5176 pat_ptrs[2] = NULL;
5177
5178 init_syn_patterns();
5179
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005180 syn_opt_arg.flags = 0;
5181 syn_opt_arg.keyword = FALSE;
5182 syn_opt_arg.sync_idx = NULL;
5183 syn_opt_arg.has_cont_list = TRUE;
5184 syn_opt_arg.cont_list = NULL;
5185 syn_opt_arg.cont_in_list = NULL;
5186 syn_opt_arg.next_list = NULL;
5187
Bram Moolenaar071d4272004-06-13 20:20:40 +00005188 /*
5189 * get the options, patterns and matchgroup.
5190 */
5191 while (rest != NULL && !ends_excmd(*rest))
5192 {
5193 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005194 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005195 if (rest == NULL || ends_excmd(*rest))
5196 break;
5197
5198 /* must be a pattern or matchgroup then */
5199 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005200 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005201 ++key_end;
5202 vim_free(key);
5203 key = vim_strnsave_up(rest, (int)(key_end - rest));
5204 if (key == NULL) /* out of memory */
5205 {
5206 rest = NULL;
5207 break;
5208 }
5209 if (STRCMP(key, "MATCHGROUP") == 0)
5210 item = ITEM_MATCHGROUP;
5211 else if (STRCMP(key, "START") == 0)
5212 item = ITEM_START;
5213 else if (STRCMP(key, "END") == 0)
5214 item = ITEM_END;
5215 else if (STRCMP(key, "SKIP") == 0)
5216 {
5217 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5218 {
5219 illegal = TRUE;
5220 break;
5221 }
5222 item = ITEM_SKIP;
5223 }
5224 else
5225 break;
5226 rest = skipwhite(key_end);
5227 if (*rest != '=')
5228 {
5229 rest = NULL;
5230 EMSG2(_("E398: Missing '=': %s"), arg);
5231 break;
5232 }
5233 rest = skipwhite(rest + 1);
5234 if (*rest == NUL)
5235 {
5236 not_enough = TRUE;
5237 break;
5238 }
5239
5240 if (item == ITEM_MATCHGROUP)
5241 {
5242 p = skiptowhite(rest);
5243 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5244 matchgroup_id = 0;
5245 else
5246 {
5247 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5248 if (matchgroup_id == 0)
5249 {
5250 illegal = TRUE;
5251 break;
5252 }
5253 }
5254 rest = skipwhite(p);
5255 }
5256 else
5257 {
5258 /*
5259 * Allocate room for a syn_pattern, and link it in the list of
5260 * syn_patterns for this item, at the start (because the list is
5261 * used from end to start).
5262 */
5263 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5264 if (ppp == NULL)
5265 {
5266 rest = NULL;
5267 break;
5268 }
5269 ppp->pp_next = pat_ptrs[item];
5270 pat_ptrs[item] = ppp;
5271 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5272 if (ppp->pp_synp == NULL)
5273 {
5274 rest = NULL;
5275 break;
5276 }
5277
5278 /*
5279 * Get the syntax pattern and the following offset(s).
5280 */
5281 /* Enable the appropriate \z specials. */
5282 if (item == ITEM_START)
5283 reg_do_extmatch = REX_SET;
5284 else if (item == ITEM_SKIP || item == ITEM_END)
5285 reg_do_extmatch = REX_USE;
5286 rest = get_syn_pattern(rest, ppp->pp_synp);
5287 reg_do_extmatch = 0;
5288 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005289 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5291 ppp->pp_matchgroup_id = matchgroup_id;
5292 ++pat_count;
5293 }
5294 }
5295 vim_free(key);
5296 if (illegal || not_enough)
5297 rest = NULL;
5298
5299 /*
5300 * Must have a "start" and "end" pattern.
5301 */
5302 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5303 pat_ptrs[ITEM_END] == NULL))
5304 {
5305 not_enough = TRUE;
5306 rest = NULL;
5307 }
5308
5309 if (rest != NULL)
5310 {
5311 /*
5312 * Check for trailing garbage or command.
5313 * If OK, add the item.
5314 */
5315 eap->nextcmd = check_nextcmd(rest);
5316 if (!ends_excmd(*rest) || eap->skip)
5317 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005318 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319 && (syn_id = syn_check_group(arg,
5320 (int)(group_name_end - arg))) != 0)
5321 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005322 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005323 /*
5324 * Store the start/skip/end in the syn_items list
5325 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005326 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005327 for (item = ITEM_START; item <= ITEM_END; ++item)
5328 {
5329 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5330 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005331 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5332 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5333 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005334 (item == ITEM_START) ? SPTYPE_START :
5335 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005336 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5337 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005338 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5339 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005340 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005341 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005342#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005343 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005344#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345 if (item == ITEM_START)
5346 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005347 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005348 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005349 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005350 syn_opt_arg.cont_in_list;
5351 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005352 curwin->w_s->b_syn_containedin = TRUE;
5353 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005354 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005355 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005356 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357 ++idx;
5358#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005359 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005360 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005361#endif
5362 }
5363 }
5364
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005365 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005366 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005367 success = TRUE; /* don't free the progs and patterns now */
5368 }
5369 }
5370
5371 /*
5372 * Free the allocated memory.
5373 */
5374 for (item = ITEM_START; item <= ITEM_END; ++item)
5375 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5376 {
5377 if (!success)
5378 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005379 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005380 vim_free(ppp->pp_synp->sp_pattern);
5381 }
5382 vim_free(ppp->pp_synp);
5383 ppp_next = ppp->pp_next;
5384 vim_free(ppp);
5385 }
5386
5387 if (!success)
5388 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005389 vim_free(syn_opt_arg.cont_list);
5390 vim_free(syn_opt_arg.cont_in_list);
5391 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005392 if (not_enough)
5393 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5394 else if (illegal || rest == NULL)
5395 EMSG2(_(e_invarg2), arg);
5396 }
5397}
5398
5399/*
5400 * A simple syntax group ID comparison function suitable for use in qsort()
5401 */
5402 static int
5403#ifdef __BORLANDC__
5404_RTLENTRYF
5405#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005406syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407{
5408 const short *s1 = v1;
5409 const short *s2 = v2;
5410
5411 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5412}
5413
5414/*
5415 * Combines lists of syntax clusters.
5416 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5417 */
5418 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005419syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005420{
5421 int count1 = 0;
5422 int count2 = 0;
5423 short *g1;
5424 short *g2;
5425 short *clstr = NULL;
5426 int count;
5427 int round;
5428
5429 /*
5430 * Handle degenerate cases.
5431 */
5432 if (*clstr2 == NULL)
5433 return;
5434 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5435 {
5436 if (list_op == CLUSTER_REPLACE)
5437 vim_free(*clstr1);
5438 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5439 *clstr1 = *clstr2;
5440 else
5441 vim_free(*clstr2);
5442 return;
5443 }
5444
5445 for (g1 = *clstr1; *g1; g1++)
5446 ++count1;
5447 for (g2 = *clstr2; *g2; g2++)
5448 ++count2;
5449
5450 /*
5451 * For speed purposes, sort both lists.
5452 */
5453 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5454 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5455
5456 /*
5457 * We proceed in two passes; in round 1, we count the elements to place
5458 * in the new list, and in round 2, we allocate and populate the new
5459 * list. For speed, we use a mergesort-like method, adding the smaller
5460 * of the current elements in each list to the new list.
5461 */
5462 for (round = 1; round <= 2; round++)
5463 {
5464 g1 = *clstr1;
5465 g2 = *clstr2;
5466 count = 0;
5467
5468 /*
5469 * First, loop through the lists until one of them is empty.
5470 */
5471 while (*g1 && *g2)
5472 {
5473 /*
5474 * We always want to add from the first list.
5475 */
5476 if (*g1 < *g2)
5477 {
5478 if (round == 2)
5479 clstr[count] = *g1;
5480 count++;
5481 g1++;
5482 continue;
5483 }
5484 /*
5485 * We only want to add from the second list if we're adding the
5486 * lists.
5487 */
5488 if (list_op == CLUSTER_ADD)
5489 {
5490 if (round == 2)
5491 clstr[count] = *g2;
5492 count++;
5493 }
5494 if (*g1 == *g2)
5495 g1++;
5496 g2++;
5497 }
5498
5499 /*
5500 * Now add the leftovers from whichever list didn't get finished
5501 * first. As before, we only want to add from the second list if
5502 * we're adding the lists.
5503 */
5504 for (; *g1; g1++, count++)
5505 if (round == 2)
5506 clstr[count] = *g1;
5507 if (list_op == CLUSTER_ADD)
5508 for (; *g2; g2++, count++)
5509 if (round == 2)
5510 clstr[count] = *g2;
5511
5512 if (round == 1)
5513 {
5514 /*
5515 * If the group ended up empty, we don't need to allocate any
5516 * space for it.
5517 */
5518 if (count == 0)
5519 {
5520 clstr = NULL;
5521 break;
5522 }
5523 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5524 if (clstr == NULL)
5525 break;
5526 clstr[count] = 0;
5527 }
5528 }
5529
5530 /*
5531 * Finally, put the new list in place.
5532 */
5533 vim_free(*clstr1);
5534 vim_free(*clstr2);
5535 *clstr1 = clstr;
5536}
5537
5538/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005539 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005540 * If it is not found, 0 is returned.
5541 */
5542 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005543syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005544{
5545 int i;
5546 char_u *name_u;
5547
5548 /* Avoid using stricmp() too much, it's slow on some systems */
5549 name_u = vim_strsave_up(name);
5550 if (name_u == NULL)
5551 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005552 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5553 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5554 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 break;
5556 vim_free(name_u);
5557 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5558}
5559
5560/*
5561 * Like syn_scl_name2id(), but take a pointer + length argument.
5562 */
5563 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005564syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565{
5566 char_u *name;
5567 int id = 0;
5568
5569 name = vim_strnsave(linep, len);
5570 if (name != NULL)
5571 {
5572 id = syn_scl_name2id(name);
5573 vim_free(name);
5574 }
5575 return id;
5576}
5577
5578/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005579 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 * The argument is a pointer to the name and the length of the name.
5581 * If it doesn't exist yet, a new entry is created.
5582 * Return 0 for failure.
5583 */
5584 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005585syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586{
5587 int id;
5588 char_u *name;
5589
5590 name = vim_strnsave(pp, len);
5591 if (name == NULL)
5592 return 0;
5593
5594 id = syn_scl_name2id(name);
5595 if (id == 0) /* doesn't exist yet */
5596 id = syn_add_cluster(name);
5597 else
5598 vim_free(name);
5599 return id;
5600}
5601
5602/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005603 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005604 * "name" must be an allocated string, it will be consumed.
5605 * Return 0 for failure.
5606 */
5607 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005608syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005609{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005610 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005611
5612 /*
5613 * First call for this growarray: init growing array.
5614 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005615 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005617 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5618 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 }
5620
Bram Moolenaar42431a72011-04-01 14:44:59 +02005621 len = curwin->w_s->b_syn_clusters.ga_len;
5622 if (len >= MAX_CLUSTER_ID)
5623 {
5624 EMSG((char_u *)_("E848: Too many syntax clusters"));
5625 vim_free(name);
5626 return 0;
5627 }
5628
Bram Moolenaar071d4272004-06-13 20:20:40 +00005629 /*
5630 * Make room for at least one other cluster entry.
5631 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005632 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633 {
5634 vim_free(name);
5635 return 0;
5636 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637
Bram Moolenaar860cae12010-06-05 23:22:07 +02005638 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5639 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5640 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5641 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5642 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005643
Bram Moolenaar217ad922005-03-20 22:37:15 +00005644 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005645 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005646 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005647 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005648
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649 return len + SYNID_CLUSTER;
5650}
5651
5652/*
5653 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5654 * [add={groupname},..] [remove={groupname},..]".
5655 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005656 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005657syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658{
5659 char_u *arg = eap->arg;
5660 char_u *group_name_end;
5661 char_u *rest;
5662 int scl_id;
5663 short *clstr_list;
5664 int got_clstr = FALSE;
5665 int opt_len;
5666 int list_op;
5667
5668 eap->nextcmd = find_nextcmd(arg);
5669 if (eap->skip)
5670 return;
5671
5672 rest = get_group_name(arg, &group_name_end);
5673
5674 if (rest != NULL)
5675 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005676 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5677 if (scl_id == 0)
5678 return;
5679 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680
5681 for (;;)
5682 {
5683 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005684 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 {
5686 opt_len = 3;
5687 list_op = CLUSTER_ADD;
5688 }
5689 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005690 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005691 {
5692 opt_len = 6;
5693 list_op = CLUSTER_SUBTRACT;
5694 }
5695 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005696 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005697 {
5698 opt_len = 8;
5699 list_op = CLUSTER_REPLACE;
5700 }
5701 else
5702 break;
5703
5704 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005705 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 {
5707 EMSG2(_(e_invarg2), rest);
5708 break;
5709 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005710 if (scl_id >= 0)
5711 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005712 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005713 else
5714 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005715 got_clstr = TRUE;
5716 }
5717
5718 if (got_clstr)
5719 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005720 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005721 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722 }
5723 }
5724
5725 if (!got_clstr)
5726 EMSG(_("E400: No cluster specified"));
5727 if (rest == NULL || !ends_excmd(*rest))
5728 EMSG2(_(e_invarg2), arg);
5729}
5730
5731/*
5732 * On first call for current buffer: Init growing array.
5733 */
5734 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005735init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005737 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5738 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739}
5740
5741/*
5742 * Get one pattern for a ":syntax match" or ":syntax region" command.
5743 * Stores the pattern and program in a synpat_T.
5744 * Returns a pointer to the next argument, or NULL in case of an error.
5745 */
5746 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005747get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748{
5749 char_u *end;
5750 int *p;
5751 int idx;
5752 char_u *cpo_save;
5753
5754 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005755 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005756 return NULL;
5757
5758 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5759 if (*end != *arg) /* end delimiter not found */
5760 {
5761 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5762 return NULL;
5763 }
5764 /* store the pattern and compiled regexp program */
5765 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5766 return NULL;
5767
5768 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5769 cpo_save = p_cpo;
5770 p_cpo = (char_u *)"";
5771 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5772 p_cpo = cpo_save;
5773
5774 if (ci->sp_prog == NULL)
5775 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005776 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005777#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005778 syn_clear_time(&ci->sp_time);
5779#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780
5781 /*
5782 * Check for a match, highlight or region offset.
5783 */
5784 ++end;
5785 do
5786 {
5787 for (idx = SPO_COUNT; --idx >= 0; )
5788 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5789 break;
5790 if (idx >= 0)
5791 {
5792 p = &(ci->sp_offsets[idx]);
5793 if (idx != SPO_LC_OFF)
5794 switch (end[3])
5795 {
5796 case 's': break;
5797 case 'b': break;
5798 case 'e': idx += SPO_COUNT; break;
5799 default: idx = -1; break;
5800 }
5801 if (idx >= 0)
5802 {
5803 ci->sp_off_flags |= (1 << idx);
5804 if (idx == SPO_LC_OFF) /* lc=99 */
5805 {
5806 end += 3;
5807 *p = getdigits(&end);
5808
5809 /* "lc=" offset automatically sets "ms=" offset */
5810 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5811 {
5812 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5813 ci->sp_offsets[SPO_MS_OFF] = *p;
5814 }
5815 }
5816 else /* yy=x+99 */
5817 {
5818 end += 4;
5819 if (*end == '+')
5820 {
5821 ++end;
5822 *p = getdigits(&end); /* positive offset */
5823 }
5824 else if (*end == '-')
5825 {
5826 ++end;
5827 *p = -getdigits(&end); /* negative offset */
5828 }
5829 }
5830 if (*end != ',')
5831 break;
5832 ++end;
5833 }
5834 }
5835 } while (idx >= 0);
5836
Bram Moolenaar1c465442017-03-12 20:10:05 +01005837 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838 {
5839 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5840 return NULL;
5841 }
5842 return skipwhite(end);
5843}
5844
5845/*
5846 * Handle ":syntax sync .." command.
5847 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005848 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005849syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850{
5851 char_u *arg_start = eap->arg;
5852 char_u *arg_end;
5853 char_u *key = NULL;
5854 char_u *next_arg;
5855 int illegal = FALSE;
5856 int finished = FALSE;
5857 long n;
5858 char_u *cpo_save;
5859
5860 if (ends_excmd(*arg_start))
5861 {
5862 syn_cmd_list(eap, TRUE);
5863 return;
5864 }
5865
5866 while (!ends_excmd(*arg_start))
5867 {
5868 arg_end = skiptowhite(arg_start);
5869 next_arg = skipwhite(arg_end);
5870 vim_free(key);
5871 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5872 if (STRCMP(key, "CCOMMENT") == 0)
5873 {
5874 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005875 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005876 if (!ends_excmd(*next_arg))
5877 {
5878 arg_end = skiptowhite(next_arg);
5879 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005880 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881 (int)(arg_end - next_arg));
5882 next_arg = skipwhite(arg_end);
5883 }
5884 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005885 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 }
5887 else if ( STRNCMP(key, "LINES", 5) == 0
5888 || STRNCMP(key, "MINLINES", 8) == 0
5889 || STRNCMP(key, "MAXLINES", 8) == 0
5890 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5891 {
5892 if (key[4] == 'S')
5893 arg_end = key + 6;
5894 else if (key[0] == 'L')
5895 arg_end = key + 11;
5896 else
5897 arg_end = key + 9;
5898 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5899 {
5900 illegal = TRUE;
5901 break;
5902 }
5903 n = getdigits(&arg_end);
5904 if (!eap->skip)
5905 {
5906 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005907 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005908 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005909 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005910 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005911 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005912 }
5913 }
5914 else if (STRCMP(key, "FROMSTART") == 0)
5915 {
5916 if (!eap->skip)
5917 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005918 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5919 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005920 }
5921 }
5922 else if (STRCMP(key, "LINECONT") == 0)
5923 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005924 if (*next_arg == NUL) /* missing pattern */
5925 {
5926 illegal = TRUE;
5927 break;
5928 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005929 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005930 {
5931 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5932 finished = TRUE;
5933 break;
5934 }
5935 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5936 if (*arg_end != *next_arg) /* end delimiter not found */
5937 {
5938 illegal = TRUE;
5939 break;
5940 }
5941
5942 if (!eap->skip)
5943 {
5944 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005945 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946 (int)(arg_end - next_arg - 1))) == NULL)
5947 {
5948 finished = TRUE;
5949 break;
5950 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005951 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952
5953 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5954 cpo_save = p_cpo;
5955 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005956 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005957 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005958 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005959#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005960 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5961#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005962
Bram Moolenaar860cae12010-06-05 23:22:07 +02005963 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005964 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005965 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005966 finished = TRUE;
5967 break;
5968 }
5969 }
5970 next_arg = skipwhite(arg_end + 1);
5971 }
5972 else
5973 {
5974 eap->arg = next_arg;
5975 if (STRCMP(key, "MATCH") == 0)
5976 syn_cmd_match(eap, TRUE);
5977 else if (STRCMP(key, "REGION") == 0)
5978 syn_cmd_region(eap, TRUE);
5979 else if (STRCMP(key, "CLEAR") == 0)
5980 syn_cmd_clear(eap, TRUE);
5981 else
5982 illegal = TRUE;
5983 finished = TRUE;
5984 break;
5985 }
5986 arg_start = next_arg;
5987 }
5988 vim_free(key);
5989 if (illegal)
5990 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5991 else if (!finished)
5992 {
5993 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005994 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005995 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005996 }
5997}
5998
5999/*
6000 * Convert a line of highlight group names into a list of group ID numbers.
6001 * "arg" should point to the "contains" or "nextgroup" keyword.
6002 * "arg" is advanced to after the last group name.
6003 * Careful: the argument is modified (NULs added).
6004 * returns FAIL for some error, OK for success.
6005 */
6006 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006007get_id_list(
6008 char_u **arg,
6009 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006010 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006012 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006013{
6014 char_u *p = NULL;
6015 char_u *end;
6016 int round;
6017 int count;
6018 int total_count = 0;
6019 short *retval = NULL;
6020 char_u *name;
6021 regmatch_T regmatch;
6022 int id;
6023 int i;
6024 int failed = FALSE;
6025
6026 /*
6027 * We parse the list twice:
6028 * round == 1: count the number of items, allocate the array.
6029 * round == 2: fill the array with the items.
6030 * In round 1 new groups may be added, causing the number of items to
6031 * grow when a regexp is used. In that case round 1 is done once again.
6032 */
6033 for (round = 1; round <= 2; ++round)
6034 {
6035 /*
6036 * skip "contains"
6037 */
6038 p = skipwhite(*arg + keylen);
6039 if (*p != '=')
6040 {
6041 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6042 break;
6043 }
6044 p = skipwhite(p + 1);
6045 if (ends_excmd(*p))
6046 {
6047 EMSG2(_("E406: Empty argument: %s"), *arg);
6048 break;
6049 }
6050
6051 /*
6052 * parse the arguments after "contains"
6053 */
6054 count = 0;
6055 while (!ends_excmd(*p))
6056 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006057 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006058 ;
6059 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6060 if (name == NULL)
6061 {
6062 failed = TRUE;
6063 break;
6064 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006065 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006066 if ( STRCMP(name + 1, "ALLBUT") == 0
6067 || STRCMP(name + 1, "ALL") == 0
6068 || STRCMP(name + 1, "TOP") == 0
6069 || STRCMP(name + 1, "CONTAINED") == 0)
6070 {
6071 if (TOUPPER_ASC(**arg) != 'C')
6072 {
6073 EMSG2(_("E407: %s not allowed here"), name + 1);
6074 failed = TRUE;
6075 vim_free(name);
6076 break;
6077 }
6078 if (count != 0)
6079 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006080 EMSG2(_("E408: %s must be first in contains list"),
6081 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006082 failed = TRUE;
6083 vim_free(name);
6084 break;
6085 }
6086 if (name[1] == 'A')
6087 id = SYNID_ALLBUT;
6088 else if (name[1] == 'T')
6089 id = SYNID_TOP;
6090 else
6091 id = SYNID_CONTAINED;
6092 id += current_syn_inc_tag;
6093 }
6094 else if (name[1] == '@')
6095 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006096 if (skip)
6097 id = -1;
6098 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006099 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006100 }
6101 else
6102 {
6103 /*
6104 * Handle full group name.
6105 */
6106 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6107 id = syn_check_group(name + 1, (int)(end - p));
6108 else
6109 {
6110 /*
6111 * Handle match of regexp with group names.
6112 */
6113 *name = '^';
6114 STRCAT(name, "$");
6115 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6116 if (regmatch.regprog == NULL)
6117 {
6118 failed = TRUE;
6119 vim_free(name);
6120 break;
6121 }
6122
6123 regmatch.rm_ic = TRUE;
6124 id = 0;
6125 for (i = highlight_ga.ga_len; --i >= 0; )
6126 {
6127 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6128 (colnr_T)0))
6129 {
6130 if (round == 2)
6131 {
6132 /* Got more items than expected; can happen
6133 * when adding items that match:
6134 * "contains=a.*b,axb".
6135 * Go back to first round */
6136 if (count >= total_count)
6137 {
6138 vim_free(retval);
6139 round = 1;
6140 }
6141 else
6142 retval[count] = i + 1;
6143 }
6144 ++count;
6145 id = -1; /* remember that we found one */
6146 }
6147 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006148 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006149 }
6150 }
6151 vim_free(name);
6152 if (id == 0)
6153 {
6154 EMSG2(_("E409: Unknown group name: %s"), p);
6155 failed = TRUE;
6156 break;
6157 }
6158 if (id > 0)
6159 {
6160 if (round == 2)
6161 {
6162 /* Got more items than expected, go back to first round */
6163 if (count >= total_count)
6164 {
6165 vim_free(retval);
6166 round = 1;
6167 }
6168 else
6169 retval[count] = id;
6170 }
6171 ++count;
6172 }
6173 p = skipwhite(end);
6174 if (*p != ',')
6175 break;
6176 p = skipwhite(p + 1); /* skip comma in between arguments */
6177 }
6178 if (failed)
6179 break;
6180 if (round == 1)
6181 {
6182 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6183 if (retval == NULL)
6184 break;
6185 retval[count] = 0; /* zero means end of the list */
6186 total_count = count;
6187 }
6188 }
6189
6190 *arg = p;
6191 if (failed || retval == NULL)
6192 {
6193 vim_free(retval);
6194 return FAIL;
6195 }
6196
6197 if (*list == NULL)
6198 *list = retval;
6199 else
6200 vim_free(retval); /* list already found, don't overwrite it */
6201
6202 return OK;
6203}
6204
6205/*
6206 * Make a copy of an ID list.
6207 */
6208 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006209copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006210{
6211 int len;
6212 int count;
6213 short *retval;
6214
6215 if (list == NULL)
6216 return NULL;
6217
6218 for (count = 0; list[count]; ++count)
6219 ;
6220 len = (count + 1) * sizeof(short);
6221 retval = (short *)alloc((unsigned)len);
6222 if (retval != NULL)
6223 mch_memmove(retval, list, (size_t)len);
6224
6225 return retval;
6226}
6227
6228/*
6229 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6230 * "cur_si" can be NULL if not checking the "containedin" list.
6231 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6232 * the current item.
6233 * This function is called very often, keep it fast!!
6234 */
6235 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006236in_id_list(
6237 stateitem_T *cur_si, /* current item or NULL */
6238 short *list, /* id list */
6239 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6240 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006241{
6242 int retval;
6243 short *scl_list;
6244 short item;
6245 short id = ssp->id;
6246 static int depth = 0;
6247 int r;
6248
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006249 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006250 if (cur_si != NULL && ssp->cont_in_list != NULL
6251 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252 {
6253 /* Ignore transparent items without a contains argument. Double check
6254 * that we don't go back past the first one. */
6255 while ((cur_si->si_flags & HL_TRANS_CONT)
6256 && cur_si > (stateitem_T *)(current_state.ga_data))
6257 --cur_si;
6258 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6259 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006260 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6261 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262 return TRUE;
6263 }
6264
6265 if (list == NULL)
6266 return FALSE;
6267
6268 /*
6269 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6270 * inside anything. Only allow not-contained groups.
6271 */
6272 if (list == ID_LIST_ALL)
6273 return !contained;
6274
6275 /*
6276 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6277 * contains list. We also require that "id" is at the same ":syn include"
6278 * level as the list.
6279 */
6280 item = *list;
6281 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6282 {
6283 if (item < SYNID_TOP)
6284 {
6285 /* ALL or ALLBUT: accept all groups in the same file */
6286 if (item - SYNID_ALLBUT != ssp->inc_tag)
6287 return FALSE;
6288 }
6289 else if (item < SYNID_CONTAINED)
6290 {
6291 /* TOP: accept all not-contained groups in the same file */
6292 if (item - SYNID_TOP != ssp->inc_tag || contained)
6293 return FALSE;
6294 }
6295 else
6296 {
6297 /* CONTAINED: accept all contained groups in the same file */
6298 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6299 return FALSE;
6300 }
6301 item = *++list;
6302 retval = FALSE;
6303 }
6304 else
6305 retval = TRUE;
6306
6307 /*
6308 * Return "retval" if id is in the contains list.
6309 */
6310 while (item != 0)
6311 {
6312 if (item == id)
6313 return retval;
6314 if (item >= SYNID_CLUSTER)
6315 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006316 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317 /* restrict recursiveness to 30 to avoid an endless loop for a
6318 * cluster that includes itself (indirectly) */
6319 if (scl_list != NULL && depth < 30)
6320 {
6321 ++depth;
6322 r = in_id_list(NULL, scl_list, ssp, contained);
6323 --depth;
6324 if (r)
6325 return retval;
6326 }
6327 }
6328 item = *++list;
6329 }
6330 return !retval;
6331}
6332
6333struct subcommand
6334{
Bram Moolenaard99df422016-01-29 23:20:40 +01006335 char *name; /* subcommand name */
6336 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006337};
6338
6339static struct subcommand subcommands[] =
6340{
6341 {"case", syn_cmd_case},
6342 {"clear", syn_cmd_clear},
6343 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006344 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006345 {"enable", syn_cmd_enable},
6346 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006347 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348 {"keyword", syn_cmd_keyword},
6349 {"list", syn_cmd_list},
6350 {"manual", syn_cmd_manual},
6351 {"match", syn_cmd_match},
6352 {"on", syn_cmd_on},
6353 {"off", syn_cmd_off},
6354 {"region", syn_cmd_region},
6355 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006356 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357 {"sync", syn_cmd_sync},
6358 {"", syn_cmd_list},
6359 {NULL, NULL}
6360};
6361
6362/*
6363 * ":syntax".
6364 * This searches the subcommands[] table for the subcommand name, and calls a
6365 * syntax_subcommand() function to do the rest.
6366 */
6367 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006368ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369{
6370 char_u *arg = eap->arg;
6371 char_u *subcmd_end;
6372 char_u *subcmd_name;
6373 int i;
6374
6375 syn_cmdlinep = eap->cmdlinep;
6376
6377 /* isolate subcommand name */
6378 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6379 ;
6380 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6381 if (subcmd_name != NULL)
6382 {
6383 if (eap->skip) /* skip error messages for all subcommands */
6384 ++emsg_skip;
6385 for (i = 0; ; ++i)
6386 {
6387 if (subcommands[i].name == NULL)
6388 {
6389 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6390 break;
6391 }
6392 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6393 {
6394 eap->arg = skipwhite(subcmd_end);
6395 (subcommands[i].func)(eap, FALSE);
6396 break;
6397 }
6398 }
6399 vim_free(subcmd_name);
6400 if (eap->skip)
6401 --emsg_skip;
6402 }
6403}
6404
Bram Moolenaar860cae12010-06-05 23:22:07 +02006405 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006406ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006408 char_u *old_value;
6409 char_u *new_value;
6410
Bram Moolenaar860cae12010-06-05 23:22:07 +02006411 if (curwin->w_s == &curwin->w_buffer->b_s)
6412 {
6413 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6414 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006415 hash_init(&curwin->w_s->b_keywtab);
6416 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006417#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006418 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006419 curwin->w_p_spell = FALSE; /* No spell checking */
6420 clear_string_option(&curwin->w_s->b_p_spc);
6421 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006422 clear_string_option(&curwin->w_s->b_p_spl);
6423#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006424 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006425 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006426
6427 /* save value of b:current_syntax */
6428 old_value = get_var_value((char_u *)"b:current_syntax");
6429 if (old_value != NULL)
6430 old_value = vim_strsave(old_value);
6431
6432 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6433 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006434 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006435
6436 /* move value of b:current_syntax to w:current_syntax */
6437 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006438 if (new_value != NULL)
6439 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006440
6441 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006442 if (old_value == NULL)
6443 do_unlet((char_u *)"b:current_syntax", TRUE);
6444 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006445 {
6446 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6447 vim_free(old_value);
6448 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006449}
6450
6451 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006452syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006453{
6454 return (win->w_s->b_syn_patterns.ga_len != 0
6455 || win->w_s->b_syn_clusters.ga_len != 0
6456 || win->w_s->b_keywtab.ht_used > 0
6457 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006458}
6459
6460#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6461
6462static enum
6463{
6464 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006465 EXP_CASE, /* expand ":syn case" arguments */
6466 EXP_SPELL, /* expand ":syn spell" arguments */
6467 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006468} expand_what;
6469
Bram Moolenaar4f688582007-07-24 12:34:30 +00006470/*
6471 * Reset include_link, include_default, include_none to 0.
6472 * Called when we are done expanding.
6473 */
6474 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006475reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006476{
6477 include_link = include_default = include_none = 0;
6478}
6479
6480/*
6481 * Handle command line completion for :match and :echohl command: Add "None"
6482 * as highlight group.
6483 */
6484 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006485set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006486{
6487 xp->xp_context = EXPAND_HIGHLIGHT;
6488 xp->xp_pattern = arg;
6489 include_none = 1;
6490}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006491
6492/*
6493 * Handle command line completion for :syntax command.
6494 */
6495 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006496set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006497{
6498 char_u *p;
6499
6500 /* Default: expand subcommands */
6501 xp->xp_context = EXPAND_SYNTAX;
6502 expand_what = EXP_SUBCMD;
6503 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006504 include_link = 0;
6505 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506
6507 /* (part of) subcommand already typed */
6508 if (*arg != NUL)
6509 {
6510 p = skiptowhite(arg);
6511 if (*p != NUL) /* past first word */
6512 {
6513 xp->xp_pattern = skipwhite(p);
6514 if (*skiptowhite(xp->xp_pattern) != NUL)
6515 xp->xp_context = EXPAND_NOTHING;
6516 else if (STRNICMP(arg, "case", p - arg) == 0)
6517 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006518 else if (STRNICMP(arg, "spell", p - arg) == 0)
6519 expand_what = EXP_SPELL;
6520 else if (STRNICMP(arg, "sync", p - arg) == 0)
6521 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006522 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6523 || STRNICMP(arg, "region", p - arg) == 0
6524 || STRNICMP(arg, "match", p - arg) == 0
6525 || STRNICMP(arg, "list", p - arg) == 0)
6526 xp->xp_context = EXPAND_HIGHLIGHT;
6527 else
6528 xp->xp_context = EXPAND_NOTHING;
6529 }
6530 }
6531}
6532
Bram Moolenaar071d4272004-06-13 20:20:40 +00006533/*
6534 * Function given to ExpandGeneric() to obtain the list syntax names for
6535 * expansion.
6536 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006537 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006538get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006540 switch (expand_what)
6541 {
6542 case EXP_SUBCMD:
6543 return (char_u *)subcommands[idx].name;
6544 case EXP_CASE:
6545 {
6546 static char *case_args[] = {"match", "ignore", NULL};
6547 return (char_u *)case_args[idx];
6548 }
6549 case EXP_SPELL:
6550 {
6551 static char *spell_args[] =
6552 {"toplevel", "notoplevel", "default", NULL};
6553 return (char_u *)spell_args[idx];
6554 }
6555 case EXP_SYNC:
6556 {
6557 static char *sync_args[] =
6558 {"ccomment", "clear", "fromstart",
6559 "linebreaks=", "linecont", "lines=", "match",
6560 "maxlines=", "minlines=", "region", NULL};
6561 return (char_u *)sync_args[idx];
6562 }
6563 }
6564 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565}
6566
6567#endif /* FEAT_CMDL_COMPL */
6568
Bram Moolenaar071d4272004-06-13 20:20:40 +00006569/*
6570 * Function called for expression evaluation: get syntax ID at file position.
6571 */
6572 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006573syn_get_id(
6574 win_T *wp,
6575 long lnum,
6576 colnr_T col,
6577 int trans, /* remove transparency */
6578 int *spellp, /* return: can do spell checking */
6579 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006580{
6581 /* When the position is not after the current position and in the same
6582 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006583 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006585 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006586 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006587 else if (wp->w_buffer == syn_buf
6588 && lnum == current_lnum
6589 && col > current_col)
6590 /* next_match may not be correct when moving around, e.g. with the
6591 * "skip" expression in searchpair() */
6592 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006593
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006594 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006595
6596 return (trans ? current_trans_id : current_id);
6597}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006598
Bram Moolenaar860cae12010-06-05 23:22:07 +02006599#if defined(FEAT_CONCEAL) || defined(PROTO)
6600/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006601 * Get extra information about the syntax item. Must be called right after
6602 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006603 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006604 * Returns the current flags.
6605 */
6606 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006607get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006608{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006609 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006610 return current_flags;
6611}
6612
6613/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006614 * Return conceal substitution character
6615 */
6616 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006617syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006618{
6619 return current_sub_char;
6620}
6621#endif
6622
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006623#if defined(FEAT_EVAL) || defined(PROTO)
6624/*
6625 * Return the syntax ID at position "i" in the current stack.
6626 * The caller must have called syn_get_id() before to fill the stack.
6627 * Returns -1 when "i" is out of range.
6628 */
6629 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006630syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006631{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006632 if (i >= current_state.ga_len)
6633 {
6634 /* Need to invalidate the state, because we didn't properly finish it
6635 * for the last character, "keep_state" was TRUE. */
6636 invalidate_current_state();
6637 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006638 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006639 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006640 return CUR_STATE(i).si_id;
6641}
6642#endif
6643
Bram Moolenaar071d4272004-06-13 20:20:40 +00006644#if defined(FEAT_FOLDING) || defined(PROTO)
6645/*
6646 * Function called to get folding level for line "lnum" in window "wp".
6647 */
6648 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006649syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006650{
6651 int level = 0;
6652 int i;
6653
6654 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006655 if (wp->w_s->b_syn_folditems != 0
6656 && !wp->w_s->b_syn_error
6657# ifdef SYN_TIME_LIMIT
6658 && !wp->w_s->b_syn_slow
6659# endif
6660 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006661 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006662 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006663
6664 for (i = 0; i < current_state.ga_len; ++i)
6665 if (CUR_STATE(i).si_flags & HL_FOLD)
6666 ++level;
6667 }
6668 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006669 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006670 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006671 if (level < 0)
6672 level = 0;
6673 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006674 return level;
6675}
6676#endif
6677
Bram Moolenaar01615492015-02-03 13:00:38 +01006678#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006679/*
6680 * ":syntime".
6681 */
6682 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006683ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006684{
6685 if (STRCMP(eap->arg, "on") == 0)
6686 syn_time_on = TRUE;
6687 else if (STRCMP(eap->arg, "off") == 0)
6688 syn_time_on = FALSE;
6689 else if (STRCMP(eap->arg, "clear") == 0)
6690 syntime_clear();
6691 else if (STRCMP(eap->arg, "report") == 0)
6692 syntime_report();
6693 else
6694 EMSG2(_(e_invarg2), eap->arg);
6695}
6696
6697 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006698syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006699{
6700 profile_zero(&st->total);
6701 profile_zero(&st->slowest);
6702 st->count = 0;
6703 st->match = 0;
6704}
6705
6706/*
6707 * Clear the syntax timing for the current buffer.
6708 */
6709 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006710syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006711{
6712 int idx;
6713 synpat_T *spp;
6714
6715 if (!syntax_present(curwin))
6716 {
6717 MSG(_(msg_no_items));
6718 return;
6719 }
6720 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6721 {
6722 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6723 syn_clear_time(&spp->sp_time);
6724 }
6725}
6726
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006727#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6728/*
6729 * Function given to ExpandGeneric() to obtain the possible arguments of the
6730 * ":syntime {on,off,clear,report}" command.
6731 */
6732 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006733get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006734{
6735 switch (idx)
6736 {
6737 case 0: return (char_u *)"on";
6738 case 1: return (char_u *)"off";
6739 case 2: return (char_u *)"clear";
6740 case 3: return (char_u *)"report";
6741 }
6742 return NULL;
6743}
6744#endif
6745
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006746typedef struct
6747{
6748 proftime_T total;
6749 int count;
6750 int match;
6751 proftime_T slowest;
6752 proftime_T average;
6753 int id;
6754 char_u *pattern;
6755} time_entry_T;
6756
6757 static int
6758#ifdef __BORLANDC__
6759_RTLENTRYF
6760#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006761syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006762{
6763 const time_entry_T *s1 = v1;
6764 const time_entry_T *s2 = v2;
6765
6766 return profile_cmp(&s1->total, &s2->total);
6767}
6768
6769/*
6770 * Clear the syntax timing for the current buffer.
6771 */
6772 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006773syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006774{
6775 int idx;
6776 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006777# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006778 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006779# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006780 int len;
6781 proftime_T total_total;
6782 int total_count = 0;
6783 garray_T ga;
6784 time_entry_T *p;
6785
6786 if (!syntax_present(curwin))
6787 {
6788 MSG(_(msg_no_items));
6789 return;
6790 }
6791
6792 ga_init2(&ga, sizeof(time_entry_T), 50);
6793 profile_zero(&total_total);
6794 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6795 {
6796 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6797 if (spp->sp_time.count > 0)
6798 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006799 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006800 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6801 p->total = spp->sp_time.total;
6802 profile_add(&total_total, &spp->sp_time.total);
6803 p->count = spp->sp_time.count;
6804 p->match = spp->sp_time.match;
6805 total_count += spp->sp_time.count;
6806 p->slowest = spp->sp_time.slowest;
6807# ifdef FEAT_FLOAT
6808 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6809 p->average = tm;
6810# endif
6811 p->id = spp->sp_syn.id;
6812 p->pattern = spp->sp_pattern;
6813 ++ga.ga_len;
6814 }
6815 }
6816
Bram Moolenaara2162552017-01-08 17:46:20 +01006817 /* Sort on total time. Skip if there are no items to avoid passing NULL
6818 * pointer to qsort(). */
6819 if (ga.ga_len > 1)
6820 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006821 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006822
6823 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6824 MSG_PUTS("\n");
6825 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6826 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006827 p = ((time_entry_T *)ga.ga_data) + idx;
6828
6829 MSG_PUTS(profile_msg(&p->total));
6830 MSG_PUTS(" "); /* make sure there is always a separating space */
6831 msg_advance(13);
6832 msg_outnum(p->count);
6833 MSG_PUTS(" ");
6834 msg_advance(20);
6835 msg_outnum(p->match);
6836 MSG_PUTS(" ");
6837 msg_advance(26);
6838 MSG_PUTS(profile_msg(&p->slowest));
6839 MSG_PUTS(" ");
6840 msg_advance(38);
6841# ifdef FEAT_FLOAT
6842 MSG_PUTS(profile_msg(&p->average));
6843 MSG_PUTS(" ");
6844# endif
6845 msg_advance(50);
6846 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6847 MSG_PUTS(" ");
6848
6849 msg_advance(69);
6850 if (Columns < 80)
6851 len = 20; /* will wrap anyway */
6852 else
6853 len = Columns - 70;
6854 if (len > (int)STRLEN(p->pattern))
6855 len = (int)STRLEN(p->pattern);
6856 msg_outtrans_len(p->pattern, len);
6857 MSG_PUTS("\n");
6858 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006859 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006860 if (!got_int)
6861 {
6862 MSG_PUTS("\n");
6863 MSG_PUTS(profile_msg(&total_total));
6864 msg_advance(13);
6865 msg_outnum(total_count);
6866 MSG_PUTS("\n");
6867 }
6868}
6869#endif
6870
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871#endif /* FEAT_SYN_HL */
6872
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873/**************************************
6874 * Highlighting stuff *
6875 **************************************/
6876
6877/*
6878 * The default highlight groups. These are compiled-in for fast startup and
6879 * they still work when the runtime files can't be found.
6880 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006881 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6882 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006883 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006884#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006885# define CENT(a, b) b
6886#else
6887# define CENT(a, b) a
6888#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006889static char *(highlight_init_both[]) = {
6890 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6891 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6892 CENT("IncSearch term=reverse cterm=reverse",
6893 "IncSearch term=reverse cterm=reverse gui=reverse"),
6894 CENT("ModeMsg term=bold cterm=bold",
6895 "ModeMsg term=bold cterm=bold gui=bold"),
6896 CENT("NonText term=bold ctermfg=Blue",
6897 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6898 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6899 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6900 CENT("StatusLineNC term=reverse cterm=reverse",
6901 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6902 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006903 CENT("VertSplit term=reverse cterm=reverse",
6904 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006905#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006906 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6907 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006908#endif
6909#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006910 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6911 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006912#endif
6913#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006914 CENT("PmenuSbar ctermbg=Grey",
6915 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006916#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006917 CENT("TabLineSel term=bold cterm=bold",
6918 "TabLineSel term=bold cterm=bold gui=bold"),
6919 CENT("TabLineFill term=reverse cterm=reverse",
6920 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006921#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006922 "Cursor guibg=fg guifg=bg",
6923 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006924#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006925 "default link QuickFixLine Search",
6926 NULL
6927};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006928
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006929/* Default colors only used with a light background. */
6930static char *(highlight_init_light[]) = {
6931 CENT("Directory term=bold ctermfg=DarkBlue",
6932 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6933 CENT("LineNr term=underline ctermfg=Brown",
6934 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6935 CENT("CursorLineNr term=bold ctermfg=Brown",
6936 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6937 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6938 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6939 CENT("Question term=standout ctermfg=DarkGreen",
6940 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6941 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6942 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006943#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006944 CENT("SpellBad term=reverse ctermbg=LightRed",
6945 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6946 CENT("SpellCap term=reverse ctermbg=LightBlue",
6947 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6948 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6949 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6950 CENT("SpellLocal term=underline ctermbg=Cyan",
6951 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006952#endif
6953#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006954 CENT("PmenuThumb ctermbg=Black",
6955 "PmenuThumb ctermbg=Black guibg=Black"),
6956 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6957 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6958 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6959 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006960#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006961 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6962 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6963 CENT("Title term=bold ctermfg=DarkMagenta",
6964 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6965 CENT("WarningMsg term=standout ctermfg=DarkRed",
6966 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006967#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006968 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6969 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006970#endif
6971#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006972 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6973 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6974 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6975 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006976#endif
6977#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006978 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6979 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006980#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006981 CENT("Visual term=reverse",
6982 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006983#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006984 CENT("DiffAdd term=bold ctermbg=LightBlue",
6985 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6986 CENT("DiffChange term=bold ctermbg=LightMagenta",
6987 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6988 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6989 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006990#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006991 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6992 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006993#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006994 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6995 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6996 CENT("CursorLine term=underline cterm=underline",
6997 "CursorLine term=underline cterm=underline guibg=Grey90"),
6998 CENT("ColorColumn term=reverse ctermbg=LightRed",
6999 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007000#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007001#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007002 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7003 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007004#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007005 CENT("MatchParen term=reverse ctermbg=Cyan",
7006 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007007#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007008 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007009#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007010#ifdef FEAT_TERMINAL
7011 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7012 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7013 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7014 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7015#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007016#ifdef FEAT_MENU
7017 CENT("ToolbarLine term=underline ctermbg=LightGrey",
7018 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
7019 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007020 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007021#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007022 NULL
7023};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007024
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007025/* Default colors only used with a dark background. */
7026static char *(highlight_init_dark[]) = {
7027 CENT("Directory term=bold ctermfg=LightCyan",
7028 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7029 CENT("LineNr term=underline ctermfg=Yellow",
7030 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7031 CENT("CursorLineNr term=bold ctermfg=Yellow",
7032 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7033 CENT("MoreMsg term=bold ctermfg=LightGreen",
7034 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7035 CENT("Question term=standout ctermfg=LightGreen",
7036 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7037 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7038 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7039 CENT("SpecialKey term=bold ctermfg=LightBlue",
7040 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007041#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007042 CENT("SpellBad term=reverse ctermbg=Red",
7043 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7044 CENT("SpellCap term=reverse ctermbg=Blue",
7045 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7046 CENT("SpellRare term=reverse ctermbg=Magenta",
7047 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7048 CENT("SpellLocal term=underline ctermbg=Cyan",
7049 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007050#endif
7051#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007052 CENT("PmenuThumb ctermbg=White",
7053 "PmenuThumb ctermbg=White guibg=White"),
7054 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7055 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7056 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7057 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007058#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007059 CENT("Title term=bold ctermfg=LightMagenta",
7060 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7061 CENT("WarningMsg term=standout ctermfg=LightRed",
7062 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007063#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007064 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7065 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007066#endif
7067#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007068 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7069 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7070 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7071 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007072#endif
7073#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007074 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7075 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007076#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007077 CENT("Visual term=reverse",
7078 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007079#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007080 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7081 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7082 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7083 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7084 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7085 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007086#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007087 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7088 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007089#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007090 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7091 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7092 CENT("CursorLine term=underline cterm=underline",
7093 "CursorLine term=underline cterm=underline guibg=Grey40"),
7094 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7095 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007096#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007097 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7098 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007099#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007100 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7101 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007102#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007103#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007104 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007105#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007106#ifdef FEAT_TERMINAL
7107 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7108 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7109 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7110 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7111#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007112#ifdef FEAT_MENU
7113 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007114 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007115 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7116 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7117#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007118 NULL
7119};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007120
7121 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007122init_highlight(
7123 int both, /* include groups where 'bg' doesn't matter */
7124 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007125{
7126 int i;
7127 char **pp;
7128 static int had_both = FALSE;
7129#ifdef FEAT_EVAL
7130 char_u *p;
7131
7132 /*
7133 * Try finding the color scheme file. Used when a color file was loaded
7134 * and 'background' or 't_Co' is changed.
7135 */
7136 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007137 if (p != NULL)
7138 {
7139 /* The value of g:colors_name could be freed when sourcing the script,
7140 * making "p" invalid, so copy it. */
7141 char_u *copy_p = vim_strsave(p);
7142 int r;
7143
7144 if (copy_p != NULL)
7145 {
7146 r = load_colors(copy_p);
7147 vim_free(copy_p);
7148 if (r == OK)
7149 return;
7150 }
7151 }
7152
Bram Moolenaar071d4272004-06-13 20:20:40 +00007153#endif
7154
7155 /*
7156 * Didn't use a color file, use the compiled-in colors.
7157 */
7158 if (both)
7159 {
7160 had_both = TRUE;
7161 pp = highlight_init_both;
7162 for (i = 0; pp[i] != NULL; ++i)
7163 do_highlight((char_u *)pp[i], reset, TRUE);
7164 }
7165 else if (!had_both)
7166 /* Don't do anything before the call with both == TRUE from main().
7167 * Not everything has been setup then, and that call will overrule
7168 * everything anyway. */
7169 return;
7170
7171 if (*p_bg == 'l')
7172 pp = highlight_init_light;
7173 else
7174 pp = highlight_init_dark;
7175 for (i = 0; pp[i] != NULL; ++i)
7176 do_highlight((char_u *)pp[i], reset, TRUE);
7177
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007178 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007179 * depend on the number of colors available.
7180 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007181 * to avoid Statement highlighted text disappears.
7182 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007183 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007184 do_highlight((char_u *)(*p_bg == 'l'
7185 ? "Visual cterm=NONE ctermbg=LightGrey"
7186 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007187 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007188 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007189 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7190 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007191 if (*p_bg == 'l')
7192 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7193 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007194
Bram Moolenaar071d4272004-06-13 20:20:40 +00007195#ifdef FEAT_SYN_HL
7196 /*
7197 * If syntax highlighting is enabled load the highlighting for it.
7198 */
7199 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007200 {
7201 static int recursive = 0;
7202
7203 if (recursive >= 5)
7204 EMSG(_("E679: recursive loop loading syncolor.vim"));
7205 else
7206 {
7207 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007208 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007209 --recursive;
7210 }
7211 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212#endif
7213}
7214
7215/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007216 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007217 * Return OK for success, FAIL for failure.
7218 */
7219 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007220load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007221{
7222 char_u *buf;
7223 int retval = FAIL;
7224 static int recursive = FALSE;
7225
7226 /* When being called recursively, this is probably because setting
7227 * 'background' caused the highlighting to be reloaded. This means it is
7228 * working, thus we should return OK. */
7229 if (recursive)
7230 return OK;
7231
7232 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007233 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007234 if (buf != NULL)
7235 {
Bram Moolenaar60a68362018-04-30 15:40:48 +02007236 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
7237 curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007238 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007239 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007240 vim_free(buf);
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007241 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242 }
7243 recursive = FALSE;
7244
7245 return retval;
7246}
7247
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007248static char *(color_names[28]) = {
7249 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7250 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7251 "Gray", "Grey", "LightGray", "LightGrey",
7252 "DarkGray", "DarkGrey",
7253 "Blue", "LightBlue", "Green", "LightGreen",
7254 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7255 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7256 /* indices:
7257 * 0, 1, 2, 3,
7258 * 4, 5, 6, 7,
7259 * 8, 9, 10, 11,
7260 * 12, 13,
7261 * 14, 15, 16, 17,
7262 * 18, 19, 20, 21, 22,
7263 * 23, 24, 25, 26, 27 */
7264static int color_numbers_16[28] = {0, 1, 2, 3,
7265 4, 5, 6, 6,
7266 7, 7, 7, 7,
7267 8, 8,
7268 9, 9, 10, 10,
7269 11, 11, 12, 12, 13,
7270 13, 14, 14, 15, -1};
7271/* for xterm with 88 colors... */
7272static int color_numbers_88[28] = {0, 4, 2, 6,
7273 1, 5, 32, 72,
7274 84, 84, 7, 7,
7275 82, 82,
7276 12, 43, 10, 61,
7277 14, 63, 9, 74, 13,
7278 75, 11, 78, 15, -1};
7279/* for xterm with 256 colors... */
7280static int color_numbers_256[28] = {0, 4, 2, 6,
7281 1, 5, 130, 130,
7282 248, 248, 7, 7,
7283 242, 242,
7284 12, 81, 10, 121,
7285 14, 159, 9, 224, 13,
7286 225, 11, 229, 15, -1};
7287/* for terminals with less than 16 colors... */
7288static int color_numbers_8[28] = {0, 4, 2, 6,
7289 1, 5, 3, 3,
7290 7, 7, 7, 7,
7291 0+8, 0+8,
7292 4+8, 4+8, 2+8, 2+8,
7293 6+8, 6+8, 1+8, 1+8, 5+8,
7294 5+8, 3+8, 3+8, 7+8, -1};
7295
7296/*
7297 * Lookup the "cterm" value to be used for color with index "idx" in
7298 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007299 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7300 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007301 */
7302 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007303lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007304{
7305 int color = color_numbers_16[idx];
7306 char_u *p;
7307
7308 /* Use the _16 table to check if it's a valid color name. */
7309 if (color < 0)
7310 return -1;
7311
7312 if (t_colors == 8)
7313 {
7314 /* t_Co is 8: use the 8 colors table */
7315#if defined(__QNXNTO__)
7316 color = color_numbers_8_qansi[idx];
7317#else
7318 color = color_numbers_8[idx];
7319#endif
7320 if (foreground)
7321 {
7322 /* set/reset bold attribute to get light foreground
7323 * colors (on some terminals, e.g. "linux") */
7324 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007325 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007326 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007327 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007328 }
7329 color &= 7; /* truncate to 8 colors */
7330 }
7331 else if (t_colors == 16 || t_colors == 88
7332 || t_colors >= 256)
7333 {
7334 /*
7335 * Guess: if the termcap entry ends in 'm', it is
7336 * probably an xterm-like terminal. Use the changed
7337 * order for colors.
7338 */
7339 if (*T_CAF != NUL)
7340 p = T_CAF;
7341 else
7342 p = T_CSF;
7343 if (*p != NUL && (t_colors > 256
7344 || *(p + STRLEN(p) - 1) == 'm'))
7345 {
7346 if (t_colors == 88)
7347 color = color_numbers_88[idx];
7348 else if (t_colors >= 256)
7349 color = color_numbers_256[idx];
7350 else
7351 color = color_numbers_8[idx];
7352 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007353#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007354 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7355 /* Terminal.app has a bug: 15 is light grey. Use white
7356 * from the color cube instead. */
7357 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007358#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007359 }
7360 return color;
7361}
7362
Bram Moolenaar071d4272004-06-13 20:20:40 +00007363/*
7364 * Handle the ":highlight .." command.
7365 * When using ":hi clear" this is called recursively for each group with
7366 * "forceit" and "init" both TRUE.
7367 */
7368 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007369do_highlight(
7370 char_u *line,
7371 int forceit,
7372 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007373{
7374 char_u *name_end;
7375 char_u *p;
7376 char_u *linep;
7377 char_u *key_start;
7378 char_u *arg_start;
7379 char_u *key = NULL, *arg = NULL;
7380 long i;
7381 int off;
7382 int len;
7383 int attr;
7384 int id;
7385 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007386 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007387 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007388 int dodefault = FALSE;
7389 int doclear = FALSE;
7390 int dolink = FALSE;
7391 int error = FALSE;
7392 int color;
7393 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007394#ifdef FEAT_TERMINAL
7395 int is_terminal_group = FALSE; /* "Terminal" group */
7396#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007397#ifdef FEAT_GUI_X11
7398 int is_menu_group = FALSE; /* "Menu" group */
7399 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7400 int is_tooltip_group = FALSE; /* "Tooltip" group */
7401 int do_colors = FALSE; /* need to update colors? */
7402#else
7403# define is_menu_group 0
7404# define is_tooltip_group 0
7405#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007406#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7407 int did_highlight_changed = FALSE;
7408#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007409
7410 /*
7411 * If no argument, list current highlighting.
7412 */
7413 if (ends_excmd(*line))
7414 {
7415 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7416 /* TODO: only call when the group has attributes set */
7417 highlight_list_one((int)i);
7418 return;
7419 }
7420
7421 /*
7422 * Isolate the name.
7423 */
7424 name_end = skiptowhite(line);
7425 linep = skipwhite(name_end);
7426
7427 /*
7428 * Check for "default" argument.
7429 */
7430 if (STRNCMP(line, "default", name_end - line) == 0)
7431 {
7432 dodefault = TRUE;
7433 line = linep;
7434 name_end = skiptowhite(line);
7435 linep = skipwhite(name_end);
7436 }
7437
7438 /*
7439 * Check for "clear" or "link" argument.
7440 */
7441 if (STRNCMP(line, "clear", name_end - line) == 0)
7442 doclear = TRUE;
7443 if (STRNCMP(line, "link", name_end - line) == 0)
7444 dolink = TRUE;
7445
7446 /*
7447 * ":highlight {group-name}": list highlighting for one group.
7448 */
7449 if (!doclear && !dolink && ends_excmd(*linep))
7450 {
7451 id = syn_namen2id(line, (int)(name_end - line));
7452 if (id == 0)
7453 EMSG2(_("E411: highlight group not found: %s"), line);
7454 else
7455 highlight_list_one(id);
7456 return;
7457 }
7458
7459 /*
7460 * Handle ":highlight link {from} {to}" command.
7461 */
7462 if (dolink)
7463 {
7464 char_u *from_start = linep;
7465 char_u *from_end;
7466 char_u *to_start;
7467 char_u *to_end;
7468 int from_id;
7469 int to_id;
7470
7471 from_end = skiptowhite(from_start);
7472 to_start = skipwhite(from_end);
7473 to_end = skiptowhite(to_start);
7474
7475 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7476 {
7477 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7478 from_start);
7479 return;
7480 }
7481
7482 if (!ends_excmd(*skipwhite(to_end)))
7483 {
7484 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7485 return;
7486 }
7487
7488 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7489 if (STRNCMP(to_start, "NONE", 4) == 0)
7490 to_id = 0;
7491 else
7492 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7493
Bram Moolenaar414168d2017-09-10 15:21:55 +02007494 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007495 {
7496 /*
7497 * Don't allow a link when there already is some highlighting
7498 * for the group, unless '!' is used
7499 */
7500 if (to_id > 0 && !forceit && !init
7501 && hl_has_settings(from_id - 1, dodefault))
7502 {
7503 if (sourcing_name == NULL && !dodefault)
7504 EMSG(_("E414: group has settings, highlight link ignored"));
7505 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007506 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007507#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007508 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
7509 != current_sctx.sc_sid
Bram Moolenaar99433292017-09-08 12:37:47 +02007510#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007511 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007512 {
7513 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007514 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7515 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007516#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02007517 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
7518 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007519#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007520 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007521 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007522
7523 /* Only call highlight_changed() once after multiple changes. */
7524 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007525 }
7526 }
7527
Bram Moolenaar071d4272004-06-13 20:20:40 +00007528 return;
7529 }
7530
7531 if (doclear)
7532 {
7533 /*
7534 * ":highlight clear [group]" command.
7535 */
7536 line = linep;
7537 if (ends_excmd(*line))
7538 {
7539#ifdef FEAT_GUI
7540 /* First, we do not destroy the old values, but allocate the new
7541 * ones and update the display. THEN we destroy the old values.
7542 * If we destroy the old values first, then the old values
7543 * (such as GuiFont's or GuiFontset's) will still be displayed but
7544 * invalid because they were free'd.
7545 */
7546 if (gui.in_use)
7547 {
7548# ifdef FEAT_BEVAL_TIP
7549 gui_init_tooltip_font();
7550# endif
7551# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7552 gui_init_menu_font();
7553# endif
7554 }
7555# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7556 gui_mch_def_colors();
7557# endif
7558# ifdef FEAT_GUI_X11
7559# ifdef FEAT_MENU
7560
7561 /* This only needs to be done when there is no Menu highlight
7562 * group defined by default, which IS currently the case.
7563 */
7564 gui_mch_new_menu_colors();
7565# endif
7566 if (gui.in_use)
7567 {
7568 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007569# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007570 gui_mch_new_tooltip_colors();
7571# endif
7572# ifdef FEAT_MENU
7573 gui_mch_new_menu_font();
7574# endif
7575 }
7576# endif
7577
7578 /* Ok, we're done allocating the new default graphics items.
7579 * The screen should already be refreshed at this point.
7580 * It is now Ok to clear out the old data.
7581 */
7582#endif
7583#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007584 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007585#endif
7586 restore_cterm_colors();
7587
7588 /*
7589 * Clear all default highlight groups and load the defaults.
7590 */
7591 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7592 highlight_clear(idx);
7593 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007594#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007595 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007596 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007597 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007598#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007599 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007600 redraw_later_clear();
7601 return;
7602 }
7603 name_end = skiptowhite(line);
7604 linep = skipwhite(name_end);
7605 }
7606
7607 /*
7608 * Find the group name in the table. If it does not exist yet, add it.
7609 */
7610 id = syn_check_group(line, (int)(name_end - line));
7611 if (id == 0) /* failed (out of memory) */
7612 return;
7613 idx = id - 1; /* index is ID minus one */
7614
7615 /* Return if "default" was used and the group already has settings. */
7616 if (dodefault && hl_has_settings(idx, TRUE))
7617 return;
7618
Bram Moolenaar99433292017-09-08 12:37:47 +02007619 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007620 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007621
Bram Moolenaar414168d2017-09-10 15:21:55 +02007622 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007623 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007624#ifdef FEAT_TERMINAL
7625 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7626 is_terminal_group = TRUE;
7627#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007628#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007629 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007630 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007631 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007632 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007633 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007634 is_tooltip_group = TRUE;
7635#endif
7636
7637 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7638 if (doclear || (forceit && init))
7639 {
7640 highlight_clear(idx);
7641 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007642 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007643 }
7644
7645 if (!doclear)
7646 while (!ends_excmd(*linep))
7647 {
7648 key_start = linep;
7649 if (*linep == '=')
7650 {
7651 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7652 error = TRUE;
7653 break;
7654 }
7655
7656 /*
7657 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7658 * "guibg").
7659 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007660 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007661 ++linep;
7662 vim_free(key);
7663 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7664 if (key == NULL)
7665 {
7666 error = TRUE;
7667 break;
7668 }
7669 linep = skipwhite(linep);
7670
7671 if (STRCMP(key, "NONE") == 0)
7672 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007673 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007674 {
7675 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007676 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007677 highlight_clear(idx);
7678 }
7679 continue;
7680 }
7681
7682 /*
7683 * Check for the equal sign.
7684 */
7685 if (*linep != '=')
7686 {
7687 EMSG2(_("E416: missing equal sign: %s"), key_start);
7688 error = TRUE;
7689 break;
7690 }
7691 ++linep;
7692
7693 /*
7694 * Isolate the argument.
7695 */
7696 linep = skipwhite(linep);
7697 if (*linep == '\'') /* guifg='color name' */
7698 {
7699 arg_start = ++linep;
7700 linep = vim_strchr(linep, '\'');
7701 if (linep == NULL)
7702 {
7703 EMSG2(_(e_invarg2), key_start);
7704 error = TRUE;
7705 break;
7706 }
7707 }
7708 else
7709 {
7710 arg_start = linep;
7711 linep = skiptowhite(linep);
7712 }
7713 if (linep == arg_start)
7714 {
7715 EMSG2(_("E417: missing argument: %s"), key_start);
7716 error = TRUE;
7717 break;
7718 }
7719 vim_free(arg);
7720 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7721 if (arg == NULL)
7722 {
7723 error = TRUE;
7724 break;
7725 }
7726 if (*linep == '\'')
7727 ++linep;
7728
7729 /*
7730 * Store the argument.
7731 */
7732 if ( STRCMP(key, "TERM") == 0
7733 || STRCMP(key, "CTERM") == 0
7734 || STRCMP(key, "GUI") == 0)
7735 {
7736 attr = 0;
7737 off = 0;
7738 while (arg[off] != NUL)
7739 {
7740 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7741 {
7742 len = (int)STRLEN(hl_name_table[i]);
7743 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7744 {
7745 attr |= hl_attr_table[i];
7746 off += len;
7747 break;
7748 }
7749 }
7750 if (i < 0)
7751 {
7752 EMSG2(_("E418: Illegal value: %s"), arg);
7753 error = TRUE;
7754 break;
7755 }
7756 if (arg[off] == ',') /* another one follows */
7757 ++off;
7758 }
7759 if (error)
7760 break;
7761 if (*key == 'T')
7762 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007763 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007764 {
7765 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007766 HL_TABLE()[idx].sg_set |= SG_TERM;
7767 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007768 }
7769 }
7770 else if (*key == 'C')
7771 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007772 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773 {
7774 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007775 HL_TABLE()[idx].sg_set |= SG_CTERM;
7776 HL_TABLE()[idx].sg_cterm = attr;
7777 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778 }
7779 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007780#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007781 else
7782 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007783 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007784 {
7785 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007786 HL_TABLE()[idx].sg_set |= SG_GUI;
7787 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007788 }
7789 }
7790#endif
7791 }
7792 else if (STRCMP(key, "FONT") == 0)
7793 {
7794 /* in non-GUI fonts are simply ignored */
7795#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007796 if (HL_TABLE()[idx].sg_font_name != NULL
7797 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007798 {
7799 /* Font name didn't change, ignore. */
7800 }
7801 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007802 {
7803 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007804 vim_free(HL_TABLE()[idx].sg_font_name);
7805 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007806 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007807 }
7808 else
7809 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007810 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007812 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813# endif
7814 /* First, save the current font/fontset.
7815 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007816 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007817 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7818 */
7819
Bram Moolenaar414168d2017-09-10 15:21:55 +02007820 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007822 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823# endif
7824 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007825 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007826
7827# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007828 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007829 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007830 /* New fontset was accepted. Free the old one, if there
7831 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007832 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007833 vim_free(HL_TABLE()[idx].sg_font_name);
7834 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007835 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836 }
7837 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007838 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007839# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007840 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007841 {
7842 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007843 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007844 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007845 vim_free(HL_TABLE()[idx].sg_font_name);
7846 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007847 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007848 }
7849 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007850 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007851 }
7852#endif
7853 }
7854 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7855 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007856 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007857 {
7858 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007859 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007860
7861 /* When setting the foreground color, and previously the "bold"
7862 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007863 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007864 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007865 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7866 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007867 }
7868
7869 if (VIM_ISDIGIT(*arg))
7870 color = atoi((char *)arg);
7871 else if (STRICMP(arg, "fg") == 0)
7872 {
7873 if (cterm_normal_fg_color)
7874 color = cterm_normal_fg_color - 1;
7875 else
7876 {
7877 EMSG(_("E419: FG color unknown"));
7878 error = TRUE;
7879 break;
7880 }
7881 }
7882 else if (STRICMP(arg, "bg") == 0)
7883 {
7884 if (cterm_normal_bg_color > 0)
7885 color = cterm_normal_bg_color - 1;
7886 else
7887 {
7888 EMSG(_("E420: BG color unknown"));
7889 error = TRUE;
7890 break;
7891 }
7892 }
7893 else
7894 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007895 int bold = MAYBE;
7896
Bram Moolenaar071d4272004-06-13 20:20:40 +00007897#if defined(__QNXNTO__)
7898 static int *color_numbers_8_qansi = color_numbers_8;
7899 /* On qnx, the 8 & 16 color arrays are the same */
7900 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7901 color_numbers_8_qansi = color_numbers_16;
7902#endif
7903
7904 /* reduce calls to STRICMP a bit, it can be slow */
7905 off = TOUPPER_ASC(*arg);
7906 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7907 if (off == color_names[i][0]
7908 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7909 break;
7910 if (i < 0)
7911 {
7912 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7913 error = TRUE;
7914 break;
7915 }
7916
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007917 color = lookup_color(i, key[5] == 'F', &bold);
7918
7919 /* set/reset bold attribute to get light foreground
7920 * colors (on some terminals, e.g. "linux") */
7921 if (bold == TRUE)
7922 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007923 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7924 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007925 }
7926 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007927 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007928 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007929
Bram Moolenaarccbab932010-05-13 15:40:30 +02007930 /* Add one to the argument, to avoid zero. Zero is used for
7931 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007932 if (key[5] == 'F')
7933 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007934 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007935 if (is_normal_group)
7936 {
7937 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007938 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007939#ifdef FEAT_GUI
7940 /* Don't do this if the GUI is used. */
7941 if (!gui.in_use && !gui.starting)
7942#endif
7943 {
7944 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007945 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007946 term_fg_color(color);
7947 }
7948 }
7949 }
7950 else
7951 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007952 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007953 if (is_normal_group)
7954 {
7955 cterm_normal_bg_color = color + 1;
7956#ifdef FEAT_GUI
7957 /* Don't mess with 'background' if the GUI is used. */
7958 if (!gui.in_use && !gui.starting)
7959#endif
7960 {
7961 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007962 if (color >= 0)
7963 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007964 int dark = -1;
7965
Bram Moolenaarccbab932010-05-13 15:40:30 +02007966 if (termcap_active)
7967 term_bg_color(color);
7968 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007969 dark = (color == 0 || color == 4);
7970 /* Limit the heuristic to the standard 16 colors */
7971 else if (color < 16)
7972 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007973 /* Set the 'background' option if the value is
7974 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007975 if (dark != -1
7976 && dark != (*p_bg == 'd')
7977 && !option_was_set((char_u *)"bg"))
7978 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007979 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007980 (char_u *)(dark ? "dark" : "light"), 0);
7981 reset_option_was_set((char_u *)"bg");
7982 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007983 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007984 }
7985 }
7986 }
7987 }
7988 }
7989 else if (STRCMP(key, "GUIFG") == 0)
7990 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007991#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007992 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7993
Bram Moolenaar414168d2017-09-10 15:21:55 +02007994 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007995 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007996 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007997 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007998
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007999# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008000 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008001 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02008002 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008003 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008004 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008005# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008006 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8007 {
8008 vim_free(*namep);
8009 if (STRCMP(arg, "NONE") != 0)
8010 *namep = vim_strsave(arg);
8011 else
8012 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008013 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008014 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008015# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008016# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008017 if (is_menu_group && gui.menu_fg_pixel != i)
8018 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008019 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008020 do_colors = TRUE;
8021 }
8022 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8023 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008024 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008025 do_colors = TRUE;
8026 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008027# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008028 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8029 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008030 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008031 do_colors = TRUE;
8032 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008033# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008034# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008035 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008036# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008037 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008038#endif
8039 }
8040 else if (STRCMP(key, "GUIBG") == 0)
8041 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008042#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008043 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8044
Bram Moolenaar414168d2017-09-10 15:21:55 +02008045 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008046 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008047 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008048 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008049
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008050# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008051 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008052 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008053 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008054 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008055 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008056# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008057 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8058 {
8059 vim_free(*namep);
8060 if (STRCMP(arg, "NONE") != 0)
8061 *namep = vim_strsave(arg);
8062 else
8063 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008064 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008065 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008066# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008067# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008068 if (is_menu_group && gui.menu_bg_pixel != i)
8069 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008070 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008071 do_colors = TRUE;
8072 }
8073 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8074 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008075 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008076 do_colors = TRUE;
8077 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008078# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008079 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8080 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008081 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008082 do_colors = TRUE;
8083 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008084# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008085# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008086 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008087# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008088 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008089#endif
8090 }
8091 else if (STRCMP(key, "GUISP") == 0)
8092 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008093#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008094 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8095
Bram Moolenaar414168d2017-09-10 15:21:55 +02008096 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008097 {
8098 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008099 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008100
Bram Moolenaar61623362010-07-14 22:04:22 +02008101# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008102 i = color_name2handle(arg);
8103 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8104 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008105 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008106# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008107 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8108 {
8109 vim_free(*namep);
8110 if (STRCMP(arg, "NONE") != 0)
8111 *namep = vim_strsave(arg);
8112 else
8113 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008114 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008115 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008116# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008117 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008118# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008119 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008120#endif
8121 }
8122 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8123 {
8124 char_u buf[100];
8125 char_u *tname;
8126
8127 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008128 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008129
8130 /*
8131 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008132 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008133 */
8134 if (STRNCMP(arg, "t_", 2) == 0)
8135 {
8136 off = 0;
8137 buf[0] = 0;
8138 while (arg[off] != NUL)
8139 {
8140 /* Isolate one termcap name */
8141 for (len = 0; arg[off + len] &&
8142 arg[off + len] != ','; ++len)
8143 ;
8144 tname = vim_strnsave(arg + off, len);
8145 if (tname == NULL) /* out of memory */
8146 {
8147 error = TRUE;
8148 break;
8149 }
8150 /* lookup the escape sequence for the item */
8151 p = get_term_code(tname);
8152 vim_free(tname);
8153 if (p == NULL) /* ignore non-existing things */
8154 p = (char_u *)"";
8155
8156 /* Append it to the already found stuff */
8157 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8158 {
8159 EMSG2(_("E422: terminal code too long: %s"), arg);
8160 error = TRUE;
8161 break;
8162 }
8163 STRCAT(buf, p);
8164
8165 /* Advance to the next item */
8166 off += len;
8167 if (arg[off] == ',') /* another one follows */
8168 ++off;
8169 }
8170 }
8171 else
8172 {
8173 /*
8174 * Copy characters from arg[] to buf[], translating <> codes.
8175 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008176 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008177 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008178 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008179 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008180 off += len;
8181 else /* copy as normal char */
8182 buf[off++] = *p++;
8183 }
8184 buf[off] = NUL;
8185 }
8186 if (error)
8187 break;
8188
8189 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8190 p = NULL;
8191 else
8192 p = vim_strsave(buf);
8193 if (key[2] == 'A')
8194 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008195 vim_free(HL_TABLE()[idx].sg_start);
8196 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197 }
8198 else
8199 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008200 vim_free(HL_TABLE()[idx].sg_stop);
8201 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008202 }
8203 }
8204 else
8205 {
8206 EMSG2(_("E423: Illegal argument: %s"), key_start);
8207 error = TRUE;
8208 break;
8209 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008210 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211
8212 /*
8213 * When highlighting has been given for a group, don't link it.
8214 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008215 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8216 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217
8218 /*
8219 * Continue with next argument.
8220 */
8221 linep = skipwhite(linep);
8222 }
8223
8224 /*
8225 * If there is an error, and it's a new entry, remove it from the table.
8226 */
8227 if (error && idx == highlight_ga.ga_len)
8228 syn_unadd_group();
8229 else
8230 {
8231 if (is_normal_group)
8232 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008233 HL_TABLE()[idx].sg_term_attr = 0;
8234 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008235#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008236 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237 /*
8238 * Need to update all groups, because they might be using "bg"
8239 * and/or "fg", which have been changed now.
8240 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008241#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008242#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008243 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008244 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008245 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008246 did_highlight_changed = TRUE;
8247 redraw_all_later(NOT_VALID);
8248 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249#endif
8250 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008251#ifdef FEAT_TERMINAL
8252 else if (is_terminal_group)
8253 set_terminal_default_colors(
8254 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8255#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008256#ifdef FEAT_GUI_X11
8257# ifdef FEAT_MENU
8258 else if (is_menu_group)
8259 {
8260 if (gui.in_use && do_colors)
8261 gui_mch_new_menu_colors();
8262 }
8263# endif
8264 else if (is_scrollbar_group)
8265 {
8266 if (gui.in_use && do_colors)
8267 gui_new_scrollbar_colors();
8268 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008269# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008270 else if (is_tooltip_group)
8271 {
8272 if (gui.in_use && do_colors)
8273 gui_mch_new_tooltip_colors();
8274 }
8275# endif
8276#endif
8277 else
8278 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008279#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008280 HL_TABLE()[idx].sg_script_ctx = current_sctx;
8281 HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008282#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008283 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008284
Bram Moolenaar071d4272004-06-13 20:20:40 +00008285 vim_free(key);
8286 vim_free(arg);
8287
Bram Moolenaar99433292017-09-08 12:37:47 +02008288 /* Only call highlight_changed() once, after a sequence of highlight
8289 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008290 if ((did_change
8291 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008292#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8293 && !did_highlight_changed
8294#endif
8295 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008296 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008297 /* Do not trigger a redraw when highlighting is changed while
8298 * redrawing. This may happen when evaluating 'statusline' changes the
8299 * StatusLine group. */
8300 if (!updating_screen)
8301 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008302 need_highlight_changed = TRUE;
8303 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008304}
8305
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008306#if defined(EXITFREE) || defined(PROTO)
8307 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008308free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008309{
8310 int i;
8311
8312 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008313 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008314 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008315 vim_free(HL_TABLE()[i].sg_name);
8316 vim_free(HL_TABLE()[i].sg_name_u);
8317 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008318 ga_clear(&highlight_ga);
8319}
8320#endif
8321
Bram Moolenaar071d4272004-06-13 20:20:40 +00008322/*
8323 * Reset the cterm colors to what they were before Vim was started, if
8324 * possible. Otherwise reset them to zero.
8325 */
8326 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008327restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008328{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008329#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008330 /* Since t_me has been set, this probably means that the user
8331 * wants to use this as default colors. Need to reset default
8332 * background/foreground colors. */
8333 mch_set_normal_colors();
8334#else
8335 cterm_normal_fg_color = 0;
8336 cterm_normal_fg_bold = 0;
8337 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008338# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008339 cterm_normal_fg_gui_color = INVALCOLOR;
8340 cterm_normal_bg_gui_color = INVALCOLOR;
8341# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008342#endif
8343}
8344
8345/*
8346 * Return TRUE if highlight group "idx" has any settings.
8347 * When "check_link" is TRUE also check for an existing link.
8348 */
8349 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008350hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008351{
8352 return ( HL_TABLE()[idx].sg_term_attr != 0
8353 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008354 || HL_TABLE()[idx].sg_cterm_fg != 0
8355 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008356#ifdef FEAT_GUI
8357 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008358 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8359 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8360 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008361 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362#endif
8363 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8364}
8365
8366/*
8367 * Clear highlighting for one group.
8368 */
8369 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008370highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008371{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008372 HL_TABLE()[idx].sg_cleared = TRUE;
8373
Bram Moolenaar071d4272004-06-13 20:20:40 +00008374 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008375 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8376 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008377 HL_TABLE()[idx].sg_term_attr = 0;
8378 HL_TABLE()[idx].sg_cterm = 0;
8379 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8380 HL_TABLE()[idx].sg_cterm_fg = 0;
8381 HL_TABLE()[idx].sg_cterm_bg = 0;
8382 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008383#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008384 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008385 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8386 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8387 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008388#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008389#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008390 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8391 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008392#endif
8393#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008394 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008395 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8396 HL_TABLE()[idx].sg_font = NOFONT;
8397# ifdef FEAT_XFONTSET
8398 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8399 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8400# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008401 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008402 HL_TABLE()[idx].sg_gui_attr = 0;
8403#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008404#ifdef FEAT_EVAL
8405 /* Clear the script ID only when there is no link, since that is not
8406 * cleared. */
8407 if (HL_TABLE()[idx].sg_link == 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02008408 {
8409 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
8410 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
8411 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008412#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008413}
8414
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008415#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008416/*
8417 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008418 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008419 * "Tooltip" colors.
8420 */
8421 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008422set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008423{
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008424# ifdef FEAT_GUI
8425# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008426 if (gui.in_use)
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008427# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008428 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008429 if (set_group_colors((char_u *)"Normal",
8430 &gui.norm_pixel, &gui.back_pixel,
8431 FALSE, TRUE, FALSE))
8432 {
8433 gui_mch_new_colors();
8434 must_redraw = CLEAR;
8435 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008436# ifdef FEAT_GUI_X11
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008437 if (set_group_colors((char_u *)"Menu",
8438 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8439 TRUE, FALSE, FALSE))
8440 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008441# ifdef FEAT_MENU
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008442 gui_mch_new_menu_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008443# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008444 must_redraw = CLEAR;
8445 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008446# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008447 if (set_group_colors((char_u *)"Tooltip",
8448 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8449 FALSE, FALSE, TRUE))
8450 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008451# ifdef FEAT_TOOLBAR
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008452 gui_mch_new_tooltip_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008453# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008454 must_redraw = CLEAR;
8455 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008456# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008457 if (set_group_colors((char_u *)"Scrollbar",
8458 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8459 FALSE, FALSE, FALSE))
8460 {
8461 gui_new_scrollbar_colors();
8462 must_redraw = CLEAR;
8463 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008464# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008465 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008466# endif
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008467# ifdef FEAT_TERMGUICOLORS
8468# ifdef FEAT_GUI
8469 else
8470# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008471 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008472 int idx;
8473
8474 idx = syn_name2id((char_u *)"Normal") - 1;
8475 if (idx >= 0)
8476 {
8477 gui_do_one_color(idx, FALSE, FALSE);
8478
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008479 /* If the normal fg or bg color changed a complete redraw is
8480 * required. */
8481 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
8482 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008483 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008484 /* if the GUI color is INVALCOLOR then we use the default cterm
8485 * color */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008486 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008487 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8488 must_redraw = CLEAR;
8489 }
8490 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008491 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008492# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008493}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008494#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008495
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008496#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008497/*
8498 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8499 */
8500 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008501set_group_colors(
8502 char_u *name,
8503 guicolor_T *fgp,
8504 guicolor_T *bgp,
8505 int do_menu,
8506 int use_norm,
8507 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008508{
8509 int idx;
8510
8511 idx = syn_name2id(name) - 1;
8512 if (idx >= 0)
8513 {
8514 gui_do_one_color(idx, do_menu, do_tooltip);
8515
8516 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8517 *fgp = HL_TABLE()[idx].sg_gui_fg;
8518 else if (use_norm)
8519 *fgp = gui.def_norm_pixel;
8520 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8521 *bgp = HL_TABLE()[idx].sg_gui_bg;
8522 else if (use_norm)
8523 *bgp = gui.def_back_pixel;
8524 return TRUE;
8525 }
8526 return FALSE;
8527}
8528
8529/*
8530 * Get the font of the "Normal" group.
8531 * Returns "" when it's not found or not set.
8532 */
8533 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008534hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008535{
8536 int id;
8537 char_u *s;
8538
8539 id = syn_name2id((char_u *)"Normal");
8540 if (id > 0)
8541 {
8542 s = HL_TABLE()[id - 1].sg_font_name;
8543 if (s != NULL)
8544 return s;
8545 }
8546 return (char_u *)"";
8547}
8548
8549/*
8550 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8551 * actually chosen to be used.
8552 */
8553 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008554hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008555{
8556 int id;
8557
8558 id = syn_name2id((char_u *)"Normal");
8559 if (id > 0)
8560 {
8561 vim_free(HL_TABLE()[id - 1].sg_font_name);
8562 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8563 }
8564}
8565
8566/*
8567 * Set background color for "Normal" group. Called by gui_set_bg_color()
8568 * when the color is known.
8569 */
8570 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008571hl_set_bg_color_name(
8572 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008573{
8574 int id;
8575
8576 if (name != NULL)
8577 {
8578 id = syn_name2id((char_u *)"Normal");
8579 if (id > 0)
8580 {
8581 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8582 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8583 }
8584 }
8585}
8586
8587/*
8588 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8589 * when the color is known.
8590 */
8591 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008592hl_set_fg_color_name(
8593 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008594{
8595 int id;
8596
8597 if (name != NULL)
8598 {
8599 id = syn_name2id((char_u *)"Normal");
8600 if (id > 0)
8601 {
8602 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8603 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8604 }
8605 }
8606}
8607
8608/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008609 * Return the handle for a font name.
8610 * Returns NOFONT when failed.
8611 */
8612 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008613font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008614{
8615 if (STRCMP(name, "NONE") == 0)
8616 return NOFONT;
8617
8618 return gui_mch_get_font(name, TRUE);
8619}
8620
8621# ifdef FEAT_XFONTSET
8622/*
8623 * Return the handle for a fontset name.
8624 * Returns NOFONTSET when failed.
8625 */
8626 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008627fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628{
8629 if (STRCMP(name, "NONE") == 0)
8630 return NOFONTSET;
8631
8632 return gui_mch_get_fontset(name, TRUE, fixed_width);
8633}
8634# endif
8635
8636/*
8637 * Get the font or fontset for one highlight group.
8638 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008639 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008640hl_do_font(
8641 int idx,
8642 char_u *arg,
8643 int do_normal, /* set normal font */
8644 int do_menu UNUSED, /* set menu font */
8645 int do_tooltip UNUSED, /* set tooltip font */
8646 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008647{
8648# ifdef FEAT_XFONTSET
8649 /* If 'guifontset' is not empty, first try using the name as a
8650 * fontset. If that doesn't work, use it as a font name. */
8651 if (*p_guifontset != NUL
8652# ifdef FONTSET_ALWAYS
8653 || do_menu
8654# endif
8655# ifdef FEAT_BEVAL_TIP
8656 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8657 || do_tooltip
8658# endif
8659 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008660 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008661 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008662 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008663 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8664# ifdef FONTSET_ALWAYS
8665 || do_menu
8666# endif
8667# ifdef FEAT_BEVAL_TIP
8668 || do_tooltip
8669# endif
8670 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008671 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008672 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8673 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008674 /* If it worked and it's the Normal group, use it as the normal
8675 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008676 if (do_normal)
8677 gui_init_font(arg, TRUE);
8678# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8679 if (do_menu)
8680 {
8681# ifdef FONTSET_ALWAYS
8682 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8683# else
8684 /* YIKES! This is a bug waiting to crash the program */
8685 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8686# endif
8687 gui_mch_new_menu_font();
8688 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008689# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008690 if (do_tooltip)
8691 {
8692 /* The Athena widget set cannot currently handle switching between
8693 * displaying a single font and a fontset.
8694 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008695 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 * XFontStruct is used.
8697 */
8698 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8699 gui_mch_new_tooltip_font();
8700 }
8701# endif
8702# endif
8703 }
8704 else
8705# endif
8706 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008707 if (free_font)
8708 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008709 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8710 /* If it worked and it's the Normal group, use it as the
8711 * normal font. Same for the Menu group. */
8712 if (HL_TABLE()[idx].sg_font != NOFONT)
8713 {
8714 if (do_normal)
8715 gui_init_font(arg, FALSE);
8716#ifndef FONTSET_ALWAYS
8717# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8718 if (do_menu)
8719 {
8720 gui.menu_font = HL_TABLE()[idx].sg_font;
8721 gui_mch_new_menu_font();
8722 }
8723# endif
8724#endif
8725 }
8726 }
8727}
8728
8729#endif /* FEAT_GUI */
8730
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008731#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008732/*
8733 * Return the handle for a color name.
8734 * Returns INVALCOLOR when failed.
8735 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008736 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008737color_name2handle(char_u *name)
8738{
8739 if (STRCMP(name, "NONE") == 0)
8740 return INVALCOLOR;
8741
8742 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8743 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008744#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008745 if (gui.in_use)
8746#endif
8747#ifdef FEAT_GUI
8748 return gui.norm_pixel;
8749#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008750#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008751 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008752 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008753 /* Guess that the foreground is black or white. */
8754 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008755#endif
8756 }
8757 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8758 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008759#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008760 if (gui.in_use)
8761#endif
8762#ifdef FEAT_GUI
8763 return gui.back_pixel;
8764#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008765#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008766 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008767 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008768 /* Guess that the background is white or black. */
8769 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008770#endif
8771 }
8772
8773 return GUI_GET_COLOR(name);
8774}
8775#endif
8776
Bram Moolenaar071d4272004-06-13 20:20:40 +00008777/*
8778 * Table with the specifications for an attribute number.
8779 * Note that this table is used by ALL buffers. This is required because the
8780 * GUI can redraw at any time for any buffer.
8781 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008782static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008783
8784#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8785
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008786static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008787
8788#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8789
8790#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008791static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008792
8793#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8794#endif
8795
8796/*
8797 * Return the attr number for a set of colors and font.
8798 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8799 * if the combination is new.
8800 * Return 0 for error (no more room).
8801 */
8802 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008803get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008804{
8805 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008806 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008807 static int recursive = FALSE;
8808
8809 /*
8810 * Init the table, in case it wasn't done yet.
8811 */
8812 table->ga_itemsize = sizeof(attrentry_T);
8813 table->ga_growsize = 7;
8814
8815 /*
8816 * Try to find an entry with the same specifications.
8817 */
8818 for (i = 0; i < table->ga_len; ++i)
8819 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008820 taep = &(((attrentry_T *)table->ga_data)[i]);
8821 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008822 && (
8823#ifdef FEAT_GUI
8824 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008825 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8826 && aep->ae_u.gui.bg_color
8827 == taep->ae_u.gui.bg_color
8828 && aep->ae_u.gui.sp_color
8829 == taep->ae_u.gui.sp_color
8830 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008831# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008832 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008833# endif
8834 ))
8835 ||
8836#endif
8837 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008838 && (aep->ae_u.term.start == NULL)
8839 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008840 && (aep->ae_u.term.start == NULL
8841 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008842 taep->ae_u.term.start) == 0)
8843 && (aep->ae_u.term.stop == NULL)
8844 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008845 && (aep->ae_u.term.stop == NULL
8846 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008847 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008848 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008849 && aep->ae_u.cterm.fg_color
8850 == taep->ae_u.cterm.fg_color
8851 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008852 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008853#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008854 && aep->ae_u.cterm.fg_rgb
8855 == taep->ae_u.cterm.fg_rgb
8856 && aep->ae_u.cterm.bg_rgb
8857 == taep->ae_u.cterm.bg_rgb
8858#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008859 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008860
8861 return i + ATTR_OFF;
8862 }
8863
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008864 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008865 {
8866 /*
8867 * Running out of attribute entries! remove all attributes, and
8868 * compute new ones for all groups.
8869 * When called recursively, we are really out of numbers.
8870 */
8871 if (recursive)
8872 {
8873 EMSG(_("E424: Too many different highlighting attributes in use"));
8874 return 0;
8875 }
8876 recursive = TRUE;
8877
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008878 clear_hl_tables();
8879
Bram Moolenaar071d4272004-06-13 20:20:40 +00008880 must_redraw = CLEAR;
8881
8882 for (i = 0; i < highlight_ga.ga_len; ++i)
8883 set_hl_attr(i);
8884
8885 recursive = FALSE;
8886 }
8887
8888 /*
8889 * This is a new combination of colors and font, add an entry.
8890 */
8891 if (ga_grow(table, 1) == FAIL)
8892 return 0;
8893
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008894 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8895 vim_memset(taep, 0, sizeof(attrentry_T));
8896 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008897#ifdef FEAT_GUI
8898 if (table == &gui_attr_table)
8899 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008900 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8901 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8902 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8903 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008904# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008905 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008906# endif
8907 }
8908#endif
8909 if (table == &term_attr_table)
8910 {
8911 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008912 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008913 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008914 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008915 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008916 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008917 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008918 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008919 }
8920 else if (table == &cterm_attr_table)
8921 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008922 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8923 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008924#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008925 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8926 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8927#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008928 }
8929 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008930 return (table->ga_len - 1 + ATTR_OFF);
8931}
8932
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008933/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008934 * Get an attribute index for a cterm entry.
8935 * Uses an existing entry when possible or adds one when needed.
8936 */
8937 int
8938get_cterm_attr_idx(int attr, int fg, int bg)
8939{
8940 attrentry_T at_en;
8941
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008942 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008943#ifdef FEAT_TERMGUICOLORS
8944 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8945 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8946#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008947 at_en.ae_attr = attr;
8948 at_en.ae_u.cterm.fg_color = fg;
8949 at_en.ae_u.cterm.bg_color = bg;
8950 return get_attr_entry(&cterm_attr_table, &at_en);
8951}
8952
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008953#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8954/*
8955 * Get an attribute index for a 'termguicolors' entry.
8956 * Uses an existing entry when possible or adds one when needed.
8957 */
8958 int
8959get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8960{
8961 attrentry_T at_en;
8962
8963 vim_memset(&at_en, 0, sizeof(attrentry_T));
8964 at_en.ae_attr = attr;
Bram Moolenaard4fc5772018-02-27 14:39:03 +01008965 if (fg == INVALCOLOR && bg == INVALCOLOR)
8966 {
8967 /* If both GUI colors are not set fall back to the cterm colors. Helps
8968 * if the GUI only has an attribute, such as undercurl. */
8969 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
8970 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
8971 }
8972 else
8973 {
8974 at_en.ae_u.cterm.fg_rgb = fg;
8975 at_en.ae_u.cterm.bg_rgb = bg;
8976 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008977 return get_attr_entry(&cterm_attr_table, &at_en);
8978}
8979#endif
8980
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008981#if defined(FEAT_GUI) || defined(PROTO)
8982/*
8983 * Get an attribute index for a cterm entry.
8984 * Uses an existing entry when possible or adds one when needed.
8985 */
8986 int
8987get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8988{
8989 attrentry_T at_en;
8990
8991 vim_memset(&at_en, 0, sizeof(attrentry_T));
8992 at_en.ae_attr = attr;
8993 at_en.ae_u.gui.fg_color = fg;
8994 at_en.ae_u.gui.bg_color = bg;
8995 return get_attr_entry(&gui_attr_table, &at_en);
8996}
8997#endif
8998
Bram Moolenaareeac6772017-07-23 15:48:37 +02008999/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00009000 * Clear all highlight tables.
9001 */
9002 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009003clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00009004{
9005 int i;
9006 attrentry_T *taep;
9007
9008#ifdef FEAT_GUI
9009 ga_clear(&gui_attr_table);
9010#endif
9011 for (i = 0; i < term_attr_table.ga_len; ++i)
9012 {
9013 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
9014 vim_free(taep->ae_u.term.start);
9015 vim_free(taep->ae_u.term.stop);
9016 }
9017 ga_clear(&term_attr_table);
9018 ga_clear(&cterm_attr_table);
9019}
9020
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009021#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009022/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00009023 * Combine special attributes (e.g., for spelling) with other attributes
9024 * (e.g., for syntax highlighting).
9025 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00009026 * This creates a new group when required.
9027 * Since we expect there to be few spelling mistakes we don't cache the
9028 * result.
9029 * Return the resulting attributes.
9030 */
9031 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009032hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009033{
9034 attrentry_T *char_aep = NULL;
9035 attrentry_T *spell_aep;
9036 attrentry_T new_en;
9037
9038 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00009039 return prim_attr;
9040 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009041 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009042#ifdef FEAT_GUI
9043 if (gui.in_use)
9044 {
9045 if (char_attr > HL_ALL)
9046 char_aep = syn_gui_attr2entry(char_attr);
9047 if (char_aep != NULL)
9048 new_en = *char_aep;
9049 else
9050 {
9051 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009052 new_en.ae_u.gui.fg_color = INVALCOLOR;
9053 new_en.ae_u.gui.bg_color = INVALCOLOR;
9054 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009055 if (char_attr <= HL_ALL)
9056 new_en.ae_attr = char_attr;
9057 }
9058
Bram Moolenaar30abd282005-06-22 22:35:10 +00009059 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009060 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009061 else
9062 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009063 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009064 if (spell_aep != NULL)
9065 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009066 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9067 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009068 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9069 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9070 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9071 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9072 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9073 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9074 if (spell_aep->ae_u.gui.font != NOFONT)
9075 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9076# ifdef FEAT_XFONTSET
9077 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9078 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9079# endif
9080 }
9081 }
9082 return get_attr_entry(&gui_attr_table, &new_en);
9083 }
9084#endif
9085
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009086 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009087 {
9088 if (char_attr > HL_ALL)
9089 char_aep = syn_cterm_attr2entry(char_attr);
9090 if (char_aep != NULL)
9091 new_en = *char_aep;
9092 else
9093 {
9094 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009095#ifdef FEAT_TERMGUICOLORS
9096 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9097 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9098#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009099 if (char_attr <= HL_ALL)
9100 new_en.ae_attr = char_attr;
9101 }
9102
Bram Moolenaar30abd282005-06-22 22:35:10 +00009103 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009104 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009105 else
9106 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009107 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009108 if (spell_aep != NULL)
9109 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009110 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9111 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009112 if (spell_aep->ae_u.cterm.fg_color > 0)
9113 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9114 if (spell_aep->ae_u.cterm.bg_color > 0)
9115 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009116#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009117 /* If both fg and bg are not set fall back to cterm colors.
9118 * Helps for SpellBad which uses undercurl in the GUI. */
9119 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
9120 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
9121 {
9122 if (spell_aep->ae_u.cterm.fg_color > 0)
9123 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9124 if (spell_aep->ae_u.cterm.bg_color > 0)
9125 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9126 }
9127 else
9128 {
9129 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
9130 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
9131 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
9132 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9133 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009134#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009135 }
9136 }
9137 return get_attr_entry(&cterm_attr_table, &new_en);
9138 }
9139
9140 if (char_attr > HL_ALL)
9141 char_aep = syn_term_attr2entry(char_attr);
9142 if (char_aep != NULL)
9143 new_en = *char_aep;
9144 else
9145 {
9146 vim_memset(&new_en, 0, sizeof(new_en));
9147 if (char_attr <= HL_ALL)
9148 new_en.ae_attr = char_attr;
9149 }
9150
Bram Moolenaar30abd282005-06-22 22:35:10 +00009151 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009152 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009153 else
9154 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009155 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009156 if (spell_aep != NULL)
9157 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009158 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009159 if (spell_aep->ae_u.term.start != NULL)
9160 {
9161 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9162 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9163 }
9164 }
9165 }
9166 return get_attr_entry(&term_attr_table, &new_en);
9167}
9168#endif
9169
Bram Moolenaar071d4272004-06-13 20:20:40 +00009170#ifdef FEAT_GUI
9171
9172 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009173syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009174{
9175 attr -= ATTR_OFF;
9176 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9177 return NULL;
9178 return &(GUI_ATTR_ENTRY(attr));
9179}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009180#endif /* FEAT_GUI */
9181
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009182/*
9183 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9184 * Only to be used when "attr" > HL_ALL.
9185 */
9186 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009187syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009188{
9189 attrentry_T *aep;
9190
9191#ifdef FEAT_GUI
9192 if (gui.in_use)
9193 aep = syn_gui_attr2entry(attr);
9194 else
9195#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009196 if (IS_CTERM)
9197 aep = syn_cterm_attr2entry(attr);
9198 else
9199 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009200
9201 if (aep == NULL) /* highlighting not set */
9202 return 0;
9203 return aep->ae_attr;
9204}
9205
9206
Bram Moolenaar071d4272004-06-13 20:20:40 +00009207 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009208syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009209{
9210 attr -= ATTR_OFF;
9211 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9212 return NULL;
9213 return &(TERM_ATTR_ENTRY(attr));
9214}
9215
9216 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009217syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009218{
9219 attr -= ATTR_OFF;
9220 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9221 return NULL;
9222 return &(CTERM_ATTR_ENTRY(attr));
9223}
9224
9225#define LIST_ATTR 1
9226#define LIST_STRING 2
9227#define LIST_INT 3
9228
9229 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009230highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009231{
9232 struct hl_group *sgp;
9233 int didh = FALSE;
9234
9235 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9236
9237 didh = highlight_list_arg(id, didh, LIST_ATTR,
9238 sgp->sg_term, NULL, "term");
9239 didh = highlight_list_arg(id, didh, LIST_STRING,
9240 0, sgp->sg_start, "start");
9241 didh = highlight_list_arg(id, didh, LIST_STRING,
9242 0, sgp->sg_stop, "stop");
9243
9244 didh = highlight_list_arg(id, didh, LIST_ATTR,
9245 sgp->sg_cterm, NULL, "cterm");
9246 didh = highlight_list_arg(id, didh, LIST_INT,
9247 sgp->sg_cterm_fg, NULL, "ctermfg");
9248 didh = highlight_list_arg(id, didh, LIST_INT,
9249 sgp->sg_cterm_bg, NULL, "ctermbg");
9250
Bram Moolenaar61623362010-07-14 22:04:22 +02009251#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009252 didh = highlight_list_arg(id, didh, LIST_ATTR,
9253 sgp->sg_gui, NULL, "gui");
9254 didh = highlight_list_arg(id, didh, LIST_STRING,
9255 0, sgp->sg_gui_fg_name, "guifg");
9256 didh = highlight_list_arg(id, didh, LIST_STRING,
9257 0, sgp->sg_gui_bg_name, "guibg");
9258 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009259 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009260#endif
9261#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009262 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009263 0, sgp->sg_font_name, "font");
9264#endif
9265
Bram Moolenaar661b1822005-07-28 22:36:45 +00009266 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009267 {
9268 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009269 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009270 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009271 msg_putchar(' ');
9272 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9273 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009274
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009275 if (!didh)
9276 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009277#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009278 if (p_verbose > 0)
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02009279 last_set_msg(sgp->sg_script_ctx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009280#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009281}
9282
9283 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009284highlight_list_arg(
9285 int id,
9286 int didh,
9287 int type,
9288 int iarg,
9289 char_u *sarg,
9290 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009291{
9292 char_u buf[100];
9293 char_u *ts;
9294 int i;
9295
Bram Moolenaar661b1822005-07-28 22:36:45 +00009296 if (got_int)
9297 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009298 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9299 {
9300 ts = buf;
9301 if (type == LIST_INT)
9302 sprintf((char *)buf, "%d", iarg - 1);
9303 else if (type == LIST_STRING)
9304 ts = sarg;
9305 else /* type == LIST_ATTR */
9306 {
9307 buf[0] = NUL;
9308 for (i = 0; hl_attr_table[i] != 0; ++i)
9309 {
9310 if (iarg & hl_attr_table[i])
9311 {
9312 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009313 vim_strcat(buf, (char_u *)",", 100);
9314 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009315 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9316 }
9317 }
9318 }
9319
9320 (void)syn_list_header(didh,
9321 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9322 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009323 if (!got_int)
9324 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009325 if (*name != NUL)
9326 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009327 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9328 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009329 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009330 msg_outtrans(ts);
9331 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009332 }
9333 return didh;
9334}
9335
9336#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9337/*
9338 * Return "1" if highlight group "id" has attribute "flag".
9339 * Return NULL otherwise.
9340 */
9341 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009342highlight_has_attr(
9343 int id,
9344 int flag,
9345 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009346{
9347 int attr;
9348
9349 if (id <= 0 || id > highlight_ga.ga_len)
9350 return NULL;
9351
Bram Moolenaar61623362010-07-14 22:04:22 +02009352#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009353 if (modec == 'g')
9354 attr = HL_TABLE()[id - 1].sg_gui;
9355 else
9356#endif
9357 if (modec == 'c')
9358 attr = HL_TABLE()[id - 1].sg_cterm;
9359 else
9360 attr = HL_TABLE()[id - 1].sg_term;
9361
9362 if (attr & flag)
9363 return (char_u *)"1";
9364 return NULL;
9365}
9366#endif
9367
9368#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9369/*
9370 * Return color name of highlight group "id".
9371 */
9372 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009373highlight_color(
9374 int id,
9375 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9376 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009377{
9378 static char_u name[20];
9379 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009380 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009381 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009382 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009383
9384 if (id <= 0 || id > highlight_ga.ga_len)
9385 return NULL;
9386
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009387 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009388 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009389 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009390 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009391 font = TRUE;
9392 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009393 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009394 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9395 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009396 if (modec == 'g')
9397 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009398# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009399# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009400 /* return font name */
9401 if (font)
9402 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009403# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009404
Bram Moolenaar071d4272004-06-13 20:20:40 +00009405 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009406 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407 {
9408 guicolor_T color;
9409 long_u rgb;
9410 static char_u buf[10];
9411
9412 if (fg)
9413 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009414 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009415# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009416 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009417# else
9418 color = INVALCOLOR;
9419# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009420 else
9421 color = HL_TABLE()[id - 1].sg_gui_bg;
9422 if (color == INVALCOLOR)
9423 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009424 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009425 sprintf((char *)buf, "#%02x%02x%02x",
9426 (unsigned)(rgb >> 16),
9427 (unsigned)(rgb >> 8) & 255,
9428 (unsigned)rgb & 255);
9429 return buf;
9430 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009431# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009432 if (fg)
9433 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009434 if (sp)
9435 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009436 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9437 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009438 if (font || sp)
9439 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009440 if (modec == 'c')
9441 {
9442 if (fg)
9443 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9444 else
9445 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009446 if (n < 0)
9447 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009448 sprintf((char *)name, "%d", n);
9449 return name;
9450 }
9451 /* term doesn't have color */
9452 return NULL;
9453}
9454#endif
9455
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009456#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009457 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009458 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009459/*
9460 * Return color name of highlight group "id" as RGB value.
9461 */
9462 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009463highlight_gui_color_rgb(
9464 int id,
9465 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009466{
9467 guicolor_T color;
9468
9469 if (id <= 0 || id > highlight_ga.ga_len)
9470 return 0L;
9471
9472 if (fg)
9473 color = HL_TABLE()[id - 1].sg_gui_fg;
9474 else
9475 color = HL_TABLE()[id - 1].sg_gui_bg;
9476
9477 if (color == INVALCOLOR)
9478 return 0L;
9479
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009480 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009481}
9482#endif
9483
9484/*
9485 * Output the syntax list header.
9486 * Return TRUE when started a new line.
9487 */
9488 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009489syn_list_header(
9490 int did_header, /* did header already */
9491 int outlen, /* length of string that comes */
9492 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009493{
9494 int endcol = 19;
9495 int newline = TRUE;
9496
9497 if (!did_header)
9498 {
9499 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009500 if (got_int)
9501 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009502 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9503 endcol = 15;
9504 }
9505 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009506 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009507 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009508 if (got_int)
9509 return TRUE;
9510 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009511 else
9512 {
9513 if (msg_col >= endcol) /* wrap around is like starting a new line */
9514 newline = FALSE;
9515 }
9516
9517 if (msg_col >= endcol) /* output at least one space */
9518 endcol = msg_col + 1;
9519 if (Columns <= endcol) /* avoid hang for tiny window */
9520 endcol = Columns - 1;
9521
9522 msg_advance(endcol);
9523
9524 /* Show "xxx" with the attributes. */
9525 if (!did_header)
9526 {
9527 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9528 msg_putchar(' ');
9529 }
9530
9531 return newline;
9532}
9533
9534/*
9535 * Set the attribute numbers for a highlight group.
9536 * Called after one of the attributes has changed.
9537 */
9538 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009539set_hl_attr(
9540 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009541{
9542 attrentry_T at_en;
9543 struct hl_group *sgp = HL_TABLE() + idx;
9544
9545 /* The "Normal" group doesn't need an attribute number */
9546 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9547 return;
9548
9549#ifdef FEAT_GUI
9550 /*
9551 * For the GUI mode: If there are other than "normal" highlighting
9552 * attributes, need to allocate an attr number.
9553 */
9554 if (sgp->sg_gui_fg == INVALCOLOR
9555 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009556 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009557 && sgp->sg_font == NOFONT
9558# ifdef FEAT_XFONTSET
9559 && sgp->sg_fontset == NOFONTSET
9560# endif
9561 )
9562 {
9563 sgp->sg_gui_attr = sgp->sg_gui;
9564 }
9565 else
9566 {
9567 at_en.ae_attr = sgp->sg_gui;
9568 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9569 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009570 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009571 at_en.ae_u.gui.font = sgp->sg_font;
9572# ifdef FEAT_XFONTSET
9573 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9574# endif
9575 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9576 }
9577#endif
9578 /*
9579 * For the term mode: If there are other than "normal" highlighting
9580 * attributes, need to allocate an attr number.
9581 */
9582 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9583 sgp->sg_term_attr = sgp->sg_term;
9584 else
9585 {
9586 at_en.ae_attr = sgp->sg_term;
9587 at_en.ae_u.term.start = sgp->sg_start;
9588 at_en.ae_u.term.stop = sgp->sg_stop;
9589 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9590 }
9591
9592 /*
9593 * For the color term mode: If there are other than "normal"
9594 * highlighting attributes, need to allocate an attr number.
9595 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009596 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009597# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009598 && sgp->sg_gui_fg == INVALCOLOR
9599 && sgp->sg_gui_bg == INVALCOLOR
9600# endif
9601 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009602 sgp->sg_cterm_attr = sgp->sg_cterm;
9603 else
9604 {
9605 at_en.ae_attr = sgp->sg_cterm;
9606 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9607 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009608# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009609# ifdef WIN3264
9610 {
9611 int id;
9612 guicolor_T fg, bg;
9613
9614 id = syn_name2id((char_u *)"Normal");
9615 if (id > 0)
9616 {
9617 syn_id2colors(id, &fg, &bg);
9618 if (sgp->sg_gui_fg == INVALCOLOR)
9619 sgp->sg_gui_fg = fg;
9620 if (sgp->sg_gui_bg == INVALCOLOR)
9621 sgp->sg_gui_bg = bg;
9622 }
9623
9624 }
9625# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009626 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9627 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009628 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
9629 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
9630 {
9631 /* If both fg and bg are invalid fall back to the cterm colors.
9632 * Helps when the GUI only uses an attribute, e.g. undercurl. */
9633 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9634 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9635 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009636# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009637 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9638 }
9639}
9640
9641/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009642 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009643 * If it is not found, 0 is returned.
9644 */
9645 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009646syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009647{
9648 int i;
9649 char_u name_u[200];
9650
9651 /* Avoid using stricmp() too much, it's slow on some systems */
9652 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9653 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009654 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009655 vim_strup(name_u);
9656 for (i = highlight_ga.ga_len; --i >= 0; )
9657 if (HL_TABLE()[i].sg_name_u != NULL
9658 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9659 break;
9660 return i + 1;
9661}
9662
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009663/*
9664 * Lookup a highlight group name and return its attributes.
9665 * Return zero if not found.
9666 */
9667 int
9668syn_name2attr(char_u *name)
9669{
9670 int id = syn_name2id(name);
9671
9672 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009673 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009674 return 0;
9675}
9676
Bram Moolenaar071d4272004-06-13 20:20:40 +00009677#if defined(FEAT_EVAL) || defined(PROTO)
9678/*
9679 * Return TRUE if highlight group "name" exists.
9680 */
9681 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009682highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009683{
9684 return (syn_name2id(name) > 0);
9685}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009686
9687# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9688/*
9689 * Return the name of highlight group "id".
9690 * When not a valid ID return an empty string.
9691 */
9692 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009693syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009694{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009695 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009696 return (char_u *)"";
9697 return HL_TABLE()[id - 1].sg_name;
9698}
9699# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009700#endif
9701
9702/*
9703 * Like syn_name2id(), but take a pointer + length argument.
9704 */
9705 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009706syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009707{
9708 char_u *name;
9709 int id = 0;
9710
9711 name = vim_strnsave(linep, len);
9712 if (name != NULL)
9713 {
9714 id = syn_name2id(name);
9715 vim_free(name);
9716 }
9717 return id;
9718}
9719
9720/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009721 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009722 * The argument is a pointer to the name and the length of the name.
9723 * If it doesn't exist yet, a new entry is created.
9724 * Return 0 for failure.
9725 */
9726 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009727syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009728{
9729 int id;
9730 char_u *name;
9731
9732 name = vim_strnsave(pp, len);
9733 if (name == NULL)
9734 return 0;
9735
9736 id = syn_name2id(name);
9737 if (id == 0) /* doesn't exist yet */
9738 id = syn_add_group(name);
9739 else
9740 vim_free(name);
9741 return id;
9742}
9743
9744/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009745 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009746 * "name" must be an allocated string, it will be consumed.
9747 * Return 0 for failure.
9748 */
9749 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009750syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009751{
9752 char_u *p;
9753
9754 /* Check that the name is ASCII letters, digits and underscore. */
9755 for (p = name; *p != NUL; ++p)
9756 {
9757 if (!vim_isprintc(*p))
9758 {
9759 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009760 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009761 return 0;
9762 }
9763 else if (!ASCII_ISALNUM(*p) && *p != '_')
9764 {
9765 /* This is an error, but since there previously was no check only
9766 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009767 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009768 MSG(_("W18: Invalid character in group name"));
9769 break;
9770 }
9771 }
9772
9773 /*
9774 * First call for this growarray: init growing array.
9775 */
9776 if (highlight_ga.ga_data == NULL)
9777 {
9778 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9779 highlight_ga.ga_growsize = 10;
9780 }
9781
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009782 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009783 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009784 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009785 vim_free(name);
9786 return 0;
9787 }
9788
Bram Moolenaar071d4272004-06-13 20:20:40 +00009789 /*
9790 * Make room for at least one other syntax_highlight entry.
9791 */
9792 if (ga_grow(&highlight_ga, 1) == FAIL)
9793 {
9794 vim_free(name);
9795 return 0;
9796 }
9797
9798 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9799 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9800 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009801#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009802 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9803 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009804# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009805 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009806# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009807#endif
9808 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009809
9810 return highlight_ga.ga_len; /* ID is index plus one */
9811}
9812
9813/*
9814 * When, just after calling syn_add_group(), an error is discovered, this
9815 * function deletes the new name.
9816 */
9817 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009818syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009819{
9820 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009821 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9822 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9823}
9824
9825/*
9826 * Translate a group ID to highlight attributes.
9827 */
9828 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009829syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009830{
9831 int attr;
9832 struct hl_group *sgp;
9833
9834 hl_id = syn_get_final_id(hl_id);
9835 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9836
9837#ifdef FEAT_GUI
9838 /*
9839 * Only use GUI attr when the GUI is being used.
9840 */
9841 if (gui.in_use)
9842 attr = sgp->sg_gui_attr;
9843 else
9844#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009845 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009846 attr = sgp->sg_cterm_attr;
9847 else
9848 attr = sgp->sg_term_attr;
9849
9850 return attr;
9851}
9852
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009853#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009854/*
9855 * Get the GUI colors and attributes for a group ID.
9856 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9857 */
9858 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009859syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009860{
9861 struct hl_group *sgp;
9862
9863 hl_id = syn_get_final_id(hl_id);
9864 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9865
9866 *fgp = sgp->sg_gui_fg;
9867 *bgp = sgp->sg_gui_bg;
9868 return sgp->sg_gui;
9869}
9870#endif
9871
Bram Moolenaara772baf2018-05-20 13:35:44 +02009872#if (defined(WIN3264) \
9873 && !defined(FEAT_GUI_W32) \
9874 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009875 void
9876syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9877{
9878 struct hl_group *sgp;
9879
9880 hl_id = syn_get_final_id(hl_id);
9881 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9882 *fgp = sgp->sg_cterm_fg - 1;
9883 *bgp = sgp->sg_cterm_bg - 1;
9884}
9885#endif
9886
Bram Moolenaar071d4272004-06-13 20:20:40 +00009887/*
9888 * Translate a group ID to the final group ID (following links).
9889 */
9890 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009891syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009892{
9893 int count;
9894 struct hl_group *sgp;
9895
9896 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9897 return 0; /* Can be called from eval!! */
9898
9899 /*
9900 * Follow links until there is no more.
9901 * Look out for loops! Break after 100 links.
9902 */
9903 for (count = 100; --count >= 0; )
9904 {
9905 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9906 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9907 break;
9908 hl_id = sgp->sg_link;
9909 }
9910
9911 return hl_id;
9912}
9913
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009914#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009915/*
9916 * Call this function just after the GUI has started.
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009917 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009918 * It finds the font and color handles for the highlighting groups.
9919 */
9920 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009921highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009922{
9923 int idx;
9924
9925 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009926 if (USE_24BIT)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009927 set_normal_colors();
Bram Moolenaar071d4272004-06-13 20:20:40 +00009928
9929 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9930 gui_do_one_color(idx, FALSE, FALSE);
9931
9932 highlight_changed();
9933}
9934
9935 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009936gui_do_one_color(
9937 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009938 int do_menu UNUSED, /* TRUE: might set the menu font */
9939 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009940{
9941 int didit = FALSE;
9942
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009943# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009944# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009945 if (gui.in_use)
9946# endif
9947 if (HL_TABLE()[idx].sg_font_name != NULL)
9948 {
9949 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009950 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009951 didit = TRUE;
9952 }
9953# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009954 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9955 {
9956 HL_TABLE()[idx].sg_gui_fg =
9957 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9958 didit = TRUE;
9959 }
9960 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9961 {
9962 HL_TABLE()[idx].sg_gui_bg =
9963 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9964 didit = TRUE;
9965 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009966# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009967 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9968 {
9969 HL_TABLE()[idx].sg_gui_sp =
9970 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9971 didit = TRUE;
9972 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009973# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009974 if (didit) /* need to get a new attr number */
9975 set_hl_attr(idx);
9976}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009977#endif
9978
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009979#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9980/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009981 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009982 */
9983 static void
9984combine_stl_hlt(
9985 int id,
9986 int id_S,
9987 int id_alt,
9988 int hlcnt,
9989 int i,
9990 int hlf,
9991 int *table)
9992{
9993 struct hl_group *hlt = HL_TABLE();
9994
9995 if (id_alt == 0)
9996 {
9997 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9998 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9999 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
10000# if defined(FEAT_GUI) || defined(FEAT_EVAL)
10001 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
10002# endif
10003 }
10004 else
10005 mch_memmove(&hlt[hlcnt + i],
10006 &hlt[id_alt - 1],
10007 sizeof(struct hl_group));
10008 hlt[hlcnt + i].sg_link = 0;
10009
10010 hlt[hlcnt + i].sg_term ^=
10011 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
10012 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
10013 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
10014 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
10015 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
10016 hlt[hlcnt + i].sg_cterm ^=
10017 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
10018 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
10019 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
10020 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
10021 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
10022# if defined(FEAT_GUI) || defined(FEAT_EVAL)
10023 hlt[hlcnt + i].sg_gui ^=
10024 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
10025# endif
10026# ifdef FEAT_GUI
10027 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
10028 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
10029 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
10030 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
10031 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
10032 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
10033 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
10034 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
10035# ifdef FEAT_XFONTSET
10036 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
10037 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
10038# endif
10039# endif
10040 highlight_ga.ga_len = hlcnt + i + 1;
10041 set_hl_attr(hlcnt + i); /* At long last we can apply */
10042 table[i] = syn_id2attr(hlcnt + i + 1);
10043}
10044#endif
10045
Bram Moolenaar071d4272004-06-13 20:20:40 +000010046/*
10047 * Translate the 'highlight' option into attributes in highlight_attr[] and
10048 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
10049 * corresponding highlights to use on top of HLF_SNC is computed.
10050 * Called only when the 'highlight' option has been changed and upon first
10051 * screen redraw after any :highlight command.
10052 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10053 */
10054 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010055highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010056{
10057 int hlf;
10058 int i;
10059 char_u *p;
10060 int attr;
10061 char_u *end;
10062 int id;
10063#ifdef USER_HIGHLIGHT
10064 char_u userhl[10];
10065# ifdef FEAT_STL_OPT
Bram Moolenaar071d4272004-06-13 20:20:40 +000010066 int id_S = -1;
Bram Moolenaar61859032018-03-20 13:00:25 +010010067 int id_SNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010068# ifdef FEAT_TERMINAL
Bram Moolenaar61859032018-03-20 13:00:25 +010010069 int id_ST = 0;
10070 int id_STNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010071# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010072 int hlcnt;
10073# endif
10074#endif
10075 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10076
10077 need_highlight_changed = FALSE;
10078
10079 /*
10080 * Clear all attributes.
10081 */
10082 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10083 highlight_attr[hlf] = 0;
10084
10085 /*
10086 * First set all attributes to their default value.
10087 * Then use the attributes from the 'highlight' option.
10088 */
10089 for (i = 0; i < 2; ++i)
10090 {
10091 if (i)
10092 p = p_hl;
10093 else
10094 p = get_highlight_default();
10095 if (p == NULL) /* just in case */
10096 continue;
10097
10098 while (*p)
10099 {
10100 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10101 if (hl_flags[hlf] == *p)
10102 break;
10103 ++p;
10104 if (hlf == (int)HLF_COUNT || *p == NUL)
10105 return FAIL;
10106
10107 /*
10108 * Allow several hl_flags to be combined, like "bu" for
10109 * bold-underlined.
10110 */
10111 attr = 0;
10112 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10113 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010114 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010115 continue;
10116
10117 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10118 return FAIL;
10119
10120 switch (*p)
10121 {
10122 case 'b': attr |= HL_BOLD;
10123 break;
10124 case 'i': attr |= HL_ITALIC;
10125 break;
10126 case '-':
10127 case 'n': /* no highlighting */
10128 break;
10129 case 'r': attr |= HL_INVERSE;
10130 break;
10131 case 's': attr |= HL_STANDOUT;
10132 break;
10133 case 'u': attr |= HL_UNDERLINE;
10134 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010135 case 'c': attr |= HL_UNDERCURL;
10136 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010137 case 't': attr |= HL_STRIKETHROUGH;
10138 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010139 case ':': ++p; /* highlight group name */
10140 if (attr || *p == NUL) /* no combinations */
10141 return FAIL;
10142 end = vim_strchr(p, ',');
10143 if (end == NULL)
10144 end = p + STRLEN(p);
10145 id = syn_check_group(p, (int)(end - p));
10146 if (id == 0)
10147 return FAIL;
10148 attr = syn_id2attr(id);
10149 p = end - 1;
10150#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10151 if (hlf == (int)HLF_SNC)
10152 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010153# ifdef FEAT_TERMINAL
10154 else if (hlf == (int)HLF_ST)
10155 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010156 else if (hlf == (int)HLF_STNC)
10157 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010158# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010159 else if (hlf == (int)HLF_S)
10160 id_S = syn_get_final_id(id);
10161#endif
10162 break;
10163 default: return FAIL;
10164 }
10165 }
10166 highlight_attr[hlf] = attr;
10167
10168 p = skip_to_option_part(p); /* skip comma and spaces */
10169 }
10170 }
10171
10172#ifdef USER_HIGHLIGHT
10173 /* Setup the user highlights
10174 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010175 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010176 * 9 for User1-User9 combined with StatusLineNC
10177 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010178 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010179 * 1 for StatusLine default
10180 * Have to be in there simultaneously in case of table overflows in
10181 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010182 */
10183# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010184 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010185 return FAIL;
10186 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010187 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010188 {
10189 /* Make sure id_S is always valid to simplify code below. Use the last
10190 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010191 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010192 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10193 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010194 }
10195# endif
10196 for (i = 0; i < 9; i++)
10197 {
10198 sprintf((char *)userhl, "User%d", i + 1);
10199 id = syn_name2id(userhl);
10200 if (id == 0)
10201 {
10202 highlight_user[i] = 0;
10203# ifdef FEAT_STL_OPT
10204 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010205# ifdef FEAT_TERMINAL
10206 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010207 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010208# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010209# endif
10210 }
10211 else
10212 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010213 highlight_user[i] = syn_id2attr(id);
10214# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010215 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10216 HLF_SNC, highlight_stlnc);
10217# ifdef FEAT_TERMINAL
10218 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10219 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010220 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10221 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010222# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010223# endif
10224 }
10225 }
10226# ifdef FEAT_STL_OPT
10227 highlight_ga.ga_len = hlcnt;
10228# endif
10229
10230#endif /* USER_HIGHLIGHT */
10231
10232 return OK;
10233}
10234
Bram Moolenaar4f688582007-07-24 12:34:30 +000010235#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010236
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010237static void highlight_list(void);
10238static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010239
10240/*
10241 * Handle command line completion for :highlight command.
10242 */
10243 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010244set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010245{
10246 char_u *p;
10247
10248 /* Default: expand group names */
10249 xp->xp_context = EXPAND_HIGHLIGHT;
10250 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010251 include_link = 2;
10252 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010253
10254 /* (part of) subcommand already typed */
10255 if (*arg != NUL)
10256 {
10257 p = skiptowhite(arg);
10258 if (*p != NUL) /* past "default" or group name */
10259 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010260 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010261 if (STRNCMP("default", arg, p - arg) == 0)
10262 {
10263 arg = skipwhite(p);
10264 xp->xp_pattern = arg;
10265 p = skiptowhite(arg);
10266 }
10267 if (*p != NUL) /* past group name */
10268 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010269 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010270 if (arg[1] == 'i' && arg[0] == 'N')
10271 highlight_list();
10272 if (STRNCMP("link", arg, p - arg) == 0
10273 || STRNCMP("clear", arg, p - arg) == 0)
10274 {
10275 xp->xp_pattern = skipwhite(p);
10276 p = skiptowhite(xp->xp_pattern);
10277 if (*p != NUL) /* past first group name */
10278 {
10279 xp->xp_pattern = skipwhite(p);
10280 p = skiptowhite(xp->xp_pattern);
10281 }
10282 }
10283 if (*p != NUL) /* past group name(s) */
10284 xp->xp_context = EXPAND_NOTHING;
10285 }
10286 }
10287 }
10288}
10289
10290/*
10291 * List highlighting matches in a nice way.
10292 */
10293 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010294highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010295{
10296 int i;
10297
10298 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010299 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010300 for (i = 40; --i >= 0; )
10301 highlight_list_two(99, 0);
10302}
10303
10304 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010305highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010306{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010307 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010308 msg_clr_eos();
10309 out_flush();
10310 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10311}
10312
10313#endif /* FEAT_CMDL_COMPL */
10314
10315#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10316 || defined(FEAT_SIGNS) || defined(PROTO)
10317/*
10318 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010319 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010320 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010321get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010322{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010323 return get_highlight_name_ext(xp, idx, TRUE);
10324}
10325
10326/*
10327 * Obtain a highlight group name.
10328 * When "skip_cleared" is TRUE don't return a cleared entry.
10329 */
10330 char_u *
10331get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10332{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010333 if (idx < 0)
10334 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010335
10336 /* Items are never removed from the table, skip the ones that were
10337 * cleared. */
10338 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10339 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010340
Bram Moolenaar071d4272004-06-13 20:20:40 +000010341#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010342 if (idx == highlight_ga.ga_len && include_none != 0)
10343 return (char_u *)"none";
10344 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010345 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010346 if (idx == highlight_ga.ga_len + include_none + include_default
10347 && include_link != 0)
10348 return (char_u *)"link";
10349 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10350 && include_link != 0)
10351 return (char_u *)"clear";
10352#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010353 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010354 return NULL;
10355 return HL_TABLE()[idx].sg_name;
10356}
10357#endif
10358
Bram Moolenaar4f688582007-07-24 12:34:30 +000010359#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010360/*
10361 * Free all the highlight group fonts.
10362 * Used when quitting for systems which need it.
10363 */
10364 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010365free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010366{
10367 int idx;
10368
10369 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10370 {
10371 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10372 HL_TABLE()[idx].sg_font = NOFONT;
10373# ifdef FEAT_XFONTSET
10374 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10375 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10376# endif
10377 }
10378
10379 gui_mch_free_font(gui.norm_font);
10380# ifdef FEAT_XFONTSET
10381 gui_mch_free_fontset(gui.fontset);
10382# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010383# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010384 gui_mch_free_font(gui.bold_font);
10385 gui_mch_free_font(gui.ital_font);
10386 gui_mch_free_font(gui.boldital_font);
10387# endif
10388}
10389#endif
10390
10391/**************************************
10392 * End of Highlighting stuff *
10393 **************************************/