blob: bb695b573493953b77bcbc42bc82390a9afb0bf1 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010025 int sg_cleared; /* "hi clear" was used */
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/* for normal terminals */
27 int sg_term; /* "term=" highlighting attributes */
28 char_u *sg_start; /* terminal string for start highl */
29 char_u *sg_stop; /* terminal string for stop highl */
30 int sg_term_attr; /* Screen attr for term mode */
31/* for color terminals */
32 int sg_cterm; /* "cterm=" highlighting attr */
33 int sg_cterm_bold; /* bold attr was set for light color */
34 int sg_cterm_fg; /* terminal fg color number + 1 */
35 int sg_cterm_bg; /* terminal bg color number + 1 */
36 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000037/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000040 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020041#endif
42#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 GuiFont sg_font; /* GUI font handle */
45#ifdef FEAT_XFONTSET
46 GuiFontset sg_fontset; /* GUI fontset handle */
47#endif
48 char_u *sg_font_name; /* GUI font or fontset name */
49 int sg_gui_attr; /* Screen attr for GUI mode */
50#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020051#if defined(FEAT_GUI) || defined(FEAT_EVAL)
52/* Store the sp color name for the GUI or synIDattr() */
53 int sg_gui; /* "gui=" highlighting attributes */
54 char_u *sg_gui_fg_name;/* GUI foreground color name */
55 char_u *sg_gui_bg_name;/* GUI background color name */
56 char_u *sg_gui_sp_name;/* GUI special color name */
57#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 int sg_link; /* link to this highlight group ID */
59 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000060#ifdef FEAT_EVAL
61 scid_T sg_scriptID; /* script in which the group was last set */
62#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000063};
64
65#define SG_TERM 1 /* term has been set */
66#define SG_CTERM 2 /* cterm has been set */
67#define SG_GUI 4 /* gui has been set */
68#define SG_LINK 8 /* link has been set */
69
70static garray_T highlight_ga; /* highlight groups for 'highlight' option */
71
72#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
73
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020074#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000077/* Flags to indicate an additional string for highlight name completion. */
78static int include_none = 0; /* when 1 include "None" */
79static int include_default = 0; /* when 1 include "default" */
80static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000081#endif
82
83/*
84 * The "term", "cterm" and "gui" arguments can be any combination of the
85 * following names, separated by commas (but no spaces!).
86 */
87static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {"bold", "standout", "underline", "undercurl",
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020089 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
Bram Moolenaar0cd2a942017-08-12 15:12:30 +020092#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
Bram Moolenaar071d4272004-06-13 20:20:40 +000093
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010094static int get_attr_entry(garray_T *table, attrentry_T *aep);
95static void syn_unadd_group(void);
96static void set_hl_attr(int idx);
97static void highlight_list_one(int id);
98static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
99static int syn_add_group(char_u *name);
100static int syn_list_header(int did_header, int outlen, int id);
101static int hl_has_settings(int idx, int check_link);
102static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200104#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100105static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200106#endif
107#ifdef FEAT_GUI
108static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100109static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100111static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100113static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#endif
115
116/*
117 * An attribute number is the index in attr_table plus ATTR_OFF.
118 */
119#define ATTR_OFF (HL_ALL + 1)
120
121#if defined(FEAT_SYN_HL) || defined(PROTO)
122
123#define SYN_NAMELEN 50 /* maximum length of a syntax name */
124
125/* different types of offsets that are possible */
126#define SPO_MS_OFF 0 /* match start offset */
127#define SPO_ME_OFF 1 /* match end offset */
128#define SPO_HS_OFF 2 /* highl. start offset */
129#define SPO_HE_OFF 3 /* highl. end offset */
130#define SPO_RS_OFF 4 /* region start offset */
131#define SPO_RE_OFF 5 /* region end offset */
132#define SPO_LC_OFF 6 /* leading context offset */
133#define SPO_COUNT 7
134
135static char *(spo_name_tab[SPO_COUNT]) =
136 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
137
138/*
139 * The patterns that are being searched for are stored in a syn_pattern.
140 * A match item consists of one pattern.
141 * A start/end item consists of n start patterns and m end patterns.
142 * A start/skip/end item consists of n start patterns, one skip pattern and m
143 * end patterns.
144 * For the latter two, the patterns are always consecutive: start-skip-end.
145 *
146 * A character offset can be given for the matched text (_m_start and _m_end)
147 * and for the actually highlighted text (_h_start and _h_end).
Bram Moolenaar36f92302018-02-24 21:36:34 +0100148 *
149 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 */
151typedef struct syn_pattern
152{
153 char sp_type; /* see SPTYPE_ defines below */
154 char sp_syncing; /* this item used for syncing */
Bram Moolenaar36f92302018-02-24 21:36:34 +0100155 short sp_syn_match_id; /* highlight group ID of pattern */
156 short sp_off_flags; /* see below */
157 int sp_offsets[SPO_COUNT]; /* offsets */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200158 int sp_flags; /* see HL_ defines below */
159#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200160 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200161#endif
Bram Moolenaar36f92302018-02-24 21:36:34 +0100162 int sp_ic; /* ignore-case flag for sp_prog */
163 int sp_sync_idx; /* sync item index (syncing only) */
164 int sp_line_id; /* ID of last line where tried */
165 int sp_startcol; /* next match in sp_line_id line */
166 short *sp_cont_list; /* cont. group IDs, if non-zero */
167 short *sp_next_list; /* next group IDs, if non-zero */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 struct sp_syn sp_syn; /* struct passed to in_id_list() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169 char_u *sp_pattern; /* regexp to match, pattern */
170 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200171#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200172 syn_time_T sp_time;
173#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174} synpat_T;
175
176/* The sp_off_flags are computed like this:
177 * offset from the start of the matched text: (1 << SPO_XX_OFF)
178 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
179 * When both are present, only one is used.
180 */
181
182#define SPTYPE_MATCH 1 /* match keyword with this group ID */
183#define SPTYPE_START 2 /* match a regexp, start of item */
184#define SPTYPE_END 3 /* match a regexp, end of item */
185#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
186
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187
188#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
189
190#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
191
192/*
193 * Flags for b_syn_sync_flags:
194 */
195#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
196#define SF_MATCH 0x02 /* sync by matching a pattern */
197
198#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
199
Bram Moolenaar071d4272004-06-13 20:20:40 +0000200#define MAXKEYWLEN 80 /* maximum length of a keyword */
201
202/*
203 * The attributes of the syntax item that has been recognized.
204 */
205static int current_attr = 0; /* attr of current syntax word */
206#ifdef FEAT_EVAL
207static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000208static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200210#ifdef FEAT_CONCEAL
211static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200212static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200213static int current_sub_char = 0;
214#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215
Bram Moolenaar217ad922005-03-20 22:37:15 +0000216typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217{
218 char_u *scl_name; /* syntax cluster name */
219 char_u *scl_name_u; /* uppercase of scl_name */
220 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000221} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222
223/*
224 * Methods of combining two clusters
225 */
226#define CLUSTER_REPLACE 1 /* replace first list with second */
227#define CLUSTER_ADD 2 /* add second list to first */
228#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
229
Bram Moolenaar217ad922005-03-20 22:37:15 +0000230#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231
232/*
233 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200234 * 0 - 19999 normal syntax groups
235 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
236 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
237 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
238 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200240#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200241#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
242#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
243#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
244
Bram Moolenaar42431a72011-04-01 14:44:59 +0200245#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
246#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247
248/*
249 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
250 * expand_filename(). Most of the other syntax commands don't need it, so
251 * instead of passing it to them, we stow it here.
252 */
253static char_u **syn_cmdlinep;
254
255/*
256 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200257 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258 * rules in each ":syn include"'d file.
259 */
260static int current_syn_inc_tag = 0;
261static int running_syn_inc_tag = 0;
262
263/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000264 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
265 * This avoids adding a pointer to the hashtable item.
266 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
267 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
268 * HI2KE() converts a hashitem pointer to a var pointer.
269 */
270static keyentry_T dumkey;
271#define KE2HIKEY(kp) ((kp)->keyword)
272#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
273#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
274
275/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276 * To reduce the time spent in keepend(), remember at which level in the state
277 * stack the first item with "keepend" is present. When "-1", there is no
278 * "keepend" on the stack.
279 */
280static int keepend_level = -1;
281
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200282static char msg_no_items[] = N_("No Syntax items defined for this buffer");
283
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284/*
285 * For the current state we need to remember more than just the idx.
286 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
287 * (The end positions have the column number of the next char)
288 */
289typedef struct state_item
290{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000291 int si_idx; /* index of syntax pattern or
292 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000294 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295 int si_m_lnum; /* lnum of the match */
296 int si_m_startcol; /* starting column of the match */
297 lpos_T si_m_endpos; /* just after end posn of the match */
298 lpos_T si_h_startpos; /* start position of the highlighting */
299 lpos_T si_h_endpos; /* end position of the highlighting */
300 lpos_T si_eoe_pos; /* end position of end pattern */
301 int si_end_idx; /* group ID for end pattern or zero */
302 int si_ends; /* if match ends before si_m_endpos */
303 int si_attr; /* attributes in this state */
304 long si_flags; /* HL_HAS_EOL flag in this state, and
305 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200306#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200307 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200308 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200309#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000310 short *si_cont_list; /* list of contained groups */
311 short *si_next_list; /* nextgroup IDs after this item ends */
312 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
313 * pattern */
314} stateitem_T;
315
316#define KEYWORD_IDX -1 /* value of si_idx for keywords */
317#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
318 but contained groups */
319
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100321static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200322#endif
323
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000325 * Struct to reduce the number of arguments to get_syn_options(), it's used
326 * very often.
327 */
328typedef struct
329{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000330 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000331 int keyword; /* TRUE for ":syn keyword" */
332 int *sync_idx; /* syntax item for "grouphere" argument, NULL
333 if not allowed */
334 char has_cont_list; /* TRUE if "cont_list" can be used */
335 short *cont_list; /* group IDs for "contains" argument */
336 short *cont_in_list; /* group IDs for "containedin" argument */
337 short *next_list; /* group IDs for "nextgroup" argument */
338} syn_opt_arg_T;
339
340/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 * The next possible match in the current line for any pattern is remembered,
342 * to avoid having to try for a match in each column.
343 * If next_match_idx == -1, not tried (in this line) yet.
344 * If next_match_col == MAXCOL, no match found in this line.
345 * (All end positions have the column of the char after the end)
346 */
347static int next_match_col; /* column for start of next match */
348static lpos_T next_match_m_endpos; /* position for end of next match */
349static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
350static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
351static int next_match_idx; /* index of matched item */
352static long next_match_flags; /* flags for next match */
353static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
354static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
355static int next_match_end_idx; /* ID of group for end pattn or zero */
356static reg_extmatch_T *next_match_extmatch = NULL;
357
358/*
359 * A state stack is an array of integers or stateitem_T, stored in a
360 * garray_T. A state stack is invalid if it's itemsize entry is zero.
361 */
362#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
363#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
364
365/*
366 * The current state (within the line) of the recognition engine.
367 * When current_state.ga_itemsize is 0 the current state is invalid.
368 */
369static win_T *syn_win; /* current window for highlighting */
370static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200371static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200372#ifdef FEAT_RELTIME
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200373static proftime_T *syn_tm; /* timeout limit */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200374#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375static linenr_T current_lnum = 0; /* lnum of current state */
376static colnr_T current_col = 0; /* column of current state */
377static int current_state_stored = 0; /* TRUE if stored current state
378 * after setting current_finished */
379static int current_finished = 0; /* current line has been finished */
380static garray_T current_state /* current stack of state_items */
381 = {0, 0, 0, 0, NULL};
382static short *current_next_list = NULL; /* when non-zero, nextgroup list */
383static int current_next_flags = 0; /* flags for current_next_list */
384static int current_line_id = 0; /* unique number for current line */
385
386#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
387
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100388static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100389static void save_chartab(char_u *chartab);
390static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100391static int syn_match_linecont(linenr_T lnum);
392static void syn_start_line(void);
393static void syn_update_ends(int startofline);
394static void syn_stack_alloc(void);
395static int syn_stack_cleanup(void);
396static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
397static synstate_T *syn_stack_find_entry(linenr_T lnum);
398static synstate_T *store_current_state(void);
399static void load_current_state(synstate_T *from);
400static void invalidate_current_state(void);
401static int syn_stack_equal(synstate_T *sp);
402static void validate_current_state(void);
403static int syn_finish_line(int syncing);
404static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
405static int did_match_already(int idx, garray_T *gap);
406static stateitem_T *push_next_match(stateitem_T *cur_si);
407static void check_state_ends(void);
408static void update_si_attr(int idx);
409static void check_keepend(void);
410static void update_si_end(stateitem_T *sip, int startcol, int force);
411static short *copy_id_list(short *list);
412static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
413static int push_current_state(int idx);
414static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200415#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100416static void syn_clear_time(syn_time_T *tt);
417static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200420#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200422#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100423static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200424static int syn_time_on = FALSE;
425# define IF_SYN_TIME(p) (p)
426#else
427# define IF_SYN_TIME(p) NULL
428typedef int syn_time_T;
429#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100431static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
432static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
433static void clear_syn_state(synstate_T *p);
434static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100436static void limit_pos(lpos_T *pos, lpos_T *limit);
437static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
438static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
439static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
440static char_u *syn_getcurline(void);
441static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
442static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
443static void syn_cmd_case(exarg_T *eap, int syncing);
444static void syn_cmd_spell(exarg_T *eap, int syncing);
445static void syntax_sync_clear(void);
446static void syn_remove_pattern(synblock_T *block, int idx);
447static void syn_clear_pattern(synblock_T *block, int i);
448static void syn_clear_cluster(synblock_T *block, int i);
449static void syn_cmd_clear(exarg_T *eap, int syncing);
450static void syn_cmd_conceal(exarg_T *eap, int syncing);
451static void syn_clear_one(int id, int syncing);
452static void syn_cmd_on(exarg_T *eap, int syncing);
453static void syn_cmd_enable(exarg_T *eap, int syncing);
454static void syn_cmd_reset(exarg_T *eap, int syncing);
455static void syn_cmd_manual(exarg_T *eap, int syncing);
456static void syn_cmd_off(exarg_T *eap, int syncing);
457static void syn_cmd_onoff(exarg_T *eap, char *name);
458static void syn_cmd_list(exarg_T *eap, int syncing);
459static void syn_lines_msg(void);
460static void syn_match_msg(void);
461static void syn_stack_free_block(synblock_T *block);
462static void syn_list_one(int id, int syncing, int link_only);
463static void syn_list_cluster(int id);
464static void put_id_list(char_u *name, short *list, int attr);
465static void put_pattern(char *s, int c, synpat_T *spp, int attr);
466static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
467static void syn_clear_keyword(int id, hashtab_T *ht);
468static void clear_keywtab(hashtab_T *ht);
469static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
470static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100471static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100472static void syn_cmd_include(exarg_T *eap, int syncing);
473static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
474static void syn_cmd_keyword(exarg_T *eap, int syncing);
475static void syn_cmd_match(exarg_T *eap, int syncing);
476static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100478static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100480static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100482static void syn_cmd_cluster(exarg_T *eap, int syncing);
483static int syn_scl_name2id(char_u *name);
484static int syn_scl_namen2id(char_u *linep, int len);
485static int syn_check_cluster(char_u *pp, int len);
486static int syn_add_cluster(char_u *name);
487static void init_syn_patterns(void);
488static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
489static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100490static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100491static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
492static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200494#if defined(FEAT_RELTIME) || defined(PROTO)
495/*
496 * Set the timeout used for syntax highlighting.
497 * Use NULL to reset, no timeout.
498 */
499 void
500syn_set_timeout(proftime_T *tm)
501{
502 syn_tm = tm;
503}
504#endif
505
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506/*
507 * Start the syntax recognition for a line. This function is normally called
508 * from the screen updating, once for each displayed line.
509 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
510 * it. Careful: curbuf and curwin are likely to point to another buffer and
511 * window.
512 */
513 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200514syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515{
516 synstate_T *p;
517 synstate_T *last_valid = NULL;
518 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000519 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 linenr_T parsed_lnum;
521 linenr_T first_stored;
522 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100523 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200525#ifdef FEAT_CONCEAL
526 current_sub_char = NUL;
527#endif
528
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 /*
530 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000531 * Also do this when a change was made, the current state may be invalid
532 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200534 if (syn_block != wp->w_s
535 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100536 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537 {
538 invalidate_current_state();
539 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100542 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 syn_win = wp;
544
545 /*
546 * Allocate syntax stack when needed.
547 */
548 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200549 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000550 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200551 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000552
553 /*
554 * If the state of the end of the previous line is useful, store it.
555 */
556 if (VALID_STATE(&current_state)
557 && current_lnum < lnum
558 && current_lnum < syn_buf->b_ml.ml_line_count)
559 {
560 (void)syn_finish_line(FALSE);
561 if (!current_state_stored)
562 {
563 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000564 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565 }
566
567 /*
568 * If the current_lnum is now the same as "lnum", keep the current
569 * state (this happens very often!). Otherwise invalidate
570 * current_state and figure it out below.
571 */
572 if (current_lnum != lnum)
573 invalidate_current_state();
574 }
575 else
576 invalidate_current_state();
577
578 /*
579 * Try to synchronize from a saved state in b_sst_array[].
580 * Only do this if lnum is not before and not to far beyond a saved state.
581 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200582 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 {
584 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200585 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 {
587 if (p->sst_lnum > lnum)
588 break;
589 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
590 {
591 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200592 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593 last_min_valid = p;
594 }
595 }
596 if (last_min_valid != NULL)
597 load_current_state(last_min_valid);
598 }
599
600 /*
601 * If "lnum" is before or far beyond a line with a saved state, need to
602 * re-synchronize.
603 */
604 if (INVALID_STATE(&current_state))
605 {
606 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200607 if (current_lnum == 1)
608 /* First line is always valid, no matter "minlines". */
609 first_stored = 1;
610 else
611 /* Need to parse "minlines" lines before state can be considered
612 * valid to store. */
613 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 }
615 else
616 first_stored = current_lnum;
617
618 /*
619 * Advance from the sync point or saved state until the current line.
620 * Save some entries for syncing with later on.
621 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200622 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000623 dist = 999999;
624 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200625 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626 while (current_lnum < lnum)
627 {
628 syn_start_line();
629 (void)syn_finish_line(FALSE);
630 ++current_lnum;
631
632 /* If we parsed at least "minlines" lines or started at a valid
633 * state, the current state is considered valid. */
634 if (current_lnum >= first_stored)
635 {
636 /* Check if the saved state entry is for the current line and is
637 * equal to the current state. If so, then validate all saved
638 * states that depended on a change before the parsed line. */
639 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000640 prev = syn_stack_find_entry(current_lnum - 1);
641 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200642 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000644 sp = prev;
645 while (sp != NULL && sp->sst_lnum < current_lnum)
646 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 if (sp != NULL
648 && sp->sst_lnum == current_lnum
649 && syn_stack_equal(sp))
650 {
651 parsed_lnum = current_lnum;
652 prev = sp;
653 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
654 {
655 if (sp->sst_lnum <= lnum)
656 /* valid state before desired line, use this one */
657 prev = sp;
658 else if (sp->sst_change_lnum == 0)
659 /* past saved states depending on change, break here. */
660 break;
661 sp->sst_change_lnum = 0;
662 sp = sp->sst_next;
663 }
664 load_current_state(prev);
665 }
666 /* Store the state at this line when it's the first one, the line
667 * where we start parsing, or some distance from the previously
668 * saved state. But only when parsed at least 'minlines'. */
669 else if (prev == NULL
670 || current_lnum == lnum
671 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000672 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000673 }
674
675 /* This can take a long time: break when CTRL-C pressed. The current
676 * state will be wrong then. */
677 line_breakcheck();
678 if (got_int)
679 {
680 current_lnum = lnum;
681 break;
682 }
683 }
684
685 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686}
687
688/*
689 * We cannot simply discard growarrays full of state_items or buf_states; we
690 * have to manually release their extmatch pointers first.
691 */
692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100693clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694{
695 int i;
696 garray_T *gap;
697
698 if (p->sst_stacksize > SST_FIX_STATES)
699 {
700 gap = &(p->sst_union.sst_ga);
701 for (i = 0; i < gap->ga_len; i++)
702 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
703 ga_clear(gap);
704 }
705 else
706 {
707 for (i = 0; i < p->sst_stacksize; i++)
708 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
709 }
710}
711
712/*
713 * Cleanup the current_state stack.
714 */
715 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100716clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717{
718 int i;
719 stateitem_T *sip;
720
721 sip = (stateitem_T *)(current_state.ga_data);
722 for (i = 0; i < current_state.ga_len; i++)
723 unref_extmatch(sip[i].si_extmatch);
724 ga_clear(&current_state);
725}
726
727/*
728 * Try to find a synchronisation point for line "lnum".
729 *
730 * This sets current_lnum and the current state. One of three methods is
731 * used:
732 * 1. Search backwards for the end of a C-comment.
733 * 2. Search backwards for given sync patterns.
734 * 3. Simply start on a given number of lines above "lnum".
735 */
736 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100737syn_sync(
738 win_T *wp,
739 linenr_T start_lnum,
740 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741{
742 buf_T *curbuf_save;
743 win_T *curwin_save;
744 pos_T cursor_save;
745 int idx;
746 linenr_T lnum;
747 linenr_T end_lnum;
748 linenr_T break_lnum;
749 int had_sync_point;
750 stateitem_T *cur_si;
751 synpat_T *spp;
752 char_u *line;
753 int found_flags = 0;
754 int found_match_idx = 0;
755 linenr_T found_current_lnum = 0;
756 int found_current_col= 0;
757 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000758 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759
760 /*
761 * Clear any current state that might be hanging around.
762 */
763 invalidate_current_state();
764
765 /*
766 * Start at least "minlines" back. Default starting point for parsing is
767 * there.
768 * Start further back, to avoid that scrolling backwards will result in
769 * resyncing for every line. Now it resyncs only one out of N lines,
770 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
771 * Watch out for overflow when minlines is MAXLNUM.
772 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200773 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 start_lnum = 1;
775 else
776 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200777 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200779 else if (syn_block->b_syn_sync_minlines < 10)
780 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200782 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
783 if (syn_block->b_syn_sync_maxlines != 0
784 && lnum > syn_block->b_syn_sync_maxlines)
785 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 if (lnum >= start_lnum)
787 start_lnum = 1;
788 else
789 start_lnum -= lnum;
790 }
791 current_lnum = start_lnum;
792
793 /*
794 * 1. Search backwards for the end of a C-style comment.
795 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200796 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000797 {
798 /* Need to make syn_buf the current buffer for a moment, to be able to
799 * use find_start_comment(). */
800 curwin_save = curwin;
801 curwin = wp;
802 curbuf_save = curbuf;
803 curbuf = syn_buf;
804
805 /*
806 * Skip lines that end in a backslash.
807 */
808 for ( ; start_lnum > 1; --start_lnum)
809 {
810 line = ml_get(start_lnum - 1);
811 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
812 break;
813 }
814 current_lnum = start_lnum;
815
816 /* set cursor to start of search */
817 cursor_save = wp->w_cursor;
818 wp->w_cursor.lnum = start_lnum;
819 wp->w_cursor.col = 0;
820
821 /*
822 * If the line is inside a comment, need to find the syntax item that
823 * defines the comment.
824 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
825 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200826 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000827 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200828 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
829 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
830 == syn_block->b_syn_sync_id
831 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 {
833 validate_current_state();
834 if (push_current_state(idx) == OK)
835 update_si_attr(current_state.ga_len - 1);
836 break;
837 }
838 }
839
840 /* restore cursor and buffer */
841 wp->w_cursor = cursor_save;
842 curwin = curwin_save;
843 curbuf = curbuf_save;
844 }
845
846 /*
847 * 2. Search backwards for given sync patterns.
848 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200849 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000850 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200851 if (syn_block->b_syn_sync_maxlines != 0
852 && start_lnum > syn_block->b_syn_sync_maxlines)
853 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854 else
855 break_lnum = 0;
856
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000857 found_m_endpos.lnum = 0;
858 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000859 end_lnum = start_lnum;
860 lnum = start_lnum;
861 while (--lnum > break_lnum)
862 {
863 /* This can take a long time: break when CTRL-C pressed. */
864 line_breakcheck();
865 if (got_int)
866 {
867 invalidate_current_state();
868 current_lnum = start_lnum;
869 break;
870 }
871
872 /* Check if we have run into a valid saved state stack now. */
873 if (last_valid != NULL && lnum == last_valid->sst_lnum)
874 {
875 load_current_state(last_valid);
876 break;
877 }
878
879 /*
880 * Check if the previous line has the line-continuation pattern.
881 */
882 if (lnum > 1 && syn_match_linecont(lnum - 1))
883 continue;
884
885 /*
886 * Start with nothing on the state stack
887 */
888 validate_current_state();
889
890 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
891 {
892 syn_start_line();
893 for (;;)
894 {
895 had_sync_point = syn_finish_line(TRUE);
896 /*
897 * When a sync point has been found, remember where, and
898 * continue to look for another one, further on in the line.
899 */
900 if (had_sync_point && current_state.ga_len)
901 {
902 cur_si = &CUR_STATE(current_state.ga_len - 1);
903 if (cur_si->si_m_endpos.lnum > start_lnum)
904 {
905 /* ignore match that goes to after where started */
906 current_lnum = end_lnum;
907 break;
908 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000909 if (cur_si->si_idx < 0)
910 {
911 /* Cannot happen? */
912 found_flags = 0;
913 found_match_idx = KEYWORD_IDX;
914 }
915 else
916 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200917 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000918 found_flags = spp->sp_flags;
919 found_match_idx = spp->sp_sync_idx;
920 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921 found_current_lnum = current_lnum;
922 found_current_col = current_col;
923 found_m_endpos = cur_si->si_m_endpos;
924 /*
925 * Continue after the match (be aware of a zero-length
926 * match).
927 */
928 if (found_m_endpos.lnum > current_lnum)
929 {
930 current_lnum = found_m_endpos.lnum;
931 current_col = found_m_endpos.col;
932 if (current_lnum >= end_lnum)
933 break;
934 }
935 else if (found_m_endpos.col > current_col)
936 current_col = found_m_endpos.col;
937 else
938 ++current_col;
939
940 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000941 * an item that ends here, need to do that now. Be
942 * careful not to go past the NUL. */
943 prev_current_col = current_col;
944 if (syn_getcurline()[current_col] != NUL)
945 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000947 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000948 }
949 else
950 break;
951 }
952 }
953
954 /*
955 * If a sync point was encountered, break here.
956 */
957 if (found_flags)
958 {
959 /*
960 * Put the item that was specified by the sync point on the
961 * state stack. If there was no item specified, make the
962 * state stack empty.
963 */
964 clear_current_state();
965 if (found_match_idx >= 0
966 && push_current_state(found_match_idx) == OK)
967 update_si_attr(current_state.ga_len - 1);
968
969 /*
970 * When using "grouphere", continue from the sync point
971 * match, until the end of the line. Parsing starts at
972 * the next line.
973 * For "groupthere" the parsing starts at start_lnum.
974 */
975 if (found_flags & HL_SYNC_HERE)
976 {
977 if (current_state.ga_len)
978 {
979 cur_si = &CUR_STATE(current_state.ga_len - 1);
980 cur_si->si_h_startpos.lnum = found_current_lnum;
981 cur_si->si_h_startpos.col = found_current_col;
982 update_si_end(cur_si, (int)current_col, TRUE);
983 check_keepend();
984 }
985 current_col = found_m_endpos.col;
986 current_lnum = found_m_endpos.lnum;
987 (void)syn_finish_line(FALSE);
988 ++current_lnum;
989 }
990 else
991 current_lnum = start_lnum;
992
993 break;
994 }
995
996 end_lnum = lnum;
997 invalidate_current_state();
998 }
999
1000 /* Ran into start of the file or exceeded maximum number of lines */
1001 if (lnum <= break_lnum)
1002 {
1003 invalidate_current_state();
1004 current_lnum = break_lnum + 1;
1005 }
1006 }
1007
1008 validate_current_state();
1009}
1010
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001011 static void
1012save_chartab(char_u *chartab)
1013{
1014 if (syn_block->b_syn_isk != empty_option)
1015 {
1016 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1017 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1018 (size_t)32);
1019 }
1020}
1021
1022 static void
1023restore_chartab(char_u *chartab)
1024{
1025 if (syn_win->w_s->b_syn_isk != empty_option)
1026 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1027}
1028
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029/*
1030 * Return TRUE if the line-continuation pattern matches in line "lnum".
1031 */
1032 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001033syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034{
1035 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001036 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001037 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038
Bram Moolenaar860cae12010-06-05 23:22:07 +02001039 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001041 /* use syntax iskeyword option */
1042 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001043 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1044 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001045 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001046 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001047 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001048 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001049 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 }
1051 return FALSE;
1052}
1053
1054/*
1055 * Prepare the current state for the start of a line.
1056 */
1057 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001058syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059{
1060 current_finished = FALSE;
1061 current_col = 0;
1062
1063 /*
1064 * Need to update the end of a start/skip/end that continues from the
1065 * previous line and regions that have "keepend".
1066 */
1067 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001068 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001070 check_state_ends();
1071 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072
1073 next_match_idx = -1;
1074 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001075#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001076 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001077#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001078}
1079
1080/*
1081 * Check for items in the stack that need their end updated.
1082 * When "startofline" is TRUE the last item is always updated.
1083 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1084 */
1085 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001086syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087{
1088 stateitem_T *cur_si;
1089 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001090 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091
1092 if (startofline)
1093 {
1094 /* Check for a match carried over from a previous line with a
1095 * contained region. The match ends as soon as the region ends. */
1096 for (i = 0; i < current_state.ga_len; ++i)
1097 {
1098 cur_si = &CUR_STATE(i);
1099 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001100 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 == SPTYPE_MATCH
1102 && cur_si->si_m_endpos.lnum < current_lnum)
1103 {
1104 cur_si->si_flags |= HL_MATCHCONT;
1105 cur_si->si_m_endpos.lnum = 0;
1106 cur_si->si_m_endpos.col = 0;
1107 cur_si->si_h_endpos = cur_si->si_m_endpos;
1108 cur_si->si_ends = TRUE;
1109 }
1110 }
1111 }
1112
1113 /*
1114 * Need to update the end of a start/skip/end that continues from the
1115 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001116 * influence contained items. If we've just removed "extend"
1117 * (startofline == 0) then we should update ends of normal regions
1118 * contained inside "keepend" because "extend" could have extended
1119 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 * Then check for items ending in column 0.
1121 */
1122 i = current_state.ga_len - 1;
1123 if (keepend_level >= 0)
1124 for ( ; i > keepend_level; --i)
1125 if (CUR_STATE(i).si_flags & HL_EXTEND)
1126 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001127
1128 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 for ( ; i < current_state.ga_len; ++i)
1130 {
1131 cur_si = &CUR_STATE(i);
1132 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001133 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 || (i == current_state.ga_len - 1 && startofline))
1135 {
1136 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1137 cur_si->si_h_startpos.lnum = current_lnum;
1138
1139 if (!(cur_si->si_flags & HL_MATCHCONT))
1140 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001141
1142 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1143 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 }
1145 }
1146 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147}
1148
1149/****************************************
1150 * Handling of the state stack cache.
1151 */
1152
1153/*
1154 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1155 *
1156 * To speed up syntax highlighting, the state stack for the start of some
1157 * lines is cached. These entries can be used to start parsing at that point.
1158 *
1159 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1160 * valid entries. b_sst_first points to the first one, then follow sst_next.
1161 * The entries are sorted on line number. The first entry is often for line 2
1162 * (line 1 always starts with an empty stack).
1163 * There is also a list for free entries. This construction is used to avoid
1164 * having to allocate and free memory blocks too often.
1165 *
1166 * When making changes to the buffer, this is logged in b_mod_*. When calling
1167 * update_screen() to update the display, it will call
1168 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1169 * entries. The entries which are inside the changed area are removed,
1170 * because they must be recomputed. Entries below the changed have their line
1171 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1172 * set to indicate that a check must be made if the changed lines would change
1173 * the cached entry.
1174 *
1175 * When later displaying lines, an entry is stored for each line. Displayed
1176 * lines are likely to be displayed again, in which case the state at the
1177 * start of the line is needed.
1178 * For not displayed lines, an entry is stored for every so many lines. These
1179 * entries will be used e.g., when scrolling backwards. The distance between
1180 * entries depends on the number of lines in the buffer. For small buffers
1181 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1182 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1183 */
1184
Bram Moolenaar860cae12010-06-05 23:22:07 +02001185 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001186syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187{
1188 synstate_T *p;
1189
1190 if (block->b_sst_array != NULL)
1191 {
1192 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1193 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001194 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001195 block->b_sst_len = 0;
1196 }
1197}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198/*
1199 * Free b_sst_array[] for buffer "buf".
1200 * Used when syntax items changed to force resyncing everywhere.
1201 */
1202 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001203syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001205#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001207#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208
Bram Moolenaar860cae12010-06-05 23:22:07 +02001209 syn_stack_free_block(block);
1210
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211#ifdef FEAT_FOLDING
1212 /* When using "syntax" fold method, must update all folds. */
1213 FOR_ALL_WINDOWS(wp)
1214 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001215 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216 foldUpdateAll(wp);
1217 }
1218#endif
1219}
1220
1221/*
1222 * Allocate the syntax state stack for syn_buf when needed.
1223 * If the number of entries in b_sst_array[] is much too big or a bit too
1224 * small, reallocate it.
1225 * Also used to allocate b_sst_array[] for the first time.
1226 */
1227 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001228syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229{
1230 long len;
1231 synstate_T *to, *from;
1232 synstate_T *sstp;
1233
1234 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1235 if (len < SST_MIN_ENTRIES)
1236 len = SST_MIN_ENTRIES;
1237 else if (len > SST_MAX_ENTRIES)
1238 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001239 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240 {
1241 /* Allocate 50% too much, to avoid reallocating too often. */
1242 len = syn_buf->b_ml.ml_line_count;
1243 len = (len + len / 2) / SST_DIST + Rows * 2;
1244 if (len < SST_MIN_ENTRIES)
1245 len = SST_MIN_ENTRIES;
1246 else if (len > SST_MAX_ENTRIES)
1247 len = SST_MAX_ENTRIES;
1248
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 {
1251 /* When shrinking the array, cleanup the existing stack.
1252 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 && syn_stack_cleanup())
1255 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1257 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 }
1259
1260 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1261 if (sstp == NULL) /* out of memory! */
1262 return;
1263
1264 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 {
1267 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 from = from->sst_next)
1270 {
1271 ++to;
1272 *to = *from;
1273 to->sst_next = to + 1;
1274 }
1275 }
1276 if (to != sstp - 1)
1277 {
1278 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 syn_block->b_sst_first = sstp;
1280 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 }
1282 else
1283 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 syn_block->b_sst_first = NULL;
1285 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 }
1287
1288 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001289 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 while (++to < sstp + len)
1291 to->sst_next = to + 1;
1292 (sstp + len - 1)->sst_next = NULL;
1293
Bram Moolenaar860cae12010-06-05 23:22:07 +02001294 vim_free(syn_block->b_sst_array);
1295 syn_block->b_sst_array = sstp;
1296 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 }
1298}
1299
1300/*
1301 * Check for changes in a buffer to affect stored syntax states. Uses the
1302 * b_mod_* fields.
1303 * Called from update_screen(), before screen is being updated, once for each
1304 * displayed buffer.
1305 */
1306 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001307syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 win_T *wp;
1310
1311 syn_stack_apply_changes_block(&buf->b_s, buf);
1312
1313 FOR_ALL_WINDOWS(wp)
1314 {
1315 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1316 syn_stack_apply_changes_block(wp->w_s, buf);
1317 }
1318}
1319
1320 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001321syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 synstate_T *p, *prev, *np;
1324 linenr_T n;
1325
Bram Moolenaar860cae12010-06-05 23:22:07 +02001326 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 return;
1328
1329 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001332 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 {
1334 n = p->sst_lnum + buf->b_mod_xlines;
1335 if (n <= buf->b_mod_bot)
1336 {
1337 /* this state is inside the changed area, remove it */
1338 np = p->sst_next;
1339 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 else
1342 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 p = np;
1345 continue;
1346 }
1347 /* This state is below the changed area. Remember the line
1348 * that needs to be parsed before this entry can be made valid
1349 * again. */
1350 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1351 {
1352 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1353 p->sst_change_lnum += buf->b_mod_xlines;
1354 else
1355 p->sst_change_lnum = buf->b_mod_top;
1356 }
1357 if (p->sst_change_lnum == 0
1358 || p->sst_change_lnum < buf->b_mod_bot)
1359 p->sst_change_lnum = buf->b_mod_bot;
1360
1361 p->sst_lnum = n;
1362 }
1363 prev = p;
1364 p = p->sst_next;
1365 }
1366}
1367
1368/*
1369 * Reduce the number of entries in the state stack for syn_buf.
1370 * Returns TRUE if at least one entry was freed.
1371 */
1372 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001373syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374{
1375 synstate_T *p, *prev;
1376 disptick_T tick;
1377 int above;
1378 int dist;
1379 int retval = FALSE;
1380
Bram Moolenaar860cae12010-06-05 23:22:07 +02001381 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 return retval;
1383
1384 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001386 dist = 999999;
1387 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001388 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389
1390 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001391 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001392 * be removed. Set "above" when the "tick" for the oldest entry is above
1393 * "b_sst_lasttick" (the display tick wraps around).
1394 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001397 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1399 {
1400 if (prev->sst_lnum + dist > p->sst_lnum)
1401 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001402 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 {
1404 if (!above || p->sst_tick < tick)
1405 tick = p->sst_tick;
1406 above = TRUE;
1407 }
1408 else if (!above && p->sst_tick < tick)
1409 tick = p->sst_tick;
1410 }
1411 }
1412
1413 /*
1414 * Go through the list to make the entries for the oldest tick at an
1415 * interval of several lines.
1416 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001417 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1419 {
1420 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1421 {
1422 /* Move this entry from used list to free list */
1423 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001424 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001425 p = prev;
1426 retval = TRUE;
1427 }
1428 }
1429 return retval;
1430}
1431
1432/*
1433 * Free the allocated memory for a syn_state item.
1434 * Move the entry into the free list.
1435 */
1436 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001437syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438{
1439 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 p->sst_next = block->b_sst_firstfree;
1441 block->b_sst_firstfree = p;
1442 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443}
1444
1445/*
1446 * Find an entry in the list of state stacks at or before "lnum".
1447 * Returns NULL when there is no entry or the first entry is after "lnum".
1448 */
1449 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001450syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451{
1452 synstate_T *p, *prev;
1453
1454 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001455 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 {
1457 if (p->sst_lnum == lnum)
1458 return p;
1459 if (p->sst_lnum > lnum)
1460 break;
1461 }
1462 return prev;
1463}
1464
1465/*
1466 * Try saving the current state in b_sst_array[].
1467 * The current state must be valid for the start of the current_lnum line!
1468 */
1469 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001470store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471{
1472 int i;
1473 synstate_T *p;
1474 bufstate_T *bp;
1475 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001476 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477
1478 /*
1479 * If the current state contains a start or end pattern that continues
1480 * from the previous line, we can't use it. Don't store it then.
1481 */
1482 for (i = current_state.ga_len - 1; i >= 0; --i)
1483 {
1484 cur_si = &CUR_STATE(i);
1485 if (cur_si->si_h_startpos.lnum >= current_lnum
1486 || cur_si->si_m_endpos.lnum >= current_lnum
1487 || cur_si->si_h_endpos.lnum >= current_lnum
1488 || (cur_si->si_end_idx
1489 && cur_si->si_eoe_pos.lnum >= current_lnum))
1490 break;
1491 }
1492 if (i >= 0)
1493 {
1494 if (sp != NULL)
1495 {
1496 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001497 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 else
1501 {
1502 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001503 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 if (p->sst_next == sp)
1505 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001506 if (p != NULL) /* just in case */
1507 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001509 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 sp = NULL;
1511 }
1512 }
1513 else if (sp == NULL || sp->sst_lnum != current_lnum)
1514 {
1515 /*
1516 * Add a new entry
1517 */
1518 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001519 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001520 {
1521 (void)syn_stack_cleanup();
1522 /* "sp" may have been moved to the freelist now */
1523 sp = syn_stack_find_entry(current_lnum);
1524 }
1525 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001526 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 sp = NULL;
1528 else
1529 {
1530 /* Take the first item from the free list and put it in the used
1531 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001532 p = syn_block->b_sst_firstfree;
1533 syn_block->b_sst_firstfree = p->sst_next;
1534 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535 if (sp == NULL)
1536 {
1537 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001538 p->sst_next = syn_block->b_sst_first;
1539 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540 }
1541 else
1542 {
1543 /* insert in list after *sp */
1544 p->sst_next = sp->sst_next;
1545 sp->sst_next = p;
1546 }
1547 sp = p;
1548 sp->sst_stacksize = 0;
1549 sp->sst_lnum = current_lnum;
1550 }
1551 }
1552 if (sp != NULL)
1553 {
1554 /* When overwriting an existing state stack, clear it first */
1555 clear_syn_state(sp);
1556 sp->sst_stacksize = current_state.ga_len;
1557 if (current_state.ga_len > SST_FIX_STATES)
1558 {
1559 /* Need to clear it, might be something remaining from when the
1560 * length was less than SST_FIX_STATES. */
1561 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1562 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1563 sp->sst_stacksize = 0;
1564 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1567 }
1568 else
1569 bp = sp->sst_union.sst_stack;
1570 for (i = 0; i < sp->sst_stacksize; ++i)
1571 {
1572 bp[i].bs_idx = CUR_STATE(i).si_idx;
1573 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001574#ifdef FEAT_CONCEAL
1575 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1576 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1579 }
1580 sp->sst_next_flags = current_next_flags;
1581 sp->sst_next_list = current_next_list;
1582 sp->sst_tick = display_tick;
1583 sp->sst_change_lnum = 0;
1584 }
1585 current_state_stored = TRUE;
1586 return sp;
1587}
1588
1589/*
1590 * Copy a state stack from "from" in b_sst_array[] to current_state;
1591 */
1592 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001593load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001594{
1595 int i;
1596 bufstate_T *bp;
1597
1598 clear_current_state();
1599 validate_current_state();
1600 keepend_level = -1;
1601 if (from->sst_stacksize
1602 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1603 {
1604 if (from->sst_stacksize > SST_FIX_STATES)
1605 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1606 else
1607 bp = from->sst_union.sst_stack;
1608 for (i = 0; i < from->sst_stacksize; ++i)
1609 {
1610 CUR_STATE(i).si_idx = bp[i].bs_idx;
1611 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001612#ifdef FEAT_CONCEAL
1613 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1614 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1615#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1617 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1618 keepend_level = i;
1619 CUR_STATE(i).si_ends = FALSE;
1620 CUR_STATE(i).si_m_lnum = 0;
1621 if (CUR_STATE(i).si_idx >= 0)
1622 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001623 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 else
1625 CUR_STATE(i).si_next_list = NULL;
1626 update_si_attr(i);
1627 }
1628 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 }
1630 current_next_list = from->sst_next_list;
1631 current_next_flags = from->sst_next_flags;
1632 current_lnum = from->sst_lnum;
1633}
1634
1635/*
1636 * Compare saved state stack "*sp" with the current state.
1637 * Return TRUE when they are equal.
1638 */
1639 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001640syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641{
1642 int i, j;
1643 bufstate_T *bp;
1644 reg_extmatch_T *six, *bsx;
1645
1646 /* First a quick check if the stacks have the same size end nextlist. */
1647 if (sp->sst_stacksize == current_state.ga_len
1648 && sp->sst_next_list == current_next_list)
1649 {
1650 /* Need to compare all states on both stacks. */
1651 if (sp->sst_stacksize > SST_FIX_STATES)
1652 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1653 else
1654 bp = sp->sst_union.sst_stack;
1655
1656 for (i = current_state.ga_len; --i >= 0; )
1657 {
1658 /* If the item has another index the state is different. */
1659 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1660 break;
1661 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1662 {
1663 /* When the extmatch pointers are different, the strings in
1664 * them can still be the same. Check if the extmatch
1665 * references are equal. */
1666 bsx = bp[i].bs_extmatch;
1667 six = CUR_STATE(i).si_extmatch;
1668 /* If one of the extmatch pointers is NULL the states are
1669 * different. */
1670 if (bsx == NULL || six == NULL)
1671 break;
1672 for (j = 0; j < NSUBEXP; ++j)
1673 {
1674 /* Check each referenced match string. They must all be
1675 * equal. */
1676 if (bsx->matches[j] != six->matches[j])
1677 {
1678 /* If the pointer is different it can still be the
1679 * same text. Compare the strings, ignore case when
1680 * the start item has the sp_ic flag set. */
1681 if (bsx->matches[j] == NULL
1682 || six->matches[j] == NULL)
1683 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001684 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 ? MB_STRICMP(bsx->matches[j],
1686 six->matches[j]) != 0
1687 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1688 break;
1689 }
1690 }
1691 if (j != NSUBEXP)
1692 break;
1693 }
1694 }
1695 if (i < 0)
1696 return TRUE;
1697 }
1698 return FALSE;
1699}
1700
1701/*
1702 * We stop parsing syntax above line "lnum". If the stored state at or below
1703 * this line depended on a change before it, it now depends on the line below
1704 * the last parsed line.
1705 * The window looks like this:
1706 * line which changed
1707 * displayed line
1708 * displayed line
1709 * lnum -> line below window
1710 */
1711 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001712syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713{
1714 synstate_T *sp;
1715
1716 sp = syn_stack_find_entry(lnum);
1717 if (sp != NULL && sp->sst_lnum < lnum)
1718 sp = sp->sst_next;
1719
1720 if (sp != NULL && sp->sst_change_lnum != 0)
1721 sp->sst_change_lnum = lnum;
1722}
1723
1724/*
1725 * End of handling of the state stack.
1726 ****************************************/
1727
1728 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731 clear_current_state();
1732 current_state.ga_itemsize = 0; /* mark current_state invalid */
1733 current_next_list = NULL;
1734 keepend_level = -1;
1735}
1736
1737 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001738validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739{
1740 current_state.ga_itemsize = sizeof(stateitem_T);
1741 current_state.ga_growsize = 3;
1742}
1743
1744/*
1745 * Return TRUE if the syntax at start of lnum changed since last time.
1746 * This will only be called just after get_syntax_attr() for the previous
1747 * line, to check if the next line needs to be redrawn too.
1748 */
1749 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001750syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751{
1752 int retval = TRUE;
1753 synstate_T *sp;
1754
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 /*
1756 * Check the state stack when:
1757 * - lnum is just below the previously syntaxed line.
1758 * - lnum is not before the lines with saved states.
1759 * - lnum is not past the lines with saved states.
1760 * - lnum is at or before the last changed line.
1761 */
1762 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1763 {
1764 sp = syn_stack_find_entry(lnum);
1765 if (sp != NULL && sp->sst_lnum == lnum)
1766 {
1767 /*
1768 * finish the previous line (needed when not all of the line was
1769 * drawn)
1770 */
1771 (void)syn_finish_line(FALSE);
1772
1773 /*
1774 * Compare the current state with the previously saved state of
1775 * the line.
1776 */
1777 if (syn_stack_equal(sp))
1778 retval = FALSE;
1779
1780 /*
1781 * Store the current state in b_sst_array[] for later use.
1782 */
1783 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001784 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 }
1786 }
1787
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 return retval;
1789}
1790
1791/*
1792 * Finish the current line.
1793 * This doesn't return any attributes, it only gets the state at the end of
1794 * the line. It can start anywhere in the line, as long as the current state
1795 * is valid.
1796 */
1797 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001798syn_finish_line(
1799 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800{
1801 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001802 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001804 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001806 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1807 /*
1808 * When syncing, and found some item, need to check the item.
1809 */
1810 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001813 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001815 cur_si = &CUR_STATE(current_state.ga_len - 1);
1816 if (cur_si->si_idx >= 0
1817 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1818 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1819 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001821 /* syn_current_attr() will have skipped the check for an item
1822 * that ends here, need to do that now. Be careful not to go
1823 * past the NUL. */
1824 prev_current_col = current_col;
1825 if (syn_getcurline()[current_col] != NUL)
1826 ++current_col;
1827 check_state_ends();
1828 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001830 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 }
1832 return FALSE;
1833}
1834
1835/*
1836 * Return highlight attributes for next character.
1837 * Must first call syntax_start() once for the line.
1838 * "col" is normally 0 for the first use in a line, and increments by one each
1839 * time. It's allowed to skip characters and to stop before the end of the
1840 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001841 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1842 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 */
1844 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001845get_syntax_attr(
1846 colnr_T col,
1847 int *can_spell,
1848 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849{
1850 int attr = 0;
1851
Bram Moolenaar349955a2007-08-14 21:07:36 +00001852 if (can_spell != NULL)
1853 /* Default: Only do spelling when there is no @Spell cluster or when
1854 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001855 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1856 ? (syn_block->b_spell_cluster_id == 0)
1857 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001858
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001860 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 return 0;
1862
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001863 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001864 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001865 {
1866 clear_current_state();
1867#ifdef FEAT_EVAL
1868 current_id = 0;
1869 current_trans_id = 0;
1870#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001871#ifdef FEAT_CONCEAL
1872 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001873 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001874#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001875 return 0;
1876 }
1877
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 /* Make sure current_state is valid */
1879 if (INVALID_STATE(&current_state))
1880 validate_current_state();
1881
1882 /*
1883 * Skip from the current column to "col", get the attributes for "col".
1884 */
1885 while (current_col <= col)
1886 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001887 attr = syn_current_attr(FALSE, TRUE, can_spell,
1888 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001889 ++current_col;
1890 }
1891
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 return attr;
1893}
1894
1895/*
1896 * Get syntax attributes for current_lnum, current_col.
1897 */
1898 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001899syn_current_attr(
1900 int syncing, /* When 1: called for syncing */
1901 int displaying, /* result will be displayed */
1902 int *can_spell, /* return: do spell checking */
1903 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001904{
1905 int syn_id;
1906 lpos_T endpos; /* was: char_u *endp; */
1907 lpos_T hl_startpos; /* was: int hl_startcol; */
1908 lpos_T hl_endpos;
1909 lpos_T eos_pos; /* end-of-start match (start region) */
1910 lpos_T eoe_pos; /* end-of-end pattern */
1911 int end_idx; /* group ID for end pattern */
1912 int idx;
1913 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001914 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 int startcol;
1916 int endcol;
1917 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001918 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 short *next_list;
1920 int found_match; /* found usable match */
1921 static int try_next_column = FALSE; /* must try in next col */
1922 int do_keywords;
1923 regmmatch_T regmatch;
1924 lpos_T pos;
1925 int lc_col;
1926 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001927 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928 char_u *line; /* current line. NOTE: becomes invalid after
1929 looking for a pattern match! */
1930
1931 /* variables for zero-width matches that have a "nextgroup" argument */
1932 int keep_next_list;
1933 int zero_width_next_list = FALSE;
1934 garray_T zero_width_next_ga;
1935
1936 /*
1937 * No character, no attributes! Past end of line?
1938 * Do try matching with an empty line (could be the start of a region).
1939 */
1940 line = syn_getcurline();
1941 if (line[current_col] == NUL && current_col != 0)
1942 {
1943 /*
1944 * If we found a match after the last column, use it.
1945 */
1946 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1947 && next_match_col != MAXCOL)
1948 (void)push_next_match(NULL);
1949
1950 current_finished = TRUE;
1951 current_state_stored = FALSE;
1952 return 0;
1953 }
1954
1955 /* if the current or next character is NUL, we will finish the line now */
1956 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1957 {
1958 current_finished = TRUE;
1959 current_state_stored = FALSE;
1960 }
1961
1962 /*
1963 * When in the previous column there was a match but it could not be used
1964 * (empty match or already matched in this column) need to try again in
1965 * the next column.
1966 */
1967 if (try_next_column)
1968 {
1969 next_match_idx = -1;
1970 try_next_column = FALSE;
1971 }
1972
1973 /* Only check for keywords when not syncing and there are some. */
1974 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001975 && (syn_block->b_keywtab.ht_used > 0
1976 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977
1978 /* Init the list of zero-width matches with a nextlist. This is used to
1979 * avoid matching the same item in the same position twice. */
1980 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1981
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001982 /* use syntax iskeyword option */
1983 save_chartab(buf_chartab);
1984
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 /*
1986 * Repeat matching keywords and patterns, to find contained items at the
1987 * same column. This stops when there are no extra matches at the current
1988 * column.
1989 */
1990 do
1991 {
1992 found_match = FALSE;
1993 keep_next_list = FALSE;
1994 syn_id = 0;
1995
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001996
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 /*
1998 * 1. Check for a current state.
1999 * Only when there is no current state, or if the current state may
2000 * contain other things, we need to check for keywords and patterns.
2001 * Always need to check for contained items if some item has the
2002 * "containedin" argument (takes extra time!).
2003 */
2004 if (current_state.ga_len)
2005 cur_si = &CUR_STATE(current_state.ga_len - 1);
2006 else
2007 cur_si = NULL;
2008
Bram Moolenaar860cae12010-06-05 23:22:07 +02002009 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 || cur_si->si_cont_list != NULL)
2011 {
2012 /*
2013 * 2. Check for keywords, if on a keyword char after a non-keyword
2014 * char. Don't do this when syncing.
2015 */
2016 if (do_keywords)
2017 {
2018 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002019 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002021 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022#ifdef FEAT_MBYTE
2023 - (has_mbyte
2024 ? (*mb_head_off)(line, line + current_col - 1)
2025 : 0)
2026#endif
2027 , syn_buf)))
2028 {
2029 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002030 &endcol, &flags, &next_list, cur_si,
2031 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002032 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 {
2034 if (push_current_state(KEYWORD_IDX) == OK)
2035 {
2036 cur_si = &CUR_STATE(current_state.ga_len - 1);
2037 cur_si->si_m_startcol = current_col;
2038 cur_si->si_h_startpos.lnum = current_lnum;
2039 cur_si->si_h_startpos.col = 0; /* starts right away */
2040 cur_si->si_m_endpos.lnum = current_lnum;
2041 cur_si->si_m_endpos.col = endcol;
2042 cur_si->si_h_endpos.lnum = current_lnum;
2043 cur_si->si_h_endpos.col = endcol;
2044 cur_si->si_ends = TRUE;
2045 cur_si->si_end_idx = 0;
2046 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002047#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002048 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002049 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002050 if (current_state.ga_len > 1)
2051 cur_si->si_flags |=
2052 CUR_STATE(current_state.ga_len - 2).si_flags
2053 & HL_CONCEAL;
2054#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055 cur_si->si_id = syn_id;
2056 cur_si->si_trans_id = syn_id;
2057 if (flags & HL_TRANSP)
2058 {
2059 if (current_state.ga_len < 2)
2060 {
2061 cur_si->si_attr = 0;
2062 cur_si->si_trans_id = 0;
2063 }
2064 else
2065 {
2066 cur_si->si_attr = CUR_STATE(
2067 current_state.ga_len - 2).si_attr;
2068 cur_si->si_trans_id = CUR_STATE(
2069 current_state.ga_len - 2).si_trans_id;
2070 }
2071 }
2072 else
2073 cur_si->si_attr = syn_id2attr(syn_id);
2074 cur_si->si_cont_list = NULL;
2075 cur_si->si_next_list = next_list;
2076 check_keepend();
2077 }
2078 else
2079 vim_free(next_list);
2080 }
2081 }
2082 }
2083
2084 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002085 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002087 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002088 {
2089 /*
2090 * If we didn't check for a match yet, or we are past it, check
2091 * for any match with a pattern.
2092 */
2093 if (next_match_idx < 0 || next_match_col < (int)current_col)
2094 {
2095 /*
2096 * Check all relevant patterns for a match at this
2097 * position. This is complicated, because matching with a
2098 * pattern takes quite a bit of time, thus we want to
2099 * avoid doing it when it's not needed.
2100 */
2101 next_match_idx = 0; /* no match in this line yet */
2102 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002103 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002105 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 if ( spp->sp_syncing == syncing
2107 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2108 && (spp->sp_type == SPTYPE_MATCH
2109 || spp->sp_type == SPTYPE_START)
2110 && (current_next_list != NULL
2111 ? in_id_list(NULL, current_next_list,
2112 &spp->sp_syn, 0)
2113 : (cur_si == NULL
2114 ? !(spp->sp_flags & HL_CONTAINED)
2115 : in_id_list(cur_si,
2116 cur_si->si_cont_list, &spp->sp_syn,
2117 spp->sp_flags & HL_CONTAINED))))
2118 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002119 int r;
2120
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121 /* If we already tried matching in this line, and
2122 * there isn't a match before next_match_col, skip
2123 * this item. */
2124 if (spp->sp_line_id == current_line_id
2125 && spp->sp_startcol >= next_match_col)
2126 continue;
2127 spp->sp_line_id = current_line_id;
2128
2129 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2130 if (lc_col < 0)
2131 lc_col = 0;
2132
2133 regmatch.rmm_ic = spp->sp_ic;
2134 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002135 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002136 current_lnum,
2137 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002138 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002139 spp->sp_prog = regmatch.regprog;
2140 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 {
2142 /* no match in this line, try another one */
2143 spp->sp_startcol = MAXCOL;
2144 continue;
2145 }
2146
2147 /*
2148 * Compute the first column of the match.
2149 */
2150 syn_add_start_off(&pos, &regmatch,
2151 spp, SPO_MS_OFF, -1);
2152 if (pos.lnum > current_lnum)
2153 {
2154 /* must have used end of match in a next line,
2155 * we can't handle that */
2156 spp->sp_startcol = MAXCOL;
2157 continue;
2158 }
2159 startcol = pos.col;
2160
2161 /* remember the next column where this pattern
2162 * matches in the current line */
2163 spp->sp_startcol = startcol;
2164
2165 /*
2166 * If a previously found match starts at a lower
2167 * column number, don't use this one.
2168 */
2169 if (startcol >= next_match_col)
2170 continue;
2171
2172 /*
2173 * If we matched this pattern at this position
2174 * before, skip it. Must retry in the next
2175 * column, because it may match from there.
2176 */
2177 if (did_match_already(idx, &zero_width_next_ga))
2178 {
2179 try_next_column = TRUE;
2180 continue;
2181 }
2182
2183 endpos.lnum = regmatch.endpos[0].lnum;
2184 endpos.col = regmatch.endpos[0].col;
2185
2186 /* Compute the highlight start. */
2187 syn_add_start_off(&hl_startpos, &regmatch,
2188 spp, SPO_HS_OFF, -1);
2189
2190 /* Compute the region start. */
2191 /* Default is to use the end of the match. */
2192 syn_add_end_off(&eos_pos, &regmatch,
2193 spp, SPO_RS_OFF, 0);
2194
2195 /*
2196 * Grab the external submatches before they get
2197 * overwritten. Reference count doesn't change.
2198 */
2199 unref_extmatch(cur_extmatch);
2200 cur_extmatch = re_extmatch_out;
2201 re_extmatch_out = NULL;
2202
2203 flags = 0;
2204 eoe_pos.lnum = 0; /* avoid warning */
2205 eoe_pos.col = 0;
2206 end_idx = 0;
2207 hl_endpos.lnum = 0;
2208
2209 /*
2210 * For a "oneline" the end must be found in the
2211 * same line too. Search for it after the end of
2212 * the match with the start pattern. Set the
2213 * resulting end positions at the same time.
2214 */
2215 if (spp->sp_type == SPTYPE_START
2216 && (spp->sp_flags & HL_ONELINE))
2217 {
2218 lpos_T startpos;
2219
2220 startpos = endpos;
2221 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2222 &flags, &eoe_pos, &end_idx, cur_extmatch);
2223 if (endpos.lnum == 0)
2224 continue; /* not found */
2225 }
2226
2227 /*
2228 * For a "match" the size must be > 0 after the
2229 * end offset needs has been added. Except when
2230 * syncing.
2231 */
2232 else if (spp->sp_type == SPTYPE_MATCH)
2233 {
2234 syn_add_end_off(&hl_endpos, &regmatch, spp,
2235 SPO_HE_OFF, 0);
2236 syn_add_end_off(&endpos, &regmatch, spp,
2237 SPO_ME_OFF, 0);
2238 if (endpos.lnum == current_lnum
2239 && (int)endpos.col + syncing < startcol)
2240 {
2241 /*
2242 * If an empty string is matched, may need
2243 * to try matching again at next column.
2244 */
2245 if (regmatch.startpos[0].col
2246 == regmatch.endpos[0].col)
2247 try_next_column = TRUE;
2248 continue;
2249 }
2250 }
2251
2252 /*
2253 * keep the best match so far in next_match_*
2254 */
2255 /* Highlighting must start after startpos and end
2256 * before endpos. */
2257 if (hl_startpos.lnum == current_lnum
2258 && (int)hl_startpos.col < startcol)
2259 hl_startpos.col = startcol;
2260 limit_pos_zero(&hl_endpos, &endpos);
2261
2262 next_match_idx = idx;
2263 next_match_col = startcol;
2264 next_match_m_endpos = endpos;
2265 next_match_h_endpos = hl_endpos;
2266 next_match_h_startpos = hl_startpos;
2267 next_match_flags = flags;
2268 next_match_eos_pos = eos_pos;
2269 next_match_eoe_pos = eoe_pos;
2270 next_match_end_idx = end_idx;
2271 unref_extmatch(next_match_extmatch);
2272 next_match_extmatch = cur_extmatch;
2273 cur_extmatch = NULL;
2274 }
2275 }
2276 }
2277
2278 /*
2279 * If we found a match at the current column, use it.
2280 */
2281 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2282 {
2283 synpat_T *lspp;
2284
2285 /* When a zero-width item matched which has a nextgroup,
2286 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002287 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 if (next_match_m_endpos.lnum == current_lnum
2289 && next_match_m_endpos.col == current_col
2290 && lspp->sp_next_list != NULL)
2291 {
2292 current_next_list = lspp->sp_next_list;
2293 current_next_flags = lspp->sp_flags;
2294 keep_next_list = TRUE;
2295 zero_width_next_list = TRUE;
2296
2297 /* Add the index to a list, so that we can check
2298 * later that we don't match it again (and cause an
2299 * endless loop). */
2300 if (ga_grow(&zero_width_next_ga, 1) == OK)
2301 {
2302 ((int *)(zero_width_next_ga.ga_data))
2303 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 }
2305 next_match_idx = -1;
2306 }
2307 else
2308 cur_si = push_next_match(cur_si);
2309 found_match = TRUE;
2310 }
2311 }
2312 }
2313
2314 /*
2315 * Handle searching for nextgroup match.
2316 */
2317 if (current_next_list != NULL && !keep_next_list)
2318 {
2319 /*
2320 * If a nextgroup was not found, continue looking for one if:
2321 * - this is an empty line and the "skipempty" option was given
2322 * - we are on white space and the "skipwhite" option was given
2323 */
2324 if (!found_match)
2325 {
2326 line = syn_getcurline();
2327 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002328 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 || ((current_next_flags & HL_SKIPEMPTY)
2330 && *line == NUL))
2331 break;
2332 }
2333
2334 /*
2335 * If a nextgroup was found: Use it, and continue looking for
2336 * contained matches.
2337 * If a nextgroup was not found: Continue looking for a normal
2338 * match.
2339 * When did set current_next_list for a zero-width item and no
2340 * match was found don't loop (would get stuck).
2341 */
2342 current_next_list = NULL;
2343 next_match_idx = -1;
2344 if (!zero_width_next_list)
2345 found_match = TRUE;
2346 }
2347
2348 } while (found_match);
2349
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002350 restore_chartab(buf_chartab);
2351
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 /*
2353 * Use attributes from the current state, if within its highlighting.
2354 * If not, use attributes from the current-but-one state, etc.
2355 */
2356 current_attr = 0;
2357#ifdef FEAT_EVAL
2358 current_id = 0;
2359 current_trans_id = 0;
2360#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002361#ifdef FEAT_CONCEAL
2362 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002363 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002364#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 if (cur_si != NULL)
2366 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002367#ifndef FEAT_EVAL
2368 int current_trans_id = 0;
2369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002370 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2371 {
2372 sip = &CUR_STATE(idx);
2373 if ((current_lnum > sip->si_h_startpos.lnum
2374 || (current_lnum == sip->si_h_startpos.lnum
2375 && current_col >= sip->si_h_startpos.col))
2376 && (sip->si_h_endpos.lnum == 0
2377 || current_lnum < sip->si_h_endpos.lnum
2378 || (current_lnum == sip->si_h_endpos.lnum
2379 && current_col < sip->si_h_endpos.col)))
2380 {
2381 current_attr = sip->si_attr;
2382#ifdef FEAT_EVAL
2383 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002386#ifdef FEAT_CONCEAL
2387 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002388 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002389 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002390#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391 break;
2392 }
2393 }
2394
Bram Moolenaar217ad922005-03-20 22:37:15 +00002395 if (can_spell != NULL)
2396 {
2397 struct sp_syn sps;
2398
2399 /*
2400 * set "can_spell" to TRUE if spell checking is supposed to be
2401 * done in the current item.
2402 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002403 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002404 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002405 /* There is no @Spell cluster: Do spelling for items without
2406 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002407 if (syn_block->b_nospell_cluster_id == 0
2408 || current_trans_id == 0)
2409 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002410 else
2411 {
2412 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002413 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002414 sps.cont_in_list = NULL;
2415 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2416 }
2417 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002418 else
2419 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002420 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002421 * the @Spell cluster. But not when @NoSpell is also there.
2422 * At the toplevel only spell check when ":syn spell toplevel"
2423 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002425 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002426 else
2427 {
2428 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002429 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002430 sps.cont_in_list = NULL;
2431 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2432
Bram Moolenaar860cae12010-06-05 23:22:07 +02002433 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002434 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002435 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002436 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2437 *can_spell = FALSE;
2438 }
2439 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002440 }
2441 }
2442
2443
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 /*
2445 * Check for end of current state (and the states before it) at the
2446 * next column. Don't do this for syncing, because we would miss a
2447 * single character match.
2448 * First check if the current state ends at the current column. It
2449 * may be for an empty match and a containing item might end in the
2450 * current column.
2451 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002452 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002453 {
2454 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002455 if (current_state.ga_len > 0
2456 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 {
2458 ++current_col;
2459 check_state_ends();
2460 --current_col;
2461 }
2462 }
2463 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002464 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002465 /* Default: Only do spelling when there is no @Spell cluster or when
2466 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002467 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2468 ? (syn_block->b_spell_cluster_id == 0)
2469 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002471 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 if (current_next_list != NULL
Bram Moolenaar069dafc2018-03-03 20:02:19 +01002473 && (line = syn_getcurline())[current_col] != NUL
2474 && line[current_col + 1] == NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2476 current_next_list = NULL;
2477
2478 if (zero_width_next_ga.ga_len > 0)
2479 ga_clear(&zero_width_next_ga);
2480
2481 /* No longer need external matches. But keep next_match_extmatch. */
2482 unref_extmatch(re_extmatch_out);
2483 re_extmatch_out = NULL;
2484 unref_extmatch(cur_extmatch);
2485
2486 return current_attr;
2487}
2488
2489
2490/*
2491 * Check if we already matched pattern "idx" at the current column.
2492 */
2493 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002494did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002495{
2496 int i;
2497
2498 for (i = current_state.ga_len; --i >= 0; )
2499 if (CUR_STATE(i).si_m_startcol == (int)current_col
2500 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2501 && CUR_STATE(i).si_idx == idx)
2502 return TRUE;
2503
2504 /* Zero-width matches with a nextgroup argument are not put on the syntax
2505 * stack, and can only be matched once anyway. */
2506 for (i = gap->ga_len; --i >= 0; )
2507 if (((int *)(gap->ga_data))[i] == idx)
2508 return TRUE;
2509
2510 return FALSE;
2511}
2512
2513/*
2514 * Push the next match onto the stack.
2515 */
2516 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002517push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518{
2519 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002520#ifdef FEAT_CONCEAL
2521 int save_flags;
2522#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523
Bram Moolenaar860cae12010-06-05 23:22:07 +02002524 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002525
2526 /*
2527 * Push the item in current_state stack;
2528 */
2529 if (push_current_state(next_match_idx) == OK)
2530 {
2531 /*
2532 * If it's a start-skip-end type that crosses lines, figure out how
2533 * much it continues in this line. Otherwise just fill in the length.
2534 */
2535 cur_si = &CUR_STATE(current_state.ga_len - 1);
2536 cur_si->si_h_startpos = next_match_h_startpos;
2537 cur_si->si_m_startcol = current_col;
2538 cur_si->si_m_lnum = current_lnum;
2539 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002540#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002541 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002542 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002543 if (current_state.ga_len > 1)
2544 cur_si->si_flags |=
2545 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2546#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 cur_si->si_next_list = spp->sp_next_list;
2548 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2549 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2550 {
2551 /* Try to find the end pattern in the current line */
2552 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2553 check_keepend();
2554 }
2555 else
2556 {
2557 cur_si->si_m_endpos = next_match_m_endpos;
2558 cur_si->si_h_endpos = next_match_h_endpos;
2559 cur_si->si_ends = TRUE;
2560 cur_si->si_flags |= next_match_flags;
2561 cur_si->si_eoe_pos = next_match_eoe_pos;
2562 cur_si->si_end_idx = next_match_end_idx;
2563 }
2564 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2565 keepend_level = current_state.ga_len - 1;
2566 check_keepend();
2567 update_si_attr(current_state.ga_len - 1);
2568
Bram Moolenaar860cae12010-06-05 23:22:07 +02002569#ifdef FEAT_CONCEAL
2570 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2571#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 /*
2573 * If the start pattern has another highlight group, push another item
2574 * on the stack for the start pattern.
2575 */
2576 if ( spp->sp_type == SPTYPE_START
2577 && spp->sp_syn_match_id != 0
2578 && push_current_state(next_match_idx) == OK)
2579 {
2580 cur_si = &CUR_STATE(current_state.ga_len - 1);
2581 cur_si->si_h_startpos = next_match_h_startpos;
2582 cur_si->si_m_startcol = current_col;
2583 cur_si->si_m_lnum = current_lnum;
2584 cur_si->si_m_endpos = next_match_eos_pos;
2585 cur_si->si_h_endpos = next_match_eos_pos;
2586 cur_si->si_ends = TRUE;
2587 cur_si->si_end_idx = 0;
2588 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002589#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002590 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002591 cur_si->si_flags |= save_flags;
2592 if (cur_si->si_flags & HL_CONCEALENDS)
2593 cur_si->si_flags |= HL_CONCEAL;
2594#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595 cur_si->si_next_list = NULL;
2596 check_keepend();
2597 update_si_attr(current_state.ga_len - 1);
2598 }
2599 }
2600
2601 next_match_idx = -1; /* try other match next time */
2602
2603 return cur_si;
2604}
2605
2606/*
2607 * Check for end of current state (and the states before it).
2608 */
2609 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002610check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002611{
2612 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002613 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614
2615 cur_si = &CUR_STATE(current_state.ga_len - 1);
2616 for (;;)
2617 {
2618 if (cur_si->si_ends
2619 && (cur_si->si_m_endpos.lnum < current_lnum
2620 || (cur_si->si_m_endpos.lnum == current_lnum
2621 && cur_si->si_m_endpos.col <= current_col)))
2622 {
2623 /*
2624 * If there is an end pattern group ID, highlight the end pattern
2625 * now. No need to pop the current item from the stack.
2626 * Only do this if the end pattern continues beyond the current
2627 * position.
2628 */
2629 if (cur_si->si_end_idx
2630 && (cur_si->si_eoe_pos.lnum > current_lnum
2631 || (cur_si->si_eoe_pos.lnum == current_lnum
2632 && cur_si->si_eoe_pos.col > current_col)))
2633 {
2634 cur_si->si_idx = cur_si->si_end_idx;
2635 cur_si->si_end_idx = 0;
2636 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2637 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2638 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002639#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002640 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002641 if (cur_si->si_flags & HL_CONCEALENDS)
2642 cur_si->si_flags |= HL_CONCEAL;
2643#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002644 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002645
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002646 /* nextgroup= should not match in the end pattern */
2647 current_next_list = NULL;
2648
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002649 /* what matches next may be different now, clear it */
2650 next_match_idx = 0;
2651 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652 break;
2653 }
2654 else
2655 {
2656 /* handle next_list, unless at end of line and no "skipnl" or
2657 * "skipempty" */
2658 current_next_list = cur_si->si_next_list;
2659 current_next_flags = cur_si->si_flags;
2660 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2661 && syn_getcurline()[current_col] == NUL)
2662 current_next_list = NULL;
2663
2664 /* When the ended item has "extend", another item with
2665 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002666 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002667
2668 pop_current_state();
2669
2670 if (current_state.ga_len == 0)
2671 break;
2672
Bram Moolenaar81993f42008-01-11 20:27:45 +00002673 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 {
2675 syn_update_ends(FALSE);
2676 if (current_state.ga_len == 0)
2677 break;
2678 }
2679
2680 cur_si = &CUR_STATE(current_state.ga_len - 1);
2681
2682 /*
2683 * Only for a region the search for the end continues after
2684 * the end of the contained item. If the contained match
2685 * included the end-of-line, break here, the region continues.
2686 * Don't do this when:
2687 * - "keepend" is used for the contained item
2688 * - not at the end of the line (could be end="x$"me=e-1).
2689 * - "excludenl" is used (HL_HAS_EOL won't be set)
2690 */
2691 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002692 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002693 == SPTYPE_START
2694 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2695 {
2696 update_si_end(cur_si, (int)current_col, TRUE);
2697 check_keepend();
2698 if ((current_next_flags & HL_HAS_EOL)
2699 && keepend_level < 0
2700 && syn_getcurline()[current_col] == NUL)
2701 break;
2702 }
2703 }
2704 }
2705 else
2706 break;
2707 }
2708}
2709
2710/*
2711 * Update an entry in the current_state stack for a match or region. This
2712 * fills in si_attr, si_next_list and si_cont_list.
2713 */
2714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002715update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002716{
2717 stateitem_T *sip = &CUR_STATE(idx);
2718 synpat_T *spp;
2719
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002720 /* This should not happen... */
2721 if (sip->si_idx < 0)
2722 return;
2723
Bram Moolenaar860cae12010-06-05 23:22:07 +02002724 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725 if (sip->si_flags & HL_MATCH)
2726 sip->si_id = spp->sp_syn_match_id;
2727 else
2728 sip->si_id = spp->sp_syn.id;
2729 sip->si_attr = syn_id2attr(sip->si_id);
2730 sip->si_trans_id = sip->si_id;
2731 if (sip->si_flags & HL_MATCH)
2732 sip->si_cont_list = NULL;
2733 else
2734 sip->si_cont_list = spp->sp_cont_list;
2735
2736 /*
2737 * For transparent items, take attr from outer item.
2738 * Also take cont_list, if there is none.
2739 * Don't do this for the matchgroup of a start or end pattern.
2740 */
2741 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2742 {
2743 if (idx == 0)
2744 {
2745 sip->si_attr = 0;
2746 sip->si_trans_id = 0;
2747 if (sip->si_cont_list == NULL)
2748 sip->si_cont_list = ID_LIST_ALL;
2749 }
2750 else
2751 {
2752 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2753 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002754 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2755 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 if (sip->si_cont_list == NULL)
2757 {
2758 sip->si_flags |= HL_TRANS_CONT;
2759 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2760 }
2761 }
2762 }
2763}
2764
2765/*
2766 * Check the current stack for patterns with "keepend" flag.
2767 * Propagate the match-end to contained items, until a "skipend" item is found.
2768 */
2769 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002770check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771{
2772 int i;
2773 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002774 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 stateitem_T *sip;
2776
2777 /*
2778 * This check can consume a lot of time; only do it from the level where
2779 * there really is a keepend.
2780 */
2781 if (keepend_level < 0)
2782 return;
2783
2784 /*
2785 * Find the last index of an "extend" item. "keepend" items before that
2786 * won't do anything. If there is no "extend" item "i" will be
2787 * "keepend_level" and all "keepend" items will work normally.
2788 */
2789 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2790 if (CUR_STATE(i).si_flags & HL_EXTEND)
2791 break;
2792
2793 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002794 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002795 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002796 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 for ( ; i < current_state.ga_len; ++i)
2798 {
2799 sip = &CUR_STATE(i);
2800 if (maxpos.lnum != 0)
2801 {
2802 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002803 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2805 sip->si_ends = TRUE;
2806 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002807 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2808 {
2809 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810 || maxpos.lnum > sip->si_m_endpos.lnum
2811 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002812 && maxpos.col > sip->si_m_endpos.col))
2813 maxpos = sip->si_m_endpos;
2814 if (maxpos_h.lnum == 0
2815 || maxpos_h.lnum > sip->si_h_endpos.lnum
2816 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2817 && maxpos_h.col > sip->si_h_endpos.col))
2818 maxpos_h = sip->si_h_endpos;
2819 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 }
2821}
2822
2823/*
2824 * Update an entry in the current_state stack for a start-skip-end pattern.
2825 * This finds the end of the current item, if it's in the current line.
2826 *
2827 * Return the flags for the matched END.
2828 */
2829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002830update_si_end(
2831 stateitem_T *sip,
2832 int startcol, /* where to start searching for the end */
2833 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834{
2835 lpos_T startpos;
2836 lpos_T endpos;
2837 lpos_T hl_endpos;
2838 lpos_T end_endpos;
2839 int end_idx;
2840
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002841 /* return quickly for a keyword */
2842 if (sip->si_idx < 0)
2843 return;
2844
Bram Moolenaar071d4272004-06-13 20:20:40 +00002845 /* Don't update when it's already done. Can be a match of an end pattern
2846 * that started in a previous line. Watch out: can also be a "keepend"
2847 * from a containing item. */
2848 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2849 return;
2850
2851 /*
2852 * We need to find the end of the region. It may continue in the next
2853 * line.
2854 */
2855 end_idx = 0;
2856 startpos.lnum = current_lnum;
2857 startpos.col = startcol;
2858 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2859 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2860
2861 if (endpos.lnum == 0)
2862 {
2863 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002864 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 {
2866 /* a "oneline" never continues in the next line */
2867 sip->si_ends = TRUE;
2868 sip->si_m_endpos.lnum = current_lnum;
2869 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2870 }
2871 else
2872 {
2873 /* continues in the next line */
2874 sip->si_ends = FALSE;
2875 sip->si_m_endpos.lnum = 0;
2876 }
2877 sip->si_h_endpos = sip->si_m_endpos;
2878 }
2879 else
2880 {
2881 /* match within this line */
2882 sip->si_m_endpos = endpos;
2883 sip->si_h_endpos = hl_endpos;
2884 sip->si_eoe_pos = end_endpos;
2885 sip->si_ends = TRUE;
2886 sip->si_end_idx = end_idx;
2887 }
2888}
2889
2890/*
2891 * Add a new state to the current state stack.
2892 * It is cleared and the index set to "idx".
2893 * Return FAIL if it's not possible (out of memory).
2894 */
2895 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002896push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897{
2898 if (ga_grow(&current_state, 1) == FAIL)
2899 return FAIL;
2900 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2901 CUR_STATE(current_state.ga_len).si_idx = idx;
2902 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 return OK;
2904}
2905
2906/*
2907 * Remove a state from the current_state stack.
2908 */
2909 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002910pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911{
2912 if (current_state.ga_len)
2913 {
2914 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2915 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 }
2917 /* after the end of a pattern, try matching a keyword or pattern */
2918 next_match_idx = -1;
2919
2920 /* if first state with "keepend" is popped, reset keepend_level */
2921 if (keepend_level >= current_state.ga_len)
2922 keepend_level = -1;
2923}
2924
2925/*
2926 * Find the end of a start/skip/end syntax region after "startpos".
2927 * Only checks one line.
2928 * Also handles a match item that continued from a previous line.
2929 * If not found, the syntax item continues in the next line. m_endpos->lnum
2930 * will be 0.
2931 * If found, the end of the region and the end of the highlighting is
2932 * computed.
2933 */
2934 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002935find_endpos(
2936 int idx, /* index of the pattern */
2937 lpos_T *startpos, /* where to start looking for an END match */
2938 lpos_T *m_endpos, /* return: end of match */
2939 lpos_T *hl_endpos, /* return: end of highlighting */
2940 long *flagsp, /* return: flags of matching END */
2941 lpos_T *end_endpos, /* return: end of end pattern match */
2942 int *end_idx, /* return: group ID for end pat. match, or 0 */
2943 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944{
2945 colnr_T matchcol;
2946 synpat_T *spp, *spp_skip;
2947 int start_idx;
2948 int best_idx;
2949 regmmatch_T regmatch;
2950 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2951 lpos_T pos;
2952 char_u *line;
2953 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002954 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002956 /* just in case we are invoked for a keyword */
2957 if (idx < 0)
2958 return;
2959
Bram Moolenaar071d4272004-06-13 20:20:40 +00002960 /*
2961 * Check for being called with a START pattern.
2962 * Can happen with a match that continues to the next line, because it
2963 * contained a region.
2964 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002965 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002966 if (spp->sp_type != SPTYPE_START)
2967 {
2968 *hl_endpos = *startpos;
2969 return;
2970 }
2971
2972 /*
2973 * Find the SKIP or first END pattern after the last START pattern.
2974 */
2975 for (;;)
2976 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002977 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 if (spp->sp_type != SPTYPE_START)
2979 break;
2980 ++idx;
2981 }
2982
2983 /*
2984 * Lookup the SKIP pattern (if present)
2985 */
2986 if (spp->sp_type == SPTYPE_SKIP)
2987 {
2988 spp_skip = spp;
2989 ++idx;
2990 }
2991 else
2992 spp_skip = NULL;
2993
2994 /* Setup external matches for syn_regexec(). */
2995 unref_extmatch(re_extmatch_in);
2996 re_extmatch_in = ref_extmatch(start_ext);
2997
2998 matchcol = startpos->col; /* start looking for a match at sstart */
2999 start_idx = idx; /* remember the first END pattern. */
3000 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003001
3002 /* use syntax iskeyword option */
3003 save_chartab(buf_chartab);
3004
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005 for (;;)
3006 {
3007 /*
3008 * Find end pattern that matches first after "matchcol".
3009 */
3010 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003011 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 {
3013 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003014 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015
Bram Moolenaar860cae12010-06-05 23:22:07 +02003016 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003017 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3018 break;
3019 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3020 if (lc_col < 0)
3021 lc_col = 0;
3022
3023 regmatch.rmm_ic = spp->sp_ic;
3024 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003025 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3026 IF_SYN_TIME(&spp->sp_time));
3027 spp->sp_prog = regmatch.regprog;
3028 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029 {
3030 if (best_idx == -1 || regmatch.startpos[0].col
3031 < best_regmatch.startpos[0].col)
3032 {
3033 best_idx = idx;
3034 best_regmatch.startpos[0] = regmatch.startpos[0];
3035 best_regmatch.endpos[0] = regmatch.endpos[0];
3036 }
3037 }
3038 }
3039
3040 /*
3041 * If all end patterns have been tried, and there is no match, the
3042 * item continues until end-of-line.
3043 */
3044 if (best_idx == -1)
3045 break;
3046
3047 /*
3048 * If the skip pattern matches before the end pattern,
3049 * continue searching after the skip pattern.
3050 */
3051 if (spp_skip != NULL)
3052 {
3053 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003054 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003055
3056 if (lc_col < 0)
3057 lc_col = 0;
3058 regmatch.rmm_ic = spp_skip->sp_ic;
3059 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003060 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3061 IF_SYN_TIME(&spp_skip->sp_time));
3062 spp_skip->sp_prog = regmatch.regprog;
3063 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 <= best_regmatch.startpos[0].col)
3065 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003066 int line_len;
3067
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068 /* Add offset to skip pattern match */
3069 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3070
3071 /* If the skip pattern goes on to the next line, there is no
3072 * match with an end pattern in this line. */
3073 if (pos.lnum > startpos->lnum)
3074 break;
3075
3076 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003077 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078
3079 /* take care of an empty match or negative offset */
3080 if (pos.col <= matchcol)
3081 ++matchcol;
3082 else if (pos.col <= regmatch.endpos[0].col)
3083 matchcol = pos.col;
3084 else
3085 /* Be careful not to jump over the NUL at the end-of-line */
3086 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003087 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088 ++matchcol)
3089 ;
3090
3091 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003092 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 break;
3094
3095 continue; /* start with first end pattern again */
3096 }
3097 }
3098
3099 /*
3100 * Match from start pattern to end pattern.
3101 * Correct for match and highlight offset of end pattern.
3102 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003103 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3105 /* can't end before the start */
3106 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3107 m_endpos->col = startpos->col;
3108
3109 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3110 /* can't end before the start */
3111 if (end_endpos->lnum == startpos->lnum
3112 && end_endpos->col < startpos->col)
3113 end_endpos->col = startpos->col;
3114 /* can't end after the match */
3115 limit_pos(end_endpos, m_endpos);
3116
3117 /*
3118 * If the end group is highlighted differently, adjust the pointers.
3119 */
3120 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3121 {
3122 *end_idx = best_idx;
3123 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3124 {
3125 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3126 hl_endpos->col = best_regmatch.endpos[0].col;
3127 }
3128 else
3129 {
3130 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3131 hl_endpos->col = best_regmatch.startpos[0].col;
3132 }
3133 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3134
3135 /* can't end before the start */
3136 if (hl_endpos->lnum == startpos->lnum
3137 && hl_endpos->col < startpos->col)
3138 hl_endpos->col = startpos->col;
3139 limit_pos(hl_endpos, m_endpos);
3140
3141 /* now the match ends where the highlighting ends, it is turned
3142 * into the matchgroup for the end */
3143 *m_endpos = *hl_endpos;
3144 }
3145 else
3146 {
3147 *end_idx = 0;
3148 *hl_endpos = *end_endpos;
3149 }
3150
3151 *flagsp = spp->sp_flags;
3152
3153 had_match = TRUE;
3154 break;
3155 }
3156
3157 /* no match for an END pattern in this line */
3158 if (!had_match)
3159 m_endpos->lnum = 0;
3160
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003161 restore_chartab(buf_chartab);
3162
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163 /* Remove external matches. */
3164 unref_extmatch(re_extmatch_in);
3165 re_extmatch_in = NULL;
3166}
3167
3168/*
3169 * Limit "pos" not to be after "limit".
3170 */
3171 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003172limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173{
3174 if (pos->lnum > limit->lnum)
3175 *pos = *limit;
3176 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3177 pos->col = limit->col;
3178}
3179
3180/*
3181 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3182 */
3183 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003184limit_pos_zero(
3185 lpos_T *pos,
3186 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187{
3188 if (pos->lnum == 0)
3189 *pos = *limit;
3190 else
3191 limit_pos(pos, limit);
3192}
3193
3194/*
3195 * Add offset to matched text for end of match or highlight.
3196 */
3197 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003198syn_add_end_off(
3199 lpos_T *result, /* returned position */
3200 regmmatch_T *regmatch, /* start/end of match */
3201 synpat_T *spp, /* matched pattern */
3202 int idx, /* index of offset */
3203 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204{
3205 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003206 int off;
3207 char_u *base;
3208 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003209
3210 if (spp->sp_off_flags & (1 << idx))
3211 {
3212 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003213 col = regmatch->startpos[0].col;
3214 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 }
3216 else
3217 {
3218 result->lnum = regmatch->endpos[0].lnum;
3219 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003222 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3223 * is a matchgroup. Watch out for match with last NL in the buffer. */
3224 if (result->lnum > syn_buf->b_ml.ml_line_count)
3225 col = 0;
3226 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003227 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003228 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3229 p = base + col;
3230 if (off > 0)
3231 {
3232 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003233 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003234 }
3235 else if (off < 0)
3236 {
3237 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003238 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003239 }
3240 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003241 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003242 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243}
3244
3245/*
3246 * Add offset to matched text for start of match or highlight.
3247 * Avoid resulting column to become negative.
3248 */
3249 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003250syn_add_start_off(
3251 lpos_T *result, /* returned position */
3252 regmmatch_T *regmatch, /* start/end of match */
3253 synpat_T *spp,
3254 int idx,
3255 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256{
3257 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003258 int off;
3259 char_u *base;
3260 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261
3262 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3263 {
3264 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003265 col = regmatch->endpos[0].col;
3266 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 }
3268 else
3269 {
3270 result->lnum = regmatch->startpos[0].lnum;
3271 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003272 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003274 if (result->lnum > syn_buf->b_ml.ml_line_count)
3275 {
3276 /* a "\n" at the end of the pattern may take us below the last line */
3277 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003278 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003279 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003280 if (off != 0)
3281 {
3282 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3283 p = base + col;
3284 if (off > 0)
3285 {
3286 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003287 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003288 }
3289 else if (off < 0)
3290 {
3291 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003292 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003293 }
3294 col = (int)(p - base);
3295 }
3296 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297}
3298
3299/*
3300 * Get current line in syntax buffer.
3301 */
3302 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003303syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003304{
3305 return ml_get_buf(syn_buf, current_lnum, FALSE);
3306}
3307
3308/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003309 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310 * Returns TRUE when there is a match.
3311 */
3312 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003313syn_regexec(
3314 regmmatch_T *rmp,
3315 linenr_T lnum,
3316 colnr_T col,
3317 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003319 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003320#ifdef FEAT_RELTIME
3321 int timed_out = FALSE;
3322#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003323#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003324 proftime_T pt;
3325
3326 if (syn_time_on)
3327 profile_start(&pt);
3328#endif
3329
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003330 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003331 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3332#ifdef FEAT_RELTIME
3333 syn_tm, &timed_out
3334#else
3335 NULL, NULL
3336#endif
3337 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003338
Bram Moolenaarf7512552013-06-06 14:55:19 +02003339#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003340 if (syn_time_on)
3341 {
3342 profile_end(&pt);
3343 profile_add(&st->total, &pt);
3344 if (profile_cmp(&pt, &st->slowest) < 0)
3345 st->slowest = pt;
3346 ++st->count;
3347 if (r > 0)
3348 ++st->match;
3349 }
3350#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003351#ifdef FEAT_RELTIME
3352 if (timed_out)
3353 syn_win->w_s->b_syn_slow = TRUE;
3354#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003355
3356 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 {
3358 rmp->startpos[0].lnum += lnum;
3359 rmp->endpos[0].lnum += lnum;
3360 return TRUE;
3361 }
3362 return FALSE;
3363}
3364
3365/*
3366 * Check one position in a line for a matching keyword.
3367 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003368 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369 */
3370 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003371check_keyword_id(
3372 char_u *line,
3373 int startcol, /* position in line to check for keyword */
3374 int *endcolp, /* return: character after found keyword */
3375 long *flagsp, /* return: flags of matching keyword */
3376 short **next_listp, /* return: next_list of matching keyword */
3377 stateitem_T *cur_si, /* item at the top of the stack */
3378 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003380 keyentry_T *kp;
3381 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003383 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003385 hashtab_T *ht;
3386 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387
3388 /* Find first character after the keyword. First character was already
3389 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003390 kwp = line + startcol;
3391 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392 do
3393 {
3394#ifdef FEAT_MBYTE
3395 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003396 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397 else
3398#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003399 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003401 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003402
Bram Moolenaardad6b692005-01-25 22:14:34 +00003403 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404 return 0;
3405
3406 /*
3407 * Must make a copy of the keyword, so we can add a NUL and make it
3408 * lowercase.
3409 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003410 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411
3412 /*
3413 * Try twice:
3414 * 1. matching case
3415 * 2. ignoring case
3416 */
3417 for (round = 1; round <= 2; ++round)
3418 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003419 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003420 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003422 if (round == 2) /* ignore case */
3423 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424
3425 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003426 * Find keywords that match. There can be several with different
3427 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428 * When current_next_list is non-zero accept only that group, otherwise:
3429 * Accept a not-contained keyword at toplevel.
3430 * Accept a keyword at other levels only if it is in the contains list.
3431 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003432 hi = hash_find(ht, keyword);
3433 if (!HASHITEM_EMPTY(hi))
3434 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003435 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003436 if (current_next_list != 0
3437 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3438 : (cur_si == NULL
3439 ? !(kp->flags & HL_CONTAINED)
3440 : in_id_list(cur_si, cur_si->si_cont_list,
3441 &kp->k_syn, kp->flags & HL_CONTAINED)))
3442 {
3443 *endcolp = startcol + kwlen;
3444 *flagsp = kp->flags;
3445 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003446#ifdef FEAT_CONCEAL
3447 *ccharp = kp->k_char;
3448#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003449 return kp->k_syn.id;
3450 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451 }
3452 }
3453 return 0;
3454}
3455
3456/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003457 * Handle ":syntax conceal" command.
3458 */
3459 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003460syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003461{
3462#ifdef FEAT_CONCEAL
3463 char_u *arg = eap->arg;
3464 char_u *next;
3465
3466 eap->nextcmd = find_nextcmd(arg);
3467 if (eap->skip)
3468 return;
3469
3470 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003471 if (*arg == NUL)
3472 {
3473 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003474 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003475 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003476 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003477 }
3478 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 curwin->w_s->b_syn_conceal = TRUE;
3480 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3481 curwin->w_s->b_syn_conceal = FALSE;
3482 else
3483 EMSG2(_("E390: Illegal argument: %s"), arg);
3484#endif
3485}
3486
3487/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003488 * Handle ":syntax case" command.
3489 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003491syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492{
3493 char_u *arg = eap->arg;
3494 char_u *next;
3495
3496 eap->nextcmd = find_nextcmd(arg);
3497 if (eap->skip)
3498 return;
3499
3500 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003501 if (*arg == NUL)
3502 {
3503 if (curwin->w_s->b_syn_ic)
3504 MSG(_("syntax case ignore"));
3505 else
3506 MSG(_("syntax case match"));
3507 }
3508 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 else
3513 EMSG2(_("E390: Illegal argument: %s"), arg);
3514}
3515
3516/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003517 * Handle ":syntax spell" command.
3518 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003519 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003520syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003521{
3522 char_u *arg = eap->arg;
3523 char_u *next;
3524
3525 eap->nextcmd = find_nextcmd(arg);
3526 if (eap->skip)
3527 return;
3528
3529 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003530 if (*arg == NUL)
3531 {
3532 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3533 MSG(_("syntax spell toplevel"));
3534 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3535 MSG(_("syntax spell notoplevel"));
3536 else
3537 MSG(_("syntax spell default"));
3538 }
3539 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003540 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003541 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003542 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003543 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003544 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003545 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003546 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003547 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003548 return;
3549 }
3550
3551 /* assume spell checking changed, force a redraw */
3552 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003553}
3554
3555/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003556 * Handle ":syntax iskeyword" command.
3557 */
3558 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003559syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003560{
3561 char_u *arg = eap->arg;
3562 char_u save_chartab[32];
3563 char_u *save_isk;
3564
3565 if (eap->skip)
3566 return;
3567
3568 arg = skipwhite(arg);
3569 if (*arg == NUL)
3570 {
3571 MSG_PUTS("\n");
3572 MSG_PUTS(_("syntax iskeyword "));
3573 if (curwin->w_s->b_syn_isk != empty_option)
3574 msg_outtrans(curwin->w_s->b_syn_isk);
3575 else
3576 msg_outtrans((char_u *)"not set");
3577 }
3578 else
3579 {
3580 if (STRNICMP(arg, "clear", 5) == 0)
3581 {
3582 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3583 (size_t)32);
3584 clear_string_option(&curwin->w_s->b_syn_isk);
3585 }
3586 else
3587 {
3588 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3589 save_isk = curbuf->b_p_isk;
3590 curbuf->b_p_isk = vim_strsave(arg);
3591
3592 buf_init_chartab(curbuf, FALSE);
3593 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3594 (size_t)32);
3595 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3596 clear_string_option(&curwin->w_s->b_syn_isk);
3597 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3598 curbuf->b_p_isk = save_isk;
3599 }
3600 }
3601 redraw_win_later(curwin, NOT_VALID);
3602}
3603
3604/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003605 * Clear all syntax info for one buffer.
3606 */
3607 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003608syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609{
3610 int i;
3611
Bram Moolenaar860cae12010-06-05 23:22:07 +02003612 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003613#ifdef FEAT_RELTIME
3614 block->b_syn_slow = FALSE; /* clear previous timeout */
3615#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003616 block->b_syn_ic = FALSE; /* Use case, by default */
3617 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3618 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003619#ifdef FEAT_CONCEAL
3620 block->b_syn_conceal = FALSE;
3621#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003622
3623 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003624 clear_keywtab(&block->b_keywtab);
3625 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003626
3627 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003628 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3629 syn_clear_pattern(block, i);
3630 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631
3632 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003633 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3634 syn_clear_cluster(block, i);
3635 ga_clear(&block->b_syn_clusters);
3636 block->b_spell_cluster_id = 0;
3637 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638
Bram Moolenaar860cae12010-06-05 23:22:07 +02003639 block->b_syn_sync_flags = 0;
3640 block->b_syn_sync_minlines = 0;
3641 block->b_syn_sync_maxlines = 0;
3642 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643
Bram Moolenaar473de612013-06-08 18:19:48 +02003644 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003645 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003646 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003650 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651
3652 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003653 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003655
3656 /* Reset the counter for ":syn include" */
3657 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658}
3659
3660/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003661 * Get rid of ownsyntax for window "wp".
3662 */
3663 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003664reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003665{
3666 if (wp->w_s != &wp->w_buffer->b_s)
3667 {
3668 syntax_clear(wp->w_s);
3669 vim_free(wp->w_s);
3670 wp->w_s = &wp->w_buffer->b_s;
3671 }
3672}
3673
3674/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 * Clear syncing info for one buffer.
3676 */
3677 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003678syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679{
3680 int i;
3681
3682 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3684 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3685 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686
Bram Moolenaar860cae12010-06-05 23:22:07 +02003687 curwin->w_s->b_syn_sync_flags = 0;
3688 curwin->w_s->b_syn_sync_minlines = 0;
3689 curwin->w_s->b_syn_sync_maxlines = 0;
3690 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691
Bram Moolenaar473de612013-06-08 18:19:48 +02003692 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003693 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003694 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003695 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003697 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698}
3699
3700/*
3701 * Remove one pattern from the buffer's pattern list.
3702 */
3703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003704syn_remove_pattern(
3705 synblock_T *block,
3706 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707{
3708 synpat_T *spp;
3709
Bram Moolenaar860cae12010-06-05 23:22:07 +02003710 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711#ifdef FEAT_FOLDING
3712 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003713 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003715 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3718 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719}
3720
3721/*
3722 * Clear and free one syntax pattern. When clearing all, must be called from
3723 * last to first!
3724 */
3725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003726syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003729 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003731 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003733 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3734 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3735 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 }
3737}
3738
3739/*
3740 * Clear and free one syntax cluster.
3741 */
3742 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003743syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003745 vim_free(SYN_CLSTR(block)[i].scl_name);
3746 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3747 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748}
3749
3750/*
3751 * Handle ":syntax clear" command.
3752 */
3753 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003754syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755{
3756 char_u *arg = eap->arg;
3757 char_u *arg_end;
3758 int id;
3759
3760 eap->nextcmd = find_nextcmd(arg);
3761 if (eap->skip)
3762 return;
3763
3764 /*
3765 * We have to disable this within ":syn include @group filename",
3766 * because otherwise @group would get deleted.
3767 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3768 * clear".
3769 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003770 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771 return;
3772
3773 if (ends_excmd(*arg))
3774 {
3775 /*
3776 * No argument: Clear all syntax items.
3777 */
3778 if (syncing)
3779 syntax_sync_clear();
3780 else
3781 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003782 syntax_clear(curwin->w_s);
3783 if (curwin->w_s == &curwin->w_buffer->b_s)
3784 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003785 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 }
3787 }
3788 else
3789 {
3790 /*
3791 * Clear the group IDs that are in the argument.
3792 */
3793 while (!ends_excmd(*arg))
3794 {
3795 arg_end = skiptowhite(arg);
3796 if (*arg == '@')
3797 {
3798 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3799 if (id == 0)
3800 {
3801 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3802 break;
3803 }
3804 else
3805 {
3806 /*
3807 * We can't physically delete a cluster without changing
3808 * the IDs of other clusters, so we do the next best thing
3809 * and make it empty.
3810 */
3811 short scl_id = id - SYNID_CLUSTER;
3812
Bram Moolenaard23a8232018-02-10 18:45:26 +01003813 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 }
3815 }
3816 else
3817 {
3818 id = syn_namen2id(arg, (int)(arg_end - arg));
3819 if (id == 0)
3820 {
3821 EMSG2(_(e_nogroup), arg);
3822 break;
3823 }
3824 else
3825 syn_clear_one(id, syncing);
3826 }
3827 arg = skipwhite(arg_end);
3828 }
3829 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003830 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003831 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832}
3833
3834/*
3835 * Clear one syntax group for the current buffer.
3836 */
3837 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003838syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839{
3840 synpat_T *spp;
3841 int idx;
3842
3843 /* Clear keywords only when not ":syn sync clear group-name" */
3844 if (!syncing)
3845 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3847 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 }
3849
3850 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003851 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3855 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003856 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 }
3858}
3859
3860/*
3861 * Handle ":syntax on" command.
3862 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003864syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865{
3866 syn_cmd_onoff(eap, "syntax");
3867}
3868
3869/*
3870 * Handle ":syntax enable" command.
3871 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003872 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003873syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874{
3875 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3876 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003877 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003878}
3879
3880/*
3881 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003882 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003885syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886{
3887 eap->nextcmd = check_nextcmd(eap->arg);
3888 if (!eap->skip)
3889 {
3890 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3891 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003892 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 }
3894}
3895
3896/*
3897 * Handle ":syntax manual" command.
3898 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003900syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901{
3902 syn_cmd_onoff(eap, "manual");
3903}
3904
3905/*
3906 * Handle ":syntax off" command.
3907 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003909syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910{
3911 syn_cmd_onoff(eap, "nosyntax");
3912}
3913
3914 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003915syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916{
3917 char_u buf[100];
3918
3919 eap->nextcmd = check_nextcmd(eap->arg);
3920 if (!eap->skip)
3921 {
3922 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003923 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924 do_cmdline_cmd(buf);
3925 }
3926}
3927
3928/*
3929 * Handle ":syntax [list]" command: list current syntax words.
3930 */
3931 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003932syn_cmd_list(
3933 exarg_T *eap,
3934 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935{
3936 char_u *arg = eap->arg;
3937 int id;
3938 char_u *arg_end;
3939
3940 eap->nextcmd = find_nextcmd(arg);
3941 if (eap->skip)
3942 return;
3943
Bram Moolenaar860cae12010-06-05 23:22:07 +02003944 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003946 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 return;
3948 }
3949
3950 if (syncing)
3951 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003952 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953 {
3954 MSG_PUTS(_("syncing on C-style comments"));
3955 syn_lines_msg();
3956 syn_match_msg();
3957 return;
3958 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003959 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003961 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962 MSG_PUTS(_("no syncing"));
3963 else
3964 {
3965 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003966 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003967 MSG_PUTS(_(" lines before top line"));
3968 syn_match_msg();
3969 }
3970 return;
3971 }
3972 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003973 if (curwin->w_s->b_syn_sync_minlines > 0
3974 || curwin->w_s->b_syn_sync_maxlines > 0
3975 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 {
3977 MSG_PUTS(_("\nsyncing on items"));
3978 syn_lines_msg();
3979 syn_match_msg();
3980 }
3981 }
3982 else
3983 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3984 if (ends_excmd(*arg))
3985 {
3986 /*
3987 * No argument: List all group IDs and all syntax clusters.
3988 */
3989 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3990 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003991 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992 syn_list_cluster(id);
3993 }
3994 else
3995 {
3996 /*
3997 * List the group IDs and syntax clusters that are in the argument.
3998 */
3999 while (!ends_excmd(*arg) && !got_int)
4000 {
4001 arg_end = skiptowhite(arg);
4002 if (*arg == '@')
4003 {
4004 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
4005 if (id == 0)
4006 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4007 else
4008 syn_list_cluster(id - SYNID_CLUSTER);
4009 }
4010 else
4011 {
4012 id = syn_namen2id(arg, (int)(arg_end - arg));
4013 if (id == 0)
4014 EMSG2(_(e_nogroup), arg);
4015 else
4016 syn_list_one(id, syncing, TRUE);
4017 }
4018 arg = skipwhite(arg_end);
4019 }
4020 }
4021 eap->nextcmd = check_nextcmd(arg);
4022}
4023
4024 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004025syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004027 if (curwin->w_s->b_syn_sync_maxlines > 0
4028 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 {
4030 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004031 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 {
4033 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004034 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4035 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004036 MSG_PUTS(", ");
4037 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004038 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 {
4040 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004041 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042 }
4043 MSG_PUTS(_(" lines before top line"));
4044 }
4045}
4046
4047 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004048syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004049{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004050 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 {
4052 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004053 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054 MSG_PUTS(_(" line breaks"));
4055 }
4056}
4057
4058static int last_matchgroup;
4059
4060struct name_list
4061{
4062 int flag;
4063 char *name;
4064};
4065
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004066static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067
4068/*
4069 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4070 */
4071 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004072syn_list_one(
4073 int id,
4074 int syncing, /* when TRUE: list syncing items */
4075 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004076{
4077 int attr;
4078 int idx;
4079 int did_header = FALSE;
4080 synpat_T *spp;
4081 static struct name_list namelist1[] =
4082 {
4083 {HL_DISPLAY, "display"},
4084 {HL_CONTAINED, "contained"},
4085 {HL_ONELINE, "oneline"},
4086 {HL_KEEPEND, "keepend"},
4087 {HL_EXTEND, "extend"},
4088 {HL_EXCLUDENL, "excludenl"},
4089 {HL_TRANSP, "transparent"},
4090 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004091#ifdef FEAT_CONCEAL
4092 {HL_CONCEAL, "conceal"},
4093 {HL_CONCEALENDS, "concealends"},
4094#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004095 {0, NULL}
4096 };
4097 static struct name_list namelist2[] =
4098 {
4099 {HL_SKIPWHITE, "skipwhite"},
4100 {HL_SKIPNL, "skipnl"},
4101 {HL_SKIPEMPTY, "skipempty"},
4102 {0, NULL}
4103 };
4104
Bram Moolenaar8820b482017-03-16 17:23:31 +01004105 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106
4107 /* list the keywords for "id" */
4108 if (!syncing)
4109 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004110 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4111 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 did_header, attr);
4113 }
4114
4115 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004116 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004118 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4120 continue;
4121
4122 (void)syn_list_header(did_header, 999, id);
4123 did_header = TRUE;
4124 last_matchgroup = 0;
4125 if (spp->sp_type == SPTYPE_MATCH)
4126 {
4127 put_pattern("match", ' ', spp, attr);
4128 msg_putchar(' ');
4129 }
4130 else if (spp->sp_type == SPTYPE_START)
4131 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004132 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4133 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4134 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4135 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4136 while (idx < curwin->w_s->b_syn_patterns.ga_len
4137 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4138 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 --idx;
4140 msg_putchar(' ');
4141 }
4142 syn_list_flags(namelist1, spp->sp_flags, attr);
4143
4144 if (spp->sp_cont_list != NULL)
4145 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4146
4147 if (spp->sp_syn.cont_in_list != NULL)
4148 put_id_list((char_u *)"containedin",
4149 spp->sp_syn.cont_in_list, attr);
4150
4151 if (spp->sp_next_list != NULL)
4152 {
4153 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4154 syn_list_flags(namelist2, spp->sp_flags, attr);
4155 }
4156 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4157 {
4158 if (spp->sp_flags & HL_SYNC_HERE)
4159 msg_puts_attr((char_u *)"grouphere", attr);
4160 else
4161 msg_puts_attr((char_u *)"groupthere", attr);
4162 msg_putchar(' ');
4163 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004164 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4166 else
4167 MSG_PUTS("NONE");
4168 msg_putchar(' ');
4169 }
4170 }
4171
4172 /* list the link, if there is one */
4173 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4174 {
4175 (void)syn_list_header(did_header, 999, id);
4176 msg_puts_attr((char_u *)"links to", attr);
4177 msg_putchar(' ');
4178 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4179 }
4180}
4181
4182 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004183syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184{
4185 int i;
4186
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004187 for (i = 0; nlist[i].flag != 0; ++i)
4188 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004190 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004191 msg_putchar(' ');
4192 }
4193}
4194
4195/*
4196 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4197 */
4198 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004199syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004200{
4201 int endcol = 15;
4202
4203 /* slight hack: roughly duplicate the guts of syn_list_header() */
4204 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004205 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206
4207 if (msg_col >= endcol) /* output at least one space */
4208 endcol = msg_col + 1;
4209 if (Columns <= endcol) /* avoid hang for tiny window */
4210 endcol = Columns - 1;
4211
4212 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004213 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004215 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004216 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 }
4218 else
4219 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004220 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 msg_puts((char_u *)"=NONE");
4222 }
4223}
4224
4225 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004226put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227{
4228 short *p;
4229
4230 msg_puts_attr(name, attr);
4231 msg_putchar('=');
4232 for (p = list; *p; ++p)
4233 {
4234 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4235 {
4236 if (p[1])
4237 MSG_PUTS("ALLBUT");
4238 else
4239 MSG_PUTS("ALL");
4240 }
4241 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4242 {
4243 MSG_PUTS("TOP");
4244 }
4245 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4246 {
4247 MSG_PUTS("CONTAINED");
4248 }
4249 else if (*p >= SYNID_CLUSTER)
4250 {
4251 short scl_id = *p - SYNID_CLUSTER;
4252
4253 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004254 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 }
4256 else
4257 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4258 if (p[1])
4259 msg_putchar(',');
4260 }
4261 msg_putchar(' ');
4262}
4263
4264 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004265put_pattern(
4266 char *s,
4267 int c,
4268 synpat_T *spp,
4269 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004270{
4271 long n;
4272 int mask;
4273 int first;
4274 static char *sepchars = "/+=-#@\"|'^&";
4275 int i;
4276
4277 /* May have to write "matchgroup=group" */
4278 if (last_matchgroup != spp->sp_syn_match_id)
4279 {
4280 last_matchgroup = spp->sp_syn_match_id;
4281 msg_puts_attr((char_u *)"matchgroup", attr);
4282 msg_putchar('=');
4283 if (last_matchgroup == 0)
4284 msg_outtrans((char_u *)"NONE");
4285 else
4286 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4287 msg_putchar(' ');
4288 }
4289
4290 /* output the name of the pattern and an '=' or ' ' */
4291 msg_puts_attr((char_u *)s, attr);
4292 msg_putchar(c);
4293
4294 /* output the pattern, in between a char that is not in the pattern */
4295 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4296 if (sepchars[++i] == NUL)
4297 {
4298 i = 0; /* no good char found, just use the first one */
4299 break;
4300 }
4301 msg_putchar(sepchars[i]);
4302 msg_outtrans(spp->sp_pattern);
4303 msg_putchar(sepchars[i]);
4304
4305 /* output any pattern options */
4306 first = TRUE;
4307 for (i = 0; i < SPO_COUNT; ++i)
4308 {
4309 mask = (1 << i);
4310 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4311 {
4312 if (!first)
4313 msg_putchar(','); /* separate with commas */
4314 msg_puts((char_u *)spo_name_tab[i]);
4315 n = spp->sp_offsets[i];
4316 if (i != SPO_LC_OFF)
4317 {
4318 if (spp->sp_off_flags & mask)
4319 msg_putchar('s');
4320 else
4321 msg_putchar('e');
4322 if (n > 0)
4323 msg_putchar('+');
4324 }
4325 if (n || i == SPO_LC_OFF)
4326 msg_outnum(n);
4327 first = FALSE;
4328 }
4329 }
4330 msg_putchar(' ');
4331}
4332
4333/*
4334 * List or clear the keywords for one syntax group.
4335 * Return TRUE if the header has been printed.
4336 */
4337 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004338syn_list_keywords(
4339 int id,
4340 hashtab_T *ht,
4341 int did_header, /* header has already been printed */
4342 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004345 hashitem_T *hi;
4346 keyentry_T *kp;
4347 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 int prev_contained = 0;
4349 short *prev_next_list = NULL;
4350 short *prev_cont_in_list = NULL;
4351 int prev_skipnl = 0;
4352 int prev_skipwhite = 0;
4353 int prev_skipempty = 0;
4354
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 /*
4356 * Unfortunately, this list of keywords is not sorted on alphabet but on
4357 * hash value...
4358 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004359 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004364 --todo;
4365 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004367 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 if (prev_contained != (kp->flags & HL_CONTAINED)
4370 || prev_skipnl != (kp->flags & HL_SKIPNL)
4371 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4372 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4373 || prev_cont_in_list != kp->k_syn.cont_in_list
4374 || prev_next_list != kp->next_list)
4375 outlen = 9999;
4376 else
4377 outlen = (int)STRLEN(kp->keyword);
4378 /* output "contained" and "nextgroup" on each line */
4379 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 prev_contained = 0;
4382 prev_next_list = NULL;
4383 prev_cont_in_list = NULL;
4384 prev_skipnl = 0;
4385 prev_skipwhite = 0;
4386 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 did_header = TRUE;
4389 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004391 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004393 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004395 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004397 put_id_list((char_u *)"containedin",
4398 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004400 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004402 if (kp->next_list != prev_next_list)
4403 {
4404 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4405 msg_putchar(' ');
4406 prev_next_list = kp->next_list;
4407 if (kp->flags & HL_SKIPNL)
4408 {
4409 msg_puts_attr((char_u *)"skipnl", attr);
4410 msg_putchar(' ');
4411 prev_skipnl = (kp->flags & HL_SKIPNL);
4412 }
4413 if (kp->flags & HL_SKIPWHITE)
4414 {
4415 msg_puts_attr((char_u *)"skipwhite", attr);
4416 msg_putchar(' ');
4417 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4418 }
4419 if (kp->flags & HL_SKIPEMPTY)
4420 {
4421 msg_puts_attr((char_u *)"skipempty", attr);
4422 msg_putchar(' ');
4423 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4424 }
4425 }
4426 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004427 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428 }
4429 }
4430 }
4431
4432 return did_header;
4433}
4434
4435 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004436syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004438 hashitem_T *hi;
4439 keyentry_T *kp;
4440 keyentry_T *kp_prev;
4441 keyentry_T *kp_next;
4442 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004445 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004448 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004450 --todo;
4451 kp_prev = NULL;
4452 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004454 if (kp->k_syn.id == id)
4455 {
4456 kp_next = kp->ke_next;
4457 if (kp_prev == NULL)
4458 {
4459 if (kp_next == NULL)
4460 hash_remove(ht, hi);
4461 else
4462 hi->hi_key = KE2HIKEY(kp_next);
4463 }
4464 else
4465 kp_prev->ke_next = kp_next;
4466 vim_free(kp->next_list);
4467 vim_free(kp->k_syn.cont_in_list);
4468 vim_free(kp);
4469 kp = kp_next;
4470 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004472 {
4473 kp_prev = kp;
4474 kp = kp->ke_next;
4475 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476 }
4477 }
4478 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004479 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480}
4481
4482/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004483 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 */
4485 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004486clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004488 hashitem_T *hi;
4489 int todo;
4490 keyentry_T *kp;
4491 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004493 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004496 if (!HASHITEM_EMPTY(hi))
4497 {
4498 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004499 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004501 kp_next = kp->ke_next;
4502 vim_free(kp->next_list);
4503 vim_free(kp->k_syn.cont_in_list);
4504 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004506 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004507 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004508 hash_clear(ht);
4509 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510}
4511
4512/*
4513 * Add a keyword to the list of keywords.
4514 */
4515 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004516add_keyword(
4517 char_u *name, /* name of keyword */
4518 int id, /* group ID for this keyword */
4519 int flags, /* flags for this keyword */
4520 short *cont_in_list, /* containedin for this keyword */
4521 short *next_list, /* nextgroup for this keyword */
4522 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004524 keyentry_T *kp;
4525 hashtab_T *ht;
4526 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004527 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004528 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004529 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530
Bram Moolenaar860cae12010-06-05 23:22:07 +02004531 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004532 name_ic = str_foldcase(name, (int)STRLEN(name),
4533 name_folded, MAXKEYWLEN + 1);
4534 else
4535 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004536 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4537 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004539 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004540 kp->k_syn.id = id;
4541 kp->k_syn.inc_tag = current_syn_inc_tag;
4542 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004543 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004544 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004546 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004547 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548
Bram Moolenaar860cae12010-06-05 23:22:07 +02004549 if (curwin->w_s->b_syn_ic)
4550 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004552 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553
Bram Moolenaardad6b692005-01-25 22:14:34 +00004554 hash = hash_hash(kp->keyword);
4555 hi = hash_lookup(ht, kp->keyword, hash);
4556 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004558 /* new keyword, add to hashtable */
4559 kp->ke_next = NULL;
4560 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004562 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004563 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004564 /* keyword already exists, prepend to list */
4565 kp->ke_next = HI2KE(hi);
4566 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568}
4569
4570/*
4571 * Get the start and end of the group name argument.
4572 * Return a pointer to the first argument.
4573 * Return NULL if the end of the command was found instead of further args.
4574 */
4575 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004576get_group_name(
4577 char_u *arg, /* start of the argument */
4578 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579{
4580 char_u *rest;
4581
4582 *name_end = skiptowhite(arg);
4583 rest = skipwhite(*name_end);
4584
4585 /*
4586 * Check if there are enough arguments. The first argument may be a
4587 * pattern, where '|' is allowed, so only check for NUL.
4588 */
4589 if (ends_excmd(*arg) || *rest == NUL)
4590 return NULL;
4591 return rest;
4592}
4593
4594/*
4595 * Check for syntax command option arguments.
4596 * This can be called at any place in the list of arguments, and just picks
4597 * out the arguments that are known. Can be called several times in a row to
4598 * collect all options in between other arguments.
4599 * Return a pointer to the next argument (which isn't an option).
4600 * Return NULL for any error;
4601 */
4602 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004603get_syn_options(
4604 char_u *arg, /* next argument to be checked */
4605 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004606 int *conceal_char UNUSED,
4607 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609 char_u *gname_start, *gname;
4610 int syn_id;
4611 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004612 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004613 int i;
4614 int fidx;
4615 static struct flag
4616 {
4617 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004618 int argtype;
4619 int flags;
4620 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4621 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4622 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4623 {"eExXtTeEnNdD", 0, HL_EXTEND},
4624 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4625 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4626 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4627 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4628 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4629 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4630 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4631 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4632 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004633 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4634 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4635 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004636 {"cCoOnNtTaAiInNsS", 1, 0},
4637 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4638 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004640 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641
4642 if (arg == NULL) /* already detected error */
4643 return NULL;
4644
Bram Moolenaar860cae12010-06-05 23:22:07 +02004645#ifdef FEAT_CONCEAL
4646 if (curwin->w_s->b_syn_conceal)
4647 opt->flags |= HL_CONCEAL;
4648#endif
4649
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650 for (;;)
4651 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004652 /*
4653 * This is used very often when a large number of keywords is defined.
4654 * Need to skip quickly when no option name is found.
4655 * Also avoid tolower(), it's slow.
4656 */
4657 if (strchr(first_letters, *arg) == NULL)
4658 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659
4660 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4661 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004662 p = flagtab[fidx].name;
4663 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4664 if (arg[len] != p[i] && arg[len] != p[i + 1])
4665 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004666 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004667 || (flagtab[fidx].argtype > 0
4668 ? arg[len] == '='
4669 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004671 if (opt->keyword
4672 && (flagtab[fidx].flags == HL_DISPLAY
4673 || flagtab[fidx].flags == HL_FOLD
4674 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675 /* treat "display", "fold" and "extend" as a keyword */
4676 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677 break;
4678 }
4679 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004680 if (fidx < 0) /* no match found */
4681 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004683 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004685 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 {
4687 EMSG(_("E395: contains argument not accepted here"));
4688 return NULL;
4689 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004690 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 return NULL;
4692 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004693 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004695 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696 return NULL;
4697 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004698 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004700 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004701 return NULL;
4702 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004703 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4704 {
4705#ifdef FEAT_MBYTE
4706 /* cchar=? */
4707 if (has_mbyte)
4708 {
4709# ifdef FEAT_CONCEAL
4710 *conceal_char = mb_ptr2char(arg + 6);
4711# endif
4712 arg += mb_ptr2len(arg + 6) - 1;
4713 }
4714 else
4715#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004716 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004717#ifdef FEAT_CONCEAL
4718 *conceal_char = arg[6];
4719#else
4720 ;
4721#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004722 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004723#ifdef FEAT_CONCEAL
4724 if (!vim_isprintc_strict(*conceal_char))
4725 {
4726 EMSG(_("E844: invalid cchar value"));
4727 return NULL;
4728 }
4729#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004730 arg = skipwhite(arg + 7);
4731 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004733 {
4734 opt->flags |= flagtab[fidx].flags;
4735 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004737 if (flagtab[fidx].flags == HL_SYNC_HERE
4738 || flagtab[fidx].flags == HL_SYNC_THERE)
4739 {
4740 if (opt->sync_idx == NULL)
4741 {
4742 EMSG(_("E393: group[t]here not accepted here"));
4743 return NULL;
4744 }
4745 gname_start = arg;
4746 arg = skiptowhite(arg);
4747 if (gname_start == arg)
4748 return NULL;
4749 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4750 if (gname == NULL)
4751 return NULL;
4752 if (STRCMP(gname, "NONE") == 0)
4753 *opt->sync_idx = NONE_IDX;
4754 else
4755 {
4756 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004757 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4758 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4759 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004760 {
4761 *opt->sync_idx = i;
4762 break;
4763 }
4764 if (i < 0)
4765 {
4766 EMSG2(_("E394: Didn't find region item for %s"), gname);
4767 vim_free(gname);
4768 return NULL;
4769 }
4770 }
4771
4772 vim_free(gname);
4773 arg = skipwhite(arg);
4774 }
4775#ifdef FEAT_FOLDING
4776 else if (flagtab[fidx].flags == HL_FOLD
4777 && foldmethodIsSyntax(curwin))
4778 /* Need to update folds later. */
4779 foldUpdateAll(curwin);
4780#endif
4781 }
4782 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783
4784 return arg;
4785}
4786
4787/*
4788 * Adjustments to syntax item when declared in a ":syn include"'d file.
4789 * Set the contained flag, and if the item is not already contained, add it
4790 * to the specified top-level group, if any.
4791 */
4792 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004793syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004795 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 return;
4797 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004798 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 {
4800 /* We have to alloc this, because syn_combine_list() will free it. */
4801 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004802 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803
4804 if (grp_list != NULL)
4805 {
4806 grp_list[0] = id;
4807 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004808 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 CLUSTER_ADD);
4810 }
4811 }
4812}
4813
4814/*
4815 * Handle ":syntax include [@{group-name}] filename" command.
4816 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004818syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004819{
4820 char_u *arg = eap->arg;
4821 int sgl_id = 1;
4822 char_u *group_name_end;
4823 char_u *rest;
4824 char_u *errormsg = NULL;
4825 int prev_toplvl_grp;
4826 int prev_syn_inc_tag;
4827 int source = FALSE;
4828
4829 eap->nextcmd = find_nextcmd(arg);
4830 if (eap->skip)
4831 return;
4832
4833 if (arg[0] == '@')
4834 {
4835 ++arg;
4836 rest = get_group_name(arg, &group_name_end);
4837 if (rest == NULL)
4838 {
4839 EMSG((char_u *)_("E397: Filename required"));
4840 return;
4841 }
4842 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004843 if (sgl_id == 0)
4844 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004845 /* separate_nextcmd() and expand_filename() depend on this */
4846 eap->arg = rest;
4847 }
4848
4849 /*
4850 * Everything that's left, up to the next command, should be the
4851 * filename to include.
4852 */
4853 eap->argt |= (XFILE | NOSPC);
4854 separate_nextcmd(eap);
4855 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4856 {
4857 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4858 * file. Need to expand the file name first. In other cases
4859 * ":runtime!" is used. */
4860 source = TRUE;
4861 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4862 {
4863 if (errormsg != NULL)
4864 EMSG(errormsg);
4865 return;
4866 }
4867 }
4868
4869 /*
4870 * Save and restore the existing top-level grouplist id and ":syn
4871 * include" tag around the actual inclusion.
4872 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004873 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4874 {
4875 EMSG((char_u *)_("E847: Too many syntax includes"));
4876 return;
4877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 prev_syn_inc_tag = current_syn_inc_tag;
4879 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004880 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4881 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004882 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004883 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004885 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 current_syn_inc_tag = prev_syn_inc_tag;
4887}
4888
4889/*
4890 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4891 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004893syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894{
4895 char_u *arg = eap->arg;
4896 char_u *group_name_end;
4897 int syn_id;
4898 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004899 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004901 char_u *kw;
4902 syn_opt_arg_T syn_opt_arg;
4903 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004904 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905
4906 rest = get_group_name(arg, &group_name_end);
4907
4908 if (rest != NULL)
4909 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004910 if (eap->skip)
4911 syn_id = -1;
4912 else
4913 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004914 if (syn_id != 0)
4915 /* allocate a buffer, for removing backslashes in the keyword */
4916 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917 if (keyword_copy != NULL)
4918 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004919 syn_opt_arg.flags = 0;
4920 syn_opt_arg.keyword = TRUE;
4921 syn_opt_arg.sync_idx = NULL;
4922 syn_opt_arg.has_cont_list = FALSE;
4923 syn_opt_arg.cont_in_list = NULL;
4924 syn_opt_arg.next_list = NULL;
4925
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926 /*
4927 * The options given apply to ALL keywords, so all options must be
4928 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004929 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004930 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004931 cnt = 0;
4932 p = keyword_copy;
4933 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004935 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4936 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004937 if (rest == NULL || ends_excmd(*rest))
4938 break;
4939 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004940 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004942 if (*rest == '\\' && rest[1] != NUL)
4943 ++rest;
4944 *p++ = *rest++;
4945 }
4946 *p++ = NUL;
4947 ++cnt;
4948 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004950 if (!eap->skip)
4951 {
4952 /* Adjust flags for use of ":syn include". */
4953 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4954
4955 /*
4956 * 2: Add an entry for each keyword.
4957 */
4958 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4959 {
4960 for (p = vim_strchr(kw, '['); ; )
4961 {
4962 if (p != NULL)
4963 *p = NUL;
4964 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004965 syn_opt_arg.cont_in_list,
4966 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004967 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004968 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004969 if (p[1] == NUL)
4970 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004971 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004972 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004973 }
4974 if (p[1] == ']')
4975 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004976 if (p[2] != NUL)
4977 {
4978 EMSG3(_("E890: trailing char after ']': %s]%s"),
4979 kw, &p[2]);
4980 goto error;
4981 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004982 kw = p + 1; /* skip over the "]" */
4983 break;
4984 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004985#ifdef FEAT_MBYTE
4986 if (has_mbyte)
4987 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004988 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004989
4990 mch_memmove(p, p + 1, l);
4991 p += l;
4992 }
4993 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004995 {
4996 p[0] = p[1];
4997 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
4999 }
5000 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02005002error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005004 vim_free(syn_opt_arg.cont_in_list);
5005 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006 }
5007 }
5008
5009 if (rest != NULL)
5010 eap->nextcmd = check_nextcmd(rest);
5011 else
5012 EMSG2(_(e_invarg2), arg);
5013
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005014 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005015 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016}
5017
5018/*
5019 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5020 *
5021 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5022 */
5023 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005024syn_cmd_match(
5025 exarg_T *eap,
5026 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027{
5028 char_u *arg = eap->arg;
5029 char_u *group_name_end;
5030 char_u *rest;
5031 synpat_T item; /* the item found in the line */
5032 int syn_id;
5033 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005034 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005036 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037
5038 /* Isolate the group name, check for validity */
5039 rest = get_group_name(arg, &group_name_end);
5040
5041 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005042 syn_opt_arg.flags = 0;
5043 syn_opt_arg.keyword = FALSE;
5044 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5045 syn_opt_arg.has_cont_list = TRUE;
5046 syn_opt_arg.cont_list = NULL;
5047 syn_opt_arg.cont_in_list = NULL;
5048 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005049 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005050
5051 /* get the pattern. */
5052 init_syn_patterns();
5053 vim_memset(&item, 0, sizeof(item));
5054 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005055 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5056 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057
5058 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005059 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060
5061 if (rest != NULL) /* all arguments are valid */
5062 {
5063 /*
5064 * Check for trailing command and illegal trailing arguments.
5065 */
5066 eap->nextcmd = check_nextcmd(rest);
5067 if (!ends_excmd(*rest) || eap->skip)
5068 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005069 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070 && (syn_id = syn_check_group(arg,
5071 (int)(group_name_end - arg))) != 0)
5072 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005073 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 /*
5075 * Store the pattern in the syn_items list
5076 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005077 idx = curwin->w_s->b_syn_patterns.ga_len;
5078 SYN_ITEMS(curwin->w_s)[idx] = item;
5079 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5080 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5081 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5082 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5083 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5084 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5085 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5086 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005087 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005088#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005089 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005090#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005091 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005092 curwin->w_s->b_syn_containedin = TRUE;
5093 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5094 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005095
5096 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005097 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005098 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005100 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005101 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005102#endif
5103
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005104 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005105 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 return; /* don't free the progs and patterns now */
5107 }
5108 }
5109
5110 /*
5111 * Something failed, free the allocated memory.
5112 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005113 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005115 vim_free(syn_opt_arg.cont_list);
5116 vim_free(syn_opt_arg.cont_in_list);
5117 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118
5119 if (rest == NULL)
5120 EMSG2(_(e_invarg2), arg);
5121}
5122
5123/*
5124 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5125 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5126 */
5127 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005128syn_cmd_region(
5129 exarg_T *eap,
5130 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131{
5132 char_u *arg = eap->arg;
5133 char_u *group_name_end;
5134 char_u *rest; /* next arg, NULL on error */
5135 char_u *key_end;
5136 char_u *key = NULL;
5137 char_u *p;
5138 int item;
5139#define ITEM_START 0
5140#define ITEM_SKIP 1
5141#define ITEM_END 2
5142#define ITEM_MATCHGROUP 3
5143 struct pat_ptr
5144 {
5145 synpat_T *pp_synp; /* pointer to syn_pattern */
5146 int pp_matchgroup_id; /* matchgroup ID */
5147 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5148 } *(pat_ptrs[3]);
5149 /* patterns found in the line */
5150 struct pat_ptr *ppp;
5151 struct pat_ptr *ppp_next;
5152 int pat_count = 0; /* nr of syn_patterns found */
5153 int syn_id;
5154 int matchgroup_id = 0;
5155 int not_enough = FALSE; /* not enough arguments */
5156 int illegal = FALSE; /* illegal arguments */
5157 int success = FALSE;
5158 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005159 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005160 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005161
5162 /* Isolate the group name, check for validity */
5163 rest = get_group_name(arg, &group_name_end);
5164
5165 pat_ptrs[0] = NULL;
5166 pat_ptrs[1] = NULL;
5167 pat_ptrs[2] = NULL;
5168
5169 init_syn_patterns();
5170
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005171 syn_opt_arg.flags = 0;
5172 syn_opt_arg.keyword = FALSE;
5173 syn_opt_arg.sync_idx = NULL;
5174 syn_opt_arg.has_cont_list = TRUE;
5175 syn_opt_arg.cont_list = NULL;
5176 syn_opt_arg.cont_in_list = NULL;
5177 syn_opt_arg.next_list = NULL;
5178
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 /*
5180 * get the options, patterns and matchgroup.
5181 */
5182 while (rest != NULL && !ends_excmd(*rest))
5183 {
5184 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005185 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005186 if (rest == NULL || ends_excmd(*rest))
5187 break;
5188
5189 /* must be a pattern or matchgroup then */
5190 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005191 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005192 ++key_end;
5193 vim_free(key);
5194 key = vim_strnsave_up(rest, (int)(key_end - rest));
5195 if (key == NULL) /* out of memory */
5196 {
5197 rest = NULL;
5198 break;
5199 }
5200 if (STRCMP(key, "MATCHGROUP") == 0)
5201 item = ITEM_MATCHGROUP;
5202 else if (STRCMP(key, "START") == 0)
5203 item = ITEM_START;
5204 else if (STRCMP(key, "END") == 0)
5205 item = ITEM_END;
5206 else if (STRCMP(key, "SKIP") == 0)
5207 {
5208 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5209 {
5210 illegal = TRUE;
5211 break;
5212 }
5213 item = ITEM_SKIP;
5214 }
5215 else
5216 break;
5217 rest = skipwhite(key_end);
5218 if (*rest != '=')
5219 {
5220 rest = NULL;
5221 EMSG2(_("E398: Missing '=': %s"), arg);
5222 break;
5223 }
5224 rest = skipwhite(rest + 1);
5225 if (*rest == NUL)
5226 {
5227 not_enough = TRUE;
5228 break;
5229 }
5230
5231 if (item == ITEM_MATCHGROUP)
5232 {
5233 p = skiptowhite(rest);
5234 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5235 matchgroup_id = 0;
5236 else
5237 {
5238 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5239 if (matchgroup_id == 0)
5240 {
5241 illegal = TRUE;
5242 break;
5243 }
5244 }
5245 rest = skipwhite(p);
5246 }
5247 else
5248 {
5249 /*
5250 * Allocate room for a syn_pattern, and link it in the list of
5251 * syn_patterns for this item, at the start (because the list is
5252 * used from end to start).
5253 */
5254 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5255 if (ppp == NULL)
5256 {
5257 rest = NULL;
5258 break;
5259 }
5260 ppp->pp_next = pat_ptrs[item];
5261 pat_ptrs[item] = ppp;
5262 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5263 if (ppp->pp_synp == NULL)
5264 {
5265 rest = NULL;
5266 break;
5267 }
5268
5269 /*
5270 * Get the syntax pattern and the following offset(s).
5271 */
5272 /* Enable the appropriate \z specials. */
5273 if (item == ITEM_START)
5274 reg_do_extmatch = REX_SET;
5275 else if (item == ITEM_SKIP || item == ITEM_END)
5276 reg_do_extmatch = REX_USE;
5277 rest = get_syn_pattern(rest, ppp->pp_synp);
5278 reg_do_extmatch = 0;
5279 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005280 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005281 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5282 ppp->pp_matchgroup_id = matchgroup_id;
5283 ++pat_count;
5284 }
5285 }
5286 vim_free(key);
5287 if (illegal || not_enough)
5288 rest = NULL;
5289
5290 /*
5291 * Must have a "start" and "end" pattern.
5292 */
5293 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5294 pat_ptrs[ITEM_END] == NULL))
5295 {
5296 not_enough = TRUE;
5297 rest = NULL;
5298 }
5299
5300 if (rest != NULL)
5301 {
5302 /*
5303 * Check for trailing garbage or command.
5304 * If OK, add the item.
5305 */
5306 eap->nextcmd = check_nextcmd(rest);
5307 if (!ends_excmd(*rest) || eap->skip)
5308 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310 && (syn_id = syn_check_group(arg,
5311 (int)(group_name_end - arg))) != 0)
5312 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005313 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 /*
5315 * Store the start/skip/end in the syn_items list
5316 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005317 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318 for (item = ITEM_START; item <= ITEM_END; ++item)
5319 {
5320 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5321 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005322 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5323 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5324 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005325 (item == ITEM_START) ? SPTYPE_START :
5326 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005327 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5328 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005329 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5330 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005331 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005332 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005333#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005334 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005335#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 if (item == ITEM_START)
5337 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005338 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005339 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005340 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005341 syn_opt_arg.cont_in_list;
5342 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005343 curwin->w_s->b_syn_containedin = TRUE;
5344 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005345 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005346 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005347 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 ++idx;
5349#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005350 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005351 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005352#endif
5353 }
5354 }
5355
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005356 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005357 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005358 success = TRUE; /* don't free the progs and patterns now */
5359 }
5360 }
5361
5362 /*
5363 * Free the allocated memory.
5364 */
5365 for (item = ITEM_START; item <= ITEM_END; ++item)
5366 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5367 {
5368 if (!success)
5369 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005370 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005371 vim_free(ppp->pp_synp->sp_pattern);
5372 }
5373 vim_free(ppp->pp_synp);
5374 ppp_next = ppp->pp_next;
5375 vim_free(ppp);
5376 }
5377
5378 if (!success)
5379 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005380 vim_free(syn_opt_arg.cont_list);
5381 vim_free(syn_opt_arg.cont_in_list);
5382 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005383 if (not_enough)
5384 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5385 else if (illegal || rest == NULL)
5386 EMSG2(_(e_invarg2), arg);
5387 }
5388}
5389
5390/*
5391 * A simple syntax group ID comparison function suitable for use in qsort()
5392 */
5393 static int
5394#ifdef __BORLANDC__
5395_RTLENTRYF
5396#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005397syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005398{
5399 const short *s1 = v1;
5400 const short *s2 = v2;
5401
5402 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5403}
5404
5405/*
5406 * Combines lists of syntax clusters.
5407 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5408 */
5409 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005410syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005411{
5412 int count1 = 0;
5413 int count2 = 0;
5414 short *g1;
5415 short *g2;
5416 short *clstr = NULL;
5417 int count;
5418 int round;
5419
5420 /*
5421 * Handle degenerate cases.
5422 */
5423 if (*clstr2 == NULL)
5424 return;
5425 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5426 {
5427 if (list_op == CLUSTER_REPLACE)
5428 vim_free(*clstr1);
5429 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5430 *clstr1 = *clstr2;
5431 else
5432 vim_free(*clstr2);
5433 return;
5434 }
5435
5436 for (g1 = *clstr1; *g1; g1++)
5437 ++count1;
5438 for (g2 = *clstr2; *g2; g2++)
5439 ++count2;
5440
5441 /*
5442 * For speed purposes, sort both lists.
5443 */
5444 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5445 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5446
5447 /*
5448 * We proceed in two passes; in round 1, we count the elements to place
5449 * in the new list, and in round 2, we allocate and populate the new
5450 * list. For speed, we use a mergesort-like method, adding the smaller
5451 * of the current elements in each list to the new list.
5452 */
5453 for (round = 1; round <= 2; round++)
5454 {
5455 g1 = *clstr1;
5456 g2 = *clstr2;
5457 count = 0;
5458
5459 /*
5460 * First, loop through the lists until one of them is empty.
5461 */
5462 while (*g1 && *g2)
5463 {
5464 /*
5465 * We always want to add from the first list.
5466 */
5467 if (*g1 < *g2)
5468 {
5469 if (round == 2)
5470 clstr[count] = *g1;
5471 count++;
5472 g1++;
5473 continue;
5474 }
5475 /*
5476 * We only want to add from the second list if we're adding the
5477 * lists.
5478 */
5479 if (list_op == CLUSTER_ADD)
5480 {
5481 if (round == 2)
5482 clstr[count] = *g2;
5483 count++;
5484 }
5485 if (*g1 == *g2)
5486 g1++;
5487 g2++;
5488 }
5489
5490 /*
5491 * Now add the leftovers from whichever list didn't get finished
5492 * first. As before, we only want to add from the second list if
5493 * we're adding the lists.
5494 */
5495 for (; *g1; g1++, count++)
5496 if (round == 2)
5497 clstr[count] = *g1;
5498 if (list_op == CLUSTER_ADD)
5499 for (; *g2; g2++, count++)
5500 if (round == 2)
5501 clstr[count] = *g2;
5502
5503 if (round == 1)
5504 {
5505 /*
5506 * If the group ended up empty, we don't need to allocate any
5507 * space for it.
5508 */
5509 if (count == 0)
5510 {
5511 clstr = NULL;
5512 break;
5513 }
5514 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5515 if (clstr == NULL)
5516 break;
5517 clstr[count] = 0;
5518 }
5519 }
5520
5521 /*
5522 * Finally, put the new list in place.
5523 */
5524 vim_free(*clstr1);
5525 vim_free(*clstr2);
5526 *clstr1 = clstr;
5527}
5528
5529/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005530 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531 * If it is not found, 0 is returned.
5532 */
5533 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005534syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005535{
5536 int i;
5537 char_u *name_u;
5538
5539 /* Avoid using stricmp() too much, it's slow on some systems */
5540 name_u = vim_strsave_up(name);
5541 if (name_u == NULL)
5542 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005543 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5544 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5545 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546 break;
5547 vim_free(name_u);
5548 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5549}
5550
5551/*
5552 * Like syn_scl_name2id(), but take a pointer + length argument.
5553 */
5554 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005555syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005556{
5557 char_u *name;
5558 int id = 0;
5559
5560 name = vim_strnsave(linep, len);
5561 if (name != NULL)
5562 {
5563 id = syn_scl_name2id(name);
5564 vim_free(name);
5565 }
5566 return id;
5567}
5568
5569/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005570 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005571 * The argument is a pointer to the name and the length of the name.
5572 * If it doesn't exist yet, a new entry is created.
5573 * Return 0 for failure.
5574 */
5575 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005576syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577{
5578 int id;
5579 char_u *name;
5580
5581 name = vim_strnsave(pp, len);
5582 if (name == NULL)
5583 return 0;
5584
5585 id = syn_scl_name2id(name);
5586 if (id == 0) /* doesn't exist yet */
5587 id = syn_add_cluster(name);
5588 else
5589 vim_free(name);
5590 return id;
5591}
5592
5593/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005594 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595 * "name" must be an allocated string, it will be consumed.
5596 * Return 0 for failure.
5597 */
5598 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005599syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005601 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602
5603 /*
5604 * First call for this growarray: init growing array.
5605 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005606 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005608 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5609 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610 }
5611
Bram Moolenaar42431a72011-04-01 14:44:59 +02005612 len = curwin->w_s->b_syn_clusters.ga_len;
5613 if (len >= MAX_CLUSTER_ID)
5614 {
5615 EMSG((char_u *)_("E848: Too many syntax clusters"));
5616 vim_free(name);
5617 return 0;
5618 }
5619
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620 /*
5621 * Make room for at least one other cluster entry.
5622 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005623 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624 {
5625 vim_free(name);
5626 return 0;
5627 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628
Bram Moolenaar860cae12010-06-05 23:22:07 +02005629 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5630 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5631 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5632 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5633 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634
Bram Moolenaar217ad922005-03-20 22:37:15 +00005635 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005636 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005637 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005638 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005639
Bram Moolenaar071d4272004-06-13 20:20:40 +00005640 return len + SYNID_CLUSTER;
5641}
5642
5643/*
5644 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5645 * [add={groupname},..] [remove={groupname},..]".
5646 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005648syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649{
5650 char_u *arg = eap->arg;
5651 char_u *group_name_end;
5652 char_u *rest;
5653 int scl_id;
5654 short *clstr_list;
5655 int got_clstr = FALSE;
5656 int opt_len;
5657 int list_op;
5658
5659 eap->nextcmd = find_nextcmd(arg);
5660 if (eap->skip)
5661 return;
5662
5663 rest = get_group_name(arg, &group_name_end);
5664
5665 if (rest != NULL)
5666 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005667 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5668 if (scl_id == 0)
5669 return;
5670 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671
5672 for (;;)
5673 {
5674 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005675 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005676 {
5677 opt_len = 3;
5678 list_op = CLUSTER_ADD;
5679 }
5680 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005681 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005682 {
5683 opt_len = 6;
5684 list_op = CLUSTER_SUBTRACT;
5685 }
5686 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005687 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005688 {
5689 opt_len = 8;
5690 list_op = CLUSTER_REPLACE;
5691 }
5692 else
5693 break;
5694
5695 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005696 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005697 {
5698 EMSG2(_(e_invarg2), rest);
5699 break;
5700 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005701 if (scl_id >= 0)
5702 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005704 else
5705 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 got_clstr = TRUE;
5707 }
5708
5709 if (got_clstr)
5710 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005711 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005712 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005713 }
5714 }
5715
5716 if (!got_clstr)
5717 EMSG(_("E400: No cluster specified"));
5718 if (rest == NULL || !ends_excmd(*rest))
5719 EMSG2(_(e_invarg2), arg);
5720}
5721
5722/*
5723 * On first call for current buffer: Init growing array.
5724 */
5725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005726init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005727{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005728 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5729 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005730}
5731
5732/*
5733 * Get one pattern for a ":syntax match" or ":syntax region" command.
5734 * Stores the pattern and program in a synpat_T.
5735 * Returns a pointer to the next argument, or NULL in case of an error.
5736 */
5737 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005738get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739{
5740 char_u *end;
5741 int *p;
5742 int idx;
5743 char_u *cpo_save;
5744
5745 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005746 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 return NULL;
5748
5749 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5750 if (*end != *arg) /* end delimiter not found */
5751 {
5752 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5753 return NULL;
5754 }
5755 /* store the pattern and compiled regexp program */
5756 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5757 return NULL;
5758
5759 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5760 cpo_save = p_cpo;
5761 p_cpo = (char_u *)"";
5762 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5763 p_cpo = cpo_save;
5764
5765 if (ci->sp_prog == NULL)
5766 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005767 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005768#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005769 syn_clear_time(&ci->sp_time);
5770#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771
5772 /*
5773 * Check for a match, highlight or region offset.
5774 */
5775 ++end;
5776 do
5777 {
5778 for (idx = SPO_COUNT; --idx >= 0; )
5779 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5780 break;
5781 if (idx >= 0)
5782 {
5783 p = &(ci->sp_offsets[idx]);
5784 if (idx != SPO_LC_OFF)
5785 switch (end[3])
5786 {
5787 case 's': break;
5788 case 'b': break;
5789 case 'e': idx += SPO_COUNT; break;
5790 default: idx = -1; break;
5791 }
5792 if (idx >= 0)
5793 {
5794 ci->sp_off_flags |= (1 << idx);
5795 if (idx == SPO_LC_OFF) /* lc=99 */
5796 {
5797 end += 3;
5798 *p = getdigits(&end);
5799
5800 /* "lc=" offset automatically sets "ms=" offset */
5801 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5802 {
5803 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5804 ci->sp_offsets[SPO_MS_OFF] = *p;
5805 }
5806 }
5807 else /* yy=x+99 */
5808 {
5809 end += 4;
5810 if (*end == '+')
5811 {
5812 ++end;
5813 *p = getdigits(&end); /* positive offset */
5814 }
5815 else if (*end == '-')
5816 {
5817 ++end;
5818 *p = -getdigits(&end); /* negative offset */
5819 }
5820 }
5821 if (*end != ',')
5822 break;
5823 ++end;
5824 }
5825 }
5826 } while (idx >= 0);
5827
Bram Moolenaar1c465442017-03-12 20:10:05 +01005828 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 {
5830 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5831 return NULL;
5832 }
5833 return skipwhite(end);
5834}
5835
5836/*
5837 * Handle ":syntax sync .." command.
5838 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005839 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005840syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005841{
5842 char_u *arg_start = eap->arg;
5843 char_u *arg_end;
5844 char_u *key = NULL;
5845 char_u *next_arg;
5846 int illegal = FALSE;
5847 int finished = FALSE;
5848 long n;
5849 char_u *cpo_save;
5850
5851 if (ends_excmd(*arg_start))
5852 {
5853 syn_cmd_list(eap, TRUE);
5854 return;
5855 }
5856
5857 while (!ends_excmd(*arg_start))
5858 {
5859 arg_end = skiptowhite(arg_start);
5860 next_arg = skipwhite(arg_end);
5861 vim_free(key);
5862 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5863 if (STRCMP(key, "CCOMMENT") == 0)
5864 {
5865 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005866 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 if (!ends_excmd(*next_arg))
5868 {
5869 arg_end = skiptowhite(next_arg);
5870 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005871 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005872 (int)(arg_end - next_arg));
5873 next_arg = skipwhite(arg_end);
5874 }
5875 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005876 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 }
5878 else if ( STRNCMP(key, "LINES", 5) == 0
5879 || STRNCMP(key, "MINLINES", 8) == 0
5880 || STRNCMP(key, "MAXLINES", 8) == 0
5881 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5882 {
5883 if (key[4] == 'S')
5884 arg_end = key + 6;
5885 else if (key[0] == 'L')
5886 arg_end = key + 11;
5887 else
5888 arg_end = key + 9;
5889 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5890 {
5891 illegal = TRUE;
5892 break;
5893 }
5894 n = getdigits(&arg_end);
5895 if (!eap->skip)
5896 {
5897 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005898 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005899 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005900 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005901 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005902 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903 }
5904 }
5905 else if (STRCMP(key, "FROMSTART") == 0)
5906 {
5907 if (!eap->skip)
5908 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005909 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5910 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911 }
5912 }
5913 else if (STRCMP(key, "LINECONT") == 0)
5914 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005915 if (*next_arg == NUL) /* missing pattern */
5916 {
5917 illegal = TRUE;
5918 break;
5919 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005920 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005921 {
5922 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5923 finished = TRUE;
5924 break;
5925 }
5926 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5927 if (*arg_end != *next_arg) /* end delimiter not found */
5928 {
5929 illegal = TRUE;
5930 break;
5931 }
5932
5933 if (!eap->skip)
5934 {
5935 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005936 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005937 (int)(arg_end - next_arg - 1))) == NULL)
5938 {
5939 finished = TRUE;
5940 break;
5941 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005942 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005943
5944 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5945 cpo_save = p_cpo;
5946 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005947 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005948 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005949 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005950#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005951 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5952#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005953
Bram Moolenaar860cae12010-06-05 23:22:07 +02005954 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005955 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005956 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005957 finished = TRUE;
5958 break;
5959 }
5960 }
5961 next_arg = skipwhite(arg_end + 1);
5962 }
5963 else
5964 {
5965 eap->arg = next_arg;
5966 if (STRCMP(key, "MATCH") == 0)
5967 syn_cmd_match(eap, TRUE);
5968 else if (STRCMP(key, "REGION") == 0)
5969 syn_cmd_region(eap, TRUE);
5970 else if (STRCMP(key, "CLEAR") == 0)
5971 syn_cmd_clear(eap, TRUE);
5972 else
5973 illegal = TRUE;
5974 finished = TRUE;
5975 break;
5976 }
5977 arg_start = next_arg;
5978 }
5979 vim_free(key);
5980 if (illegal)
5981 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5982 else if (!finished)
5983 {
5984 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005985 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005986 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005987 }
5988}
5989
5990/*
5991 * Convert a line of highlight group names into a list of group ID numbers.
5992 * "arg" should point to the "contains" or "nextgroup" keyword.
5993 * "arg" is advanced to after the last group name.
5994 * Careful: the argument is modified (NULs added).
5995 * returns FAIL for some error, OK for success.
5996 */
5997 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005998get_id_list(
5999 char_u **arg,
6000 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006001 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00006002 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006003 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006004{
6005 char_u *p = NULL;
6006 char_u *end;
6007 int round;
6008 int count;
6009 int total_count = 0;
6010 short *retval = NULL;
6011 char_u *name;
6012 regmatch_T regmatch;
6013 int id;
6014 int i;
6015 int failed = FALSE;
6016
6017 /*
6018 * We parse the list twice:
6019 * round == 1: count the number of items, allocate the array.
6020 * round == 2: fill the array with the items.
6021 * In round 1 new groups may be added, causing the number of items to
6022 * grow when a regexp is used. In that case round 1 is done once again.
6023 */
6024 for (round = 1; round <= 2; ++round)
6025 {
6026 /*
6027 * skip "contains"
6028 */
6029 p = skipwhite(*arg + keylen);
6030 if (*p != '=')
6031 {
6032 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6033 break;
6034 }
6035 p = skipwhite(p + 1);
6036 if (ends_excmd(*p))
6037 {
6038 EMSG2(_("E406: Empty argument: %s"), *arg);
6039 break;
6040 }
6041
6042 /*
6043 * parse the arguments after "contains"
6044 */
6045 count = 0;
6046 while (!ends_excmd(*p))
6047 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006048 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006049 ;
6050 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6051 if (name == NULL)
6052 {
6053 failed = TRUE;
6054 break;
6055 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006056 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057 if ( STRCMP(name + 1, "ALLBUT") == 0
6058 || STRCMP(name + 1, "ALL") == 0
6059 || STRCMP(name + 1, "TOP") == 0
6060 || STRCMP(name + 1, "CONTAINED") == 0)
6061 {
6062 if (TOUPPER_ASC(**arg) != 'C')
6063 {
6064 EMSG2(_("E407: %s not allowed here"), name + 1);
6065 failed = TRUE;
6066 vim_free(name);
6067 break;
6068 }
6069 if (count != 0)
6070 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006071 EMSG2(_("E408: %s must be first in contains list"),
6072 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006073 failed = TRUE;
6074 vim_free(name);
6075 break;
6076 }
6077 if (name[1] == 'A')
6078 id = SYNID_ALLBUT;
6079 else if (name[1] == 'T')
6080 id = SYNID_TOP;
6081 else
6082 id = SYNID_CONTAINED;
6083 id += current_syn_inc_tag;
6084 }
6085 else if (name[1] == '@')
6086 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006087 if (skip)
6088 id = -1;
6089 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006090 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006091 }
6092 else
6093 {
6094 /*
6095 * Handle full group name.
6096 */
6097 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6098 id = syn_check_group(name + 1, (int)(end - p));
6099 else
6100 {
6101 /*
6102 * Handle match of regexp with group names.
6103 */
6104 *name = '^';
6105 STRCAT(name, "$");
6106 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6107 if (regmatch.regprog == NULL)
6108 {
6109 failed = TRUE;
6110 vim_free(name);
6111 break;
6112 }
6113
6114 regmatch.rm_ic = TRUE;
6115 id = 0;
6116 for (i = highlight_ga.ga_len; --i >= 0; )
6117 {
6118 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6119 (colnr_T)0))
6120 {
6121 if (round == 2)
6122 {
6123 /* Got more items than expected; can happen
6124 * when adding items that match:
6125 * "contains=a.*b,axb".
6126 * Go back to first round */
6127 if (count >= total_count)
6128 {
6129 vim_free(retval);
6130 round = 1;
6131 }
6132 else
6133 retval[count] = i + 1;
6134 }
6135 ++count;
6136 id = -1; /* remember that we found one */
6137 }
6138 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006139 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006140 }
6141 }
6142 vim_free(name);
6143 if (id == 0)
6144 {
6145 EMSG2(_("E409: Unknown group name: %s"), p);
6146 failed = TRUE;
6147 break;
6148 }
6149 if (id > 0)
6150 {
6151 if (round == 2)
6152 {
6153 /* Got more items than expected, go back to first round */
6154 if (count >= total_count)
6155 {
6156 vim_free(retval);
6157 round = 1;
6158 }
6159 else
6160 retval[count] = id;
6161 }
6162 ++count;
6163 }
6164 p = skipwhite(end);
6165 if (*p != ',')
6166 break;
6167 p = skipwhite(p + 1); /* skip comma in between arguments */
6168 }
6169 if (failed)
6170 break;
6171 if (round == 1)
6172 {
6173 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6174 if (retval == NULL)
6175 break;
6176 retval[count] = 0; /* zero means end of the list */
6177 total_count = count;
6178 }
6179 }
6180
6181 *arg = p;
6182 if (failed || retval == NULL)
6183 {
6184 vim_free(retval);
6185 return FAIL;
6186 }
6187
6188 if (*list == NULL)
6189 *list = retval;
6190 else
6191 vim_free(retval); /* list already found, don't overwrite it */
6192
6193 return OK;
6194}
6195
6196/*
6197 * Make a copy of an ID list.
6198 */
6199 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006200copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006201{
6202 int len;
6203 int count;
6204 short *retval;
6205
6206 if (list == NULL)
6207 return NULL;
6208
6209 for (count = 0; list[count]; ++count)
6210 ;
6211 len = (count + 1) * sizeof(short);
6212 retval = (short *)alloc((unsigned)len);
6213 if (retval != NULL)
6214 mch_memmove(retval, list, (size_t)len);
6215
6216 return retval;
6217}
6218
6219/*
6220 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6221 * "cur_si" can be NULL if not checking the "containedin" list.
6222 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6223 * the current item.
6224 * This function is called very often, keep it fast!!
6225 */
6226 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006227in_id_list(
6228 stateitem_T *cur_si, /* current item or NULL */
6229 short *list, /* id list */
6230 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6231 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006232{
6233 int retval;
6234 short *scl_list;
6235 short item;
6236 short id = ssp->id;
6237 static int depth = 0;
6238 int r;
6239
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006240 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006241 if (cur_si != NULL && ssp->cont_in_list != NULL
6242 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006243 {
6244 /* Ignore transparent items without a contains argument. Double check
6245 * that we don't go back past the first one. */
6246 while ((cur_si->si_flags & HL_TRANS_CONT)
6247 && cur_si > (stateitem_T *)(current_state.ga_data))
6248 --cur_si;
6249 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6250 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006251 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6252 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006253 return TRUE;
6254 }
6255
6256 if (list == NULL)
6257 return FALSE;
6258
6259 /*
6260 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6261 * inside anything. Only allow not-contained groups.
6262 */
6263 if (list == ID_LIST_ALL)
6264 return !contained;
6265
6266 /*
6267 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6268 * contains list. We also require that "id" is at the same ":syn include"
6269 * level as the list.
6270 */
6271 item = *list;
6272 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6273 {
6274 if (item < SYNID_TOP)
6275 {
6276 /* ALL or ALLBUT: accept all groups in the same file */
6277 if (item - SYNID_ALLBUT != ssp->inc_tag)
6278 return FALSE;
6279 }
6280 else if (item < SYNID_CONTAINED)
6281 {
6282 /* TOP: accept all not-contained groups in the same file */
6283 if (item - SYNID_TOP != ssp->inc_tag || contained)
6284 return FALSE;
6285 }
6286 else
6287 {
6288 /* CONTAINED: accept all contained groups in the same file */
6289 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6290 return FALSE;
6291 }
6292 item = *++list;
6293 retval = FALSE;
6294 }
6295 else
6296 retval = TRUE;
6297
6298 /*
6299 * Return "retval" if id is in the contains list.
6300 */
6301 while (item != 0)
6302 {
6303 if (item == id)
6304 return retval;
6305 if (item >= SYNID_CLUSTER)
6306 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006307 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006308 /* restrict recursiveness to 30 to avoid an endless loop for a
6309 * cluster that includes itself (indirectly) */
6310 if (scl_list != NULL && depth < 30)
6311 {
6312 ++depth;
6313 r = in_id_list(NULL, scl_list, ssp, contained);
6314 --depth;
6315 if (r)
6316 return retval;
6317 }
6318 }
6319 item = *++list;
6320 }
6321 return !retval;
6322}
6323
6324struct subcommand
6325{
Bram Moolenaard99df422016-01-29 23:20:40 +01006326 char *name; /* subcommand name */
6327 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006328};
6329
6330static struct subcommand subcommands[] =
6331{
6332 {"case", syn_cmd_case},
6333 {"clear", syn_cmd_clear},
6334 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006335 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 {"enable", syn_cmd_enable},
6337 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006338 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006339 {"keyword", syn_cmd_keyword},
6340 {"list", syn_cmd_list},
6341 {"manual", syn_cmd_manual},
6342 {"match", syn_cmd_match},
6343 {"on", syn_cmd_on},
6344 {"off", syn_cmd_off},
6345 {"region", syn_cmd_region},
6346 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006347 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348 {"sync", syn_cmd_sync},
6349 {"", syn_cmd_list},
6350 {NULL, NULL}
6351};
6352
6353/*
6354 * ":syntax".
6355 * This searches the subcommands[] table for the subcommand name, and calls a
6356 * syntax_subcommand() function to do the rest.
6357 */
6358 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006359ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006360{
6361 char_u *arg = eap->arg;
6362 char_u *subcmd_end;
6363 char_u *subcmd_name;
6364 int i;
6365
6366 syn_cmdlinep = eap->cmdlinep;
6367
6368 /* isolate subcommand name */
6369 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6370 ;
6371 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6372 if (subcmd_name != NULL)
6373 {
6374 if (eap->skip) /* skip error messages for all subcommands */
6375 ++emsg_skip;
6376 for (i = 0; ; ++i)
6377 {
6378 if (subcommands[i].name == NULL)
6379 {
6380 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6381 break;
6382 }
6383 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6384 {
6385 eap->arg = skipwhite(subcmd_end);
6386 (subcommands[i].func)(eap, FALSE);
6387 break;
6388 }
6389 }
6390 vim_free(subcmd_name);
6391 if (eap->skip)
6392 --emsg_skip;
6393 }
6394}
6395
Bram Moolenaar860cae12010-06-05 23:22:07 +02006396 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006397ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006398{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006399 char_u *old_value;
6400 char_u *new_value;
6401
Bram Moolenaar860cae12010-06-05 23:22:07 +02006402 if (curwin->w_s == &curwin->w_buffer->b_s)
6403 {
6404 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6405 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006406 hash_init(&curwin->w_s->b_keywtab);
6407 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006408#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006409 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006410 curwin->w_p_spell = FALSE; /* No spell checking */
6411 clear_string_option(&curwin->w_s->b_p_spc);
6412 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006413 clear_string_option(&curwin->w_s->b_p_spl);
6414#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006415 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006416 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006417
6418 /* save value of b:current_syntax */
6419 old_value = get_var_value((char_u *)"b:current_syntax");
6420 if (old_value != NULL)
6421 old_value = vim_strsave(old_value);
6422
6423 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6424 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006425 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006426
6427 /* move value of b:current_syntax to w:current_syntax */
6428 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006429 if (new_value != NULL)
6430 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006431
6432 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006433 if (old_value == NULL)
6434 do_unlet((char_u *)"b:current_syntax", TRUE);
6435 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006436 {
6437 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6438 vim_free(old_value);
6439 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006440}
6441
6442 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006443syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006444{
6445 return (win->w_s->b_syn_patterns.ga_len != 0
6446 || win->w_s->b_syn_clusters.ga_len != 0
6447 || win->w_s->b_keywtab.ht_used > 0
6448 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006449}
6450
6451#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6452
6453static enum
6454{
6455 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006456 EXP_CASE, /* expand ":syn case" arguments */
6457 EXP_SPELL, /* expand ":syn spell" arguments */
6458 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459} expand_what;
6460
Bram Moolenaar4f688582007-07-24 12:34:30 +00006461/*
6462 * Reset include_link, include_default, include_none to 0.
6463 * Called when we are done expanding.
6464 */
6465 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006466reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006467{
6468 include_link = include_default = include_none = 0;
6469}
6470
6471/*
6472 * Handle command line completion for :match and :echohl command: Add "None"
6473 * as highlight group.
6474 */
6475 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006476set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006477{
6478 xp->xp_context = EXPAND_HIGHLIGHT;
6479 xp->xp_pattern = arg;
6480 include_none = 1;
6481}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482
6483/*
6484 * Handle command line completion for :syntax command.
6485 */
6486 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006487set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488{
6489 char_u *p;
6490
6491 /* Default: expand subcommands */
6492 xp->xp_context = EXPAND_SYNTAX;
6493 expand_what = EXP_SUBCMD;
6494 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006495 include_link = 0;
6496 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006497
6498 /* (part of) subcommand already typed */
6499 if (*arg != NUL)
6500 {
6501 p = skiptowhite(arg);
6502 if (*p != NUL) /* past first word */
6503 {
6504 xp->xp_pattern = skipwhite(p);
6505 if (*skiptowhite(xp->xp_pattern) != NUL)
6506 xp->xp_context = EXPAND_NOTHING;
6507 else if (STRNICMP(arg, "case", p - arg) == 0)
6508 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006509 else if (STRNICMP(arg, "spell", p - arg) == 0)
6510 expand_what = EXP_SPELL;
6511 else if (STRNICMP(arg, "sync", p - arg) == 0)
6512 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006513 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6514 || STRNICMP(arg, "region", p - arg) == 0
6515 || STRNICMP(arg, "match", p - arg) == 0
6516 || STRNICMP(arg, "list", p - arg) == 0)
6517 xp->xp_context = EXPAND_HIGHLIGHT;
6518 else
6519 xp->xp_context = EXPAND_NOTHING;
6520 }
6521 }
6522}
6523
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524/*
6525 * Function given to ExpandGeneric() to obtain the list syntax names for
6526 * expansion.
6527 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006529get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006531 switch (expand_what)
6532 {
6533 case EXP_SUBCMD:
6534 return (char_u *)subcommands[idx].name;
6535 case EXP_CASE:
6536 {
6537 static char *case_args[] = {"match", "ignore", NULL};
6538 return (char_u *)case_args[idx];
6539 }
6540 case EXP_SPELL:
6541 {
6542 static char *spell_args[] =
6543 {"toplevel", "notoplevel", "default", NULL};
6544 return (char_u *)spell_args[idx];
6545 }
6546 case EXP_SYNC:
6547 {
6548 static char *sync_args[] =
6549 {"ccomment", "clear", "fromstart",
6550 "linebreaks=", "linecont", "lines=", "match",
6551 "maxlines=", "minlines=", "region", NULL};
6552 return (char_u *)sync_args[idx];
6553 }
6554 }
6555 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556}
6557
6558#endif /* FEAT_CMDL_COMPL */
6559
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560/*
6561 * Function called for expression evaluation: get syntax ID at file position.
6562 */
6563 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006564syn_get_id(
6565 win_T *wp,
6566 long lnum,
6567 colnr_T col,
6568 int trans, /* remove transparency */
6569 int *spellp, /* return: can do spell checking */
6570 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571{
6572 /* When the position is not after the current position and in the same
6573 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006574 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006575 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006576 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006577 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006578 else if (wp->w_buffer == syn_buf
6579 && lnum == current_lnum
6580 && col > current_col)
6581 /* next_match may not be correct when moving around, e.g. with the
6582 * "skip" expression in searchpair() */
6583 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006585 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006586
6587 return (trans ? current_trans_id : current_id);
6588}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006589
Bram Moolenaar860cae12010-06-05 23:22:07 +02006590#if defined(FEAT_CONCEAL) || defined(PROTO)
6591/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006592 * Get extra information about the syntax item. Must be called right after
6593 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006594 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006595 * Returns the current flags.
6596 */
6597 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006598get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006599{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006600 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006601 return current_flags;
6602}
6603
6604/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006605 * Return conceal substitution character
6606 */
6607 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006608syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006609{
6610 return current_sub_char;
6611}
6612#endif
6613
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006614#if defined(FEAT_EVAL) || defined(PROTO)
6615/*
6616 * Return the syntax ID at position "i" in the current stack.
6617 * The caller must have called syn_get_id() before to fill the stack.
6618 * Returns -1 when "i" is out of range.
6619 */
6620 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006621syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006622{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006623 if (i >= current_state.ga_len)
6624 {
6625 /* Need to invalidate the state, because we didn't properly finish it
6626 * for the last character, "keep_state" was TRUE. */
6627 invalidate_current_state();
6628 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006629 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006630 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006631 return CUR_STATE(i).si_id;
6632}
6633#endif
6634
Bram Moolenaar071d4272004-06-13 20:20:40 +00006635#if defined(FEAT_FOLDING) || defined(PROTO)
6636/*
6637 * Function called to get folding level for line "lnum" in window "wp".
6638 */
6639 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006640syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006641{
6642 int level = 0;
6643 int i;
6644
6645 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006646 if (wp->w_s->b_syn_folditems != 0
6647 && !wp->w_s->b_syn_error
6648# ifdef SYN_TIME_LIMIT
6649 && !wp->w_s->b_syn_slow
6650# endif
6651 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006652 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006653 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006654
6655 for (i = 0; i < current_state.ga_len; ++i)
6656 if (CUR_STATE(i).si_flags & HL_FOLD)
6657 ++level;
6658 }
6659 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006660 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006661 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006662 if (level < 0)
6663 level = 0;
6664 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006665 return level;
6666}
6667#endif
6668
Bram Moolenaar01615492015-02-03 13:00:38 +01006669#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006670/*
6671 * ":syntime".
6672 */
6673 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006674ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006675{
6676 if (STRCMP(eap->arg, "on") == 0)
6677 syn_time_on = TRUE;
6678 else if (STRCMP(eap->arg, "off") == 0)
6679 syn_time_on = FALSE;
6680 else if (STRCMP(eap->arg, "clear") == 0)
6681 syntime_clear();
6682 else if (STRCMP(eap->arg, "report") == 0)
6683 syntime_report();
6684 else
6685 EMSG2(_(e_invarg2), eap->arg);
6686}
6687
6688 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006689syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006690{
6691 profile_zero(&st->total);
6692 profile_zero(&st->slowest);
6693 st->count = 0;
6694 st->match = 0;
6695}
6696
6697/*
6698 * Clear the syntax timing for the current buffer.
6699 */
6700 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006701syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006702{
6703 int idx;
6704 synpat_T *spp;
6705
6706 if (!syntax_present(curwin))
6707 {
6708 MSG(_(msg_no_items));
6709 return;
6710 }
6711 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6712 {
6713 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6714 syn_clear_time(&spp->sp_time);
6715 }
6716}
6717
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006718#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6719/*
6720 * Function given to ExpandGeneric() to obtain the possible arguments of the
6721 * ":syntime {on,off,clear,report}" command.
6722 */
6723 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006724get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006725{
6726 switch (idx)
6727 {
6728 case 0: return (char_u *)"on";
6729 case 1: return (char_u *)"off";
6730 case 2: return (char_u *)"clear";
6731 case 3: return (char_u *)"report";
6732 }
6733 return NULL;
6734}
6735#endif
6736
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006737typedef struct
6738{
6739 proftime_T total;
6740 int count;
6741 int match;
6742 proftime_T slowest;
6743 proftime_T average;
6744 int id;
6745 char_u *pattern;
6746} time_entry_T;
6747
6748 static int
6749#ifdef __BORLANDC__
6750_RTLENTRYF
6751#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006752syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006753{
6754 const time_entry_T *s1 = v1;
6755 const time_entry_T *s2 = v2;
6756
6757 return profile_cmp(&s1->total, &s2->total);
6758}
6759
6760/*
6761 * Clear the syntax timing for the current buffer.
6762 */
6763 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006764syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006765{
6766 int idx;
6767 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006768# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006769 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006770# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006771 int len;
6772 proftime_T total_total;
6773 int total_count = 0;
6774 garray_T ga;
6775 time_entry_T *p;
6776
6777 if (!syntax_present(curwin))
6778 {
6779 MSG(_(msg_no_items));
6780 return;
6781 }
6782
6783 ga_init2(&ga, sizeof(time_entry_T), 50);
6784 profile_zero(&total_total);
6785 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6786 {
6787 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6788 if (spp->sp_time.count > 0)
6789 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006790 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006791 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6792 p->total = spp->sp_time.total;
6793 profile_add(&total_total, &spp->sp_time.total);
6794 p->count = spp->sp_time.count;
6795 p->match = spp->sp_time.match;
6796 total_count += spp->sp_time.count;
6797 p->slowest = spp->sp_time.slowest;
6798# ifdef FEAT_FLOAT
6799 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6800 p->average = tm;
6801# endif
6802 p->id = spp->sp_syn.id;
6803 p->pattern = spp->sp_pattern;
6804 ++ga.ga_len;
6805 }
6806 }
6807
Bram Moolenaara2162552017-01-08 17:46:20 +01006808 /* Sort on total time. Skip if there are no items to avoid passing NULL
6809 * pointer to qsort(). */
6810 if (ga.ga_len > 1)
6811 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006812 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006813
6814 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6815 MSG_PUTS("\n");
6816 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6817 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006818 p = ((time_entry_T *)ga.ga_data) + idx;
6819
6820 MSG_PUTS(profile_msg(&p->total));
6821 MSG_PUTS(" "); /* make sure there is always a separating space */
6822 msg_advance(13);
6823 msg_outnum(p->count);
6824 MSG_PUTS(" ");
6825 msg_advance(20);
6826 msg_outnum(p->match);
6827 MSG_PUTS(" ");
6828 msg_advance(26);
6829 MSG_PUTS(profile_msg(&p->slowest));
6830 MSG_PUTS(" ");
6831 msg_advance(38);
6832# ifdef FEAT_FLOAT
6833 MSG_PUTS(profile_msg(&p->average));
6834 MSG_PUTS(" ");
6835# endif
6836 msg_advance(50);
6837 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6838 MSG_PUTS(" ");
6839
6840 msg_advance(69);
6841 if (Columns < 80)
6842 len = 20; /* will wrap anyway */
6843 else
6844 len = Columns - 70;
6845 if (len > (int)STRLEN(p->pattern))
6846 len = (int)STRLEN(p->pattern);
6847 msg_outtrans_len(p->pattern, len);
6848 MSG_PUTS("\n");
6849 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006850 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006851 if (!got_int)
6852 {
6853 MSG_PUTS("\n");
6854 MSG_PUTS(profile_msg(&total_total));
6855 msg_advance(13);
6856 msg_outnum(total_count);
6857 MSG_PUTS("\n");
6858 }
6859}
6860#endif
6861
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862#endif /* FEAT_SYN_HL */
6863
Bram Moolenaar071d4272004-06-13 20:20:40 +00006864/**************************************
6865 * Highlighting stuff *
6866 **************************************/
6867
6868/*
6869 * The default highlight groups. These are compiled-in for fast startup and
6870 * they still work when the runtime files can't be found.
6871 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006872 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6873 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006874 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006875#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006876# define CENT(a, b) b
6877#else
6878# define CENT(a, b) a
6879#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006880static char *(highlight_init_both[]) = {
6881 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6882 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6883 CENT("IncSearch term=reverse cterm=reverse",
6884 "IncSearch term=reverse cterm=reverse gui=reverse"),
6885 CENT("ModeMsg term=bold cterm=bold",
6886 "ModeMsg term=bold cterm=bold gui=bold"),
6887 CENT("NonText term=bold ctermfg=Blue",
6888 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6889 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6890 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6891 CENT("StatusLineNC term=reverse cterm=reverse",
6892 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6893 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006894 CENT("VertSplit term=reverse cterm=reverse",
6895 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006896#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006897 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6898 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006899#endif
6900#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006901 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6902 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006903#endif
6904#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006905 CENT("PmenuSbar ctermbg=Grey",
6906 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006907#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006908 CENT("TabLineSel term=bold cterm=bold",
6909 "TabLineSel term=bold cterm=bold gui=bold"),
6910 CENT("TabLineFill term=reverse cterm=reverse",
6911 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006912#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006913 "Cursor guibg=fg guifg=bg",
6914 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006915#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006916 "default link QuickFixLine Search",
6917 NULL
6918};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006919
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006920/* Default colors only used with a light background. */
6921static char *(highlight_init_light[]) = {
6922 CENT("Directory term=bold ctermfg=DarkBlue",
6923 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6924 CENT("LineNr term=underline ctermfg=Brown",
6925 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6926 CENT("CursorLineNr term=bold ctermfg=Brown",
6927 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6928 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6929 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6930 CENT("Question term=standout ctermfg=DarkGreen",
6931 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6932 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6933 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006934#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006935 CENT("SpellBad term=reverse ctermbg=LightRed",
6936 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6937 CENT("SpellCap term=reverse ctermbg=LightBlue",
6938 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6939 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6940 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6941 CENT("SpellLocal term=underline ctermbg=Cyan",
6942 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006943#endif
6944#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006945 CENT("PmenuThumb ctermbg=Black",
6946 "PmenuThumb ctermbg=Black guibg=Black"),
6947 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6948 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6949 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6950 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006951#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006952 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6953 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6954 CENT("Title term=bold ctermfg=DarkMagenta",
6955 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6956 CENT("WarningMsg term=standout ctermfg=DarkRed",
6957 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006958#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006959 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6960 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006961#endif
6962#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006963 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6964 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6965 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6966 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006967#endif
6968#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006969 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6970 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006971#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006972 CENT("Visual term=reverse",
6973 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006974#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006975 CENT("DiffAdd term=bold ctermbg=LightBlue",
6976 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6977 CENT("DiffChange term=bold ctermbg=LightMagenta",
6978 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6979 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6980 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006981#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006982 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6983 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006984#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006985 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6986 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6987 CENT("CursorLine term=underline cterm=underline",
6988 "CursorLine term=underline cterm=underline guibg=Grey90"),
6989 CENT("ColorColumn term=reverse ctermbg=LightRed",
6990 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006991#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006992#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006993 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6994 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02006995#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006996 CENT("MatchParen term=reverse ctermbg=Cyan",
6997 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006998#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006999 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007000#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007001#ifdef FEAT_TERMINAL
7002 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7003 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7004 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7005 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7006#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007007#ifdef FEAT_MENU
7008 CENT("ToolbarLine term=underline ctermbg=LightGrey",
7009 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
7010 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007011 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007012#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007013 NULL
7014};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007015
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007016/* Default colors only used with a dark background. */
7017static char *(highlight_init_dark[]) = {
7018 CENT("Directory term=bold ctermfg=LightCyan",
7019 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7020 CENT("LineNr term=underline ctermfg=Yellow",
7021 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7022 CENT("CursorLineNr term=bold ctermfg=Yellow",
7023 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7024 CENT("MoreMsg term=bold ctermfg=LightGreen",
7025 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7026 CENT("Question term=standout ctermfg=LightGreen",
7027 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7028 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7029 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7030 CENT("SpecialKey term=bold ctermfg=LightBlue",
7031 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007032#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007033 CENT("SpellBad term=reverse ctermbg=Red",
7034 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7035 CENT("SpellCap term=reverse ctermbg=Blue",
7036 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7037 CENT("SpellRare term=reverse ctermbg=Magenta",
7038 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7039 CENT("SpellLocal term=underline ctermbg=Cyan",
7040 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007041#endif
7042#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007043 CENT("PmenuThumb ctermbg=White",
7044 "PmenuThumb ctermbg=White guibg=White"),
7045 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7046 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7047 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7048 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007049#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007050 CENT("Title term=bold ctermfg=LightMagenta",
7051 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7052 CENT("WarningMsg term=standout ctermfg=LightRed",
7053 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007054#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007055 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7056 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007057#endif
7058#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007059 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7060 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7061 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7062 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007063#endif
7064#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007065 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7066 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007067#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007068 CENT("Visual term=reverse",
7069 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007070#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007071 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7072 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7073 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7074 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7075 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7076 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007077#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007078 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7079 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007080#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007081 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7082 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7083 CENT("CursorLine term=underline cterm=underline",
7084 "CursorLine term=underline cterm=underline guibg=Grey40"),
7085 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7086 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007087#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007088 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7089 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007090#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007091 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7092 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007093#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007094#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007095 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007096#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007097#ifdef FEAT_TERMINAL
7098 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7099 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7100 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7101 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7102#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007103#ifdef FEAT_MENU
7104 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007105 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007106 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7107 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7108#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007109 NULL
7110};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007111
7112 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007113init_highlight(
7114 int both, /* include groups where 'bg' doesn't matter */
7115 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007116{
7117 int i;
7118 char **pp;
7119 static int had_both = FALSE;
7120#ifdef FEAT_EVAL
7121 char_u *p;
7122
7123 /*
7124 * Try finding the color scheme file. Used when a color file was loaded
7125 * and 'background' or 't_Co' is changed.
7126 */
7127 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007128 if (p != NULL)
7129 {
7130 /* The value of g:colors_name could be freed when sourcing the script,
7131 * making "p" invalid, so copy it. */
7132 char_u *copy_p = vim_strsave(p);
7133 int r;
7134
7135 if (copy_p != NULL)
7136 {
7137 r = load_colors(copy_p);
7138 vim_free(copy_p);
7139 if (r == OK)
7140 return;
7141 }
7142 }
7143
Bram Moolenaar071d4272004-06-13 20:20:40 +00007144#endif
7145
7146 /*
7147 * Didn't use a color file, use the compiled-in colors.
7148 */
7149 if (both)
7150 {
7151 had_both = TRUE;
7152 pp = highlight_init_both;
7153 for (i = 0; pp[i] != NULL; ++i)
7154 do_highlight((char_u *)pp[i], reset, TRUE);
7155 }
7156 else if (!had_both)
7157 /* Don't do anything before the call with both == TRUE from main().
7158 * Not everything has been setup then, and that call will overrule
7159 * everything anyway. */
7160 return;
7161
7162 if (*p_bg == 'l')
7163 pp = highlight_init_light;
7164 else
7165 pp = highlight_init_dark;
7166 for (i = 0; pp[i] != NULL; ++i)
7167 do_highlight((char_u *)pp[i], reset, TRUE);
7168
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007169 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007170 * depend on the number of colors available.
7171 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007172 * to avoid Statement highlighted text disappears.
7173 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007174 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007175 do_highlight((char_u *)(*p_bg == 'l'
7176 ? "Visual cterm=NONE ctermbg=LightGrey"
7177 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007178 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007179 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007180 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7181 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007182 if (*p_bg == 'l')
7183 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7184 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007185
Bram Moolenaar071d4272004-06-13 20:20:40 +00007186#ifdef FEAT_SYN_HL
7187 /*
7188 * If syntax highlighting is enabled load the highlighting for it.
7189 */
7190 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007191 {
7192 static int recursive = 0;
7193
7194 if (recursive >= 5)
7195 EMSG(_("E679: recursive loop loading syncolor.vim"));
7196 else
7197 {
7198 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007199 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007200 --recursive;
7201 }
7202 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007203#endif
7204}
7205
7206/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007207 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208 * Return OK for success, FAIL for failure.
7209 */
7210 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007211load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212{
7213 char_u *buf;
7214 int retval = FAIL;
7215 static int recursive = FALSE;
7216
7217 /* When being called recursively, this is probably because setting
7218 * 'background' caused the highlighting to be reloaded. This means it is
7219 * working, thus we should return OK. */
7220 if (recursive)
7221 return OK;
7222
7223 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007224 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007225 if (buf != NULL)
7226 {
Bram Moolenaar60a68362018-04-30 15:40:48 +02007227 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
7228 curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007229 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007230 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007231 vim_free(buf);
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007232 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007233 }
7234 recursive = FALSE;
7235
7236 return retval;
7237}
7238
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007239static char *(color_names[28]) = {
7240 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7241 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7242 "Gray", "Grey", "LightGray", "LightGrey",
7243 "DarkGray", "DarkGrey",
7244 "Blue", "LightBlue", "Green", "LightGreen",
7245 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7246 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7247 /* indices:
7248 * 0, 1, 2, 3,
7249 * 4, 5, 6, 7,
7250 * 8, 9, 10, 11,
7251 * 12, 13,
7252 * 14, 15, 16, 17,
7253 * 18, 19, 20, 21, 22,
7254 * 23, 24, 25, 26, 27 */
7255static int color_numbers_16[28] = {0, 1, 2, 3,
7256 4, 5, 6, 6,
7257 7, 7, 7, 7,
7258 8, 8,
7259 9, 9, 10, 10,
7260 11, 11, 12, 12, 13,
7261 13, 14, 14, 15, -1};
7262/* for xterm with 88 colors... */
7263static int color_numbers_88[28] = {0, 4, 2, 6,
7264 1, 5, 32, 72,
7265 84, 84, 7, 7,
7266 82, 82,
7267 12, 43, 10, 61,
7268 14, 63, 9, 74, 13,
7269 75, 11, 78, 15, -1};
7270/* for xterm with 256 colors... */
7271static int color_numbers_256[28] = {0, 4, 2, 6,
7272 1, 5, 130, 130,
7273 248, 248, 7, 7,
7274 242, 242,
7275 12, 81, 10, 121,
7276 14, 159, 9, 224, 13,
7277 225, 11, 229, 15, -1};
7278/* for terminals with less than 16 colors... */
7279static int color_numbers_8[28] = {0, 4, 2, 6,
7280 1, 5, 3, 3,
7281 7, 7, 7, 7,
7282 0+8, 0+8,
7283 4+8, 4+8, 2+8, 2+8,
7284 6+8, 6+8, 1+8, 1+8, 5+8,
7285 5+8, 3+8, 3+8, 7+8, -1};
7286
7287/*
7288 * Lookup the "cterm" value to be used for color with index "idx" in
7289 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007290 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7291 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007292 */
7293 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007294lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007295{
7296 int color = color_numbers_16[idx];
7297 char_u *p;
7298
7299 /* Use the _16 table to check if it's a valid color name. */
7300 if (color < 0)
7301 return -1;
7302
7303 if (t_colors == 8)
7304 {
7305 /* t_Co is 8: use the 8 colors table */
7306#if defined(__QNXNTO__)
7307 color = color_numbers_8_qansi[idx];
7308#else
7309 color = color_numbers_8[idx];
7310#endif
7311 if (foreground)
7312 {
7313 /* set/reset bold attribute to get light foreground
7314 * colors (on some terminals, e.g. "linux") */
7315 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007316 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007317 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007318 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007319 }
7320 color &= 7; /* truncate to 8 colors */
7321 }
7322 else if (t_colors == 16 || t_colors == 88
7323 || t_colors >= 256)
7324 {
7325 /*
7326 * Guess: if the termcap entry ends in 'm', it is
7327 * probably an xterm-like terminal. Use the changed
7328 * order for colors.
7329 */
7330 if (*T_CAF != NUL)
7331 p = T_CAF;
7332 else
7333 p = T_CSF;
7334 if (*p != NUL && (t_colors > 256
7335 || *(p + STRLEN(p) - 1) == 'm'))
7336 {
7337 if (t_colors == 88)
7338 color = color_numbers_88[idx];
7339 else if (t_colors >= 256)
7340 color = color_numbers_256[idx];
7341 else
7342 color = color_numbers_8[idx];
7343 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007344#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007345 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7346 /* Terminal.app has a bug: 15 is light grey. Use white
7347 * from the color cube instead. */
7348 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007349#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007350 }
7351 return color;
7352}
7353
Bram Moolenaar071d4272004-06-13 20:20:40 +00007354/*
7355 * Handle the ":highlight .." command.
7356 * When using ":hi clear" this is called recursively for each group with
7357 * "forceit" and "init" both TRUE.
7358 */
7359 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007360do_highlight(
7361 char_u *line,
7362 int forceit,
7363 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007364{
7365 char_u *name_end;
7366 char_u *p;
7367 char_u *linep;
7368 char_u *key_start;
7369 char_u *arg_start;
7370 char_u *key = NULL, *arg = NULL;
7371 long i;
7372 int off;
7373 int len;
7374 int attr;
7375 int id;
7376 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007377 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007378 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007379 int dodefault = FALSE;
7380 int doclear = FALSE;
7381 int dolink = FALSE;
7382 int error = FALSE;
7383 int color;
7384 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007385#ifdef FEAT_TERMINAL
7386 int is_terminal_group = FALSE; /* "Terminal" group */
7387#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007388#ifdef FEAT_GUI_X11
7389 int is_menu_group = FALSE; /* "Menu" group */
7390 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7391 int is_tooltip_group = FALSE; /* "Tooltip" group */
7392 int do_colors = FALSE; /* need to update colors? */
7393#else
7394# define is_menu_group 0
7395# define is_tooltip_group 0
7396#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007397#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7398 int did_highlight_changed = FALSE;
7399#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007400
7401 /*
7402 * If no argument, list current highlighting.
7403 */
7404 if (ends_excmd(*line))
7405 {
7406 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7407 /* TODO: only call when the group has attributes set */
7408 highlight_list_one((int)i);
7409 return;
7410 }
7411
7412 /*
7413 * Isolate the name.
7414 */
7415 name_end = skiptowhite(line);
7416 linep = skipwhite(name_end);
7417
7418 /*
7419 * Check for "default" argument.
7420 */
7421 if (STRNCMP(line, "default", name_end - line) == 0)
7422 {
7423 dodefault = TRUE;
7424 line = linep;
7425 name_end = skiptowhite(line);
7426 linep = skipwhite(name_end);
7427 }
7428
7429 /*
7430 * Check for "clear" or "link" argument.
7431 */
7432 if (STRNCMP(line, "clear", name_end - line) == 0)
7433 doclear = TRUE;
7434 if (STRNCMP(line, "link", name_end - line) == 0)
7435 dolink = TRUE;
7436
7437 /*
7438 * ":highlight {group-name}": list highlighting for one group.
7439 */
7440 if (!doclear && !dolink && ends_excmd(*linep))
7441 {
7442 id = syn_namen2id(line, (int)(name_end - line));
7443 if (id == 0)
7444 EMSG2(_("E411: highlight group not found: %s"), line);
7445 else
7446 highlight_list_one(id);
7447 return;
7448 }
7449
7450 /*
7451 * Handle ":highlight link {from} {to}" command.
7452 */
7453 if (dolink)
7454 {
7455 char_u *from_start = linep;
7456 char_u *from_end;
7457 char_u *to_start;
7458 char_u *to_end;
7459 int from_id;
7460 int to_id;
7461
7462 from_end = skiptowhite(from_start);
7463 to_start = skipwhite(from_end);
7464 to_end = skiptowhite(to_start);
7465
7466 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7467 {
7468 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7469 from_start);
7470 return;
7471 }
7472
7473 if (!ends_excmd(*skipwhite(to_end)))
7474 {
7475 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7476 return;
7477 }
7478
7479 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7480 if (STRNCMP(to_start, "NONE", 4) == 0)
7481 to_id = 0;
7482 else
7483 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7484
Bram Moolenaar414168d2017-09-10 15:21:55 +02007485 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007486 {
7487 /*
7488 * Don't allow a link when there already is some highlighting
7489 * for the group, unless '!' is used
7490 */
7491 if (to_id > 0 && !forceit && !init
7492 && hl_has_settings(from_id - 1, dodefault))
7493 {
7494 if (sourcing_name == NULL && !dodefault)
7495 EMSG(_("E414: group has settings, highlight link ignored"));
7496 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007497 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007498#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007499 || HL_TABLE()[from_id - 1].sg_scriptID != current_SID
Bram Moolenaar99433292017-09-08 12:37:47 +02007500#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007501 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007502 {
7503 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007504 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7505 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007506#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007507 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007508#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007509 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007510 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007511
7512 /* Only call highlight_changed() once after multiple changes. */
7513 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007514 }
7515 }
7516
Bram Moolenaar071d4272004-06-13 20:20:40 +00007517 return;
7518 }
7519
7520 if (doclear)
7521 {
7522 /*
7523 * ":highlight clear [group]" command.
7524 */
7525 line = linep;
7526 if (ends_excmd(*line))
7527 {
7528#ifdef FEAT_GUI
7529 /* First, we do not destroy the old values, but allocate the new
7530 * ones and update the display. THEN we destroy the old values.
7531 * If we destroy the old values first, then the old values
7532 * (such as GuiFont's or GuiFontset's) will still be displayed but
7533 * invalid because they were free'd.
7534 */
7535 if (gui.in_use)
7536 {
7537# ifdef FEAT_BEVAL_TIP
7538 gui_init_tooltip_font();
7539# endif
7540# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7541 gui_init_menu_font();
7542# endif
7543 }
7544# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7545 gui_mch_def_colors();
7546# endif
7547# ifdef FEAT_GUI_X11
7548# ifdef FEAT_MENU
7549
7550 /* This only needs to be done when there is no Menu highlight
7551 * group defined by default, which IS currently the case.
7552 */
7553 gui_mch_new_menu_colors();
7554# endif
7555 if (gui.in_use)
7556 {
7557 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007558# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007559 gui_mch_new_tooltip_colors();
7560# endif
7561# ifdef FEAT_MENU
7562 gui_mch_new_menu_font();
7563# endif
7564 }
7565# endif
7566
7567 /* Ok, we're done allocating the new default graphics items.
7568 * The screen should already be refreshed at this point.
7569 * It is now Ok to clear out the old data.
7570 */
7571#endif
7572#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007573 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007574#endif
7575 restore_cterm_colors();
7576
7577 /*
7578 * Clear all default highlight groups and load the defaults.
7579 */
7580 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7581 highlight_clear(idx);
7582 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007583#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007584 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007585 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007586 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007587#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007588 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007589 redraw_later_clear();
7590 return;
7591 }
7592 name_end = skiptowhite(line);
7593 linep = skipwhite(name_end);
7594 }
7595
7596 /*
7597 * Find the group name in the table. If it does not exist yet, add it.
7598 */
7599 id = syn_check_group(line, (int)(name_end - line));
7600 if (id == 0) /* failed (out of memory) */
7601 return;
7602 idx = id - 1; /* index is ID minus one */
7603
7604 /* Return if "default" was used and the group already has settings. */
7605 if (dodefault && hl_has_settings(idx, TRUE))
7606 return;
7607
Bram Moolenaar99433292017-09-08 12:37:47 +02007608 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007609 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007610
Bram Moolenaar414168d2017-09-10 15:21:55 +02007611 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007612 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007613#ifdef FEAT_TERMINAL
7614 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7615 is_terminal_group = TRUE;
7616#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007617#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007618 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007619 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007620 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007621 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007622 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007623 is_tooltip_group = TRUE;
7624#endif
7625
7626 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7627 if (doclear || (forceit && init))
7628 {
7629 highlight_clear(idx);
7630 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007631 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007632 }
7633
7634 if (!doclear)
7635 while (!ends_excmd(*linep))
7636 {
7637 key_start = linep;
7638 if (*linep == '=')
7639 {
7640 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7641 error = TRUE;
7642 break;
7643 }
7644
7645 /*
7646 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7647 * "guibg").
7648 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007649 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007650 ++linep;
7651 vim_free(key);
7652 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7653 if (key == NULL)
7654 {
7655 error = TRUE;
7656 break;
7657 }
7658 linep = skipwhite(linep);
7659
7660 if (STRCMP(key, "NONE") == 0)
7661 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007662 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007663 {
7664 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007665 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007666 highlight_clear(idx);
7667 }
7668 continue;
7669 }
7670
7671 /*
7672 * Check for the equal sign.
7673 */
7674 if (*linep != '=')
7675 {
7676 EMSG2(_("E416: missing equal sign: %s"), key_start);
7677 error = TRUE;
7678 break;
7679 }
7680 ++linep;
7681
7682 /*
7683 * Isolate the argument.
7684 */
7685 linep = skipwhite(linep);
7686 if (*linep == '\'') /* guifg='color name' */
7687 {
7688 arg_start = ++linep;
7689 linep = vim_strchr(linep, '\'');
7690 if (linep == NULL)
7691 {
7692 EMSG2(_(e_invarg2), key_start);
7693 error = TRUE;
7694 break;
7695 }
7696 }
7697 else
7698 {
7699 arg_start = linep;
7700 linep = skiptowhite(linep);
7701 }
7702 if (linep == arg_start)
7703 {
7704 EMSG2(_("E417: missing argument: %s"), key_start);
7705 error = TRUE;
7706 break;
7707 }
7708 vim_free(arg);
7709 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7710 if (arg == NULL)
7711 {
7712 error = TRUE;
7713 break;
7714 }
7715 if (*linep == '\'')
7716 ++linep;
7717
7718 /*
7719 * Store the argument.
7720 */
7721 if ( STRCMP(key, "TERM") == 0
7722 || STRCMP(key, "CTERM") == 0
7723 || STRCMP(key, "GUI") == 0)
7724 {
7725 attr = 0;
7726 off = 0;
7727 while (arg[off] != NUL)
7728 {
7729 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7730 {
7731 len = (int)STRLEN(hl_name_table[i]);
7732 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7733 {
7734 attr |= hl_attr_table[i];
7735 off += len;
7736 break;
7737 }
7738 }
7739 if (i < 0)
7740 {
7741 EMSG2(_("E418: Illegal value: %s"), arg);
7742 error = TRUE;
7743 break;
7744 }
7745 if (arg[off] == ',') /* another one follows */
7746 ++off;
7747 }
7748 if (error)
7749 break;
7750 if (*key == 'T')
7751 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007752 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007753 {
7754 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007755 HL_TABLE()[idx].sg_set |= SG_TERM;
7756 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757 }
7758 }
7759 else if (*key == 'C')
7760 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007761 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007762 {
7763 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007764 HL_TABLE()[idx].sg_set |= SG_CTERM;
7765 HL_TABLE()[idx].sg_cterm = attr;
7766 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007767 }
7768 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007769#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007770 else
7771 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007772 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
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_GUI;
7776 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777 }
7778 }
7779#endif
7780 }
7781 else if (STRCMP(key, "FONT") == 0)
7782 {
7783 /* in non-GUI fonts are simply ignored */
7784#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007785 if (HL_TABLE()[idx].sg_font_name != NULL
7786 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007787 {
7788 /* Font name didn't change, ignore. */
7789 }
7790 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007791 {
7792 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007793 vim_free(HL_TABLE()[idx].sg_font_name);
7794 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007795 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007796 }
7797 else
7798 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007799 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007801 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007802# endif
7803 /* First, save the current font/fontset.
7804 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007805 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007806 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7807 */
7808
Bram Moolenaar414168d2017-09-10 15:21:55 +02007809 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007810# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007811 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007812# endif
7813 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007814 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815
7816# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007817 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007819 /* New fontset was accepted. Free the old one, if there
7820 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007822 vim_free(HL_TABLE()[idx].sg_font_name);
7823 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007824 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007825 }
7826 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007827 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007829 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007830 {
7831 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007832 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007834 vim_free(HL_TABLE()[idx].sg_font_name);
7835 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007836 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007837 }
7838 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007839 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007840 }
7841#endif
7842 }
7843 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7844 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007845 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007846 {
7847 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007848 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007849
7850 /* When setting the foreground color, and previously the "bold"
7851 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007852 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007853 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007854 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7855 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007856 }
7857
7858 if (VIM_ISDIGIT(*arg))
7859 color = atoi((char *)arg);
7860 else if (STRICMP(arg, "fg") == 0)
7861 {
7862 if (cterm_normal_fg_color)
7863 color = cterm_normal_fg_color - 1;
7864 else
7865 {
7866 EMSG(_("E419: FG color unknown"));
7867 error = TRUE;
7868 break;
7869 }
7870 }
7871 else if (STRICMP(arg, "bg") == 0)
7872 {
7873 if (cterm_normal_bg_color > 0)
7874 color = cterm_normal_bg_color - 1;
7875 else
7876 {
7877 EMSG(_("E420: BG color unknown"));
7878 error = TRUE;
7879 break;
7880 }
7881 }
7882 else
7883 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007884 int bold = MAYBE;
7885
Bram Moolenaar071d4272004-06-13 20:20:40 +00007886#if defined(__QNXNTO__)
7887 static int *color_numbers_8_qansi = color_numbers_8;
7888 /* On qnx, the 8 & 16 color arrays are the same */
7889 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7890 color_numbers_8_qansi = color_numbers_16;
7891#endif
7892
7893 /* reduce calls to STRICMP a bit, it can be slow */
7894 off = TOUPPER_ASC(*arg);
7895 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7896 if (off == color_names[i][0]
7897 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7898 break;
7899 if (i < 0)
7900 {
7901 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7902 error = TRUE;
7903 break;
7904 }
7905
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007906 color = lookup_color(i, key[5] == 'F', &bold);
7907
7908 /* set/reset bold attribute to get light foreground
7909 * colors (on some terminals, e.g. "linux") */
7910 if (bold == TRUE)
7911 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007912 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7913 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007914 }
7915 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007916 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007917 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007918
Bram Moolenaarccbab932010-05-13 15:40:30 +02007919 /* Add one to the argument, to avoid zero. Zero is used for
7920 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007921 if (key[5] == 'F')
7922 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007923 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007924 if (is_normal_group)
7925 {
7926 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007927 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007928#ifdef FEAT_GUI
7929 /* Don't do this if the GUI is used. */
7930 if (!gui.in_use && !gui.starting)
7931#endif
7932 {
7933 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007934 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007935 term_fg_color(color);
7936 }
7937 }
7938 }
7939 else
7940 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007941 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007942 if (is_normal_group)
7943 {
7944 cterm_normal_bg_color = color + 1;
7945#ifdef FEAT_GUI
7946 /* Don't mess with 'background' if the GUI is used. */
7947 if (!gui.in_use && !gui.starting)
7948#endif
7949 {
7950 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007951 if (color >= 0)
7952 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007953 int dark = -1;
7954
Bram Moolenaarccbab932010-05-13 15:40:30 +02007955 if (termcap_active)
7956 term_bg_color(color);
7957 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007958 dark = (color == 0 || color == 4);
7959 /* Limit the heuristic to the standard 16 colors */
7960 else if (color < 16)
7961 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007962 /* Set the 'background' option if the value is
7963 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007964 if (dark != -1
7965 && dark != (*p_bg == 'd')
7966 && !option_was_set((char_u *)"bg"))
7967 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007968 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007969 (char_u *)(dark ? "dark" : "light"), 0);
7970 reset_option_was_set((char_u *)"bg");
7971 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007972 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007973 }
7974 }
7975 }
7976 }
7977 }
7978 else if (STRCMP(key, "GUIFG") == 0)
7979 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007980#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007981 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7982
Bram Moolenaar414168d2017-09-10 15:21:55 +02007983 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007984 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007985 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007986 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007987
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007988# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007989 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007990 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007991 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007992 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007993 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007994# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02007995 if (*namep == NULL || STRCMP(*namep, arg) != 0)
7996 {
7997 vim_free(*namep);
7998 if (STRCMP(arg, "NONE") != 0)
7999 *namep = vim_strsave(arg);
8000 else
8001 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008002 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008003 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008004# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008005# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008006 if (is_menu_group && gui.menu_fg_pixel != i)
8007 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008008 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008009 do_colors = TRUE;
8010 }
8011 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8012 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008013 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008014 do_colors = TRUE;
8015 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008016# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008017 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8018 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008019 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008020 do_colors = TRUE;
8021 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008022# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008023# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008024 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008025# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008026 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008027#endif
8028 }
8029 else if (STRCMP(key, "GUIBG") == 0)
8030 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008031#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008032 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8033
Bram Moolenaar414168d2017-09-10 15:21:55 +02008034 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008035 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008036 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008037 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008038
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008039# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008040 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008041 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008042 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008043 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008044 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008045# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008046 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8047 {
8048 vim_free(*namep);
8049 if (STRCMP(arg, "NONE") != 0)
8050 *namep = vim_strsave(arg);
8051 else
8052 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008053 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008054 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008055# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008056# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008057 if (is_menu_group && gui.menu_bg_pixel != i)
8058 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008059 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008060 do_colors = TRUE;
8061 }
8062 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8063 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008064 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008065 do_colors = TRUE;
8066 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008067# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008068 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8069 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008070 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008071 do_colors = TRUE;
8072 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008073# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008074# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008075 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008076# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008077 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008078#endif
8079 }
8080 else if (STRCMP(key, "GUISP") == 0)
8081 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008082#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008083 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8084
Bram Moolenaar414168d2017-09-10 15:21:55 +02008085 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008086 {
8087 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008088 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008089
Bram Moolenaar61623362010-07-14 22:04:22 +02008090# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008091 i = color_name2handle(arg);
8092 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8093 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008094 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008095# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008096 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8097 {
8098 vim_free(*namep);
8099 if (STRCMP(arg, "NONE") != 0)
8100 *namep = vim_strsave(arg);
8101 else
8102 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008103 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008104 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008105# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008106 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008107# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008108 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109#endif
8110 }
8111 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8112 {
8113 char_u buf[100];
8114 char_u *tname;
8115
8116 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008117 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008118
8119 /*
8120 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008121 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008122 */
8123 if (STRNCMP(arg, "t_", 2) == 0)
8124 {
8125 off = 0;
8126 buf[0] = 0;
8127 while (arg[off] != NUL)
8128 {
8129 /* Isolate one termcap name */
8130 for (len = 0; arg[off + len] &&
8131 arg[off + len] != ','; ++len)
8132 ;
8133 tname = vim_strnsave(arg + off, len);
8134 if (tname == NULL) /* out of memory */
8135 {
8136 error = TRUE;
8137 break;
8138 }
8139 /* lookup the escape sequence for the item */
8140 p = get_term_code(tname);
8141 vim_free(tname);
8142 if (p == NULL) /* ignore non-existing things */
8143 p = (char_u *)"";
8144
8145 /* Append it to the already found stuff */
8146 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8147 {
8148 EMSG2(_("E422: terminal code too long: %s"), arg);
8149 error = TRUE;
8150 break;
8151 }
8152 STRCAT(buf, p);
8153
8154 /* Advance to the next item */
8155 off += len;
8156 if (arg[off] == ',') /* another one follows */
8157 ++off;
8158 }
8159 }
8160 else
8161 {
8162 /*
8163 * Copy characters from arg[] to buf[], translating <> codes.
8164 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008165 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008166 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008167 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008168 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169 off += len;
8170 else /* copy as normal char */
8171 buf[off++] = *p++;
8172 }
8173 buf[off] = NUL;
8174 }
8175 if (error)
8176 break;
8177
8178 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8179 p = NULL;
8180 else
8181 p = vim_strsave(buf);
8182 if (key[2] == 'A')
8183 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008184 vim_free(HL_TABLE()[idx].sg_start);
8185 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008186 }
8187 else
8188 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008189 vim_free(HL_TABLE()[idx].sg_stop);
8190 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008191 }
8192 }
8193 else
8194 {
8195 EMSG2(_("E423: Illegal argument: %s"), key_start);
8196 error = TRUE;
8197 break;
8198 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008199 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200
8201 /*
8202 * When highlighting has been given for a group, don't link it.
8203 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008204 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8205 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008206
8207 /*
8208 * Continue with next argument.
8209 */
8210 linep = skipwhite(linep);
8211 }
8212
8213 /*
8214 * If there is an error, and it's a new entry, remove it from the table.
8215 */
8216 if (error && idx == highlight_ga.ga_len)
8217 syn_unadd_group();
8218 else
8219 {
8220 if (is_normal_group)
8221 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008222 HL_TABLE()[idx].sg_term_attr = 0;
8223 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008224#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008225 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226 /*
8227 * Need to update all groups, because they might be using "bg"
8228 * and/or "fg", which have been changed now.
8229 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008230#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008231#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008232 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008233 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008234 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008235 did_highlight_changed = TRUE;
8236 redraw_all_later(NOT_VALID);
8237 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238#endif
8239 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008240#ifdef FEAT_TERMINAL
8241 else if (is_terminal_group)
8242 set_terminal_default_colors(
8243 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8244#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008245#ifdef FEAT_GUI_X11
8246# ifdef FEAT_MENU
8247 else if (is_menu_group)
8248 {
8249 if (gui.in_use && do_colors)
8250 gui_mch_new_menu_colors();
8251 }
8252# endif
8253 else if (is_scrollbar_group)
8254 {
8255 if (gui.in_use && do_colors)
8256 gui_new_scrollbar_colors();
8257 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008258# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008259 else if (is_tooltip_group)
8260 {
8261 if (gui.in_use && do_colors)
8262 gui_mch_new_tooltip_colors();
8263 }
8264# endif
8265#endif
8266 else
8267 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008268#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02008269 HL_TABLE()[idx].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008270#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008271 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008272
Bram Moolenaar071d4272004-06-13 20:20:40 +00008273 vim_free(key);
8274 vim_free(arg);
8275
Bram Moolenaar99433292017-09-08 12:37:47 +02008276 /* Only call highlight_changed() once, after a sequence of highlight
8277 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008278 if ((did_change
8279 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008280#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8281 && !did_highlight_changed
8282#endif
8283 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008284 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008285 /* Do not trigger a redraw when highlighting is changed while
8286 * redrawing. This may happen when evaluating 'statusline' changes the
8287 * StatusLine group. */
8288 if (!updating_screen)
8289 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008290 need_highlight_changed = TRUE;
8291 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008292}
8293
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008294#if defined(EXITFREE) || defined(PROTO)
8295 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008296free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008297{
8298 int i;
8299
8300 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008301 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008302 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008303 vim_free(HL_TABLE()[i].sg_name);
8304 vim_free(HL_TABLE()[i].sg_name_u);
8305 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008306 ga_clear(&highlight_ga);
8307}
8308#endif
8309
Bram Moolenaar071d4272004-06-13 20:20:40 +00008310/*
8311 * Reset the cterm colors to what they were before Vim was started, if
8312 * possible. Otherwise reset them to zero.
8313 */
8314 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008315restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008316{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008317#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008318 /* Since t_me has been set, this probably means that the user
8319 * wants to use this as default colors. Need to reset default
8320 * background/foreground colors. */
8321 mch_set_normal_colors();
8322#else
8323 cterm_normal_fg_color = 0;
8324 cterm_normal_fg_bold = 0;
8325 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008326# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008327 cterm_normal_fg_gui_color = INVALCOLOR;
8328 cterm_normal_bg_gui_color = INVALCOLOR;
8329# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008330#endif
8331}
8332
8333/*
8334 * Return TRUE if highlight group "idx" has any settings.
8335 * When "check_link" is TRUE also check for an existing link.
8336 */
8337 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008338hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339{
8340 return ( HL_TABLE()[idx].sg_term_attr != 0
8341 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008342 || HL_TABLE()[idx].sg_cterm_fg != 0
8343 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008344#ifdef FEAT_GUI
8345 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008346 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8347 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8348 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008349 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008350#endif
8351 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8352}
8353
8354/*
8355 * Clear highlighting for one group.
8356 */
8357 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008358highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008359{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008360 HL_TABLE()[idx].sg_cleared = TRUE;
8361
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008363 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8364 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008365 HL_TABLE()[idx].sg_term_attr = 0;
8366 HL_TABLE()[idx].sg_cterm = 0;
8367 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8368 HL_TABLE()[idx].sg_cterm_fg = 0;
8369 HL_TABLE()[idx].sg_cterm_bg = 0;
8370 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008371#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008372 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008373 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8374 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8375 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008376#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008377#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008378 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8379 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008380#endif
8381#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008382 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8384 HL_TABLE()[idx].sg_font = NOFONT;
8385# ifdef FEAT_XFONTSET
8386 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8387 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8388# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008389 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008390 HL_TABLE()[idx].sg_gui_attr = 0;
8391#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008392#ifdef FEAT_EVAL
8393 /* Clear the script ID only when there is no link, since that is not
8394 * cleared. */
8395 if (HL_TABLE()[idx].sg_link == 0)
8396 HL_TABLE()[idx].sg_scriptID = 0;
8397#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008398}
8399
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008400#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008401/*
8402 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008403 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008404 * "Tooltip" colors.
8405 */
8406 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008407set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008408{
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008409# ifdef FEAT_GUI
8410# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008411 if (gui.in_use)
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008412# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008413 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008414 if (set_group_colors((char_u *)"Normal",
8415 &gui.norm_pixel, &gui.back_pixel,
8416 FALSE, TRUE, FALSE))
8417 {
8418 gui_mch_new_colors();
8419 must_redraw = CLEAR;
8420 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008421# ifdef FEAT_GUI_X11
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008422 if (set_group_colors((char_u *)"Menu",
8423 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8424 TRUE, FALSE, FALSE))
8425 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008426# ifdef FEAT_MENU
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008427 gui_mch_new_menu_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008428# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008429 must_redraw = CLEAR;
8430 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008431# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008432 if (set_group_colors((char_u *)"Tooltip",
8433 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8434 FALSE, FALSE, TRUE))
8435 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008436# ifdef FEAT_TOOLBAR
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008437 gui_mch_new_tooltip_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008438# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008439 must_redraw = CLEAR;
8440 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008441# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008442 if (set_group_colors((char_u *)"Scrollbar",
8443 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8444 FALSE, FALSE, FALSE))
8445 {
8446 gui_new_scrollbar_colors();
8447 must_redraw = CLEAR;
8448 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008449# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008451# endif
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008452# ifdef FEAT_TERMGUICOLORS
8453# ifdef FEAT_GUI
8454 else
8455# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008456 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008457 int idx;
8458
8459 idx = syn_name2id((char_u *)"Normal") - 1;
8460 if (idx >= 0)
8461 {
8462 gui_do_one_color(idx, FALSE, FALSE);
8463
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008464 /* If the normal fg or bg color changed a complete redraw is
8465 * required. */
8466 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
8467 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008468 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008469 /* if the GUI color is INVALCOLOR then we use the default cterm
8470 * color */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008471 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008472 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8473 must_redraw = CLEAR;
8474 }
8475 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008476 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008477# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008478}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008479#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008480
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008481#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008482/*
8483 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8484 */
8485 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008486set_group_colors(
8487 char_u *name,
8488 guicolor_T *fgp,
8489 guicolor_T *bgp,
8490 int do_menu,
8491 int use_norm,
8492 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008493{
8494 int idx;
8495
8496 idx = syn_name2id(name) - 1;
8497 if (idx >= 0)
8498 {
8499 gui_do_one_color(idx, do_menu, do_tooltip);
8500
8501 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8502 *fgp = HL_TABLE()[idx].sg_gui_fg;
8503 else if (use_norm)
8504 *fgp = gui.def_norm_pixel;
8505 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8506 *bgp = HL_TABLE()[idx].sg_gui_bg;
8507 else if (use_norm)
8508 *bgp = gui.def_back_pixel;
8509 return TRUE;
8510 }
8511 return FALSE;
8512}
8513
8514/*
8515 * Get the font of the "Normal" group.
8516 * Returns "" when it's not found or not set.
8517 */
8518 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008519hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008520{
8521 int id;
8522 char_u *s;
8523
8524 id = syn_name2id((char_u *)"Normal");
8525 if (id > 0)
8526 {
8527 s = HL_TABLE()[id - 1].sg_font_name;
8528 if (s != NULL)
8529 return s;
8530 }
8531 return (char_u *)"";
8532}
8533
8534/*
8535 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8536 * actually chosen to be used.
8537 */
8538 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008539hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008540{
8541 int id;
8542
8543 id = syn_name2id((char_u *)"Normal");
8544 if (id > 0)
8545 {
8546 vim_free(HL_TABLE()[id - 1].sg_font_name);
8547 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8548 }
8549}
8550
8551/*
8552 * Set background color for "Normal" group. Called by gui_set_bg_color()
8553 * when the color is known.
8554 */
8555 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008556hl_set_bg_color_name(
8557 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008558{
8559 int id;
8560
8561 if (name != NULL)
8562 {
8563 id = syn_name2id((char_u *)"Normal");
8564 if (id > 0)
8565 {
8566 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8567 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8568 }
8569 }
8570}
8571
8572/*
8573 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8574 * when the color is known.
8575 */
8576 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008577hl_set_fg_color_name(
8578 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008579{
8580 int id;
8581
8582 if (name != NULL)
8583 {
8584 id = syn_name2id((char_u *)"Normal");
8585 if (id > 0)
8586 {
8587 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8588 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8589 }
8590 }
8591}
8592
8593/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008594 * Return the handle for a font name.
8595 * Returns NOFONT when failed.
8596 */
8597 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008598font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008599{
8600 if (STRCMP(name, "NONE") == 0)
8601 return NOFONT;
8602
8603 return gui_mch_get_font(name, TRUE);
8604}
8605
8606# ifdef FEAT_XFONTSET
8607/*
8608 * Return the handle for a fontset name.
8609 * Returns NOFONTSET when failed.
8610 */
8611 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008612fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613{
8614 if (STRCMP(name, "NONE") == 0)
8615 return NOFONTSET;
8616
8617 return gui_mch_get_fontset(name, TRUE, fixed_width);
8618}
8619# endif
8620
8621/*
8622 * Get the font or fontset for one highlight group.
8623 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008624 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008625hl_do_font(
8626 int idx,
8627 char_u *arg,
8628 int do_normal, /* set normal font */
8629 int do_menu UNUSED, /* set menu font */
8630 int do_tooltip UNUSED, /* set tooltip font */
8631 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008632{
8633# ifdef FEAT_XFONTSET
8634 /* If 'guifontset' is not empty, first try using the name as a
8635 * fontset. If that doesn't work, use it as a font name. */
8636 if (*p_guifontset != NUL
8637# ifdef FONTSET_ALWAYS
8638 || do_menu
8639# endif
8640# ifdef FEAT_BEVAL_TIP
8641 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8642 || do_tooltip
8643# endif
8644 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008645 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008646 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008647 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008648 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8649# ifdef FONTSET_ALWAYS
8650 || do_menu
8651# endif
8652# ifdef FEAT_BEVAL_TIP
8653 || do_tooltip
8654# endif
8655 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008656 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008657 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8658 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008659 /* If it worked and it's the Normal group, use it as the normal
8660 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008661 if (do_normal)
8662 gui_init_font(arg, TRUE);
8663# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8664 if (do_menu)
8665 {
8666# ifdef FONTSET_ALWAYS
8667 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8668# else
8669 /* YIKES! This is a bug waiting to crash the program */
8670 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8671# endif
8672 gui_mch_new_menu_font();
8673 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008674# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008675 if (do_tooltip)
8676 {
8677 /* The Athena widget set cannot currently handle switching between
8678 * displaying a single font and a fontset.
8679 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008680 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681 * XFontStruct is used.
8682 */
8683 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8684 gui_mch_new_tooltip_font();
8685 }
8686# endif
8687# endif
8688 }
8689 else
8690# endif
8691 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008692 if (free_font)
8693 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008694 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8695 /* If it worked and it's the Normal group, use it as the
8696 * normal font. Same for the Menu group. */
8697 if (HL_TABLE()[idx].sg_font != NOFONT)
8698 {
8699 if (do_normal)
8700 gui_init_font(arg, FALSE);
8701#ifndef FONTSET_ALWAYS
8702# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8703 if (do_menu)
8704 {
8705 gui.menu_font = HL_TABLE()[idx].sg_font;
8706 gui_mch_new_menu_font();
8707 }
8708# endif
8709#endif
8710 }
8711 }
8712}
8713
8714#endif /* FEAT_GUI */
8715
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008716#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008717/*
8718 * Return the handle for a color name.
8719 * Returns INVALCOLOR when failed.
8720 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008721 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008722color_name2handle(char_u *name)
8723{
8724 if (STRCMP(name, "NONE") == 0)
8725 return INVALCOLOR;
8726
8727 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8728 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008729#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008730 if (gui.in_use)
8731#endif
8732#ifdef FEAT_GUI
8733 return gui.norm_pixel;
8734#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008735#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008736 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008737 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008738 /* Guess that the foreground is black or white. */
8739 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008740#endif
8741 }
8742 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 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.back_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_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008752 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008753 /* Guess that the background is white or black. */
8754 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008755#endif
8756 }
8757
8758 return GUI_GET_COLOR(name);
8759}
8760#endif
8761
Bram Moolenaar071d4272004-06-13 20:20:40 +00008762/*
8763 * Table with the specifications for an attribute number.
8764 * Note that this table is used by ALL buffers. This is required because the
8765 * GUI can redraw at any time for any buffer.
8766 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008767static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008768
8769#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8770
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008771static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008772
8773#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8774
8775#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008776static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008777
8778#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8779#endif
8780
8781/*
8782 * Return the attr number for a set of colors and font.
8783 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8784 * if the combination is new.
8785 * Return 0 for error (no more room).
8786 */
8787 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008788get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008789{
8790 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008791 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008792 static int recursive = FALSE;
8793
8794 /*
8795 * Init the table, in case it wasn't done yet.
8796 */
8797 table->ga_itemsize = sizeof(attrentry_T);
8798 table->ga_growsize = 7;
8799
8800 /*
8801 * Try to find an entry with the same specifications.
8802 */
8803 for (i = 0; i < table->ga_len; ++i)
8804 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008805 taep = &(((attrentry_T *)table->ga_data)[i]);
8806 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008807 && (
8808#ifdef FEAT_GUI
8809 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008810 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8811 && aep->ae_u.gui.bg_color
8812 == taep->ae_u.gui.bg_color
8813 && aep->ae_u.gui.sp_color
8814 == taep->ae_u.gui.sp_color
8815 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008816# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008817 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008818# endif
8819 ))
8820 ||
8821#endif
8822 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008823 && (aep->ae_u.term.start == NULL)
8824 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008825 && (aep->ae_u.term.start == NULL
8826 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008827 taep->ae_u.term.start) == 0)
8828 && (aep->ae_u.term.stop == NULL)
8829 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008830 && (aep->ae_u.term.stop == NULL
8831 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008832 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008833 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008834 && aep->ae_u.cterm.fg_color
8835 == taep->ae_u.cterm.fg_color
8836 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008837 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008838#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008839 && aep->ae_u.cterm.fg_rgb
8840 == taep->ae_u.cterm.fg_rgb
8841 && aep->ae_u.cterm.bg_rgb
8842 == taep->ae_u.cterm.bg_rgb
8843#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008844 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008845
8846 return i + ATTR_OFF;
8847 }
8848
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008849 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008850 {
8851 /*
8852 * Running out of attribute entries! remove all attributes, and
8853 * compute new ones for all groups.
8854 * When called recursively, we are really out of numbers.
8855 */
8856 if (recursive)
8857 {
8858 EMSG(_("E424: Too many different highlighting attributes in use"));
8859 return 0;
8860 }
8861 recursive = TRUE;
8862
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008863 clear_hl_tables();
8864
Bram Moolenaar071d4272004-06-13 20:20:40 +00008865 must_redraw = CLEAR;
8866
8867 for (i = 0; i < highlight_ga.ga_len; ++i)
8868 set_hl_attr(i);
8869
8870 recursive = FALSE;
8871 }
8872
8873 /*
8874 * This is a new combination of colors and font, add an entry.
8875 */
8876 if (ga_grow(table, 1) == FAIL)
8877 return 0;
8878
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008879 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8880 vim_memset(taep, 0, sizeof(attrentry_T));
8881 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008882#ifdef FEAT_GUI
8883 if (table == &gui_attr_table)
8884 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008885 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8886 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8887 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8888 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008889# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008890 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008891# endif
8892 }
8893#endif
8894 if (table == &term_attr_table)
8895 {
8896 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008897 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008898 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008899 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008900 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008901 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008902 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008903 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008904 }
8905 else if (table == &cterm_attr_table)
8906 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008907 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8908 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008909#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008910 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8911 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8912#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008913 }
8914 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008915 return (table->ga_len - 1 + ATTR_OFF);
8916}
8917
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008918/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008919 * Get an attribute index for a cterm entry.
8920 * Uses an existing entry when possible or adds one when needed.
8921 */
8922 int
8923get_cterm_attr_idx(int attr, int fg, int bg)
8924{
8925 attrentry_T at_en;
8926
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008927 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008928#ifdef FEAT_TERMGUICOLORS
8929 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8930 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8931#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008932 at_en.ae_attr = attr;
8933 at_en.ae_u.cterm.fg_color = fg;
8934 at_en.ae_u.cterm.bg_color = bg;
8935 return get_attr_entry(&cterm_attr_table, &at_en);
8936}
8937
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008938#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8939/*
8940 * Get an attribute index for a 'termguicolors' entry.
8941 * Uses an existing entry when possible or adds one when needed.
8942 */
8943 int
8944get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8945{
8946 attrentry_T at_en;
8947
8948 vim_memset(&at_en, 0, sizeof(attrentry_T));
8949 at_en.ae_attr = attr;
Bram Moolenaard4fc5772018-02-27 14:39:03 +01008950 if (fg == INVALCOLOR && bg == INVALCOLOR)
8951 {
8952 /* If both GUI colors are not set fall back to the cterm colors. Helps
8953 * if the GUI only has an attribute, such as undercurl. */
8954 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
8955 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
8956 }
8957 else
8958 {
8959 at_en.ae_u.cterm.fg_rgb = fg;
8960 at_en.ae_u.cterm.bg_rgb = bg;
8961 }
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008962 return get_attr_entry(&cterm_attr_table, &at_en);
8963}
8964#endif
8965
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008966#if defined(FEAT_GUI) || defined(PROTO)
8967/*
8968 * Get an attribute index for a cterm entry.
8969 * Uses an existing entry when possible or adds one when needed.
8970 */
8971 int
8972get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8973{
8974 attrentry_T at_en;
8975
8976 vim_memset(&at_en, 0, sizeof(attrentry_T));
8977 at_en.ae_attr = attr;
8978 at_en.ae_u.gui.fg_color = fg;
8979 at_en.ae_u.gui.bg_color = bg;
8980 return get_attr_entry(&gui_attr_table, &at_en);
8981}
8982#endif
8983
Bram Moolenaareeac6772017-07-23 15:48:37 +02008984/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008985 * Clear all highlight tables.
8986 */
8987 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008988clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008989{
8990 int i;
8991 attrentry_T *taep;
8992
8993#ifdef FEAT_GUI
8994 ga_clear(&gui_attr_table);
8995#endif
8996 for (i = 0; i < term_attr_table.ga_len; ++i)
8997 {
8998 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8999 vim_free(taep->ae_u.term.start);
9000 vim_free(taep->ae_u.term.stop);
9001 }
9002 ga_clear(&term_attr_table);
9003 ga_clear(&cterm_attr_table);
9004}
9005
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009006#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009007/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00009008 * Combine special attributes (e.g., for spelling) with other attributes
9009 * (e.g., for syntax highlighting).
9010 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00009011 * This creates a new group when required.
9012 * Since we expect there to be few spelling mistakes we don't cache the
9013 * result.
9014 * Return the resulting attributes.
9015 */
9016 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009017hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009018{
9019 attrentry_T *char_aep = NULL;
9020 attrentry_T *spell_aep;
9021 attrentry_T new_en;
9022
9023 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00009024 return prim_attr;
9025 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009026 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009027#ifdef FEAT_GUI
9028 if (gui.in_use)
9029 {
9030 if (char_attr > HL_ALL)
9031 char_aep = syn_gui_attr2entry(char_attr);
9032 if (char_aep != NULL)
9033 new_en = *char_aep;
9034 else
9035 {
9036 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009037 new_en.ae_u.gui.fg_color = INVALCOLOR;
9038 new_en.ae_u.gui.bg_color = INVALCOLOR;
9039 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009040 if (char_attr <= HL_ALL)
9041 new_en.ae_attr = char_attr;
9042 }
9043
Bram Moolenaar30abd282005-06-22 22:35:10 +00009044 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009045 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009046 else
9047 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009048 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009049 if (spell_aep != NULL)
9050 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009051 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9052 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009053 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9054 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9055 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9056 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9057 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9058 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9059 if (spell_aep->ae_u.gui.font != NOFONT)
9060 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9061# ifdef FEAT_XFONTSET
9062 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9063 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9064# endif
9065 }
9066 }
9067 return get_attr_entry(&gui_attr_table, &new_en);
9068 }
9069#endif
9070
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009071 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009072 {
9073 if (char_attr > HL_ALL)
9074 char_aep = syn_cterm_attr2entry(char_attr);
9075 if (char_aep != NULL)
9076 new_en = *char_aep;
9077 else
9078 {
9079 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009080#ifdef FEAT_TERMGUICOLORS
9081 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9082 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9083#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009084 if (char_attr <= HL_ALL)
9085 new_en.ae_attr = char_attr;
9086 }
9087
Bram Moolenaar30abd282005-06-22 22:35:10 +00009088 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009089 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009090 else
9091 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009092 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009093 if (spell_aep != NULL)
9094 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009095 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9096 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009097 if (spell_aep->ae_u.cterm.fg_color > 0)
9098 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9099 if (spell_aep->ae_u.cterm.bg_color > 0)
9100 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009101#ifdef FEAT_TERMGUICOLORS
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009102 /* If both fg and bg are not set fall back to cterm colors.
9103 * Helps for SpellBad which uses undercurl in the GUI. */
9104 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
9105 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
9106 {
9107 if (spell_aep->ae_u.cterm.fg_color > 0)
9108 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9109 if (spell_aep->ae_u.cterm.bg_color > 0)
9110 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9111 }
9112 else
9113 {
9114 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
9115 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
9116 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
9117 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9118 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009119#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009120 }
9121 }
9122 return get_attr_entry(&cterm_attr_table, &new_en);
9123 }
9124
9125 if (char_attr > HL_ALL)
9126 char_aep = syn_term_attr2entry(char_attr);
9127 if (char_aep != NULL)
9128 new_en = *char_aep;
9129 else
9130 {
9131 vim_memset(&new_en, 0, sizeof(new_en));
9132 if (char_attr <= HL_ALL)
9133 new_en.ae_attr = char_attr;
9134 }
9135
Bram Moolenaar30abd282005-06-22 22:35:10 +00009136 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009137 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009138 else
9139 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009140 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009141 if (spell_aep != NULL)
9142 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009143 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009144 if (spell_aep->ae_u.term.start != NULL)
9145 {
9146 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9147 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9148 }
9149 }
9150 }
9151 return get_attr_entry(&term_attr_table, &new_en);
9152}
9153#endif
9154
Bram Moolenaar071d4272004-06-13 20:20:40 +00009155#ifdef FEAT_GUI
9156
9157 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009158syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009159{
9160 attr -= ATTR_OFF;
9161 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9162 return NULL;
9163 return &(GUI_ATTR_ENTRY(attr));
9164}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009165#endif /* FEAT_GUI */
9166
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009167/*
9168 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9169 * Only to be used when "attr" > HL_ALL.
9170 */
9171 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009172syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009173{
9174 attrentry_T *aep;
9175
9176#ifdef FEAT_GUI
9177 if (gui.in_use)
9178 aep = syn_gui_attr2entry(attr);
9179 else
9180#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009181 if (IS_CTERM)
9182 aep = syn_cterm_attr2entry(attr);
9183 else
9184 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009185
9186 if (aep == NULL) /* highlighting not set */
9187 return 0;
9188 return aep->ae_attr;
9189}
9190
9191
Bram Moolenaar071d4272004-06-13 20:20:40 +00009192 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009193syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009194{
9195 attr -= ATTR_OFF;
9196 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9197 return NULL;
9198 return &(TERM_ATTR_ENTRY(attr));
9199}
9200
9201 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009202syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009203{
9204 attr -= ATTR_OFF;
9205 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9206 return NULL;
9207 return &(CTERM_ATTR_ENTRY(attr));
9208}
9209
9210#define LIST_ATTR 1
9211#define LIST_STRING 2
9212#define LIST_INT 3
9213
9214 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009215highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009216{
9217 struct hl_group *sgp;
9218 int didh = FALSE;
9219
9220 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9221
9222 didh = highlight_list_arg(id, didh, LIST_ATTR,
9223 sgp->sg_term, NULL, "term");
9224 didh = highlight_list_arg(id, didh, LIST_STRING,
9225 0, sgp->sg_start, "start");
9226 didh = highlight_list_arg(id, didh, LIST_STRING,
9227 0, sgp->sg_stop, "stop");
9228
9229 didh = highlight_list_arg(id, didh, LIST_ATTR,
9230 sgp->sg_cterm, NULL, "cterm");
9231 didh = highlight_list_arg(id, didh, LIST_INT,
9232 sgp->sg_cterm_fg, NULL, "ctermfg");
9233 didh = highlight_list_arg(id, didh, LIST_INT,
9234 sgp->sg_cterm_bg, NULL, "ctermbg");
9235
Bram Moolenaar61623362010-07-14 22:04:22 +02009236#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009237 didh = highlight_list_arg(id, didh, LIST_ATTR,
9238 sgp->sg_gui, NULL, "gui");
9239 didh = highlight_list_arg(id, didh, LIST_STRING,
9240 0, sgp->sg_gui_fg_name, "guifg");
9241 didh = highlight_list_arg(id, didh, LIST_STRING,
9242 0, sgp->sg_gui_bg_name, "guibg");
9243 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009244 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009245#endif
9246#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009247 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009248 0, sgp->sg_font_name, "font");
9249#endif
9250
Bram Moolenaar661b1822005-07-28 22:36:45 +00009251 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009252 {
9253 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009254 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009255 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009256 msg_putchar(' ');
9257 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9258 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009259
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009260 if (!didh)
9261 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009262#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009263 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009264 last_set_msg(sgp->sg_scriptID);
9265#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009266}
9267
9268 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009269highlight_list_arg(
9270 int id,
9271 int didh,
9272 int type,
9273 int iarg,
9274 char_u *sarg,
9275 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009276{
9277 char_u buf[100];
9278 char_u *ts;
9279 int i;
9280
Bram Moolenaar661b1822005-07-28 22:36:45 +00009281 if (got_int)
9282 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009283 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9284 {
9285 ts = buf;
9286 if (type == LIST_INT)
9287 sprintf((char *)buf, "%d", iarg - 1);
9288 else if (type == LIST_STRING)
9289 ts = sarg;
9290 else /* type == LIST_ATTR */
9291 {
9292 buf[0] = NUL;
9293 for (i = 0; hl_attr_table[i] != 0; ++i)
9294 {
9295 if (iarg & hl_attr_table[i])
9296 {
9297 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009298 vim_strcat(buf, (char_u *)",", 100);
9299 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009300 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9301 }
9302 }
9303 }
9304
9305 (void)syn_list_header(didh,
9306 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9307 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009308 if (!got_int)
9309 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009310 if (*name != NUL)
9311 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009312 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9313 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009314 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009315 msg_outtrans(ts);
9316 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 }
9318 return didh;
9319}
9320
9321#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9322/*
9323 * Return "1" if highlight group "id" has attribute "flag".
9324 * Return NULL otherwise.
9325 */
9326 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009327highlight_has_attr(
9328 int id,
9329 int flag,
9330 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009331{
9332 int attr;
9333
9334 if (id <= 0 || id > highlight_ga.ga_len)
9335 return NULL;
9336
Bram Moolenaar61623362010-07-14 22:04:22 +02009337#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009338 if (modec == 'g')
9339 attr = HL_TABLE()[id - 1].sg_gui;
9340 else
9341#endif
9342 if (modec == 'c')
9343 attr = HL_TABLE()[id - 1].sg_cterm;
9344 else
9345 attr = HL_TABLE()[id - 1].sg_term;
9346
9347 if (attr & flag)
9348 return (char_u *)"1";
9349 return NULL;
9350}
9351#endif
9352
9353#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9354/*
9355 * Return color name of highlight group "id".
9356 */
9357 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009358highlight_color(
9359 int id,
9360 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9361 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009362{
9363 static char_u name[20];
9364 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009365 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009366 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009367 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009368
9369 if (id <= 0 || id > highlight_ga.ga_len)
9370 return NULL;
9371
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009372 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009373 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009374 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009375 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009376 font = TRUE;
9377 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009378 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009379 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9380 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009381 if (modec == 'g')
9382 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009383# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009384# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009385 /* return font name */
9386 if (font)
9387 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009388# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009389
Bram Moolenaar071d4272004-06-13 20:20:40 +00009390 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009391 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009392 {
9393 guicolor_T color;
9394 long_u rgb;
9395 static char_u buf[10];
9396
9397 if (fg)
9398 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009399 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009400# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009401 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009402# else
9403 color = INVALCOLOR;
9404# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009405 else
9406 color = HL_TABLE()[id - 1].sg_gui_bg;
9407 if (color == INVALCOLOR)
9408 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009409 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009410 sprintf((char *)buf, "#%02x%02x%02x",
9411 (unsigned)(rgb >> 16),
9412 (unsigned)(rgb >> 8) & 255,
9413 (unsigned)rgb & 255);
9414 return buf;
9415 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009416# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009417 if (fg)
9418 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009419 if (sp)
9420 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009421 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9422 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009423 if (font || sp)
9424 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009425 if (modec == 'c')
9426 {
9427 if (fg)
9428 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9429 else
9430 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009431 if (n < 0)
9432 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009433 sprintf((char *)name, "%d", n);
9434 return name;
9435 }
9436 /* term doesn't have color */
9437 return NULL;
9438}
9439#endif
9440
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009441#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009442 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009443 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009444/*
9445 * Return color name of highlight group "id" as RGB value.
9446 */
9447 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009448highlight_gui_color_rgb(
9449 int id,
9450 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009451{
9452 guicolor_T color;
9453
9454 if (id <= 0 || id > highlight_ga.ga_len)
9455 return 0L;
9456
9457 if (fg)
9458 color = HL_TABLE()[id - 1].sg_gui_fg;
9459 else
9460 color = HL_TABLE()[id - 1].sg_gui_bg;
9461
9462 if (color == INVALCOLOR)
9463 return 0L;
9464
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009465 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009466}
9467#endif
9468
9469/*
9470 * Output the syntax list header.
9471 * Return TRUE when started a new line.
9472 */
9473 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009474syn_list_header(
9475 int did_header, /* did header already */
9476 int outlen, /* length of string that comes */
9477 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009478{
9479 int endcol = 19;
9480 int newline = TRUE;
9481
9482 if (!did_header)
9483 {
9484 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009485 if (got_int)
9486 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009487 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9488 endcol = 15;
9489 }
9490 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009491 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009492 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009493 if (got_int)
9494 return TRUE;
9495 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009496 else
9497 {
9498 if (msg_col >= endcol) /* wrap around is like starting a new line */
9499 newline = FALSE;
9500 }
9501
9502 if (msg_col >= endcol) /* output at least one space */
9503 endcol = msg_col + 1;
9504 if (Columns <= endcol) /* avoid hang for tiny window */
9505 endcol = Columns - 1;
9506
9507 msg_advance(endcol);
9508
9509 /* Show "xxx" with the attributes. */
9510 if (!did_header)
9511 {
9512 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9513 msg_putchar(' ');
9514 }
9515
9516 return newline;
9517}
9518
9519/*
9520 * Set the attribute numbers for a highlight group.
9521 * Called after one of the attributes has changed.
9522 */
9523 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009524set_hl_attr(
9525 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009526{
9527 attrentry_T at_en;
9528 struct hl_group *sgp = HL_TABLE() + idx;
9529
9530 /* The "Normal" group doesn't need an attribute number */
9531 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9532 return;
9533
9534#ifdef FEAT_GUI
9535 /*
9536 * For the GUI mode: If there are other than "normal" highlighting
9537 * attributes, need to allocate an attr number.
9538 */
9539 if (sgp->sg_gui_fg == INVALCOLOR
9540 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009541 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009542 && sgp->sg_font == NOFONT
9543# ifdef FEAT_XFONTSET
9544 && sgp->sg_fontset == NOFONTSET
9545# endif
9546 )
9547 {
9548 sgp->sg_gui_attr = sgp->sg_gui;
9549 }
9550 else
9551 {
9552 at_en.ae_attr = sgp->sg_gui;
9553 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9554 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009555 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009556 at_en.ae_u.gui.font = sgp->sg_font;
9557# ifdef FEAT_XFONTSET
9558 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9559# endif
9560 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9561 }
9562#endif
9563 /*
9564 * For the term mode: If there are other than "normal" highlighting
9565 * attributes, need to allocate an attr number.
9566 */
9567 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9568 sgp->sg_term_attr = sgp->sg_term;
9569 else
9570 {
9571 at_en.ae_attr = sgp->sg_term;
9572 at_en.ae_u.term.start = sgp->sg_start;
9573 at_en.ae_u.term.stop = sgp->sg_stop;
9574 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9575 }
9576
9577 /*
9578 * For the color term mode: If there are other than "normal"
9579 * highlighting attributes, need to allocate an attr number.
9580 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009581 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009582# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009583 && sgp->sg_gui_fg == INVALCOLOR
9584 && sgp->sg_gui_bg == INVALCOLOR
9585# endif
9586 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009587 sgp->sg_cterm_attr = sgp->sg_cterm;
9588 else
9589 {
9590 at_en.ae_attr = sgp->sg_cterm;
9591 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9592 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009593# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009594# ifdef WIN3264
9595 {
9596 int id;
9597 guicolor_T fg, bg;
9598
9599 id = syn_name2id((char_u *)"Normal");
9600 if (id > 0)
9601 {
9602 syn_id2colors(id, &fg, &bg);
9603 if (sgp->sg_gui_fg == INVALCOLOR)
9604 sgp->sg_gui_fg = fg;
9605 if (sgp->sg_gui_bg == INVALCOLOR)
9606 sgp->sg_gui_bg = bg;
9607 }
9608
9609 }
9610# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009611 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9612 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaard4fc5772018-02-27 14:39:03 +01009613 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
9614 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
9615 {
9616 /* If both fg and bg are invalid fall back to the cterm colors.
9617 * Helps when the GUI only uses an attribute, e.g. undercurl. */
9618 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
9619 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
9620 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009621# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009622 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9623 }
9624}
9625
9626/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009627 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009628 * If it is not found, 0 is returned.
9629 */
9630 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009631syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009632{
9633 int i;
9634 char_u name_u[200];
9635
9636 /* Avoid using stricmp() too much, it's slow on some systems */
9637 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9638 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009639 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009640 vim_strup(name_u);
9641 for (i = highlight_ga.ga_len; --i >= 0; )
9642 if (HL_TABLE()[i].sg_name_u != NULL
9643 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9644 break;
9645 return i + 1;
9646}
9647
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009648/*
9649 * Lookup a highlight group name and return its attributes.
9650 * Return zero if not found.
9651 */
9652 int
9653syn_name2attr(char_u *name)
9654{
9655 int id = syn_name2id(name);
9656
9657 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009658 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009659 return 0;
9660}
9661
Bram Moolenaar071d4272004-06-13 20:20:40 +00009662#if defined(FEAT_EVAL) || defined(PROTO)
9663/*
9664 * Return TRUE if highlight group "name" exists.
9665 */
9666 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009667highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009668{
9669 return (syn_name2id(name) > 0);
9670}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009671
9672# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9673/*
9674 * Return the name of highlight group "id".
9675 * When not a valid ID return an empty string.
9676 */
9677 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009678syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009679{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009680 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009681 return (char_u *)"";
9682 return HL_TABLE()[id - 1].sg_name;
9683}
9684# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009685#endif
9686
9687/*
9688 * Like syn_name2id(), but take a pointer + length argument.
9689 */
9690 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009691syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009692{
9693 char_u *name;
9694 int id = 0;
9695
9696 name = vim_strnsave(linep, len);
9697 if (name != NULL)
9698 {
9699 id = syn_name2id(name);
9700 vim_free(name);
9701 }
9702 return id;
9703}
9704
9705/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009706 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009707 * The argument is a pointer to the name and the length of the name.
9708 * If it doesn't exist yet, a new entry is created.
9709 * Return 0 for failure.
9710 */
9711 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009712syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009713{
9714 int id;
9715 char_u *name;
9716
9717 name = vim_strnsave(pp, len);
9718 if (name == NULL)
9719 return 0;
9720
9721 id = syn_name2id(name);
9722 if (id == 0) /* doesn't exist yet */
9723 id = syn_add_group(name);
9724 else
9725 vim_free(name);
9726 return id;
9727}
9728
9729/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009730 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009731 * "name" must be an allocated string, it will be consumed.
9732 * Return 0 for failure.
9733 */
9734 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009735syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009736{
9737 char_u *p;
9738
9739 /* Check that the name is ASCII letters, digits and underscore. */
9740 for (p = name; *p != NUL; ++p)
9741 {
9742 if (!vim_isprintc(*p))
9743 {
9744 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009745 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009746 return 0;
9747 }
9748 else if (!ASCII_ISALNUM(*p) && *p != '_')
9749 {
9750 /* This is an error, but since there previously was no check only
9751 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009752 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009753 MSG(_("W18: Invalid character in group name"));
9754 break;
9755 }
9756 }
9757
9758 /*
9759 * First call for this growarray: init growing array.
9760 */
9761 if (highlight_ga.ga_data == NULL)
9762 {
9763 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9764 highlight_ga.ga_growsize = 10;
9765 }
9766
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009767 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009768 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009769 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009770 vim_free(name);
9771 return 0;
9772 }
9773
Bram Moolenaar071d4272004-06-13 20:20:40 +00009774 /*
9775 * Make room for at least one other syntax_highlight entry.
9776 */
9777 if (ga_grow(&highlight_ga, 1) == FAIL)
9778 {
9779 vim_free(name);
9780 return 0;
9781 }
9782
9783 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9784 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9785 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009786#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009787 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9788 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009789# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009790 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009791# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009792#endif
9793 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009794
9795 return highlight_ga.ga_len; /* ID is index plus one */
9796}
9797
9798/*
9799 * When, just after calling syn_add_group(), an error is discovered, this
9800 * function deletes the new name.
9801 */
9802 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009803syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009804{
9805 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009806 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9807 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9808}
9809
9810/*
9811 * Translate a group ID to highlight attributes.
9812 */
9813 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009814syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009815{
9816 int attr;
9817 struct hl_group *sgp;
9818
9819 hl_id = syn_get_final_id(hl_id);
9820 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9821
9822#ifdef FEAT_GUI
9823 /*
9824 * Only use GUI attr when the GUI is being used.
9825 */
9826 if (gui.in_use)
9827 attr = sgp->sg_gui_attr;
9828 else
9829#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009830 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009831 attr = sgp->sg_cterm_attr;
9832 else
9833 attr = sgp->sg_term_attr;
9834
9835 return attr;
9836}
9837
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009838#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009839/*
9840 * Get the GUI colors and attributes for a group ID.
9841 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9842 */
9843 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009844syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009845{
9846 struct hl_group *sgp;
9847
9848 hl_id = syn_get_final_id(hl_id);
9849 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9850
9851 *fgp = sgp->sg_gui_fg;
9852 *bgp = sgp->sg_gui_bg;
9853 return sgp->sg_gui;
9854}
9855#endif
9856
Bram Moolenaard371bbe2017-09-28 22:35:25 +02009857#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009858 void
9859syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9860{
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 *fgp = sgp->sg_cterm_fg - 1;
9866 *bgp = sgp->sg_cterm_bg - 1;
9867}
9868#endif
9869
Bram Moolenaar071d4272004-06-13 20:20:40 +00009870/*
9871 * Translate a group ID to the final group ID (following links).
9872 */
9873 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009874syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009875{
9876 int count;
9877 struct hl_group *sgp;
9878
9879 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9880 return 0; /* Can be called from eval!! */
9881
9882 /*
9883 * Follow links until there is no more.
9884 * Look out for loops! Break after 100 links.
9885 */
9886 for (count = 100; --count >= 0; )
9887 {
9888 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9889 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9890 break;
9891 hl_id = sgp->sg_link;
9892 }
9893
9894 return hl_id;
9895}
9896
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009897#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009898/*
9899 * Call this function just after the GUI has started.
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009900 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009901 * It finds the font and color handles for the highlighting groups.
9902 */
9903 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009904highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009905{
9906 int idx;
9907
9908 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009909 if (USE_24BIT)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009910 set_normal_colors();
Bram Moolenaar071d4272004-06-13 20:20:40 +00009911
9912 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9913 gui_do_one_color(idx, FALSE, FALSE);
9914
9915 highlight_changed();
9916}
9917
9918 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009919gui_do_one_color(
9920 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009921 int do_menu UNUSED, /* TRUE: might set the menu font */
9922 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009923{
9924 int didit = FALSE;
9925
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009926# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009927# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009928 if (gui.in_use)
9929# endif
9930 if (HL_TABLE()[idx].sg_font_name != NULL)
9931 {
9932 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009933 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009934 didit = TRUE;
9935 }
9936# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009937 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9938 {
9939 HL_TABLE()[idx].sg_gui_fg =
9940 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9941 didit = TRUE;
9942 }
9943 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9944 {
9945 HL_TABLE()[idx].sg_gui_bg =
9946 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9947 didit = TRUE;
9948 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009949# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009950 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9951 {
9952 HL_TABLE()[idx].sg_gui_sp =
9953 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9954 didit = TRUE;
9955 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009956# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009957 if (didit) /* need to get a new attr number */
9958 set_hl_attr(idx);
9959}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009960#endif
9961
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009962#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9963/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009964 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009965 */
9966 static void
9967combine_stl_hlt(
9968 int id,
9969 int id_S,
9970 int id_alt,
9971 int hlcnt,
9972 int i,
9973 int hlf,
9974 int *table)
9975{
9976 struct hl_group *hlt = HL_TABLE();
9977
9978 if (id_alt == 0)
9979 {
9980 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9981 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9982 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9983# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9984 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9985# endif
9986 }
9987 else
9988 mch_memmove(&hlt[hlcnt + i],
9989 &hlt[id_alt - 1],
9990 sizeof(struct hl_group));
9991 hlt[hlcnt + i].sg_link = 0;
9992
9993 hlt[hlcnt + i].sg_term ^=
9994 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9995 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9996 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9997 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9998 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9999 hlt[hlcnt + i].sg_cterm ^=
10000 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
10001 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
10002 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
10003 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
10004 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
10005# if defined(FEAT_GUI) || defined(FEAT_EVAL)
10006 hlt[hlcnt + i].sg_gui ^=
10007 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
10008# endif
10009# ifdef FEAT_GUI
10010 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
10011 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
10012 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
10013 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
10014 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
10015 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
10016 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
10017 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
10018# ifdef FEAT_XFONTSET
10019 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
10020 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
10021# endif
10022# endif
10023 highlight_ga.ga_len = hlcnt + i + 1;
10024 set_hl_attr(hlcnt + i); /* At long last we can apply */
10025 table[i] = syn_id2attr(hlcnt + i + 1);
10026}
10027#endif
10028
Bram Moolenaar071d4272004-06-13 20:20:40 +000010029/*
10030 * Translate the 'highlight' option into attributes in highlight_attr[] and
10031 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
10032 * corresponding highlights to use on top of HLF_SNC is computed.
10033 * Called only when the 'highlight' option has been changed and upon first
10034 * screen redraw after any :highlight command.
10035 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10036 */
10037 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010038highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010039{
10040 int hlf;
10041 int i;
10042 char_u *p;
10043 int attr;
10044 char_u *end;
10045 int id;
10046#ifdef USER_HIGHLIGHT
10047 char_u userhl[10];
10048# ifdef FEAT_STL_OPT
Bram Moolenaar071d4272004-06-13 20:20:40 +000010049 int id_S = -1;
Bram Moolenaar61859032018-03-20 13:00:25 +010010050 int id_SNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010051# ifdef FEAT_TERMINAL
Bram Moolenaar61859032018-03-20 13:00:25 +010010052 int id_ST = 0;
10053 int id_STNC = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010054# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010055 int hlcnt;
10056# endif
10057#endif
10058 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10059
10060 need_highlight_changed = FALSE;
10061
10062 /*
10063 * Clear all attributes.
10064 */
10065 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10066 highlight_attr[hlf] = 0;
10067
10068 /*
10069 * First set all attributes to their default value.
10070 * Then use the attributes from the 'highlight' option.
10071 */
10072 for (i = 0; i < 2; ++i)
10073 {
10074 if (i)
10075 p = p_hl;
10076 else
10077 p = get_highlight_default();
10078 if (p == NULL) /* just in case */
10079 continue;
10080
10081 while (*p)
10082 {
10083 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10084 if (hl_flags[hlf] == *p)
10085 break;
10086 ++p;
10087 if (hlf == (int)HLF_COUNT || *p == NUL)
10088 return FAIL;
10089
10090 /*
10091 * Allow several hl_flags to be combined, like "bu" for
10092 * bold-underlined.
10093 */
10094 attr = 0;
10095 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10096 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010097 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010098 continue;
10099
10100 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10101 return FAIL;
10102
10103 switch (*p)
10104 {
10105 case 'b': attr |= HL_BOLD;
10106 break;
10107 case 'i': attr |= HL_ITALIC;
10108 break;
10109 case '-':
10110 case 'n': /* no highlighting */
10111 break;
10112 case 'r': attr |= HL_INVERSE;
10113 break;
10114 case 's': attr |= HL_STANDOUT;
10115 break;
10116 case 'u': attr |= HL_UNDERLINE;
10117 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010118 case 'c': attr |= HL_UNDERCURL;
10119 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010120 case 't': attr |= HL_STRIKETHROUGH;
10121 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010122 case ':': ++p; /* highlight group name */
10123 if (attr || *p == NUL) /* no combinations */
10124 return FAIL;
10125 end = vim_strchr(p, ',');
10126 if (end == NULL)
10127 end = p + STRLEN(p);
10128 id = syn_check_group(p, (int)(end - p));
10129 if (id == 0)
10130 return FAIL;
10131 attr = syn_id2attr(id);
10132 p = end - 1;
10133#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10134 if (hlf == (int)HLF_SNC)
10135 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010136# ifdef FEAT_TERMINAL
10137 else if (hlf == (int)HLF_ST)
10138 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010139 else if (hlf == (int)HLF_STNC)
10140 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010141# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010142 else if (hlf == (int)HLF_S)
10143 id_S = syn_get_final_id(id);
10144#endif
10145 break;
10146 default: return FAIL;
10147 }
10148 }
10149 highlight_attr[hlf] = attr;
10150
10151 p = skip_to_option_part(p); /* skip comma and spaces */
10152 }
10153 }
10154
10155#ifdef USER_HIGHLIGHT
10156 /* Setup the user highlights
10157 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010158 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010159 * 9 for User1-User9 combined with StatusLineNC
10160 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010161 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010162 * 1 for StatusLine default
10163 * Have to be in there simultaneously in case of table overflows in
10164 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010165 */
10166# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010167 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010168 return FAIL;
10169 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010170 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010171 {
10172 /* Make sure id_S is always valid to simplify code below. Use the last
10173 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010174 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010175 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10176 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010177 }
10178# endif
10179 for (i = 0; i < 9; i++)
10180 {
10181 sprintf((char *)userhl, "User%d", i + 1);
10182 id = syn_name2id(userhl);
10183 if (id == 0)
10184 {
10185 highlight_user[i] = 0;
10186# ifdef FEAT_STL_OPT
10187 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010188# ifdef FEAT_TERMINAL
10189 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010190 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010191# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010192# endif
10193 }
10194 else
10195 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010196 highlight_user[i] = syn_id2attr(id);
10197# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010198 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10199 HLF_SNC, highlight_stlnc);
10200# ifdef FEAT_TERMINAL
10201 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10202 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010203 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10204 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010205# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010206# endif
10207 }
10208 }
10209# ifdef FEAT_STL_OPT
10210 highlight_ga.ga_len = hlcnt;
10211# endif
10212
10213#endif /* USER_HIGHLIGHT */
10214
10215 return OK;
10216}
10217
Bram Moolenaar4f688582007-07-24 12:34:30 +000010218#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010219
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010220static void highlight_list(void);
10221static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010222
10223/*
10224 * Handle command line completion for :highlight command.
10225 */
10226 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010227set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010228{
10229 char_u *p;
10230
10231 /* Default: expand group names */
10232 xp->xp_context = EXPAND_HIGHLIGHT;
10233 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010234 include_link = 2;
10235 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010236
10237 /* (part of) subcommand already typed */
10238 if (*arg != NUL)
10239 {
10240 p = skiptowhite(arg);
10241 if (*p != NUL) /* past "default" or group name */
10242 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010243 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010244 if (STRNCMP("default", arg, p - arg) == 0)
10245 {
10246 arg = skipwhite(p);
10247 xp->xp_pattern = arg;
10248 p = skiptowhite(arg);
10249 }
10250 if (*p != NUL) /* past group name */
10251 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010252 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010253 if (arg[1] == 'i' && arg[0] == 'N')
10254 highlight_list();
10255 if (STRNCMP("link", arg, p - arg) == 0
10256 || STRNCMP("clear", arg, p - arg) == 0)
10257 {
10258 xp->xp_pattern = skipwhite(p);
10259 p = skiptowhite(xp->xp_pattern);
10260 if (*p != NUL) /* past first group name */
10261 {
10262 xp->xp_pattern = skipwhite(p);
10263 p = skiptowhite(xp->xp_pattern);
10264 }
10265 }
10266 if (*p != NUL) /* past group name(s) */
10267 xp->xp_context = EXPAND_NOTHING;
10268 }
10269 }
10270 }
10271}
10272
10273/*
10274 * List highlighting matches in a nice way.
10275 */
10276 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010277highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010278{
10279 int i;
10280
10281 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010282 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010283 for (i = 40; --i >= 0; )
10284 highlight_list_two(99, 0);
10285}
10286
10287 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010288highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010289{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010290 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010291 msg_clr_eos();
10292 out_flush();
10293 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10294}
10295
10296#endif /* FEAT_CMDL_COMPL */
10297
10298#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10299 || defined(FEAT_SIGNS) || defined(PROTO)
10300/*
10301 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010302 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010303 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010304get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010305{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010306 return get_highlight_name_ext(xp, idx, TRUE);
10307}
10308
10309/*
10310 * Obtain a highlight group name.
10311 * When "skip_cleared" is TRUE don't return a cleared entry.
10312 */
10313 char_u *
10314get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10315{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010316 if (idx < 0)
10317 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010318
10319 /* Items are never removed from the table, skip the ones that were
10320 * cleared. */
10321 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10322 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010323
Bram Moolenaar071d4272004-06-13 20:20:40 +000010324#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010325 if (idx == highlight_ga.ga_len && include_none != 0)
10326 return (char_u *)"none";
10327 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010328 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010329 if (idx == highlight_ga.ga_len + include_none + include_default
10330 && include_link != 0)
10331 return (char_u *)"link";
10332 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10333 && include_link != 0)
10334 return (char_u *)"clear";
10335#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010336 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010337 return NULL;
10338 return HL_TABLE()[idx].sg_name;
10339}
10340#endif
10341
Bram Moolenaar4f688582007-07-24 12:34:30 +000010342#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010343/*
10344 * Free all the highlight group fonts.
10345 * Used when quitting for systems which need it.
10346 */
10347 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010348free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010349{
10350 int idx;
10351
10352 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10353 {
10354 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10355 HL_TABLE()[idx].sg_font = NOFONT;
10356# ifdef FEAT_XFONTSET
10357 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10358 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10359# endif
10360 }
10361
10362 gui_mch_free_font(gui.norm_font);
10363# ifdef FEAT_XFONTSET
10364 gui_mch_free_fontset(gui.fontset);
10365# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010366# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010367 gui_mch_free_font(gui.bold_font);
10368 gui_mch_free_font(gui.ital_font);
10369 gui_mch_free_font(gui.boldital_font);
10370# endif
10371}
10372#endif
10373
10374/**************************************
10375 * End of Highlighting stuff *
10376 **************************************/