blob: da2c87684d01052f88c71340c4c88f0f36d011fb [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",
89 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000092
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010093static int get_attr_entry(garray_T *table, attrentry_T *aep);
94static void syn_unadd_group(void);
95static void set_hl_attr(int idx);
96static void highlight_list_one(int id);
97static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
98static int syn_add_group(char_u *name);
99static int syn_list_header(int did_header, int outlen, int id);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100105static guicolor_T color_name2handle(char_u *name);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200106#endif
107#ifdef FEAT_GUI
108static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100109static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100111static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100113static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#endif
115
116/*
117 * An attribute number is the index in attr_table plus ATTR_OFF.
118 */
119#define ATTR_OFF (HL_ALL + 1)
120
121#if defined(FEAT_SYN_HL) || defined(PROTO)
122
123#define SYN_NAMELEN 50 /* maximum length of a syntax name */
124
125/* different types of offsets that are possible */
126#define SPO_MS_OFF 0 /* match start offset */
127#define SPO_ME_OFF 1 /* match end offset */
128#define SPO_HS_OFF 2 /* highl. start offset */
129#define SPO_HE_OFF 3 /* highl. end offset */
130#define SPO_RS_OFF 4 /* region start offset */
131#define SPO_RE_OFF 5 /* region end offset */
132#define SPO_LC_OFF 6 /* leading context offset */
133#define SPO_COUNT 7
134
135static char *(spo_name_tab[SPO_COUNT]) =
136 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
137
138/*
139 * The patterns that are being searched for are stored in a syn_pattern.
140 * A match item consists of one pattern.
141 * A start/end item consists of n start patterns and m end patterns.
142 * A start/skip/end item consists of n start patterns, one skip pattern and m
143 * end patterns.
144 * For the latter two, the patterns are always consecutive: start-skip-end.
145 *
146 * A character offset can be given for the matched text (_m_start and _m_end)
147 * and for the actually highlighted text (_h_start and _h_end).
148 */
149typedef struct syn_pattern
150{
151 char sp_type; /* see SPTYPE_ defines below */
152 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200153 int sp_flags; /* see HL_ defines below */
154#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200155 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200156#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 struct sp_syn sp_syn; /* struct passed to in_id_list() */
158 short sp_syn_match_id; /* highlight group ID of pattern */
159 char_u *sp_pattern; /* regexp to match, pattern */
160 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200161#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200162 syn_time_T sp_time;
163#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 int sp_ic; /* ignore-case flag for sp_prog */
165 short sp_off_flags; /* see below */
166 int sp_offsets[SPO_COUNT]; /* offsets */
167 short *sp_cont_list; /* cont. group IDs, if non-zero */
168 short *sp_next_list; /* next group IDs, if non-zero */
169 int sp_sync_idx; /* sync item index (syncing only) */
170 int sp_line_id; /* ID of last line where tried */
171 int sp_startcol; /* next match in sp_line_id line */
172} synpat_T;
173
174/* The sp_off_flags are computed like this:
175 * offset from the start of the matched text: (1 << SPO_XX_OFF)
176 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
177 * When both are present, only one is used.
178 */
179
180#define SPTYPE_MATCH 1 /* match keyword with this group ID */
181#define SPTYPE_START 2 /* match a regexp, start of item */
182#define SPTYPE_END 3 /* match a regexp, end of item */
183#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
184
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185
186#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
187
188#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
189
190/*
191 * Flags for b_syn_sync_flags:
192 */
193#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
194#define SF_MATCH 0x02 /* sync by matching a pattern */
195
196#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
197
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198#define MAXKEYWLEN 80 /* maximum length of a keyword */
199
200/*
201 * The attributes of the syntax item that has been recognized.
202 */
203static int current_attr = 0; /* attr of current syntax word */
204#ifdef FEAT_EVAL
205static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000206static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200208#ifdef FEAT_CONCEAL
209static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200210static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200211static int current_sub_char = 0;
212#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215{
216 char_u *scl_name; /* syntax cluster name */
217 char_u *scl_name_u; /* uppercase of scl_name */
218 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000219} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220
221/*
222 * Methods of combining two clusters
223 */
224#define CLUSTER_REPLACE 1 /* replace first list with second */
225#define CLUSTER_ADD 2 /* add second list to first */
226#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
227
Bram Moolenaar217ad922005-03-20 22:37:15 +0000228#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229
230/*
231 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200232 * 0 - 19999 normal syntax groups
233 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
234 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
235 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
236 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200238#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200239#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
240#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
241#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
242
Bram Moolenaar42431a72011-04-01 14:44:59 +0200243#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
244#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245
246/*
247 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
248 * expand_filename(). Most of the other syntax commands don't need it, so
249 * instead of passing it to them, we stow it here.
250 */
251static char_u **syn_cmdlinep;
252
253/*
254 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200255 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 * rules in each ":syn include"'d file.
257 */
258static int current_syn_inc_tag = 0;
259static int running_syn_inc_tag = 0;
260
261/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000262 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
263 * This avoids adding a pointer to the hashtable item.
264 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
265 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
266 * HI2KE() converts a hashitem pointer to a var pointer.
267 */
268static keyentry_T dumkey;
269#define KE2HIKEY(kp) ((kp)->keyword)
270#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
271#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
272
273/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 * To reduce the time spent in keepend(), remember at which level in the state
275 * stack the first item with "keepend" is present. When "-1", there is no
276 * "keepend" on the stack.
277 */
278static int keepend_level = -1;
279
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200280static char msg_no_items[] = N_("No Syntax items defined for this buffer");
281
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282/*
283 * For the current state we need to remember more than just the idx.
284 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
285 * (The end positions have the column number of the next char)
286 */
287typedef struct state_item
288{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000289 int si_idx; /* index of syntax pattern or
290 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000292 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_m_lnum; /* lnum of the match */
294 int si_m_startcol; /* starting column of the match */
295 lpos_T si_m_endpos; /* just after end posn of the match */
296 lpos_T si_h_startpos; /* start position of the highlighting */
297 lpos_T si_h_endpos; /* end position of the highlighting */
298 lpos_T si_eoe_pos; /* end position of end pattern */
299 int si_end_idx; /* group ID for end pattern or zero */
300 int si_ends; /* if match ends before si_m_endpos */
301 int si_attr; /* attributes in this state */
302 long si_flags; /* HL_HAS_EOL flag in this state, and
303 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200304#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200305 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200306 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200307#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 short *si_cont_list; /* list of contained groups */
309 short *si_next_list; /* nextgroup IDs after this item ends */
310 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
311 * pattern */
312} stateitem_T;
313
314#define KEYWORD_IDX -1 /* value of si_idx for keywords */
315#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
316 but contained groups */
317
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200318#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100319static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#endif
321
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000323 * Struct to reduce the number of arguments to get_syn_options(), it's used
324 * very often.
325 */
326typedef struct
327{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000328 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000329 int keyword; /* TRUE for ":syn keyword" */
330 int *sync_idx; /* syntax item for "grouphere" argument, NULL
331 if not allowed */
332 char has_cont_list; /* TRUE if "cont_list" can be used */
333 short *cont_list; /* group IDs for "contains" argument */
334 short *cont_in_list; /* group IDs for "containedin" argument */
335 short *next_list; /* group IDs for "nextgroup" argument */
336} syn_opt_arg_T;
337
338/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339 * The next possible match in the current line for any pattern is remembered,
340 * to avoid having to try for a match in each column.
341 * If next_match_idx == -1, not tried (in this line) yet.
342 * If next_match_col == MAXCOL, no match found in this line.
343 * (All end positions have the column of the char after the end)
344 */
345static int next_match_col; /* column for start of next match */
346static lpos_T next_match_m_endpos; /* position for end of next match */
347static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
348static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
349static int next_match_idx; /* index of matched item */
350static long next_match_flags; /* flags for next match */
351static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
352static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
353static int next_match_end_idx; /* ID of group for end pattn or zero */
354static reg_extmatch_T *next_match_extmatch = NULL;
355
356/*
357 * A state stack is an array of integers or stateitem_T, stored in a
358 * garray_T. A state stack is invalid if it's itemsize entry is zero.
359 */
360#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
361#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
362
363/*
364 * The current state (within the line) of the recognition engine.
365 * When current_state.ga_itemsize is 0 the current state is invalid.
366 */
367static win_T *syn_win; /* current window for highlighting */
368static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200369static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200370#ifdef FEAT_RELTIME
371static proftime_T *syn_tm;
372#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373static linenr_T current_lnum = 0; /* lnum of current state */
374static colnr_T current_col = 0; /* column of current state */
375static int current_state_stored = 0; /* TRUE if stored current state
376 * after setting current_finished */
377static int current_finished = 0; /* current line has been finished */
378static garray_T current_state /* current stack of state_items */
379 = {0, 0, 0, 0, NULL};
380static short *current_next_list = NULL; /* when non-zero, nextgroup list */
381static int current_next_flags = 0; /* flags for current_next_list */
382static int current_line_id = 0; /* unique number for current line */
383
384#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
385
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100386static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100387static void save_chartab(char_u *chartab);
388static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100389static int syn_match_linecont(linenr_T lnum);
390static void syn_start_line(void);
391static void syn_update_ends(int startofline);
392static void syn_stack_alloc(void);
393static int syn_stack_cleanup(void);
394static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
395static synstate_T *syn_stack_find_entry(linenr_T lnum);
396static synstate_T *store_current_state(void);
397static void load_current_state(synstate_T *from);
398static void invalidate_current_state(void);
399static int syn_stack_equal(synstate_T *sp);
400static void validate_current_state(void);
401static int syn_finish_line(int syncing);
402static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
403static int did_match_already(int idx, garray_T *gap);
404static stateitem_T *push_next_match(stateitem_T *cur_si);
405static void check_state_ends(void);
406static void update_si_attr(int idx);
407static void check_keepend(void);
408static void update_si_end(stateitem_T *sip, int startcol, int force);
409static short *copy_id_list(short *list);
410static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
411static int push_current_state(int idx);
412static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200413#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100414static void syn_clear_time(syn_time_T *tt);
415static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200416#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100417static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200420#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200422static int syn_time_on = FALSE;
423# define IF_SYN_TIME(p) (p)
424#else
425# define IF_SYN_TIME(p) NULL
426typedef int syn_time_T;
427#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100429static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
430static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
431static void clear_syn_state(synstate_T *p);
432static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000433
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100434static void limit_pos(lpos_T *pos, lpos_T *limit);
435static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
436static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
437static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
438static char_u *syn_getcurline(void);
439static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
440static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
441static void syn_cmd_case(exarg_T *eap, int syncing);
442static void syn_cmd_spell(exarg_T *eap, int syncing);
443static void syntax_sync_clear(void);
444static void syn_remove_pattern(synblock_T *block, int idx);
445static void syn_clear_pattern(synblock_T *block, int i);
446static void syn_clear_cluster(synblock_T *block, int i);
447static void syn_cmd_clear(exarg_T *eap, int syncing);
448static void syn_cmd_conceal(exarg_T *eap, int syncing);
449static void syn_clear_one(int id, int syncing);
450static void syn_cmd_on(exarg_T *eap, int syncing);
451static void syn_cmd_enable(exarg_T *eap, int syncing);
452static void syn_cmd_reset(exarg_T *eap, int syncing);
453static void syn_cmd_manual(exarg_T *eap, int syncing);
454static void syn_cmd_off(exarg_T *eap, int syncing);
455static void syn_cmd_onoff(exarg_T *eap, char *name);
456static void syn_cmd_list(exarg_T *eap, int syncing);
457static void syn_lines_msg(void);
458static void syn_match_msg(void);
459static void syn_stack_free_block(synblock_T *block);
460static void syn_list_one(int id, int syncing, int link_only);
461static void syn_list_cluster(int id);
462static void put_id_list(char_u *name, short *list, int attr);
463static void put_pattern(char *s, int c, synpat_T *spp, int attr);
464static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
465static void syn_clear_keyword(int id, hashtab_T *ht);
466static void clear_keywtab(hashtab_T *ht);
467static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
468static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100469static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100470static void syn_cmd_include(exarg_T *eap, int syncing);
471static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
472static void syn_cmd_keyword(exarg_T *eap, int syncing);
473static void syn_cmd_match(exarg_T *eap, int syncing);
474static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100476static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100478static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100480static void syn_cmd_cluster(exarg_T *eap, int syncing);
481static int syn_scl_name2id(char_u *name);
482static int syn_scl_namen2id(char_u *linep, int len);
483static int syn_check_cluster(char_u *pp, int len);
484static int syn_add_cluster(char_u *name);
485static void init_syn_patterns(void);
486static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
487static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100488static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100489static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
490static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491
492/*
493 * Start the syntax recognition for a line. This function is normally called
494 * from the screen updating, once for each displayed line.
495 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
496 * it. Careful: curbuf and curwin are likely to point to another buffer and
497 * window.
498 */
499 void
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200500syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501{
502 synstate_T *p;
503 synstate_T *last_valid = NULL;
504 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000505 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 linenr_T parsed_lnum;
507 linenr_T first_stored;
508 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100509 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200511#ifdef FEAT_CONCEAL
512 current_sub_char = NUL;
513#endif
514
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 /*
516 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000517 * Also do this when a change was made, the current state may be invalid
518 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200520 if (syn_block != wp->w_s
521 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100522 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 invalidate_current_state();
525 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200526 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100528 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 syn_win = wp;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200530#ifdef FEAT_RELTIME
531 syn_tm = syntax_tm;
532#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533
534 /*
535 * Allocate syntax stack when needed.
536 */
537 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200538 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000539 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541
542 /*
543 * If the state of the end of the previous line is useful, store it.
544 */
545 if (VALID_STATE(&current_state)
546 && current_lnum < lnum
547 && current_lnum < syn_buf->b_ml.ml_line_count)
548 {
549 (void)syn_finish_line(FALSE);
550 if (!current_state_stored)
551 {
552 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000553 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000554 }
555
556 /*
557 * If the current_lnum is now the same as "lnum", keep the current
558 * state (this happens very often!). Otherwise invalidate
559 * current_state and figure it out below.
560 */
561 if (current_lnum != lnum)
562 invalidate_current_state();
563 }
564 else
565 invalidate_current_state();
566
567 /*
568 * Try to synchronize from a saved state in b_sst_array[].
569 * Only do this if lnum is not before and not to far beyond a saved state.
570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200571 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572 {
573 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 {
576 if (p->sst_lnum > lnum)
577 break;
578 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
579 {
580 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200581 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000582 last_min_valid = p;
583 }
584 }
585 if (last_min_valid != NULL)
586 load_current_state(last_min_valid);
587 }
588
589 /*
590 * If "lnum" is before or far beyond a line with a saved state, need to
591 * re-synchronize.
592 */
593 if (INVALID_STATE(&current_state))
594 {
595 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200596 if (current_lnum == 1)
597 /* First line is always valid, no matter "minlines". */
598 first_stored = 1;
599 else
600 /* Need to parse "minlines" lines before state can be considered
601 * valid to store. */
602 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603 }
604 else
605 first_stored = current_lnum;
606
607 /*
608 * Advance from the sync point or saved state until the current line.
609 * Save some entries for syncing with later on.
610 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200611 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000612 dist = 999999;
613 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200614 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615 while (current_lnum < lnum)
616 {
617 syn_start_line();
618 (void)syn_finish_line(FALSE);
619 ++current_lnum;
620
621 /* If we parsed at least "minlines" lines or started at a valid
622 * state, the current state is considered valid. */
623 if (current_lnum >= first_stored)
624 {
625 /* Check if the saved state entry is for the current line and is
626 * equal to the current state. If so, then validate all saved
627 * states that depended on a change before the parsed line. */
628 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000629 prev = syn_stack_find_entry(current_lnum - 1);
630 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200631 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000632 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000633 sp = prev;
634 while (sp != NULL && sp->sst_lnum < current_lnum)
635 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 if (sp != NULL
637 && sp->sst_lnum == current_lnum
638 && syn_stack_equal(sp))
639 {
640 parsed_lnum = current_lnum;
641 prev = sp;
642 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
643 {
644 if (sp->sst_lnum <= lnum)
645 /* valid state before desired line, use this one */
646 prev = sp;
647 else if (sp->sst_change_lnum == 0)
648 /* past saved states depending on change, break here. */
649 break;
650 sp->sst_change_lnum = 0;
651 sp = sp->sst_next;
652 }
653 load_current_state(prev);
654 }
655 /* Store the state at this line when it's the first one, the line
656 * where we start parsing, or some distance from the previously
657 * saved state. But only when parsed at least 'minlines'. */
658 else if (prev == NULL
659 || current_lnum == lnum
660 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000661 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662 }
663
664 /* This can take a long time: break when CTRL-C pressed. The current
665 * state will be wrong then. */
666 line_breakcheck();
667 if (got_int)
668 {
669 current_lnum = lnum;
670 break;
671 }
672 }
673
674 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675}
676
677/*
678 * We cannot simply discard growarrays full of state_items or buf_states; we
679 * have to manually release their extmatch pointers first.
680 */
681 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100682clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683{
684 int i;
685 garray_T *gap;
686
687 if (p->sst_stacksize > SST_FIX_STATES)
688 {
689 gap = &(p->sst_union.sst_ga);
690 for (i = 0; i < gap->ga_len; i++)
691 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
692 ga_clear(gap);
693 }
694 else
695 {
696 for (i = 0; i < p->sst_stacksize; i++)
697 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
698 }
699}
700
701/*
702 * Cleanup the current_state stack.
703 */
704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100705clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706{
707 int i;
708 stateitem_T *sip;
709
710 sip = (stateitem_T *)(current_state.ga_data);
711 for (i = 0; i < current_state.ga_len; i++)
712 unref_extmatch(sip[i].si_extmatch);
713 ga_clear(&current_state);
714}
715
716/*
717 * Try to find a synchronisation point for line "lnum".
718 *
719 * This sets current_lnum and the current state. One of three methods is
720 * used:
721 * 1. Search backwards for the end of a C-comment.
722 * 2. Search backwards for given sync patterns.
723 * 3. Simply start on a given number of lines above "lnum".
724 */
725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100726syn_sync(
727 win_T *wp,
728 linenr_T start_lnum,
729 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730{
731 buf_T *curbuf_save;
732 win_T *curwin_save;
733 pos_T cursor_save;
734 int idx;
735 linenr_T lnum;
736 linenr_T end_lnum;
737 linenr_T break_lnum;
738 int had_sync_point;
739 stateitem_T *cur_si;
740 synpat_T *spp;
741 char_u *line;
742 int found_flags = 0;
743 int found_match_idx = 0;
744 linenr_T found_current_lnum = 0;
745 int found_current_col= 0;
746 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000747 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748
749 /*
750 * Clear any current state that might be hanging around.
751 */
752 invalidate_current_state();
753
754 /*
755 * Start at least "minlines" back. Default starting point for parsing is
756 * there.
757 * Start further back, to avoid that scrolling backwards will result in
758 * resyncing for every line. Now it resyncs only one out of N lines,
759 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
760 * Watch out for overflow when minlines is MAXLNUM.
761 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200762 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763 start_lnum = 1;
764 else
765 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200766 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200768 else if (syn_block->b_syn_sync_minlines < 10)
769 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
772 if (syn_block->b_syn_sync_maxlines != 0
773 && lnum > syn_block->b_syn_sync_maxlines)
774 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775 if (lnum >= start_lnum)
776 start_lnum = 1;
777 else
778 start_lnum -= lnum;
779 }
780 current_lnum = start_lnum;
781
782 /*
783 * 1. Search backwards for the end of a C-style comment.
784 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200785 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 {
787 /* Need to make syn_buf the current buffer for a moment, to be able to
788 * use find_start_comment(). */
789 curwin_save = curwin;
790 curwin = wp;
791 curbuf_save = curbuf;
792 curbuf = syn_buf;
793
794 /*
795 * Skip lines that end in a backslash.
796 */
797 for ( ; start_lnum > 1; --start_lnum)
798 {
799 line = ml_get(start_lnum - 1);
800 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
801 break;
802 }
803 current_lnum = start_lnum;
804
805 /* set cursor to start of search */
806 cursor_save = wp->w_cursor;
807 wp->w_cursor.lnum = start_lnum;
808 wp->w_cursor.col = 0;
809
810 /*
811 * If the line is inside a comment, need to find the syntax item that
812 * defines the comment.
813 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
814 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200815 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200817 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
818 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
819 == syn_block->b_syn_sync_id
820 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821 {
822 validate_current_state();
823 if (push_current_state(idx) == OK)
824 update_si_attr(current_state.ga_len - 1);
825 break;
826 }
827 }
828
829 /* restore cursor and buffer */
830 wp->w_cursor = cursor_save;
831 curwin = curwin_save;
832 curbuf = curbuf_save;
833 }
834
835 /*
836 * 2. Search backwards for given sync patterns.
837 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200838 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200840 if (syn_block->b_syn_sync_maxlines != 0
841 && start_lnum > syn_block->b_syn_sync_maxlines)
842 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000843 else
844 break_lnum = 0;
845
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000846 found_m_endpos.lnum = 0;
847 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000848 end_lnum = start_lnum;
849 lnum = start_lnum;
850 while (--lnum > break_lnum)
851 {
852 /* This can take a long time: break when CTRL-C pressed. */
853 line_breakcheck();
854 if (got_int)
855 {
856 invalidate_current_state();
857 current_lnum = start_lnum;
858 break;
859 }
860
861 /* Check if we have run into a valid saved state stack now. */
862 if (last_valid != NULL && lnum == last_valid->sst_lnum)
863 {
864 load_current_state(last_valid);
865 break;
866 }
867
868 /*
869 * Check if the previous line has the line-continuation pattern.
870 */
871 if (lnum > 1 && syn_match_linecont(lnum - 1))
872 continue;
873
874 /*
875 * Start with nothing on the state stack
876 */
877 validate_current_state();
878
879 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
880 {
881 syn_start_line();
882 for (;;)
883 {
884 had_sync_point = syn_finish_line(TRUE);
885 /*
886 * When a sync point has been found, remember where, and
887 * continue to look for another one, further on in the line.
888 */
889 if (had_sync_point && current_state.ga_len)
890 {
891 cur_si = &CUR_STATE(current_state.ga_len - 1);
892 if (cur_si->si_m_endpos.lnum > start_lnum)
893 {
894 /* ignore match that goes to after where started */
895 current_lnum = end_lnum;
896 break;
897 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000898 if (cur_si->si_idx < 0)
899 {
900 /* Cannot happen? */
901 found_flags = 0;
902 found_match_idx = KEYWORD_IDX;
903 }
904 else
905 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200906 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000907 found_flags = spp->sp_flags;
908 found_match_idx = spp->sp_sync_idx;
909 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 found_current_lnum = current_lnum;
911 found_current_col = current_col;
912 found_m_endpos = cur_si->si_m_endpos;
913 /*
914 * Continue after the match (be aware of a zero-length
915 * match).
916 */
917 if (found_m_endpos.lnum > current_lnum)
918 {
919 current_lnum = found_m_endpos.lnum;
920 current_col = found_m_endpos.col;
921 if (current_lnum >= end_lnum)
922 break;
923 }
924 else if (found_m_endpos.col > current_col)
925 current_col = found_m_endpos.col;
926 else
927 ++current_col;
928
929 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000930 * an item that ends here, need to do that now. Be
931 * careful not to go past the NUL. */
932 prev_current_col = current_col;
933 if (syn_getcurline()[current_col] != NUL)
934 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000936 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 }
938 else
939 break;
940 }
941 }
942
943 /*
944 * If a sync point was encountered, break here.
945 */
946 if (found_flags)
947 {
948 /*
949 * Put the item that was specified by the sync point on the
950 * state stack. If there was no item specified, make the
951 * state stack empty.
952 */
953 clear_current_state();
954 if (found_match_idx >= 0
955 && push_current_state(found_match_idx) == OK)
956 update_si_attr(current_state.ga_len - 1);
957
958 /*
959 * When using "grouphere", continue from the sync point
960 * match, until the end of the line. Parsing starts at
961 * the next line.
962 * For "groupthere" the parsing starts at start_lnum.
963 */
964 if (found_flags & HL_SYNC_HERE)
965 {
966 if (current_state.ga_len)
967 {
968 cur_si = &CUR_STATE(current_state.ga_len - 1);
969 cur_si->si_h_startpos.lnum = found_current_lnum;
970 cur_si->si_h_startpos.col = found_current_col;
971 update_si_end(cur_si, (int)current_col, TRUE);
972 check_keepend();
973 }
974 current_col = found_m_endpos.col;
975 current_lnum = found_m_endpos.lnum;
976 (void)syn_finish_line(FALSE);
977 ++current_lnum;
978 }
979 else
980 current_lnum = start_lnum;
981
982 break;
983 }
984
985 end_lnum = lnum;
986 invalidate_current_state();
987 }
988
989 /* Ran into start of the file or exceeded maximum number of lines */
990 if (lnum <= break_lnum)
991 {
992 invalidate_current_state();
993 current_lnum = break_lnum + 1;
994 }
995 }
996
997 validate_current_state();
998}
999
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001000 static void
1001save_chartab(char_u *chartab)
1002{
1003 if (syn_block->b_syn_isk != empty_option)
1004 {
1005 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1006 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1007 (size_t)32);
1008 }
1009}
1010
1011 static void
1012restore_chartab(char_u *chartab)
1013{
1014 if (syn_win->w_s->b_syn_isk != empty_option)
1015 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1016}
1017
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018/*
1019 * Return TRUE if the line-continuation pattern matches in line "lnum".
1020 */
1021 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001022syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023{
1024 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001025 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001026 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027
Bram Moolenaar860cae12010-06-05 23:22:07 +02001028 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001030 /* use syntax iskeyword option */
1031 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001032 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1033 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001034 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001035 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001036 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001037 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001038 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 }
1040 return FALSE;
1041}
1042
1043/*
1044 * Prepare the current state for the start of a line.
1045 */
1046 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001047syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048{
1049 current_finished = FALSE;
1050 current_col = 0;
1051
1052 /*
1053 * Need to update the end of a start/skip/end that continues from the
1054 * previous line and regions that have "keepend".
1055 */
1056 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001057 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001059 check_state_ends();
1060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001061
1062 next_match_idx = -1;
1063 ++current_line_id;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001064 next_seqnr = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001065}
1066
1067/*
1068 * Check for items in the stack that need their end updated.
1069 * When "startofline" is TRUE the last item is always updated.
1070 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1071 */
1072 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001073syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074{
1075 stateitem_T *cur_si;
1076 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001077 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001078
1079 if (startofline)
1080 {
1081 /* Check for a match carried over from a previous line with a
1082 * contained region. The match ends as soon as the region ends. */
1083 for (i = 0; i < current_state.ga_len; ++i)
1084 {
1085 cur_si = &CUR_STATE(i);
1086 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001087 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001088 == SPTYPE_MATCH
1089 && cur_si->si_m_endpos.lnum < current_lnum)
1090 {
1091 cur_si->si_flags |= HL_MATCHCONT;
1092 cur_si->si_m_endpos.lnum = 0;
1093 cur_si->si_m_endpos.col = 0;
1094 cur_si->si_h_endpos = cur_si->si_m_endpos;
1095 cur_si->si_ends = TRUE;
1096 }
1097 }
1098 }
1099
1100 /*
1101 * Need to update the end of a start/skip/end that continues from the
1102 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001103 * influence contained items. If we've just removed "extend"
1104 * (startofline == 0) then we should update ends of normal regions
1105 * contained inside "keepend" because "extend" could have extended
1106 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001107 * Then check for items ending in column 0.
1108 */
1109 i = current_state.ga_len - 1;
1110 if (keepend_level >= 0)
1111 for ( ; i > keepend_level; --i)
1112 if (CUR_STATE(i).si_flags & HL_EXTEND)
1113 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001114
1115 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116 for ( ; i < current_state.ga_len; ++i)
1117 {
1118 cur_si = &CUR_STATE(i);
1119 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001120 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 || (i == current_state.ga_len - 1 && startofline))
1122 {
1123 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1124 cur_si->si_h_startpos.lnum = current_lnum;
1125
1126 if (!(cur_si->si_flags & HL_MATCHCONT))
1127 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001128
1129 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1130 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001131 }
1132 }
1133 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134}
1135
1136/****************************************
1137 * Handling of the state stack cache.
1138 */
1139
1140/*
1141 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1142 *
1143 * To speed up syntax highlighting, the state stack for the start of some
1144 * lines is cached. These entries can be used to start parsing at that point.
1145 *
1146 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1147 * valid entries. b_sst_first points to the first one, then follow sst_next.
1148 * The entries are sorted on line number. The first entry is often for line 2
1149 * (line 1 always starts with an empty stack).
1150 * There is also a list for free entries. This construction is used to avoid
1151 * having to allocate and free memory blocks too often.
1152 *
1153 * When making changes to the buffer, this is logged in b_mod_*. When calling
1154 * update_screen() to update the display, it will call
1155 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1156 * entries. The entries which are inside the changed area are removed,
1157 * because they must be recomputed. Entries below the changed have their line
1158 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1159 * set to indicate that a check must be made if the changed lines would change
1160 * the cached entry.
1161 *
1162 * When later displaying lines, an entry is stored for each line. Displayed
1163 * lines are likely to be displayed again, in which case the state at the
1164 * start of the line is needed.
1165 * For not displayed lines, an entry is stored for every so many lines. These
1166 * entries will be used e.g., when scrolling backwards. The distance between
1167 * entries depends on the number of lines in the buffer. For small buffers
1168 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1169 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1170 */
1171
Bram Moolenaar860cae12010-06-05 23:22:07 +02001172 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001173syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174{
1175 synstate_T *p;
1176
1177 if (block->b_sst_array != NULL)
1178 {
1179 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1180 clear_syn_state(p);
1181 vim_free(block->b_sst_array);
1182 block->b_sst_array = NULL;
1183 block->b_sst_len = 0;
1184 }
1185}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186/*
1187 * Free b_sst_array[] for buffer "buf".
1188 * Used when syntax items changed to force resyncing everywhere.
1189 */
1190 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001191syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001193#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001195#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 syn_stack_free_block(block);
1198
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199#ifdef FEAT_FOLDING
1200 /* When using "syntax" fold method, must update all folds. */
1201 FOR_ALL_WINDOWS(wp)
1202 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001203 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 foldUpdateAll(wp);
1205 }
1206#endif
1207}
1208
1209/*
1210 * Allocate the syntax state stack for syn_buf when needed.
1211 * If the number of entries in b_sst_array[] is much too big or a bit too
1212 * small, reallocate it.
1213 * Also used to allocate b_sst_array[] for the first time.
1214 */
1215 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001216syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217{
1218 long len;
1219 synstate_T *to, *from;
1220 synstate_T *sstp;
1221
1222 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1223 if (len < SST_MIN_ENTRIES)
1224 len = SST_MIN_ENTRIES;
1225 else if (len > SST_MAX_ENTRIES)
1226 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001227 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 {
1229 /* Allocate 50% too much, to avoid reallocating too often. */
1230 len = syn_buf->b_ml.ml_line_count;
1231 len = (len + len / 2) / SST_DIST + Rows * 2;
1232 if (len < SST_MIN_ENTRIES)
1233 len = SST_MIN_ENTRIES;
1234 else if (len > SST_MAX_ENTRIES)
1235 len = SST_MAX_ENTRIES;
1236
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 {
1239 /* When shrinking the array, cleanup the existing stack.
1240 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 && syn_stack_cleanup())
1243 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001244 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1245 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001246 }
1247
1248 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1249 if (sstp == NULL) /* out of memory! */
1250 return;
1251
1252 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 {
1255 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 from = from->sst_next)
1258 {
1259 ++to;
1260 *to = *from;
1261 to->sst_next = to + 1;
1262 }
1263 }
1264 if (to != sstp - 1)
1265 {
1266 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001267 syn_block->b_sst_first = sstp;
1268 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 }
1270 else
1271 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001272 syn_block->b_sst_first = NULL;
1273 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001274 }
1275
1276 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001277 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 while (++to < sstp + len)
1279 to->sst_next = to + 1;
1280 (sstp + len - 1)->sst_next = NULL;
1281
Bram Moolenaar860cae12010-06-05 23:22:07 +02001282 vim_free(syn_block->b_sst_array);
1283 syn_block->b_sst_array = sstp;
1284 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 }
1286}
1287
1288/*
1289 * Check for changes in a buffer to affect stored syntax states. Uses the
1290 * b_mod_* fields.
1291 * Called from update_screen(), before screen is being updated, once for each
1292 * displayed buffer.
1293 */
1294 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001295syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001297 win_T *wp;
1298
1299 syn_stack_apply_changes_block(&buf->b_s, buf);
1300
1301 FOR_ALL_WINDOWS(wp)
1302 {
1303 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1304 syn_stack_apply_changes_block(wp->w_s, buf);
1305 }
1306}
1307
1308 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001309syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001310{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 synstate_T *p, *prev, *np;
1312 linenr_T n;
1313
Bram Moolenaar860cae12010-06-05 23:22:07 +02001314 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315 return;
1316
1317 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001318 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001320 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 {
1322 n = p->sst_lnum + buf->b_mod_xlines;
1323 if (n <= buf->b_mod_bot)
1324 {
1325 /* this state is inside the changed area, remove it */
1326 np = p->sst_next;
1327 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 else
1330 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001331 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 p = np;
1333 continue;
1334 }
1335 /* This state is below the changed area. Remember the line
1336 * that needs to be parsed before this entry can be made valid
1337 * again. */
1338 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1339 {
1340 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1341 p->sst_change_lnum += buf->b_mod_xlines;
1342 else
1343 p->sst_change_lnum = buf->b_mod_top;
1344 }
1345 if (p->sst_change_lnum == 0
1346 || p->sst_change_lnum < buf->b_mod_bot)
1347 p->sst_change_lnum = buf->b_mod_bot;
1348
1349 p->sst_lnum = n;
1350 }
1351 prev = p;
1352 p = p->sst_next;
1353 }
1354}
1355
1356/*
1357 * Reduce the number of entries in the state stack for syn_buf.
1358 * Returns TRUE if at least one entry was freed.
1359 */
1360 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001361syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362{
1363 synstate_T *p, *prev;
1364 disptick_T tick;
1365 int above;
1366 int dist;
1367 int retval = FALSE;
1368
Bram Moolenaar860cae12010-06-05 23:22:07 +02001369 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370 return retval;
1371
1372 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001373 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001374 dist = 999999;
1375 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377
1378 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001379 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 * be removed. Set "above" when the "tick" for the oldest entry is above
1381 * "b_sst_lasttick" (the display tick wraps around).
1382 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001383 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1387 {
1388 if (prev->sst_lnum + dist > p->sst_lnum)
1389 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001390 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391 {
1392 if (!above || p->sst_tick < tick)
1393 tick = p->sst_tick;
1394 above = TRUE;
1395 }
1396 else if (!above && p->sst_tick < tick)
1397 tick = p->sst_tick;
1398 }
1399 }
1400
1401 /*
1402 * Go through the list to make the entries for the oldest tick at an
1403 * interval of several lines.
1404 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001405 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1407 {
1408 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1409 {
1410 /* Move this entry from used list to free list */
1411 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001412 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413 p = prev;
1414 retval = TRUE;
1415 }
1416 }
1417 return retval;
1418}
1419
1420/*
1421 * Free the allocated memory for a syn_state item.
1422 * Move the entry into the free list.
1423 */
1424 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001425syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426{
1427 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001428 p->sst_next = block->b_sst_firstfree;
1429 block->b_sst_firstfree = p;
1430 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431}
1432
1433/*
1434 * Find an entry in the list of state stacks at or before "lnum".
1435 * Returns NULL when there is no entry or the first entry is after "lnum".
1436 */
1437 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001438syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001439{
1440 synstate_T *p, *prev;
1441
1442 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001443 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 {
1445 if (p->sst_lnum == lnum)
1446 return p;
1447 if (p->sst_lnum > lnum)
1448 break;
1449 }
1450 return prev;
1451}
1452
1453/*
1454 * Try saving the current state in b_sst_array[].
1455 * The current state must be valid for the start of the current_lnum line!
1456 */
1457 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001458store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459{
1460 int i;
1461 synstate_T *p;
1462 bufstate_T *bp;
1463 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001464 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465
1466 /*
1467 * If the current state contains a start or end pattern that continues
1468 * from the previous line, we can't use it. Don't store it then.
1469 */
1470 for (i = current_state.ga_len - 1; i >= 0; --i)
1471 {
1472 cur_si = &CUR_STATE(i);
1473 if (cur_si->si_h_startpos.lnum >= current_lnum
1474 || cur_si->si_m_endpos.lnum >= current_lnum
1475 || cur_si->si_h_endpos.lnum >= current_lnum
1476 || (cur_si->si_end_idx
1477 && cur_si->si_eoe_pos.lnum >= current_lnum))
1478 break;
1479 }
1480 if (i >= 0)
1481 {
1482 if (sp != NULL)
1483 {
1484 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001485 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001486 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001487 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 else
1489 {
1490 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001491 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001492 if (p->sst_next == sp)
1493 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001494 if (p != NULL) /* just in case */
1495 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001497 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 sp = NULL;
1499 }
1500 }
1501 else if (sp == NULL || sp->sst_lnum != current_lnum)
1502 {
1503 /*
1504 * Add a new entry
1505 */
1506 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001507 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 {
1509 (void)syn_stack_cleanup();
1510 /* "sp" may have been moved to the freelist now */
1511 sp = syn_stack_find_entry(current_lnum);
1512 }
1513 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001514 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 sp = NULL;
1516 else
1517 {
1518 /* Take the first item from the free list and put it in the used
1519 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001520 p = syn_block->b_sst_firstfree;
1521 syn_block->b_sst_firstfree = p->sst_next;
1522 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523 if (sp == NULL)
1524 {
1525 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001526 p->sst_next = syn_block->b_sst_first;
1527 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001528 }
1529 else
1530 {
1531 /* insert in list after *sp */
1532 p->sst_next = sp->sst_next;
1533 sp->sst_next = p;
1534 }
1535 sp = p;
1536 sp->sst_stacksize = 0;
1537 sp->sst_lnum = current_lnum;
1538 }
1539 }
1540 if (sp != NULL)
1541 {
1542 /* When overwriting an existing state stack, clear it first */
1543 clear_syn_state(sp);
1544 sp->sst_stacksize = current_state.ga_len;
1545 if (current_state.ga_len > SST_FIX_STATES)
1546 {
1547 /* Need to clear it, might be something remaining from when the
1548 * length was less than SST_FIX_STATES. */
1549 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1550 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1551 sp->sst_stacksize = 0;
1552 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1555 }
1556 else
1557 bp = sp->sst_union.sst_stack;
1558 for (i = 0; i < sp->sst_stacksize; ++i)
1559 {
1560 bp[i].bs_idx = CUR_STATE(i).si_idx;
1561 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001562#ifdef FEAT_CONCEAL
1563 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1564 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1565#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1567 }
1568 sp->sst_next_flags = current_next_flags;
1569 sp->sst_next_list = current_next_list;
1570 sp->sst_tick = display_tick;
1571 sp->sst_change_lnum = 0;
1572 }
1573 current_state_stored = TRUE;
1574 return sp;
1575}
1576
1577/*
1578 * Copy a state stack from "from" in b_sst_array[] to current_state;
1579 */
1580 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001581load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582{
1583 int i;
1584 bufstate_T *bp;
1585
1586 clear_current_state();
1587 validate_current_state();
1588 keepend_level = -1;
1589 if (from->sst_stacksize
1590 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1591 {
1592 if (from->sst_stacksize > SST_FIX_STATES)
1593 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1594 else
1595 bp = from->sst_union.sst_stack;
1596 for (i = 0; i < from->sst_stacksize; ++i)
1597 {
1598 CUR_STATE(i).si_idx = bp[i].bs_idx;
1599 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001600#ifdef FEAT_CONCEAL
1601 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1602 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1603#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1605 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1606 keepend_level = i;
1607 CUR_STATE(i).si_ends = FALSE;
1608 CUR_STATE(i).si_m_lnum = 0;
1609 if (CUR_STATE(i).si_idx >= 0)
1610 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001611 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001612 else
1613 CUR_STATE(i).si_next_list = NULL;
1614 update_si_attr(i);
1615 }
1616 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001617 }
1618 current_next_list = from->sst_next_list;
1619 current_next_flags = from->sst_next_flags;
1620 current_lnum = from->sst_lnum;
1621}
1622
1623/*
1624 * Compare saved state stack "*sp" with the current state.
1625 * Return TRUE when they are equal.
1626 */
1627 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001628syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629{
1630 int i, j;
1631 bufstate_T *bp;
1632 reg_extmatch_T *six, *bsx;
1633
1634 /* First a quick check if the stacks have the same size end nextlist. */
1635 if (sp->sst_stacksize == current_state.ga_len
1636 && sp->sst_next_list == current_next_list)
1637 {
1638 /* Need to compare all states on both stacks. */
1639 if (sp->sst_stacksize > SST_FIX_STATES)
1640 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1641 else
1642 bp = sp->sst_union.sst_stack;
1643
1644 for (i = current_state.ga_len; --i >= 0; )
1645 {
1646 /* If the item has another index the state is different. */
1647 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1648 break;
1649 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1650 {
1651 /* When the extmatch pointers are different, the strings in
1652 * them can still be the same. Check if the extmatch
1653 * references are equal. */
1654 bsx = bp[i].bs_extmatch;
1655 six = CUR_STATE(i).si_extmatch;
1656 /* If one of the extmatch pointers is NULL the states are
1657 * different. */
1658 if (bsx == NULL || six == NULL)
1659 break;
1660 for (j = 0; j < NSUBEXP; ++j)
1661 {
1662 /* Check each referenced match string. They must all be
1663 * equal. */
1664 if (bsx->matches[j] != six->matches[j])
1665 {
1666 /* If the pointer is different it can still be the
1667 * same text. Compare the strings, ignore case when
1668 * the start item has the sp_ic flag set. */
1669 if (bsx->matches[j] == NULL
1670 || six->matches[j] == NULL)
1671 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001672 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673 ? MB_STRICMP(bsx->matches[j],
1674 six->matches[j]) != 0
1675 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1676 break;
1677 }
1678 }
1679 if (j != NSUBEXP)
1680 break;
1681 }
1682 }
1683 if (i < 0)
1684 return TRUE;
1685 }
1686 return FALSE;
1687}
1688
1689/*
1690 * We stop parsing syntax above line "lnum". If the stored state at or below
1691 * this line depended on a change before it, it now depends on the line below
1692 * the last parsed line.
1693 * The window looks like this:
1694 * line which changed
1695 * displayed line
1696 * displayed line
1697 * lnum -> line below window
1698 */
1699 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001700syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701{
1702 synstate_T *sp;
1703
1704 sp = syn_stack_find_entry(lnum);
1705 if (sp != NULL && sp->sst_lnum < lnum)
1706 sp = sp->sst_next;
1707
1708 if (sp != NULL && sp->sst_change_lnum != 0)
1709 sp->sst_change_lnum = lnum;
1710}
1711
1712/*
1713 * End of handling of the state stack.
1714 ****************************************/
1715
1716 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001717invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718{
1719 clear_current_state();
1720 current_state.ga_itemsize = 0; /* mark current_state invalid */
1721 current_next_list = NULL;
1722 keepend_level = -1;
1723}
1724
1725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001726validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727{
1728 current_state.ga_itemsize = sizeof(stateitem_T);
1729 current_state.ga_growsize = 3;
1730}
1731
1732/*
1733 * Return TRUE if the syntax at start of lnum changed since last time.
1734 * This will only be called just after get_syntax_attr() for the previous
1735 * line, to check if the next line needs to be redrawn too.
1736 */
1737 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001738syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739{
1740 int retval = TRUE;
1741 synstate_T *sp;
1742
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 /*
1744 * Check the state stack when:
1745 * - lnum is just below the previously syntaxed line.
1746 * - lnum is not before the lines with saved states.
1747 * - lnum is not past the lines with saved states.
1748 * - lnum is at or before the last changed line.
1749 */
1750 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1751 {
1752 sp = syn_stack_find_entry(lnum);
1753 if (sp != NULL && sp->sst_lnum == lnum)
1754 {
1755 /*
1756 * finish the previous line (needed when not all of the line was
1757 * drawn)
1758 */
1759 (void)syn_finish_line(FALSE);
1760
1761 /*
1762 * Compare the current state with the previously saved state of
1763 * the line.
1764 */
1765 if (syn_stack_equal(sp))
1766 retval = FALSE;
1767
1768 /*
1769 * Store the current state in b_sst_array[] for later use.
1770 */
1771 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001772 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 }
1774 }
1775
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776 return retval;
1777}
1778
1779/*
1780 * Finish the current line.
1781 * This doesn't return any attributes, it only gets the state at the end of
1782 * the line. It can start anywhere in the line, as long as the current state
1783 * is valid.
1784 */
1785 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001786syn_finish_line(
1787 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788{
1789 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001790 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001792 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001794 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1795 /*
1796 * When syncing, and found some item, need to check the item.
1797 */
1798 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001801 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001803 cur_si = &CUR_STATE(current_state.ga_len - 1);
1804 if (cur_si->si_idx >= 0
1805 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1806 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1807 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001809 /* syn_current_attr() will have skipped the check for an item
1810 * that ends here, need to do that now. Be careful not to go
1811 * past the NUL. */
1812 prev_current_col = current_col;
1813 if (syn_getcurline()[current_col] != NUL)
1814 ++current_col;
1815 check_state_ends();
1816 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001817 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001818 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 }
1820 return FALSE;
1821}
1822
1823/*
1824 * Return highlight attributes for next character.
1825 * Must first call syntax_start() once for the line.
1826 * "col" is normally 0 for the first use in a line, and increments by one each
1827 * time. It's allowed to skip characters and to stop before the end of the
1828 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001829 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1830 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 */
1832 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001833get_syntax_attr(
1834 colnr_T col,
1835 int *can_spell,
1836 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837{
1838 int attr = 0;
1839
Bram Moolenaar349955a2007-08-14 21:07:36 +00001840 if (can_spell != NULL)
1841 /* Default: Only do spelling when there is no @Spell cluster or when
1842 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001843 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1844 ? (syn_block->b_spell_cluster_id == 0)
1845 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001846
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001848 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849 return 0;
1850
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001851 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001852 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001853 {
1854 clear_current_state();
1855#ifdef FEAT_EVAL
1856 current_id = 0;
1857 current_trans_id = 0;
1858#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001859#ifdef FEAT_CONCEAL
1860 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001861 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001862#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001863 return 0;
1864 }
1865
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866 /* Make sure current_state is valid */
1867 if (INVALID_STATE(&current_state))
1868 validate_current_state();
1869
1870 /*
1871 * Skip from the current column to "col", get the attributes for "col".
1872 */
1873 while (current_col <= col)
1874 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001875 attr = syn_current_attr(FALSE, TRUE, can_spell,
1876 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001877 ++current_col;
1878 }
1879
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 return attr;
1881}
1882
1883/*
1884 * Get syntax attributes for current_lnum, current_col.
1885 */
1886 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001887syn_current_attr(
1888 int syncing, /* When 1: called for syncing */
1889 int displaying, /* result will be displayed */
1890 int *can_spell, /* return: do spell checking */
1891 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892{
1893 int syn_id;
1894 lpos_T endpos; /* was: char_u *endp; */
1895 lpos_T hl_startpos; /* was: int hl_startcol; */
1896 lpos_T hl_endpos;
1897 lpos_T eos_pos; /* end-of-start match (start region) */
1898 lpos_T eoe_pos; /* end-of-end pattern */
1899 int end_idx; /* group ID for end pattern */
1900 int idx;
1901 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001902 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903 int startcol;
1904 int endcol;
1905 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001906 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001907 short *next_list;
1908 int found_match; /* found usable match */
1909 static int try_next_column = FALSE; /* must try in next col */
1910 int do_keywords;
1911 regmmatch_T regmatch;
1912 lpos_T pos;
1913 int lc_col;
1914 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001915 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001916 char_u *line; /* current line. NOTE: becomes invalid after
1917 looking for a pattern match! */
1918
1919 /* variables for zero-width matches that have a "nextgroup" argument */
1920 int keep_next_list;
1921 int zero_width_next_list = FALSE;
1922 garray_T zero_width_next_ga;
1923
1924 /*
1925 * No character, no attributes! Past end of line?
1926 * Do try matching with an empty line (could be the start of a region).
1927 */
1928 line = syn_getcurline();
1929 if (line[current_col] == NUL && current_col != 0)
1930 {
1931 /*
1932 * If we found a match after the last column, use it.
1933 */
1934 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1935 && next_match_col != MAXCOL)
1936 (void)push_next_match(NULL);
1937
1938 current_finished = TRUE;
1939 current_state_stored = FALSE;
1940 return 0;
1941 }
1942
1943 /* if the current or next character is NUL, we will finish the line now */
1944 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1945 {
1946 current_finished = TRUE;
1947 current_state_stored = FALSE;
1948 }
1949
1950 /*
1951 * When in the previous column there was a match but it could not be used
1952 * (empty match or already matched in this column) need to try again in
1953 * the next column.
1954 */
1955 if (try_next_column)
1956 {
1957 next_match_idx = -1;
1958 try_next_column = FALSE;
1959 }
1960
1961 /* Only check for keywords when not syncing and there are some. */
1962 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001963 && (syn_block->b_keywtab.ht_used > 0
1964 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965
1966 /* Init the list of zero-width matches with a nextlist. This is used to
1967 * avoid matching the same item in the same position twice. */
1968 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1969
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001970 /* use syntax iskeyword option */
1971 save_chartab(buf_chartab);
1972
Bram Moolenaar071d4272004-06-13 20:20:40 +00001973 /*
1974 * Repeat matching keywords and patterns, to find contained items at the
1975 * same column. This stops when there are no extra matches at the current
1976 * column.
1977 */
1978 do
1979 {
1980 found_match = FALSE;
1981 keep_next_list = FALSE;
1982 syn_id = 0;
1983
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001984
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 /*
1986 * 1. Check for a current state.
1987 * Only when there is no current state, or if the current state may
1988 * contain other things, we need to check for keywords and patterns.
1989 * Always need to check for contained items if some item has the
1990 * "containedin" argument (takes extra time!).
1991 */
1992 if (current_state.ga_len)
1993 cur_si = &CUR_STATE(current_state.ga_len - 1);
1994 else
1995 cur_si = NULL;
1996
Bram Moolenaar860cae12010-06-05 23:22:07 +02001997 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 || cur_si->si_cont_list != NULL)
1999 {
2000 /*
2001 * 2. Check for keywords, if on a keyword char after a non-keyword
2002 * char. Don't do this when syncing.
2003 */
2004 if (do_keywords)
2005 {
2006 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002007 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002009 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010#ifdef FEAT_MBYTE
2011 - (has_mbyte
2012 ? (*mb_head_off)(line, line + current_col - 1)
2013 : 0)
2014#endif
2015 , syn_buf)))
2016 {
2017 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002018 &endcol, &flags, &next_list, cur_si,
2019 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002020 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 {
2022 if (push_current_state(KEYWORD_IDX) == OK)
2023 {
2024 cur_si = &CUR_STATE(current_state.ga_len - 1);
2025 cur_si->si_m_startcol = current_col;
2026 cur_si->si_h_startpos.lnum = current_lnum;
2027 cur_si->si_h_startpos.col = 0; /* starts right away */
2028 cur_si->si_m_endpos.lnum = current_lnum;
2029 cur_si->si_m_endpos.col = endcol;
2030 cur_si->si_h_endpos.lnum = current_lnum;
2031 cur_si->si_h_endpos.col = endcol;
2032 cur_si->si_ends = TRUE;
2033 cur_si->si_end_idx = 0;
2034 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002035#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002036 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002037 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002038 if (current_state.ga_len > 1)
2039 cur_si->si_flags |=
2040 CUR_STATE(current_state.ga_len - 2).si_flags
2041 & HL_CONCEAL;
2042#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002043 cur_si->si_id = syn_id;
2044 cur_si->si_trans_id = syn_id;
2045 if (flags & HL_TRANSP)
2046 {
2047 if (current_state.ga_len < 2)
2048 {
2049 cur_si->si_attr = 0;
2050 cur_si->si_trans_id = 0;
2051 }
2052 else
2053 {
2054 cur_si->si_attr = CUR_STATE(
2055 current_state.ga_len - 2).si_attr;
2056 cur_si->si_trans_id = CUR_STATE(
2057 current_state.ga_len - 2).si_trans_id;
2058 }
2059 }
2060 else
2061 cur_si->si_attr = syn_id2attr(syn_id);
2062 cur_si->si_cont_list = NULL;
2063 cur_si->si_next_list = next_list;
2064 check_keepend();
2065 }
2066 else
2067 vim_free(next_list);
2068 }
2069 }
2070 }
2071
2072 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002073 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002075 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 {
2077 /*
2078 * If we didn't check for a match yet, or we are past it, check
2079 * for any match with a pattern.
2080 */
2081 if (next_match_idx < 0 || next_match_col < (int)current_col)
2082 {
2083 /*
2084 * Check all relevant patterns for a match at this
2085 * position. This is complicated, because matching with a
2086 * pattern takes quite a bit of time, thus we want to
2087 * avoid doing it when it's not needed.
2088 */
2089 next_match_idx = 0; /* no match in this line yet */
2090 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002091 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002092 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002093 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002094 if ( spp->sp_syncing == syncing
2095 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2096 && (spp->sp_type == SPTYPE_MATCH
2097 || spp->sp_type == SPTYPE_START)
2098 && (current_next_list != NULL
2099 ? in_id_list(NULL, current_next_list,
2100 &spp->sp_syn, 0)
2101 : (cur_si == NULL
2102 ? !(spp->sp_flags & HL_CONTAINED)
2103 : in_id_list(cur_si,
2104 cur_si->si_cont_list, &spp->sp_syn,
2105 spp->sp_flags & HL_CONTAINED))))
2106 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002107 int r;
2108
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109 /* If we already tried matching in this line, and
2110 * there isn't a match before next_match_col, skip
2111 * this item. */
2112 if (spp->sp_line_id == current_line_id
2113 && spp->sp_startcol >= next_match_col)
2114 continue;
2115 spp->sp_line_id = current_line_id;
2116
2117 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2118 if (lc_col < 0)
2119 lc_col = 0;
2120
2121 regmatch.rmm_ic = spp->sp_ic;
2122 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002123 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002124 current_lnum,
2125 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002126 IF_SYN_TIME(&spp->sp_time));
2127 spp->sp_prog = regmatch.regprog;
2128 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002129 {
2130 /* no match in this line, try another one */
2131 spp->sp_startcol = MAXCOL;
2132 continue;
2133 }
2134
2135 /*
2136 * Compute the first column of the match.
2137 */
2138 syn_add_start_off(&pos, &regmatch,
2139 spp, SPO_MS_OFF, -1);
2140 if (pos.lnum > current_lnum)
2141 {
2142 /* must have used end of match in a next line,
2143 * we can't handle that */
2144 spp->sp_startcol = MAXCOL;
2145 continue;
2146 }
2147 startcol = pos.col;
2148
2149 /* remember the next column where this pattern
2150 * matches in the current line */
2151 spp->sp_startcol = startcol;
2152
2153 /*
2154 * If a previously found match starts at a lower
2155 * column number, don't use this one.
2156 */
2157 if (startcol >= next_match_col)
2158 continue;
2159
2160 /*
2161 * If we matched this pattern at this position
2162 * before, skip it. Must retry in the next
2163 * column, because it may match from there.
2164 */
2165 if (did_match_already(idx, &zero_width_next_ga))
2166 {
2167 try_next_column = TRUE;
2168 continue;
2169 }
2170
2171 endpos.lnum = regmatch.endpos[0].lnum;
2172 endpos.col = regmatch.endpos[0].col;
2173
2174 /* Compute the highlight start. */
2175 syn_add_start_off(&hl_startpos, &regmatch,
2176 spp, SPO_HS_OFF, -1);
2177
2178 /* Compute the region start. */
2179 /* Default is to use the end of the match. */
2180 syn_add_end_off(&eos_pos, &regmatch,
2181 spp, SPO_RS_OFF, 0);
2182
2183 /*
2184 * Grab the external submatches before they get
2185 * overwritten. Reference count doesn't change.
2186 */
2187 unref_extmatch(cur_extmatch);
2188 cur_extmatch = re_extmatch_out;
2189 re_extmatch_out = NULL;
2190
2191 flags = 0;
2192 eoe_pos.lnum = 0; /* avoid warning */
2193 eoe_pos.col = 0;
2194 end_idx = 0;
2195 hl_endpos.lnum = 0;
2196
2197 /*
2198 * For a "oneline" the end must be found in the
2199 * same line too. Search for it after the end of
2200 * the match with the start pattern. Set the
2201 * resulting end positions at the same time.
2202 */
2203 if (spp->sp_type == SPTYPE_START
2204 && (spp->sp_flags & HL_ONELINE))
2205 {
2206 lpos_T startpos;
2207
2208 startpos = endpos;
2209 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2210 &flags, &eoe_pos, &end_idx, cur_extmatch);
2211 if (endpos.lnum == 0)
2212 continue; /* not found */
2213 }
2214
2215 /*
2216 * For a "match" the size must be > 0 after the
2217 * end offset needs has been added. Except when
2218 * syncing.
2219 */
2220 else if (spp->sp_type == SPTYPE_MATCH)
2221 {
2222 syn_add_end_off(&hl_endpos, &regmatch, spp,
2223 SPO_HE_OFF, 0);
2224 syn_add_end_off(&endpos, &regmatch, spp,
2225 SPO_ME_OFF, 0);
2226 if (endpos.lnum == current_lnum
2227 && (int)endpos.col + syncing < startcol)
2228 {
2229 /*
2230 * If an empty string is matched, may need
2231 * to try matching again at next column.
2232 */
2233 if (regmatch.startpos[0].col
2234 == regmatch.endpos[0].col)
2235 try_next_column = TRUE;
2236 continue;
2237 }
2238 }
2239
2240 /*
2241 * keep the best match so far in next_match_*
2242 */
2243 /* Highlighting must start after startpos and end
2244 * before endpos. */
2245 if (hl_startpos.lnum == current_lnum
2246 && (int)hl_startpos.col < startcol)
2247 hl_startpos.col = startcol;
2248 limit_pos_zero(&hl_endpos, &endpos);
2249
2250 next_match_idx = idx;
2251 next_match_col = startcol;
2252 next_match_m_endpos = endpos;
2253 next_match_h_endpos = hl_endpos;
2254 next_match_h_startpos = hl_startpos;
2255 next_match_flags = flags;
2256 next_match_eos_pos = eos_pos;
2257 next_match_eoe_pos = eoe_pos;
2258 next_match_end_idx = end_idx;
2259 unref_extmatch(next_match_extmatch);
2260 next_match_extmatch = cur_extmatch;
2261 cur_extmatch = NULL;
2262 }
2263 }
2264 }
2265
2266 /*
2267 * If we found a match at the current column, use it.
2268 */
2269 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2270 {
2271 synpat_T *lspp;
2272
2273 /* When a zero-width item matched which has a nextgroup,
2274 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002275 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 if (next_match_m_endpos.lnum == current_lnum
2277 && next_match_m_endpos.col == current_col
2278 && lspp->sp_next_list != NULL)
2279 {
2280 current_next_list = lspp->sp_next_list;
2281 current_next_flags = lspp->sp_flags;
2282 keep_next_list = TRUE;
2283 zero_width_next_list = TRUE;
2284
2285 /* Add the index to a list, so that we can check
2286 * later that we don't match it again (and cause an
2287 * endless loop). */
2288 if (ga_grow(&zero_width_next_ga, 1) == OK)
2289 {
2290 ((int *)(zero_width_next_ga.ga_data))
2291 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292 }
2293 next_match_idx = -1;
2294 }
2295 else
2296 cur_si = push_next_match(cur_si);
2297 found_match = TRUE;
2298 }
2299 }
2300 }
2301
2302 /*
2303 * Handle searching for nextgroup match.
2304 */
2305 if (current_next_list != NULL && !keep_next_list)
2306 {
2307 /*
2308 * If a nextgroup was not found, continue looking for one if:
2309 * - this is an empty line and the "skipempty" option was given
2310 * - we are on white space and the "skipwhite" option was given
2311 */
2312 if (!found_match)
2313 {
2314 line = syn_getcurline();
2315 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002316 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002317 || ((current_next_flags & HL_SKIPEMPTY)
2318 && *line == NUL))
2319 break;
2320 }
2321
2322 /*
2323 * If a nextgroup was found: Use it, and continue looking for
2324 * contained matches.
2325 * If a nextgroup was not found: Continue looking for a normal
2326 * match.
2327 * When did set current_next_list for a zero-width item and no
2328 * match was found don't loop (would get stuck).
2329 */
2330 current_next_list = NULL;
2331 next_match_idx = -1;
2332 if (!zero_width_next_list)
2333 found_match = TRUE;
2334 }
2335
2336 } while (found_match);
2337
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002338 restore_chartab(buf_chartab);
2339
Bram Moolenaar071d4272004-06-13 20:20:40 +00002340 /*
2341 * Use attributes from the current state, if within its highlighting.
2342 * If not, use attributes from the current-but-one state, etc.
2343 */
2344 current_attr = 0;
2345#ifdef FEAT_EVAL
2346 current_id = 0;
2347 current_trans_id = 0;
2348#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002349#ifdef FEAT_CONCEAL
2350 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002351 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002352#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002353 if (cur_si != NULL)
2354 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002355#ifndef FEAT_EVAL
2356 int current_trans_id = 0;
2357#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2359 {
2360 sip = &CUR_STATE(idx);
2361 if ((current_lnum > sip->si_h_startpos.lnum
2362 || (current_lnum == sip->si_h_startpos.lnum
2363 && current_col >= sip->si_h_startpos.col))
2364 && (sip->si_h_endpos.lnum == 0
2365 || current_lnum < sip->si_h_endpos.lnum
2366 || (current_lnum == sip->si_h_endpos.lnum
2367 && current_col < sip->si_h_endpos.col)))
2368 {
2369 current_attr = sip->si_attr;
2370#ifdef FEAT_EVAL
2371 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002372#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002373 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002374#ifdef FEAT_CONCEAL
2375 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002376 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002377 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002378#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002379 break;
2380 }
2381 }
2382
Bram Moolenaar217ad922005-03-20 22:37:15 +00002383 if (can_spell != NULL)
2384 {
2385 struct sp_syn sps;
2386
2387 /*
2388 * set "can_spell" to TRUE if spell checking is supposed to be
2389 * done in the current item.
2390 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002391 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002392 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002393 /* There is no @Spell cluster: Do spelling for items without
2394 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002395 if (syn_block->b_nospell_cluster_id == 0
2396 || current_trans_id == 0)
2397 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002398 else
2399 {
2400 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002401 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002402 sps.cont_in_list = NULL;
2403 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2404 }
2405 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002406 else
2407 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002408 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002409 * the @Spell cluster. But not when @NoSpell is also there.
2410 * At the toplevel only spell check when ":syn spell toplevel"
2411 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002412 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002413 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002414 else
2415 {
2416 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002417 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002418 sps.cont_in_list = NULL;
2419 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2420
Bram Moolenaar860cae12010-06-05 23:22:07 +02002421 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002422 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2425 *can_spell = FALSE;
2426 }
2427 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002428 }
2429 }
2430
2431
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432 /*
2433 * Check for end of current state (and the states before it) at the
2434 * next column. Don't do this for syncing, because we would miss a
2435 * single character match.
2436 * First check if the current state ends at the current column. It
2437 * may be for an empty match and a containing item might end in the
2438 * current column.
2439 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002440 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441 {
2442 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002443 if (current_state.ga_len > 0
2444 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002445 {
2446 ++current_col;
2447 check_state_ends();
2448 --current_col;
2449 }
2450 }
2451 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002452 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002453 /* Default: Only do spelling when there is no @Spell cluster or when
2454 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002455 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2456 ? (syn_block->b_spell_cluster_id == 0)
2457 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002458
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002459 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460 if (current_next_list != NULL
2461 && syn_getcurline()[current_col + 1] == NUL
2462 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2463 current_next_list = NULL;
2464
2465 if (zero_width_next_ga.ga_len > 0)
2466 ga_clear(&zero_width_next_ga);
2467
2468 /* No longer need external matches. But keep next_match_extmatch. */
2469 unref_extmatch(re_extmatch_out);
2470 re_extmatch_out = NULL;
2471 unref_extmatch(cur_extmatch);
2472
2473 return current_attr;
2474}
2475
2476
2477/*
2478 * Check if we already matched pattern "idx" at the current column.
2479 */
2480 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002481did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482{
2483 int i;
2484
2485 for (i = current_state.ga_len; --i >= 0; )
2486 if (CUR_STATE(i).si_m_startcol == (int)current_col
2487 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2488 && CUR_STATE(i).si_idx == idx)
2489 return TRUE;
2490
2491 /* Zero-width matches with a nextgroup argument are not put on the syntax
2492 * stack, and can only be matched once anyway. */
2493 for (i = gap->ga_len; --i >= 0; )
2494 if (((int *)(gap->ga_data))[i] == idx)
2495 return TRUE;
2496
2497 return FALSE;
2498}
2499
2500/*
2501 * Push the next match onto the stack.
2502 */
2503 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002504push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505{
2506 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002507#ifdef FEAT_CONCEAL
2508 int save_flags;
2509#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510
Bram Moolenaar860cae12010-06-05 23:22:07 +02002511 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512
2513 /*
2514 * Push the item in current_state stack;
2515 */
2516 if (push_current_state(next_match_idx) == OK)
2517 {
2518 /*
2519 * If it's a start-skip-end type that crosses lines, figure out how
2520 * much it continues in this line. Otherwise just fill in the length.
2521 */
2522 cur_si = &CUR_STATE(current_state.ga_len - 1);
2523 cur_si->si_h_startpos = next_match_h_startpos;
2524 cur_si->si_m_startcol = current_col;
2525 cur_si->si_m_lnum = current_lnum;
2526 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002527#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002528 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002529 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002530 if (current_state.ga_len > 1)
2531 cur_si->si_flags |=
2532 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2533#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534 cur_si->si_next_list = spp->sp_next_list;
2535 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2536 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2537 {
2538 /* Try to find the end pattern in the current line */
2539 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2540 check_keepend();
2541 }
2542 else
2543 {
2544 cur_si->si_m_endpos = next_match_m_endpos;
2545 cur_si->si_h_endpos = next_match_h_endpos;
2546 cur_si->si_ends = TRUE;
2547 cur_si->si_flags |= next_match_flags;
2548 cur_si->si_eoe_pos = next_match_eoe_pos;
2549 cur_si->si_end_idx = next_match_end_idx;
2550 }
2551 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2552 keepend_level = current_state.ga_len - 1;
2553 check_keepend();
2554 update_si_attr(current_state.ga_len - 1);
2555
Bram Moolenaar860cae12010-06-05 23:22:07 +02002556#ifdef FEAT_CONCEAL
2557 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2558#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 /*
2560 * If the start pattern has another highlight group, push another item
2561 * on the stack for the start pattern.
2562 */
2563 if ( spp->sp_type == SPTYPE_START
2564 && spp->sp_syn_match_id != 0
2565 && push_current_state(next_match_idx) == OK)
2566 {
2567 cur_si = &CUR_STATE(current_state.ga_len - 1);
2568 cur_si->si_h_startpos = next_match_h_startpos;
2569 cur_si->si_m_startcol = current_col;
2570 cur_si->si_m_lnum = current_lnum;
2571 cur_si->si_m_endpos = next_match_eos_pos;
2572 cur_si->si_h_endpos = next_match_eos_pos;
2573 cur_si->si_ends = TRUE;
2574 cur_si->si_end_idx = 0;
2575 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002576#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002577 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002578 cur_si->si_flags |= save_flags;
2579 if (cur_si->si_flags & HL_CONCEALENDS)
2580 cur_si->si_flags |= HL_CONCEAL;
2581#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002582 cur_si->si_next_list = NULL;
2583 check_keepend();
2584 update_si_attr(current_state.ga_len - 1);
2585 }
2586 }
2587
2588 next_match_idx = -1; /* try other match next time */
2589
2590 return cur_si;
2591}
2592
2593/*
2594 * Check for end of current state (and the states before it).
2595 */
2596 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002597check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598{
2599 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002600 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601
2602 cur_si = &CUR_STATE(current_state.ga_len - 1);
2603 for (;;)
2604 {
2605 if (cur_si->si_ends
2606 && (cur_si->si_m_endpos.lnum < current_lnum
2607 || (cur_si->si_m_endpos.lnum == current_lnum
2608 && cur_si->si_m_endpos.col <= current_col)))
2609 {
2610 /*
2611 * If there is an end pattern group ID, highlight the end pattern
2612 * now. No need to pop the current item from the stack.
2613 * Only do this if the end pattern continues beyond the current
2614 * position.
2615 */
2616 if (cur_si->si_end_idx
2617 && (cur_si->si_eoe_pos.lnum > current_lnum
2618 || (cur_si->si_eoe_pos.lnum == current_lnum
2619 && cur_si->si_eoe_pos.col > current_col)))
2620 {
2621 cur_si->si_idx = cur_si->si_end_idx;
2622 cur_si->si_end_idx = 0;
2623 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2624 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2625 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002626#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002627 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002628 if (cur_si->si_flags & HL_CONCEALENDS)
2629 cur_si->si_flags |= HL_CONCEAL;
2630#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002631 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002632
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002633 /* nextgroup= should not match in the end pattern */
2634 current_next_list = NULL;
2635
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002636 /* what matches next may be different now, clear it */
2637 next_match_idx = 0;
2638 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002639 break;
2640 }
2641 else
2642 {
2643 /* handle next_list, unless at end of line and no "skipnl" or
2644 * "skipempty" */
2645 current_next_list = cur_si->si_next_list;
2646 current_next_flags = cur_si->si_flags;
2647 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2648 && syn_getcurline()[current_col] == NUL)
2649 current_next_list = NULL;
2650
2651 /* When the ended item has "extend", another item with
2652 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002653 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654
2655 pop_current_state();
2656
2657 if (current_state.ga_len == 0)
2658 break;
2659
Bram Moolenaar81993f42008-01-11 20:27:45 +00002660 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002661 {
2662 syn_update_ends(FALSE);
2663 if (current_state.ga_len == 0)
2664 break;
2665 }
2666
2667 cur_si = &CUR_STATE(current_state.ga_len - 1);
2668
2669 /*
2670 * Only for a region the search for the end continues after
2671 * the end of the contained item. If the contained match
2672 * included the end-of-line, break here, the region continues.
2673 * Don't do this when:
2674 * - "keepend" is used for the contained item
2675 * - not at the end of the line (could be end="x$"me=e-1).
2676 * - "excludenl" is used (HL_HAS_EOL won't be set)
2677 */
2678 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002679 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 == SPTYPE_START
2681 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2682 {
2683 update_si_end(cur_si, (int)current_col, TRUE);
2684 check_keepend();
2685 if ((current_next_flags & HL_HAS_EOL)
2686 && keepend_level < 0
2687 && syn_getcurline()[current_col] == NUL)
2688 break;
2689 }
2690 }
2691 }
2692 else
2693 break;
2694 }
2695}
2696
2697/*
2698 * Update an entry in the current_state stack for a match or region. This
2699 * fills in si_attr, si_next_list and si_cont_list.
2700 */
2701 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002702update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703{
2704 stateitem_T *sip = &CUR_STATE(idx);
2705 synpat_T *spp;
2706
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002707 /* This should not happen... */
2708 if (sip->si_idx < 0)
2709 return;
2710
Bram Moolenaar860cae12010-06-05 23:22:07 +02002711 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002712 if (sip->si_flags & HL_MATCH)
2713 sip->si_id = spp->sp_syn_match_id;
2714 else
2715 sip->si_id = spp->sp_syn.id;
2716 sip->si_attr = syn_id2attr(sip->si_id);
2717 sip->si_trans_id = sip->si_id;
2718 if (sip->si_flags & HL_MATCH)
2719 sip->si_cont_list = NULL;
2720 else
2721 sip->si_cont_list = spp->sp_cont_list;
2722
2723 /*
2724 * For transparent items, take attr from outer item.
2725 * Also take cont_list, if there is none.
2726 * Don't do this for the matchgroup of a start or end pattern.
2727 */
2728 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2729 {
2730 if (idx == 0)
2731 {
2732 sip->si_attr = 0;
2733 sip->si_trans_id = 0;
2734 if (sip->si_cont_list == NULL)
2735 sip->si_cont_list = ID_LIST_ALL;
2736 }
2737 else
2738 {
2739 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2740 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002741 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2742 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002743 if (sip->si_cont_list == NULL)
2744 {
2745 sip->si_flags |= HL_TRANS_CONT;
2746 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2747 }
2748 }
2749 }
2750}
2751
2752/*
2753 * Check the current stack for patterns with "keepend" flag.
2754 * Propagate the match-end to contained items, until a "skipend" item is found.
2755 */
2756 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002757check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758{
2759 int i;
2760 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002761 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762 stateitem_T *sip;
2763
2764 /*
2765 * This check can consume a lot of time; only do it from the level where
2766 * there really is a keepend.
2767 */
2768 if (keepend_level < 0)
2769 return;
2770
2771 /*
2772 * Find the last index of an "extend" item. "keepend" items before that
2773 * won't do anything. If there is no "extend" item "i" will be
2774 * "keepend_level" and all "keepend" items will work normally.
2775 */
2776 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2777 if (CUR_STATE(i).si_flags & HL_EXTEND)
2778 break;
2779
2780 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002781 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002782 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002783 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784 for ( ; i < current_state.ga_len; ++i)
2785 {
2786 sip = &CUR_STATE(i);
2787 if (maxpos.lnum != 0)
2788 {
2789 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002790 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2792 sip->si_ends = TRUE;
2793 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002794 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2795 {
2796 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 || maxpos.lnum > sip->si_m_endpos.lnum
2798 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002799 && maxpos.col > sip->si_m_endpos.col))
2800 maxpos = sip->si_m_endpos;
2801 if (maxpos_h.lnum == 0
2802 || maxpos_h.lnum > sip->si_h_endpos.lnum
2803 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2804 && maxpos_h.col > sip->si_h_endpos.col))
2805 maxpos_h = sip->si_h_endpos;
2806 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 }
2808}
2809
2810/*
2811 * Update an entry in the current_state stack for a start-skip-end pattern.
2812 * This finds the end of the current item, if it's in the current line.
2813 *
2814 * Return the flags for the matched END.
2815 */
2816 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002817update_si_end(
2818 stateitem_T *sip,
2819 int startcol, /* where to start searching for the end */
2820 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002821{
2822 lpos_T startpos;
2823 lpos_T endpos;
2824 lpos_T hl_endpos;
2825 lpos_T end_endpos;
2826 int end_idx;
2827
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002828 /* return quickly for a keyword */
2829 if (sip->si_idx < 0)
2830 return;
2831
Bram Moolenaar071d4272004-06-13 20:20:40 +00002832 /* Don't update when it's already done. Can be a match of an end pattern
2833 * that started in a previous line. Watch out: can also be a "keepend"
2834 * from a containing item. */
2835 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2836 return;
2837
2838 /*
2839 * We need to find the end of the region. It may continue in the next
2840 * line.
2841 */
2842 end_idx = 0;
2843 startpos.lnum = current_lnum;
2844 startpos.col = startcol;
2845 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2846 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2847
2848 if (endpos.lnum == 0)
2849 {
2850 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002851 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852 {
2853 /* a "oneline" never continues in the next line */
2854 sip->si_ends = TRUE;
2855 sip->si_m_endpos.lnum = current_lnum;
2856 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2857 }
2858 else
2859 {
2860 /* continues in the next line */
2861 sip->si_ends = FALSE;
2862 sip->si_m_endpos.lnum = 0;
2863 }
2864 sip->si_h_endpos = sip->si_m_endpos;
2865 }
2866 else
2867 {
2868 /* match within this line */
2869 sip->si_m_endpos = endpos;
2870 sip->si_h_endpos = hl_endpos;
2871 sip->si_eoe_pos = end_endpos;
2872 sip->si_ends = TRUE;
2873 sip->si_end_idx = end_idx;
2874 }
2875}
2876
2877/*
2878 * Add a new state to the current state stack.
2879 * It is cleared and the index set to "idx".
2880 * Return FAIL if it's not possible (out of memory).
2881 */
2882 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002883push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884{
2885 if (ga_grow(&current_state, 1) == FAIL)
2886 return FAIL;
2887 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2888 CUR_STATE(current_state.ga_len).si_idx = idx;
2889 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890 return OK;
2891}
2892
2893/*
2894 * Remove a state from the current_state stack.
2895 */
2896 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002897pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002898{
2899 if (current_state.ga_len)
2900 {
2901 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2902 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 }
2904 /* after the end of a pattern, try matching a keyword or pattern */
2905 next_match_idx = -1;
2906
2907 /* if first state with "keepend" is popped, reset keepend_level */
2908 if (keepend_level >= current_state.ga_len)
2909 keepend_level = -1;
2910}
2911
2912/*
2913 * Find the end of a start/skip/end syntax region after "startpos".
2914 * Only checks one line.
2915 * Also handles a match item that continued from a previous line.
2916 * If not found, the syntax item continues in the next line. m_endpos->lnum
2917 * will be 0.
2918 * If found, the end of the region and the end of the highlighting is
2919 * computed.
2920 */
2921 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002922find_endpos(
2923 int idx, /* index of the pattern */
2924 lpos_T *startpos, /* where to start looking for an END match */
2925 lpos_T *m_endpos, /* return: end of match */
2926 lpos_T *hl_endpos, /* return: end of highlighting */
2927 long *flagsp, /* return: flags of matching END */
2928 lpos_T *end_endpos, /* return: end of end pattern match */
2929 int *end_idx, /* return: group ID for end pat. match, or 0 */
2930 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002931{
2932 colnr_T matchcol;
2933 synpat_T *spp, *spp_skip;
2934 int start_idx;
2935 int best_idx;
2936 regmmatch_T regmatch;
2937 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2938 lpos_T pos;
2939 char_u *line;
2940 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002941 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002942
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002943 /* just in case we are invoked for a keyword */
2944 if (idx < 0)
2945 return;
2946
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947 /*
2948 * Check for being called with a START pattern.
2949 * Can happen with a match that continues to the next line, because it
2950 * contained a region.
2951 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002952 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953 if (spp->sp_type != SPTYPE_START)
2954 {
2955 *hl_endpos = *startpos;
2956 return;
2957 }
2958
2959 /*
2960 * Find the SKIP or first END pattern after the last START pattern.
2961 */
2962 for (;;)
2963 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002964 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002965 if (spp->sp_type != SPTYPE_START)
2966 break;
2967 ++idx;
2968 }
2969
2970 /*
2971 * Lookup the SKIP pattern (if present)
2972 */
2973 if (spp->sp_type == SPTYPE_SKIP)
2974 {
2975 spp_skip = spp;
2976 ++idx;
2977 }
2978 else
2979 spp_skip = NULL;
2980
2981 /* Setup external matches for syn_regexec(). */
2982 unref_extmatch(re_extmatch_in);
2983 re_extmatch_in = ref_extmatch(start_ext);
2984
2985 matchcol = startpos->col; /* start looking for a match at sstart */
2986 start_idx = idx; /* remember the first END pattern. */
2987 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002988
2989 /* use syntax iskeyword option */
2990 save_chartab(buf_chartab);
2991
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 for (;;)
2993 {
2994 /*
2995 * Find end pattern that matches first after "matchcol".
2996 */
2997 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002998 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999 {
3000 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003001 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002
Bram Moolenaar860cae12010-06-05 23:22:07 +02003003 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3005 break;
3006 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3007 if (lc_col < 0)
3008 lc_col = 0;
3009
3010 regmatch.rmm_ic = spp->sp_ic;
3011 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003012 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3013 IF_SYN_TIME(&spp->sp_time));
3014 spp->sp_prog = regmatch.regprog;
3015 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 {
3017 if (best_idx == -1 || regmatch.startpos[0].col
3018 < best_regmatch.startpos[0].col)
3019 {
3020 best_idx = idx;
3021 best_regmatch.startpos[0] = regmatch.startpos[0];
3022 best_regmatch.endpos[0] = regmatch.endpos[0];
3023 }
3024 }
3025 }
3026
3027 /*
3028 * If all end patterns have been tried, and there is no match, the
3029 * item continues until end-of-line.
3030 */
3031 if (best_idx == -1)
3032 break;
3033
3034 /*
3035 * If the skip pattern matches before the end pattern,
3036 * continue searching after the skip pattern.
3037 */
3038 if (spp_skip != NULL)
3039 {
3040 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003041 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042
3043 if (lc_col < 0)
3044 lc_col = 0;
3045 regmatch.rmm_ic = spp_skip->sp_ic;
3046 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003047 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3048 IF_SYN_TIME(&spp_skip->sp_time));
3049 spp_skip->sp_prog = regmatch.regprog;
3050 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051 <= best_regmatch.startpos[0].col)
3052 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003053 int line_len;
3054
Bram Moolenaar071d4272004-06-13 20:20:40 +00003055 /* Add offset to skip pattern match */
3056 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3057
3058 /* If the skip pattern goes on to the next line, there is no
3059 * match with an end pattern in this line. */
3060 if (pos.lnum > startpos->lnum)
3061 break;
3062
3063 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003064 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065
3066 /* take care of an empty match or negative offset */
3067 if (pos.col <= matchcol)
3068 ++matchcol;
3069 else if (pos.col <= regmatch.endpos[0].col)
3070 matchcol = pos.col;
3071 else
3072 /* Be careful not to jump over the NUL at the end-of-line */
3073 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003074 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003075 ++matchcol)
3076 ;
3077
3078 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003079 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 break;
3081
3082 continue; /* start with first end pattern again */
3083 }
3084 }
3085
3086 /*
3087 * Match from start pattern to end pattern.
3088 * Correct for match and highlight offset of end pattern.
3089 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003090 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3092 /* can't end before the start */
3093 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3094 m_endpos->col = startpos->col;
3095
3096 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3097 /* can't end before the start */
3098 if (end_endpos->lnum == startpos->lnum
3099 && end_endpos->col < startpos->col)
3100 end_endpos->col = startpos->col;
3101 /* can't end after the match */
3102 limit_pos(end_endpos, m_endpos);
3103
3104 /*
3105 * If the end group is highlighted differently, adjust the pointers.
3106 */
3107 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3108 {
3109 *end_idx = best_idx;
3110 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3111 {
3112 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3113 hl_endpos->col = best_regmatch.endpos[0].col;
3114 }
3115 else
3116 {
3117 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3118 hl_endpos->col = best_regmatch.startpos[0].col;
3119 }
3120 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3121
3122 /* can't end before the start */
3123 if (hl_endpos->lnum == startpos->lnum
3124 && hl_endpos->col < startpos->col)
3125 hl_endpos->col = startpos->col;
3126 limit_pos(hl_endpos, m_endpos);
3127
3128 /* now the match ends where the highlighting ends, it is turned
3129 * into the matchgroup for the end */
3130 *m_endpos = *hl_endpos;
3131 }
3132 else
3133 {
3134 *end_idx = 0;
3135 *hl_endpos = *end_endpos;
3136 }
3137
3138 *flagsp = spp->sp_flags;
3139
3140 had_match = TRUE;
3141 break;
3142 }
3143
3144 /* no match for an END pattern in this line */
3145 if (!had_match)
3146 m_endpos->lnum = 0;
3147
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003148 restore_chartab(buf_chartab);
3149
Bram Moolenaar071d4272004-06-13 20:20:40 +00003150 /* Remove external matches. */
3151 unref_extmatch(re_extmatch_in);
3152 re_extmatch_in = NULL;
3153}
3154
3155/*
3156 * Limit "pos" not to be after "limit".
3157 */
3158 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003159limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160{
3161 if (pos->lnum > limit->lnum)
3162 *pos = *limit;
3163 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3164 pos->col = limit->col;
3165}
3166
3167/*
3168 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3169 */
3170 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003171limit_pos_zero(
3172 lpos_T *pos,
3173 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174{
3175 if (pos->lnum == 0)
3176 *pos = *limit;
3177 else
3178 limit_pos(pos, limit);
3179}
3180
3181/*
3182 * Add offset to matched text for end of match or highlight.
3183 */
3184 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003185syn_add_end_off(
3186 lpos_T *result, /* returned position */
3187 regmmatch_T *regmatch, /* start/end of match */
3188 synpat_T *spp, /* matched pattern */
3189 int idx, /* index of offset */
3190 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003191{
3192 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003193 int off;
3194 char_u *base;
3195 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196
3197 if (spp->sp_off_flags & (1 << idx))
3198 {
3199 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003200 col = regmatch->startpos[0].col;
3201 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202 }
3203 else
3204 {
3205 result->lnum = regmatch->endpos[0].lnum;
3206 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003207 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003208 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003209 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3210 * is a matchgroup. Watch out for match with last NL in the buffer. */
3211 if (result->lnum > syn_buf->b_ml.ml_line_count)
3212 col = 0;
3213 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003214 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003215 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3216 p = base + col;
3217 if (off > 0)
3218 {
3219 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003220 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003221 }
3222 else if (off < 0)
3223 {
3224 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003225 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003226 }
3227 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003228 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003229 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230}
3231
3232/*
3233 * Add offset to matched text for start of match or highlight.
3234 * Avoid resulting column to become negative.
3235 */
3236 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003237syn_add_start_off(
3238 lpos_T *result, /* returned position */
3239 regmmatch_T *regmatch, /* start/end of match */
3240 synpat_T *spp,
3241 int idx,
3242 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243{
3244 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003245 int off;
3246 char_u *base;
3247 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248
3249 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3250 {
3251 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003252 col = regmatch->endpos[0].col;
3253 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 }
3255 else
3256 {
3257 result->lnum = regmatch->startpos[0].lnum;
3258 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003259 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003261 if (result->lnum > syn_buf->b_ml.ml_line_count)
3262 {
3263 /* a "\n" at the end of the pattern may take us below the last line */
3264 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003265 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003266 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003267 if (off != 0)
3268 {
3269 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3270 p = base + col;
3271 if (off > 0)
3272 {
3273 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003274 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003275 }
3276 else if (off < 0)
3277 {
3278 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003279 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003280 }
3281 col = (int)(p - base);
3282 }
3283 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003284}
3285
3286/*
3287 * Get current line in syntax buffer.
3288 */
3289 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003290syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291{
3292 return ml_get_buf(syn_buf, current_lnum, FALSE);
3293}
3294
3295/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003296 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297 * Returns TRUE when there is a match.
3298 */
3299 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003300syn_regexec(
3301 regmmatch_T *rmp,
3302 linenr_T lnum,
3303 colnr_T col,
3304 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003305{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003306 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003307#ifdef FEAT_RELTIME
3308 int timed_out = FALSE;
3309#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003310#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003311 proftime_T pt;
3312
3313 if (syn_time_on)
3314 profile_start(&pt);
3315#endif
3316
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003317 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003318 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3319#ifdef FEAT_RELTIME
3320 syn_tm, &timed_out
3321#else
3322 NULL, NULL
3323#endif
3324 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003325
Bram Moolenaarf7512552013-06-06 14:55:19 +02003326#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003327 if (syn_time_on)
3328 {
3329 profile_end(&pt);
3330 profile_add(&st->total, &pt);
3331 if (profile_cmp(&pt, &st->slowest) < 0)
3332 st->slowest = pt;
3333 ++st->count;
3334 if (r > 0)
3335 ++st->match;
3336 }
3337#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003338#ifdef FEAT_RELTIME
3339 if (timed_out)
3340 syn_win->w_s->b_syn_slow = TRUE;
3341#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003342
3343 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344 {
3345 rmp->startpos[0].lnum += lnum;
3346 rmp->endpos[0].lnum += lnum;
3347 return TRUE;
3348 }
3349 return FALSE;
3350}
3351
3352/*
3353 * Check one position in a line for a matching keyword.
3354 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003355 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 */
3357 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003358check_keyword_id(
3359 char_u *line,
3360 int startcol, /* position in line to check for keyword */
3361 int *endcolp, /* return: character after found keyword */
3362 long *flagsp, /* return: flags of matching keyword */
3363 short **next_listp, /* return: next_list of matching keyword */
3364 stateitem_T *cur_si, /* item at the top of the stack */
3365 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003367 keyentry_T *kp;
3368 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003370 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003372 hashtab_T *ht;
3373 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374
3375 /* Find first character after the keyword. First character was already
3376 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003377 kwp = line + startcol;
3378 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379 do
3380 {
3381#ifdef FEAT_MBYTE
3382 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003383 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384 else
3385#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003386 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003388 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389
Bram Moolenaardad6b692005-01-25 22:14:34 +00003390 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391 return 0;
3392
3393 /*
3394 * Must make a copy of the keyword, so we can add a NUL and make it
3395 * lowercase.
3396 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003397 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003398
3399 /*
3400 * Try twice:
3401 * 1. matching case
3402 * 2. ignoring case
3403 */
3404 for (round = 1; round <= 2; ++round)
3405 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003406 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003407 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003408 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003409 if (round == 2) /* ignore case */
3410 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411
3412 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003413 * Find keywords that match. There can be several with different
3414 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415 * When current_next_list is non-zero accept only that group, otherwise:
3416 * Accept a not-contained keyword at toplevel.
3417 * Accept a keyword at other levels only if it is in the contains list.
3418 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003419 hi = hash_find(ht, keyword);
3420 if (!HASHITEM_EMPTY(hi))
3421 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003422 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003423 if (current_next_list != 0
3424 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3425 : (cur_si == NULL
3426 ? !(kp->flags & HL_CONTAINED)
3427 : in_id_list(cur_si, cur_si->si_cont_list,
3428 &kp->k_syn, kp->flags & HL_CONTAINED)))
3429 {
3430 *endcolp = startcol + kwlen;
3431 *flagsp = kp->flags;
3432 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003433#ifdef FEAT_CONCEAL
3434 *ccharp = kp->k_char;
3435#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003436 return kp->k_syn.id;
3437 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003438 }
3439 }
3440 return 0;
3441}
3442
3443/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003444 * Handle ":syntax conceal" command.
3445 */
3446 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003447syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003448{
3449#ifdef FEAT_CONCEAL
3450 char_u *arg = eap->arg;
3451 char_u *next;
3452
3453 eap->nextcmd = find_nextcmd(arg);
3454 if (eap->skip)
3455 return;
3456
3457 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003458 if (*arg == NUL)
3459 {
3460 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003461 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003462 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003463 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003464 }
3465 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003466 curwin->w_s->b_syn_conceal = TRUE;
3467 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3468 curwin->w_s->b_syn_conceal = FALSE;
3469 else
3470 EMSG2(_("E390: Illegal argument: %s"), arg);
3471#endif
3472}
3473
3474/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003475 * Handle ":syntax case" command.
3476 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003478syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479{
3480 char_u *arg = eap->arg;
3481 char_u *next;
3482
3483 eap->nextcmd = find_nextcmd(arg);
3484 if (eap->skip)
3485 return;
3486
3487 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003488 if (*arg == NUL)
3489 {
3490 if (curwin->w_s->b_syn_ic)
3491 MSG(_("syntax case ignore"));
3492 else
3493 MSG(_("syntax case match"));
3494 }
3495 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003496 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 else
3500 EMSG2(_("E390: Illegal argument: %s"), arg);
3501}
3502
3503/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003504 * Handle ":syntax spell" command.
3505 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003506 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003507syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003508{
3509 char_u *arg = eap->arg;
3510 char_u *next;
3511
3512 eap->nextcmd = find_nextcmd(arg);
3513 if (eap->skip)
3514 return;
3515
3516 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003517 if (*arg == NUL)
3518 {
3519 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3520 MSG(_("syntax spell toplevel"));
3521 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3522 MSG(_("syntax spell notoplevel"));
3523 else
3524 MSG(_("syntax spell default"));
3525 }
3526 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003527 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003528 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003530 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003531 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003532 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003533 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003534 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003535 return;
3536 }
3537
3538 /* assume spell checking changed, force a redraw */
3539 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003540}
3541
3542/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003543 * Handle ":syntax iskeyword" command.
3544 */
3545 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003546syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003547{
3548 char_u *arg = eap->arg;
3549 char_u save_chartab[32];
3550 char_u *save_isk;
3551
3552 if (eap->skip)
3553 return;
3554
3555 arg = skipwhite(arg);
3556 if (*arg == NUL)
3557 {
3558 MSG_PUTS("\n");
3559 MSG_PUTS(_("syntax iskeyword "));
3560 if (curwin->w_s->b_syn_isk != empty_option)
3561 msg_outtrans(curwin->w_s->b_syn_isk);
3562 else
3563 msg_outtrans((char_u *)"not set");
3564 }
3565 else
3566 {
3567 if (STRNICMP(arg, "clear", 5) == 0)
3568 {
3569 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3570 (size_t)32);
3571 clear_string_option(&curwin->w_s->b_syn_isk);
3572 }
3573 else
3574 {
3575 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3576 save_isk = curbuf->b_p_isk;
3577 curbuf->b_p_isk = vim_strsave(arg);
3578
3579 buf_init_chartab(curbuf, FALSE);
3580 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3581 (size_t)32);
3582 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3583 clear_string_option(&curwin->w_s->b_syn_isk);
3584 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3585 curbuf->b_p_isk = save_isk;
3586 }
3587 }
3588 redraw_win_later(curwin, NOT_VALID);
3589}
3590
3591/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 * Clear all syntax info for one buffer.
3593 */
3594 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003595syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596{
3597 int i;
3598
Bram Moolenaar860cae12010-06-05 23:22:07 +02003599 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003600#ifdef FEAT_RELTIME
3601 block->b_syn_slow = FALSE; /* clear previous timeout */
3602#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003603 block->b_syn_ic = FALSE; /* Use case, by default */
3604 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3605 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003606#ifdef FEAT_CONCEAL
3607 block->b_syn_conceal = FALSE;
3608#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609
3610 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 clear_keywtab(&block->b_keywtab);
3612 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613
3614 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003615 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3616 syn_clear_pattern(block, i);
3617 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618
3619 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003620 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3621 syn_clear_cluster(block, i);
3622 ga_clear(&block->b_syn_clusters);
3623 block->b_spell_cluster_id = 0;
3624 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625
Bram Moolenaar860cae12010-06-05 23:22:07 +02003626 block->b_syn_sync_flags = 0;
3627 block->b_syn_sync_minlines = 0;
3628 block->b_syn_sync_maxlines = 0;
3629 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003630
Bram Moolenaar473de612013-06-08 18:19:48 +02003631 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003632 block->b_syn_linecont_prog = NULL;
3633 vim_free(block->b_syn_linecont_pat);
3634 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003636 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003638 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639
3640 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003641 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003643
3644 /* Reset the counter for ":syn include" */
3645 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646}
3647
3648/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003649 * Get rid of ownsyntax for window "wp".
3650 */
3651 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003652reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003653{
3654 if (wp->w_s != &wp->w_buffer->b_s)
3655 {
3656 syntax_clear(wp->w_s);
3657 vim_free(wp->w_s);
3658 wp->w_s = &wp->w_buffer->b_s;
3659 }
3660}
3661
3662/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 * Clear syncing info for one buffer.
3664 */
3665 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003666syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667{
3668 int i;
3669
3670 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003671 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3672 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3673 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674
Bram Moolenaar860cae12010-06-05 23:22:07 +02003675 curwin->w_s->b_syn_sync_flags = 0;
3676 curwin->w_s->b_syn_sync_minlines = 0;
3677 curwin->w_s->b_syn_sync_maxlines = 0;
3678 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679
Bram Moolenaar473de612013-06-08 18:19:48 +02003680 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003681 curwin->w_s->b_syn_linecont_prog = NULL;
3682 vim_free(curwin->w_s->b_syn_linecont_pat);
3683 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003684 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003686 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687}
3688
3689/*
3690 * Remove one pattern from the buffer's pattern list.
3691 */
3692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003693syn_remove_pattern(
3694 synblock_T *block,
3695 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696{
3697 synpat_T *spp;
3698
Bram Moolenaar860cae12010-06-05 23:22:07 +02003699 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700#ifdef FEAT_FOLDING
3701 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003702 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003704 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003706 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3707 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708}
3709
3710/*
3711 * Clear and free one syntax pattern. When clearing all, must be called from
3712 * last to first!
3713 */
3714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003715syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003718 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003720 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003722 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3723 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3724 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725 }
3726}
3727
3728/*
3729 * Clear and free one syntax cluster.
3730 */
3731 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003732syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003734 vim_free(SYN_CLSTR(block)[i].scl_name);
3735 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3736 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737}
3738
3739/*
3740 * Handle ":syntax clear" command.
3741 */
3742 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003743syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744{
3745 char_u *arg = eap->arg;
3746 char_u *arg_end;
3747 int id;
3748
3749 eap->nextcmd = find_nextcmd(arg);
3750 if (eap->skip)
3751 return;
3752
3753 /*
3754 * We have to disable this within ":syn include @group filename",
3755 * because otherwise @group would get deleted.
3756 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3757 * clear".
3758 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003759 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 return;
3761
3762 if (ends_excmd(*arg))
3763 {
3764 /*
3765 * No argument: Clear all syntax items.
3766 */
3767 if (syncing)
3768 syntax_sync_clear();
3769 else
3770 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003771 syntax_clear(curwin->w_s);
3772 if (curwin->w_s == &curwin->w_buffer->b_s)
3773 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003774 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775 }
3776 }
3777 else
3778 {
3779 /*
3780 * Clear the group IDs that are in the argument.
3781 */
3782 while (!ends_excmd(*arg))
3783 {
3784 arg_end = skiptowhite(arg);
3785 if (*arg == '@')
3786 {
3787 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3788 if (id == 0)
3789 {
3790 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3791 break;
3792 }
3793 else
3794 {
3795 /*
3796 * We can't physically delete a cluster without changing
3797 * the IDs of other clusters, so we do the next best thing
3798 * and make it empty.
3799 */
3800 short scl_id = id - SYNID_CLUSTER;
3801
Bram Moolenaar860cae12010-06-05 23:22:07 +02003802 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3803 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003804 }
3805 }
3806 else
3807 {
3808 id = syn_namen2id(arg, (int)(arg_end - arg));
3809 if (id == 0)
3810 {
3811 EMSG2(_(e_nogroup), arg);
3812 break;
3813 }
3814 else
3815 syn_clear_one(id, syncing);
3816 }
3817 arg = skipwhite(arg_end);
3818 }
3819 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003820 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003821 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822}
3823
3824/*
3825 * Clear one syntax group for the current buffer.
3826 */
3827 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003828syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829{
3830 synpat_T *spp;
3831 int idx;
3832
3833 /* Clear keywords only when not ":syn sync clear group-name" */
3834 if (!syncing)
3835 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003836 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3837 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 }
3839
3840 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003841 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003843 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3845 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847 }
3848}
3849
3850/*
3851 * Handle ":syntax on" command.
3852 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003854syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855{
3856 syn_cmd_onoff(eap, "syntax");
3857}
3858
3859/*
3860 * Handle ":syntax enable" command.
3861 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003863syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864{
3865 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3866 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003867 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868}
3869
3870/*
3871 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003872 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003875syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876{
3877 eap->nextcmd = check_nextcmd(eap->arg);
3878 if (!eap->skip)
3879 {
3880 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3881 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003882 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 }
3884}
3885
3886/*
3887 * Handle ":syntax manual" command.
3888 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003890syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891{
3892 syn_cmd_onoff(eap, "manual");
3893}
3894
3895/*
3896 * Handle ":syntax off" command.
3897 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003899syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900{
3901 syn_cmd_onoff(eap, "nosyntax");
3902}
3903
3904 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003905syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003906{
3907 char_u buf[100];
3908
3909 eap->nextcmd = check_nextcmd(eap->arg);
3910 if (!eap->skip)
3911 {
3912 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003913 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 do_cmdline_cmd(buf);
3915 }
3916}
3917
3918/*
3919 * Handle ":syntax [list]" command: list current syntax words.
3920 */
3921 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003922syn_cmd_list(
3923 exarg_T *eap,
3924 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925{
3926 char_u *arg = eap->arg;
3927 int id;
3928 char_u *arg_end;
3929
3930 eap->nextcmd = find_nextcmd(arg);
3931 if (eap->skip)
3932 return;
3933
Bram Moolenaar860cae12010-06-05 23:22:07 +02003934 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003936 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 return;
3938 }
3939
3940 if (syncing)
3941 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003942 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943 {
3944 MSG_PUTS(_("syncing on C-style comments"));
3945 syn_lines_msg();
3946 syn_match_msg();
3947 return;
3948 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003949 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003950 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003951 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 MSG_PUTS(_("no syncing"));
3953 else
3954 {
3955 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003956 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957 MSG_PUTS(_(" lines before top line"));
3958 syn_match_msg();
3959 }
3960 return;
3961 }
3962 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003963 if (curwin->w_s->b_syn_sync_minlines > 0
3964 || curwin->w_s->b_syn_sync_maxlines > 0
3965 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003966 {
3967 MSG_PUTS(_("\nsyncing on items"));
3968 syn_lines_msg();
3969 syn_match_msg();
3970 }
3971 }
3972 else
3973 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3974 if (ends_excmd(*arg))
3975 {
3976 /*
3977 * No argument: List all group IDs and all syntax clusters.
3978 */
3979 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3980 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003981 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 syn_list_cluster(id);
3983 }
3984 else
3985 {
3986 /*
3987 * List the group IDs and syntax clusters that are in the argument.
3988 */
3989 while (!ends_excmd(*arg) && !got_int)
3990 {
3991 arg_end = skiptowhite(arg);
3992 if (*arg == '@')
3993 {
3994 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3995 if (id == 0)
3996 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3997 else
3998 syn_list_cluster(id - SYNID_CLUSTER);
3999 }
4000 else
4001 {
4002 id = syn_namen2id(arg, (int)(arg_end - arg));
4003 if (id == 0)
4004 EMSG2(_(e_nogroup), arg);
4005 else
4006 syn_list_one(id, syncing, TRUE);
4007 }
4008 arg = skipwhite(arg_end);
4009 }
4010 }
4011 eap->nextcmd = check_nextcmd(arg);
4012}
4013
4014 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004015syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004017 if (curwin->w_s->b_syn_sync_maxlines > 0
4018 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 {
4020 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004021 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004022 {
4023 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004024 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4025 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 MSG_PUTS(", ");
4027 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004028 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 {
4030 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004031 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 }
4033 MSG_PUTS(_(" lines before top line"));
4034 }
4035}
4036
4037 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004038syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004040 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041 {
4042 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004043 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 MSG_PUTS(_(" line breaks"));
4045 }
4046}
4047
4048static int last_matchgroup;
4049
4050struct name_list
4051{
4052 int flag;
4053 char *name;
4054};
4055
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004056static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004057
4058/*
4059 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4060 */
4061 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004062syn_list_one(
4063 int id,
4064 int syncing, /* when TRUE: list syncing items */
4065 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066{
4067 int attr;
4068 int idx;
4069 int did_header = FALSE;
4070 synpat_T *spp;
4071 static struct name_list namelist1[] =
4072 {
4073 {HL_DISPLAY, "display"},
4074 {HL_CONTAINED, "contained"},
4075 {HL_ONELINE, "oneline"},
4076 {HL_KEEPEND, "keepend"},
4077 {HL_EXTEND, "extend"},
4078 {HL_EXCLUDENL, "excludenl"},
4079 {HL_TRANSP, "transparent"},
4080 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004081#ifdef FEAT_CONCEAL
4082 {HL_CONCEAL, "conceal"},
4083 {HL_CONCEALENDS, "concealends"},
4084#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 {0, NULL}
4086 };
4087 static struct name_list namelist2[] =
4088 {
4089 {HL_SKIPWHITE, "skipwhite"},
4090 {HL_SKIPNL, "skipnl"},
4091 {HL_SKIPEMPTY, "skipempty"},
4092 {0, NULL}
4093 };
4094
Bram Moolenaar8820b482017-03-16 17:23:31 +01004095 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004096
4097 /* list the keywords for "id" */
4098 if (!syncing)
4099 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004100 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4101 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004102 did_header, attr);
4103 }
4104
4105 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004106 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004108 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4110 continue;
4111
4112 (void)syn_list_header(did_header, 999, id);
4113 did_header = TRUE;
4114 last_matchgroup = 0;
4115 if (spp->sp_type == SPTYPE_MATCH)
4116 {
4117 put_pattern("match", ' ', spp, attr);
4118 msg_putchar(' ');
4119 }
4120 else if (spp->sp_type == SPTYPE_START)
4121 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004122 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4123 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4124 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4125 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4126 while (idx < curwin->w_s->b_syn_patterns.ga_len
4127 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4128 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004129 --idx;
4130 msg_putchar(' ');
4131 }
4132 syn_list_flags(namelist1, spp->sp_flags, attr);
4133
4134 if (spp->sp_cont_list != NULL)
4135 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4136
4137 if (spp->sp_syn.cont_in_list != NULL)
4138 put_id_list((char_u *)"containedin",
4139 spp->sp_syn.cont_in_list, attr);
4140
4141 if (spp->sp_next_list != NULL)
4142 {
4143 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4144 syn_list_flags(namelist2, spp->sp_flags, attr);
4145 }
4146 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4147 {
4148 if (spp->sp_flags & HL_SYNC_HERE)
4149 msg_puts_attr((char_u *)"grouphere", attr);
4150 else
4151 msg_puts_attr((char_u *)"groupthere", attr);
4152 msg_putchar(' ');
4153 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004154 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4156 else
4157 MSG_PUTS("NONE");
4158 msg_putchar(' ');
4159 }
4160 }
4161
4162 /* list the link, if there is one */
4163 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4164 {
4165 (void)syn_list_header(did_header, 999, id);
4166 msg_puts_attr((char_u *)"links to", attr);
4167 msg_putchar(' ');
4168 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4169 }
4170}
4171
4172 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004173syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174{
4175 int i;
4176
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004177 for (i = 0; nlist[i].flag != 0; ++i)
4178 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004180 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 msg_putchar(' ');
4182 }
4183}
4184
4185/*
4186 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4187 */
4188 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004189syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190{
4191 int endcol = 15;
4192
4193 /* slight hack: roughly duplicate the guts of syn_list_header() */
4194 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004195 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004196
4197 if (msg_col >= endcol) /* output at least one space */
4198 endcol = msg_col + 1;
4199 if (Columns <= endcol) /* avoid hang for tiny window */
4200 endcol = Columns - 1;
4201
4202 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004203 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004205 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004206 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 }
4208 else
4209 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004210 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211 msg_puts((char_u *)"=NONE");
4212 }
4213}
4214
4215 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004216put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217{
4218 short *p;
4219
4220 msg_puts_attr(name, attr);
4221 msg_putchar('=');
4222 for (p = list; *p; ++p)
4223 {
4224 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4225 {
4226 if (p[1])
4227 MSG_PUTS("ALLBUT");
4228 else
4229 MSG_PUTS("ALL");
4230 }
4231 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4232 {
4233 MSG_PUTS("TOP");
4234 }
4235 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4236 {
4237 MSG_PUTS("CONTAINED");
4238 }
4239 else if (*p >= SYNID_CLUSTER)
4240 {
4241 short scl_id = *p - SYNID_CLUSTER;
4242
4243 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004244 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 }
4246 else
4247 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4248 if (p[1])
4249 msg_putchar(',');
4250 }
4251 msg_putchar(' ');
4252}
4253
4254 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004255put_pattern(
4256 char *s,
4257 int c,
4258 synpat_T *spp,
4259 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260{
4261 long n;
4262 int mask;
4263 int first;
4264 static char *sepchars = "/+=-#@\"|'^&";
4265 int i;
4266
4267 /* May have to write "matchgroup=group" */
4268 if (last_matchgroup != spp->sp_syn_match_id)
4269 {
4270 last_matchgroup = spp->sp_syn_match_id;
4271 msg_puts_attr((char_u *)"matchgroup", attr);
4272 msg_putchar('=');
4273 if (last_matchgroup == 0)
4274 msg_outtrans((char_u *)"NONE");
4275 else
4276 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4277 msg_putchar(' ');
4278 }
4279
4280 /* output the name of the pattern and an '=' or ' ' */
4281 msg_puts_attr((char_u *)s, attr);
4282 msg_putchar(c);
4283
4284 /* output the pattern, in between a char that is not in the pattern */
4285 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4286 if (sepchars[++i] == NUL)
4287 {
4288 i = 0; /* no good char found, just use the first one */
4289 break;
4290 }
4291 msg_putchar(sepchars[i]);
4292 msg_outtrans(spp->sp_pattern);
4293 msg_putchar(sepchars[i]);
4294
4295 /* output any pattern options */
4296 first = TRUE;
4297 for (i = 0; i < SPO_COUNT; ++i)
4298 {
4299 mask = (1 << i);
4300 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4301 {
4302 if (!first)
4303 msg_putchar(','); /* separate with commas */
4304 msg_puts((char_u *)spo_name_tab[i]);
4305 n = spp->sp_offsets[i];
4306 if (i != SPO_LC_OFF)
4307 {
4308 if (spp->sp_off_flags & mask)
4309 msg_putchar('s');
4310 else
4311 msg_putchar('e');
4312 if (n > 0)
4313 msg_putchar('+');
4314 }
4315 if (n || i == SPO_LC_OFF)
4316 msg_outnum(n);
4317 first = FALSE;
4318 }
4319 }
4320 msg_putchar(' ');
4321}
4322
4323/*
4324 * List or clear the keywords for one syntax group.
4325 * Return TRUE if the header has been printed.
4326 */
4327 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004328syn_list_keywords(
4329 int id,
4330 hashtab_T *ht,
4331 int did_header, /* header has already been printed */
4332 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 hashitem_T *hi;
4336 keyentry_T *kp;
4337 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338 int prev_contained = 0;
4339 short *prev_next_list = NULL;
4340 short *prev_cont_in_list = NULL;
4341 int prev_skipnl = 0;
4342 int prev_skipwhite = 0;
4343 int prev_skipempty = 0;
4344
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 /*
4346 * Unfortunately, this list of keywords is not sorted on alphabet but on
4347 * hash value...
4348 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004349 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004350 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 --todo;
4355 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 if (prev_contained != (kp->flags & HL_CONTAINED)
4360 || prev_skipnl != (kp->flags & HL_SKIPNL)
4361 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4362 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4363 || prev_cont_in_list != kp->k_syn.cont_in_list
4364 || prev_next_list != kp->next_list)
4365 outlen = 9999;
4366 else
4367 outlen = (int)STRLEN(kp->keyword);
4368 /* output "contained" and "nextgroup" on each line */
4369 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004370 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004371 prev_contained = 0;
4372 prev_next_list = NULL;
4373 prev_cont_in_list = NULL;
4374 prev_skipnl = 0;
4375 prev_skipwhite = 0;
4376 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 did_header = TRUE;
4379 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 put_id_list((char_u *)"containedin",
4388 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 if (kp->next_list != prev_next_list)
4393 {
4394 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4395 msg_putchar(' ');
4396 prev_next_list = kp->next_list;
4397 if (kp->flags & HL_SKIPNL)
4398 {
4399 msg_puts_attr((char_u *)"skipnl", attr);
4400 msg_putchar(' ');
4401 prev_skipnl = (kp->flags & HL_SKIPNL);
4402 }
4403 if (kp->flags & HL_SKIPWHITE)
4404 {
4405 msg_puts_attr((char_u *)"skipwhite", attr);
4406 msg_putchar(' ');
4407 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4408 }
4409 if (kp->flags & HL_SKIPEMPTY)
4410 {
4411 msg_puts_attr((char_u *)"skipempty", attr);
4412 msg_putchar(' ');
4413 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4414 }
4415 }
4416 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004417 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 }
4419 }
4420 }
4421
4422 return did_header;
4423}
4424
4425 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004426syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004427{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004428 hashitem_T *hi;
4429 keyentry_T *kp;
4430 keyentry_T *kp_prev;
4431 keyentry_T *kp_next;
4432 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433
Bram Moolenaardad6b692005-01-25 22:14:34 +00004434 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004435 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004436 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004438 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 --todo;
4441 kp_prev = NULL;
4442 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 if (kp->k_syn.id == id)
4445 {
4446 kp_next = kp->ke_next;
4447 if (kp_prev == NULL)
4448 {
4449 if (kp_next == NULL)
4450 hash_remove(ht, hi);
4451 else
4452 hi->hi_key = KE2HIKEY(kp_next);
4453 }
4454 else
4455 kp_prev->ke_next = kp_next;
4456 vim_free(kp->next_list);
4457 vim_free(kp->k_syn.cont_in_list);
4458 vim_free(kp);
4459 kp = kp_next;
4460 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004462 {
4463 kp_prev = kp;
4464 kp = kp->ke_next;
4465 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 }
4467 }
4468 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004469 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470}
4471
4472/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004473 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 */
4475 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004476clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004478 hashitem_T *hi;
4479 int todo;
4480 keyentry_T *kp;
4481 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004483 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004484 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004486 if (!HASHITEM_EMPTY(hi))
4487 {
4488 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004489 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004491 kp_next = kp->ke_next;
4492 vim_free(kp->next_list);
4493 vim_free(kp->k_syn.cont_in_list);
4494 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004496 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 hash_clear(ht);
4499 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500}
4501
4502/*
4503 * Add a keyword to the list of keywords.
4504 */
4505 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004506add_keyword(
4507 char_u *name, /* name of keyword */
4508 int id, /* group ID for this keyword */
4509 int flags, /* flags for this keyword */
4510 short *cont_in_list, /* containedin for this keyword */
4511 short *next_list, /* nextgroup for this keyword */
4512 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004514 keyentry_T *kp;
4515 hashtab_T *ht;
4516 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004518 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520
Bram Moolenaar860cae12010-06-05 23:22:07 +02004521 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004522 name_ic = str_foldcase(name, (int)STRLEN(name),
4523 name_folded, MAXKEYWLEN + 1);
4524 else
4525 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004526 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4527 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004529 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004530 kp->k_syn.id = id;
4531 kp->k_syn.inc_tag = current_syn_inc_tag;
4532 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004533 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004534 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004536 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004537 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538
Bram Moolenaar860cae12010-06-05 23:22:07 +02004539 if (curwin->w_s->b_syn_ic)
4540 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004542 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543
Bram Moolenaardad6b692005-01-25 22:14:34 +00004544 hash = hash_hash(kp->keyword);
4545 hi = hash_lookup(ht, kp->keyword, hash);
4546 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004548 /* new keyword, add to hashtable */
4549 kp->ke_next = NULL;
4550 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004552 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004554 /* keyword already exists, prepend to list */
4555 kp->ke_next = HI2KE(hi);
4556 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558}
4559
4560/*
4561 * Get the start and end of the group name argument.
4562 * Return a pointer to the first argument.
4563 * Return NULL if the end of the command was found instead of further args.
4564 */
4565 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004566get_group_name(
4567 char_u *arg, /* start of the argument */
4568 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004569{
4570 char_u *rest;
4571
4572 *name_end = skiptowhite(arg);
4573 rest = skipwhite(*name_end);
4574
4575 /*
4576 * Check if there are enough arguments. The first argument may be a
4577 * pattern, where '|' is allowed, so only check for NUL.
4578 */
4579 if (ends_excmd(*arg) || *rest == NUL)
4580 return NULL;
4581 return rest;
4582}
4583
4584/*
4585 * Check for syntax command option arguments.
4586 * This can be called at any place in the list of arguments, and just picks
4587 * out the arguments that are known. Can be called several times in a row to
4588 * collect all options in between other arguments.
4589 * Return a pointer to the next argument (which isn't an option).
4590 * Return NULL for any error;
4591 */
4592 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004593get_syn_options(
4594 char_u *arg, /* next argument to be checked */
4595 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004596 int *conceal_char UNUSED,
4597 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004599 char_u *gname_start, *gname;
4600 int syn_id;
4601 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004602 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603 int i;
4604 int fidx;
4605 static struct flag
4606 {
4607 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004608 int argtype;
4609 int flags;
4610 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4611 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4612 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4613 {"eExXtTeEnNdD", 0, HL_EXTEND},
4614 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4615 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4616 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4617 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4618 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4619 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4620 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4621 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4622 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004623 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4624 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4625 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004626 {"cCoOnNtTaAiInNsS", 1, 0},
4627 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4628 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004630 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631
4632 if (arg == NULL) /* already detected error */
4633 return NULL;
4634
Bram Moolenaar860cae12010-06-05 23:22:07 +02004635#ifdef FEAT_CONCEAL
4636 if (curwin->w_s->b_syn_conceal)
4637 opt->flags |= HL_CONCEAL;
4638#endif
4639
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640 for (;;)
4641 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004642 /*
4643 * This is used very often when a large number of keywords is defined.
4644 * Need to skip quickly when no option name is found.
4645 * Also avoid tolower(), it's slow.
4646 */
4647 if (strchr(first_letters, *arg) == NULL)
4648 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649
4650 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4651 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004652 p = flagtab[fidx].name;
4653 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4654 if (arg[len] != p[i] && arg[len] != p[i + 1])
4655 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004656 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004657 || (flagtab[fidx].argtype > 0
4658 ? arg[len] == '='
4659 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004661 if (opt->keyword
4662 && (flagtab[fidx].flags == HL_DISPLAY
4663 || flagtab[fidx].flags == HL_FOLD
4664 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665 /* treat "display", "fold" and "extend" as a keyword */
4666 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 break;
4668 }
4669 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004670 if (fidx < 0) /* no match found */
4671 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004672
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004673 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004675 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 {
4677 EMSG(_("E395: contains argument not accepted here"));
4678 return NULL;
4679 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004680 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 return NULL;
4682 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004683 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004685 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 return NULL;
4687 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004688 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004690 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 return NULL;
4692 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004693 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4694 {
4695#ifdef FEAT_MBYTE
4696 /* cchar=? */
4697 if (has_mbyte)
4698 {
4699# ifdef FEAT_CONCEAL
4700 *conceal_char = mb_ptr2char(arg + 6);
4701# endif
4702 arg += mb_ptr2len(arg + 6) - 1;
4703 }
4704 else
4705#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004706 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004707#ifdef FEAT_CONCEAL
4708 *conceal_char = arg[6];
4709#else
4710 ;
4711#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004712 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004713#ifdef FEAT_CONCEAL
4714 if (!vim_isprintc_strict(*conceal_char))
4715 {
4716 EMSG(_("E844: invalid cchar value"));
4717 return NULL;
4718 }
4719#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004720 arg = skipwhite(arg + 7);
4721 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004723 {
4724 opt->flags |= flagtab[fidx].flags;
4725 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004726
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004727 if (flagtab[fidx].flags == HL_SYNC_HERE
4728 || flagtab[fidx].flags == HL_SYNC_THERE)
4729 {
4730 if (opt->sync_idx == NULL)
4731 {
4732 EMSG(_("E393: group[t]here not accepted here"));
4733 return NULL;
4734 }
4735 gname_start = arg;
4736 arg = skiptowhite(arg);
4737 if (gname_start == arg)
4738 return NULL;
4739 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4740 if (gname == NULL)
4741 return NULL;
4742 if (STRCMP(gname, "NONE") == 0)
4743 *opt->sync_idx = NONE_IDX;
4744 else
4745 {
4746 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004747 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4748 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4749 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004750 {
4751 *opt->sync_idx = i;
4752 break;
4753 }
4754 if (i < 0)
4755 {
4756 EMSG2(_("E394: Didn't find region item for %s"), gname);
4757 vim_free(gname);
4758 return NULL;
4759 }
4760 }
4761
4762 vim_free(gname);
4763 arg = skipwhite(arg);
4764 }
4765#ifdef FEAT_FOLDING
4766 else if (flagtab[fidx].flags == HL_FOLD
4767 && foldmethodIsSyntax(curwin))
4768 /* Need to update folds later. */
4769 foldUpdateAll(curwin);
4770#endif
4771 }
4772 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004773
4774 return arg;
4775}
4776
4777/*
4778 * Adjustments to syntax item when declared in a ":syn include"'d file.
4779 * Set the contained flag, and if the item is not already contained, add it
4780 * to the specified top-level group, if any.
4781 */
4782 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004783syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004785 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786 return;
4787 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004788 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004789 {
4790 /* We have to alloc this, because syn_combine_list() will free it. */
4791 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004792 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793
4794 if (grp_list != NULL)
4795 {
4796 grp_list[0] = id;
4797 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004798 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 CLUSTER_ADD);
4800 }
4801 }
4802}
4803
4804/*
4805 * Handle ":syntax include [@{group-name}] filename" command.
4806 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004808syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809{
4810 char_u *arg = eap->arg;
4811 int sgl_id = 1;
4812 char_u *group_name_end;
4813 char_u *rest;
4814 char_u *errormsg = NULL;
4815 int prev_toplvl_grp;
4816 int prev_syn_inc_tag;
4817 int source = FALSE;
4818
4819 eap->nextcmd = find_nextcmd(arg);
4820 if (eap->skip)
4821 return;
4822
4823 if (arg[0] == '@')
4824 {
4825 ++arg;
4826 rest = get_group_name(arg, &group_name_end);
4827 if (rest == NULL)
4828 {
4829 EMSG((char_u *)_("E397: Filename required"));
4830 return;
4831 }
4832 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004833 if (sgl_id == 0)
4834 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835 /* separate_nextcmd() and expand_filename() depend on this */
4836 eap->arg = rest;
4837 }
4838
4839 /*
4840 * Everything that's left, up to the next command, should be the
4841 * filename to include.
4842 */
4843 eap->argt |= (XFILE | NOSPC);
4844 separate_nextcmd(eap);
4845 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4846 {
4847 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4848 * file. Need to expand the file name first. In other cases
4849 * ":runtime!" is used. */
4850 source = TRUE;
4851 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4852 {
4853 if (errormsg != NULL)
4854 EMSG(errormsg);
4855 return;
4856 }
4857 }
4858
4859 /*
4860 * Save and restore the existing top-level grouplist id and ":syn
4861 * include" tag around the actual inclusion.
4862 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004863 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4864 {
4865 EMSG((char_u *)_("E847: Too many syntax includes"));
4866 return;
4867 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004868 prev_syn_inc_tag = current_syn_inc_tag;
4869 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004870 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4871 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004872 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004873 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004874 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004875 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 current_syn_inc_tag = prev_syn_inc_tag;
4877}
4878
4879/*
4880 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4881 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004883syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884{
4885 char_u *arg = eap->arg;
4886 char_u *group_name_end;
4887 int syn_id;
4888 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004889 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004890 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004891 char_u *kw;
4892 syn_opt_arg_T syn_opt_arg;
4893 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004894 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895
4896 rest = get_group_name(arg, &group_name_end);
4897
4898 if (rest != NULL)
4899 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004900 if (eap->skip)
4901 syn_id = -1;
4902 else
4903 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004904 if (syn_id != 0)
4905 /* allocate a buffer, for removing backslashes in the keyword */
4906 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907 if (keyword_copy != NULL)
4908 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004909 syn_opt_arg.flags = 0;
4910 syn_opt_arg.keyword = TRUE;
4911 syn_opt_arg.sync_idx = NULL;
4912 syn_opt_arg.has_cont_list = FALSE;
4913 syn_opt_arg.cont_in_list = NULL;
4914 syn_opt_arg.next_list = NULL;
4915
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 /*
4917 * The options given apply to ALL keywords, so all options must be
4918 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004919 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004920 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004921 cnt = 0;
4922 p = keyword_copy;
4923 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004925 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4926 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004927 if (rest == NULL || ends_excmd(*rest))
4928 break;
4929 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004930 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004932 if (*rest == '\\' && rest[1] != NUL)
4933 ++rest;
4934 *p++ = *rest++;
4935 }
4936 *p++ = NUL;
4937 ++cnt;
4938 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004940 if (!eap->skip)
4941 {
4942 /* Adjust flags for use of ":syn include". */
4943 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4944
4945 /*
4946 * 2: Add an entry for each keyword.
4947 */
4948 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4949 {
4950 for (p = vim_strchr(kw, '['); ; )
4951 {
4952 if (p != NULL)
4953 *p = NUL;
4954 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004955 syn_opt_arg.cont_in_list,
4956 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004957 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004958 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004959 if (p[1] == NUL)
4960 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004961 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004962 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004963 }
4964 if (p[1] == ']')
4965 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004966 if (p[2] != NUL)
4967 {
4968 EMSG3(_("E890: trailing char after ']': %s]%s"),
4969 kw, &p[2]);
4970 goto error;
4971 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004972 kw = p + 1; /* skip over the "]" */
4973 break;
4974 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004975#ifdef FEAT_MBYTE
4976 if (has_mbyte)
4977 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004978 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004979
4980 mch_memmove(p, p + 1, l);
4981 p += l;
4982 }
4983 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004984#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004985 {
4986 p[0] = p[1];
4987 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004988 }
4989 }
4990 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004991 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004992error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004994 vim_free(syn_opt_arg.cont_in_list);
4995 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 }
4997 }
4998
4999 if (rest != NULL)
5000 eap->nextcmd = check_nextcmd(rest);
5001 else
5002 EMSG2(_(e_invarg2), arg);
5003
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005004 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005005 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006}
5007
5008/*
5009 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5010 *
5011 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5012 */
5013 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005014syn_cmd_match(
5015 exarg_T *eap,
5016 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017{
5018 char_u *arg = eap->arg;
5019 char_u *group_name_end;
5020 char_u *rest;
5021 synpat_T item; /* the item found in the line */
5022 int syn_id;
5023 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005024 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005026 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027
5028 /* Isolate the group name, check for validity */
5029 rest = get_group_name(arg, &group_name_end);
5030
5031 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005032 syn_opt_arg.flags = 0;
5033 syn_opt_arg.keyword = FALSE;
5034 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5035 syn_opt_arg.has_cont_list = TRUE;
5036 syn_opt_arg.cont_list = NULL;
5037 syn_opt_arg.cont_in_list = NULL;
5038 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005039 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040
5041 /* get the pattern. */
5042 init_syn_patterns();
5043 vim_memset(&item, 0, sizeof(item));
5044 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005045 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5046 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005047
5048 /* Get options after the pattern */
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 if (rest != NULL) /* all arguments are valid */
5052 {
5053 /*
5054 * Check for trailing command and illegal trailing arguments.
5055 */
5056 eap->nextcmd = check_nextcmd(rest);
5057 if (!ends_excmd(*rest) || eap->skip)
5058 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005059 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060 && (syn_id = syn_check_group(arg,
5061 (int)(group_name_end - arg))) != 0)
5062 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005063 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005064 /*
5065 * Store the pattern in the syn_items list
5066 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005067 idx = curwin->w_s->b_syn_patterns.ga_len;
5068 SYN_ITEMS(curwin->w_s)[idx] = item;
5069 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5070 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5071 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5072 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5073 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5074 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5075 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5076 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005077 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005078#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005079 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005080#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005081 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005082 curwin->w_s->b_syn_containedin = TRUE;
5083 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5084 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085
5086 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005087 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005088 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005090 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005091 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092#endif
5093
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005094 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005095 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096 return; /* don't free the progs and patterns now */
5097 }
5098 }
5099
5100 /*
5101 * Something failed, free the allocated memory.
5102 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005103 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005105 vim_free(syn_opt_arg.cont_list);
5106 vim_free(syn_opt_arg.cont_in_list);
5107 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108
5109 if (rest == NULL)
5110 EMSG2(_(e_invarg2), arg);
5111}
5112
5113/*
5114 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5115 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5116 */
5117 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005118syn_cmd_region(
5119 exarg_T *eap,
5120 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121{
5122 char_u *arg = eap->arg;
5123 char_u *group_name_end;
5124 char_u *rest; /* next arg, NULL on error */
5125 char_u *key_end;
5126 char_u *key = NULL;
5127 char_u *p;
5128 int item;
5129#define ITEM_START 0
5130#define ITEM_SKIP 1
5131#define ITEM_END 2
5132#define ITEM_MATCHGROUP 3
5133 struct pat_ptr
5134 {
5135 synpat_T *pp_synp; /* pointer to syn_pattern */
5136 int pp_matchgroup_id; /* matchgroup ID */
5137 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5138 } *(pat_ptrs[3]);
5139 /* patterns found in the line */
5140 struct pat_ptr *ppp;
5141 struct pat_ptr *ppp_next;
5142 int pat_count = 0; /* nr of syn_patterns found */
5143 int syn_id;
5144 int matchgroup_id = 0;
5145 int not_enough = FALSE; /* not enough arguments */
5146 int illegal = FALSE; /* illegal arguments */
5147 int success = FALSE;
5148 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005149 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005150 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005151
5152 /* Isolate the group name, check for validity */
5153 rest = get_group_name(arg, &group_name_end);
5154
5155 pat_ptrs[0] = NULL;
5156 pat_ptrs[1] = NULL;
5157 pat_ptrs[2] = NULL;
5158
5159 init_syn_patterns();
5160
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005161 syn_opt_arg.flags = 0;
5162 syn_opt_arg.keyword = FALSE;
5163 syn_opt_arg.sync_idx = NULL;
5164 syn_opt_arg.has_cont_list = TRUE;
5165 syn_opt_arg.cont_list = NULL;
5166 syn_opt_arg.cont_in_list = NULL;
5167 syn_opt_arg.next_list = NULL;
5168
Bram Moolenaar071d4272004-06-13 20:20:40 +00005169 /*
5170 * get the options, patterns and matchgroup.
5171 */
5172 while (rest != NULL && !ends_excmd(*rest))
5173 {
5174 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005175 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005176 if (rest == NULL || ends_excmd(*rest))
5177 break;
5178
5179 /* must be a pattern or matchgroup then */
5180 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005181 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182 ++key_end;
5183 vim_free(key);
5184 key = vim_strnsave_up(rest, (int)(key_end - rest));
5185 if (key == NULL) /* out of memory */
5186 {
5187 rest = NULL;
5188 break;
5189 }
5190 if (STRCMP(key, "MATCHGROUP") == 0)
5191 item = ITEM_MATCHGROUP;
5192 else if (STRCMP(key, "START") == 0)
5193 item = ITEM_START;
5194 else if (STRCMP(key, "END") == 0)
5195 item = ITEM_END;
5196 else if (STRCMP(key, "SKIP") == 0)
5197 {
5198 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5199 {
5200 illegal = TRUE;
5201 break;
5202 }
5203 item = ITEM_SKIP;
5204 }
5205 else
5206 break;
5207 rest = skipwhite(key_end);
5208 if (*rest != '=')
5209 {
5210 rest = NULL;
5211 EMSG2(_("E398: Missing '=': %s"), arg);
5212 break;
5213 }
5214 rest = skipwhite(rest + 1);
5215 if (*rest == NUL)
5216 {
5217 not_enough = TRUE;
5218 break;
5219 }
5220
5221 if (item == ITEM_MATCHGROUP)
5222 {
5223 p = skiptowhite(rest);
5224 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5225 matchgroup_id = 0;
5226 else
5227 {
5228 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5229 if (matchgroup_id == 0)
5230 {
5231 illegal = TRUE;
5232 break;
5233 }
5234 }
5235 rest = skipwhite(p);
5236 }
5237 else
5238 {
5239 /*
5240 * Allocate room for a syn_pattern, and link it in the list of
5241 * syn_patterns for this item, at the start (because the list is
5242 * used from end to start).
5243 */
5244 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5245 if (ppp == NULL)
5246 {
5247 rest = NULL;
5248 break;
5249 }
5250 ppp->pp_next = pat_ptrs[item];
5251 pat_ptrs[item] = ppp;
5252 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5253 if (ppp->pp_synp == NULL)
5254 {
5255 rest = NULL;
5256 break;
5257 }
5258
5259 /*
5260 * Get the syntax pattern and the following offset(s).
5261 */
5262 /* Enable the appropriate \z specials. */
5263 if (item == ITEM_START)
5264 reg_do_extmatch = REX_SET;
5265 else if (item == ITEM_SKIP || item == ITEM_END)
5266 reg_do_extmatch = REX_USE;
5267 rest = get_syn_pattern(rest, ppp->pp_synp);
5268 reg_do_extmatch = 0;
5269 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005270 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005271 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5272 ppp->pp_matchgroup_id = matchgroup_id;
5273 ++pat_count;
5274 }
5275 }
5276 vim_free(key);
5277 if (illegal || not_enough)
5278 rest = NULL;
5279
5280 /*
5281 * Must have a "start" and "end" pattern.
5282 */
5283 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5284 pat_ptrs[ITEM_END] == NULL))
5285 {
5286 not_enough = TRUE;
5287 rest = NULL;
5288 }
5289
5290 if (rest != NULL)
5291 {
5292 /*
5293 * Check for trailing garbage or command.
5294 * If OK, add the item.
5295 */
5296 eap->nextcmd = check_nextcmd(rest);
5297 if (!ends_excmd(*rest) || eap->skip)
5298 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005299 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005300 && (syn_id = syn_check_group(arg,
5301 (int)(group_name_end - arg))) != 0)
5302 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005303 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005304 /*
5305 * Store the start/skip/end in the syn_items list
5306 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005307 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005308 for (item = ITEM_START; item <= ITEM_END; ++item)
5309 {
5310 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5311 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005312 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5313 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5314 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315 (item == ITEM_START) ? SPTYPE_START :
5316 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005317 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5318 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005319 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5320 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005321 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005322 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005323#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005324 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005325#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005326 if (item == ITEM_START)
5327 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005328 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005329 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005330 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005331 syn_opt_arg.cont_in_list;
5332 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005333 curwin->w_s->b_syn_containedin = TRUE;
5334 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005335 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005337 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005338 ++idx;
5339#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005340 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005341 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005342#endif
5343 }
5344 }
5345
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005346 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005347 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 success = TRUE; /* don't free the progs and patterns now */
5349 }
5350 }
5351
5352 /*
5353 * Free the allocated memory.
5354 */
5355 for (item = ITEM_START; item <= ITEM_END; ++item)
5356 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5357 {
5358 if (!success)
5359 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005360 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005361 vim_free(ppp->pp_synp->sp_pattern);
5362 }
5363 vim_free(ppp->pp_synp);
5364 ppp_next = ppp->pp_next;
5365 vim_free(ppp);
5366 }
5367
5368 if (!success)
5369 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005370 vim_free(syn_opt_arg.cont_list);
5371 vim_free(syn_opt_arg.cont_in_list);
5372 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005373 if (not_enough)
5374 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5375 else if (illegal || rest == NULL)
5376 EMSG2(_(e_invarg2), arg);
5377 }
5378}
5379
5380/*
5381 * A simple syntax group ID comparison function suitable for use in qsort()
5382 */
5383 static int
5384#ifdef __BORLANDC__
5385_RTLENTRYF
5386#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005387syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005388{
5389 const short *s1 = v1;
5390 const short *s2 = v2;
5391
5392 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5393}
5394
5395/*
5396 * Combines lists of syntax clusters.
5397 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5398 */
5399 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005400syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005401{
5402 int count1 = 0;
5403 int count2 = 0;
5404 short *g1;
5405 short *g2;
5406 short *clstr = NULL;
5407 int count;
5408 int round;
5409
5410 /*
5411 * Handle degenerate cases.
5412 */
5413 if (*clstr2 == NULL)
5414 return;
5415 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5416 {
5417 if (list_op == CLUSTER_REPLACE)
5418 vim_free(*clstr1);
5419 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5420 *clstr1 = *clstr2;
5421 else
5422 vim_free(*clstr2);
5423 return;
5424 }
5425
5426 for (g1 = *clstr1; *g1; g1++)
5427 ++count1;
5428 for (g2 = *clstr2; *g2; g2++)
5429 ++count2;
5430
5431 /*
5432 * For speed purposes, sort both lists.
5433 */
5434 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5435 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5436
5437 /*
5438 * We proceed in two passes; in round 1, we count the elements to place
5439 * in the new list, and in round 2, we allocate and populate the new
5440 * list. For speed, we use a mergesort-like method, adding the smaller
5441 * of the current elements in each list to the new list.
5442 */
5443 for (round = 1; round <= 2; round++)
5444 {
5445 g1 = *clstr1;
5446 g2 = *clstr2;
5447 count = 0;
5448
5449 /*
5450 * First, loop through the lists until one of them is empty.
5451 */
5452 while (*g1 && *g2)
5453 {
5454 /*
5455 * We always want to add from the first list.
5456 */
5457 if (*g1 < *g2)
5458 {
5459 if (round == 2)
5460 clstr[count] = *g1;
5461 count++;
5462 g1++;
5463 continue;
5464 }
5465 /*
5466 * We only want to add from the second list if we're adding the
5467 * lists.
5468 */
5469 if (list_op == CLUSTER_ADD)
5470 {
5471 if (round == 2)
5472 clstr[count] = *g2;
5473 count++;
5474 }
5475 if (*g1 == *g2)
5476 g1++;
5477 g2++;
5478 }
5479
5480 /*
5481 * Now add the leftovers from whichever list didn't get finished
5482 * first. As before, we only want to add from the second list if
5483 * we're adding the lists.
5484 */
5485 for (; *g1; g1++, count++)
5486 if (round == 2)
5487 clstr[count] = *g1;
5488 if (list_op == CLUSTER_ADD)
5489 for (; *g2; g2++, count++)
5490 if (round == 2)
5491 clstr[count] = *g2;
5492
5493 if (round == 1)
5494 {
5495 /*
5496 * If the group ended up empty, we don't need to allocate any
5497 * space for it.
5498 */
5499 if (count == 0)
5500 {
5501 clstr = NULL;
5502 break;
5503 }
5504 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5505 if (clstr == NULL)
5506 break;
5507 clstr[count] = 0;
5508 }
5509 }
5510
5511 /*
5512 * Finally, put the new list in place.
5513 */
5514 vim_free(*clstr1);
5515 vim_free(*clstr2);
5516 *clstr1 = clstr;
5517}
5518
5519/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005520 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005521 * If it is not found, 0 is returned.
5522 */
5523 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005524syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525{
5526 int i;
5527 char_u *name_u;
5528
5529 /* Avoid using stricmp() too much, it's slow on some systems */
5530 name_u = vim_strsave_up(name);
5531 if (name_u == NULL)
5532 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005533 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5534 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5535 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 break;
5537 vim_free(name_u);
5538 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5539}
5540
5541/*
5542 * Like syn_scl_name2id(), but take a pointer + length argument.
5543 */
5544 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005545syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546{
5547 char_u *name;
5548 int id = 0;
5549
5550 name = vim_strnsave(linep, len);
5551 if (name != NULL)
5552 {
5553 id = syn_scl_name2id(name);
5554 vim_free(name);
5555 }
5556 return id;
5557}
5558
5559/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005560 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561 * The argument is a pointer to the name and the length of the name.
5562 * If it doesn't exist yet, a new entry is created.
5563 * Return 0 for failure.
5564 */
5565 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005566syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567{
5568 int id;
5569 char_u *name;
5570
5571 name = vim_strnsave(pp, len);
5572 if (name == NULL)
5573 return 0;
5574
5575 id = syn_scl_name2id(name);
5576 if (id == 0) /* doesn't exist yet */
5577 id = syn_add_cluster(name);
5578 else
5579 vim_free(name);
5580 return id;
5581}
5582
5583/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005584 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585 * "name" must be an allocated string, it will be consumed.
5586 * Return 0 for failure.
5587 */
5588 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005589syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005591 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592
5593 /*
5594 * First call for this growarray: init growing array.
5595 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005596 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005597 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005598 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5599 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600 }
5601
Bram Moolenaar42431a72011-04-01 14:44:59 +02005602 len = curwin->w_s->b_syn_clusters.ga_len;
5603 if (len >= MAX_CLUSTER_ID)
5604 {
5605 EMSG((char_u *)_("E848: Too many syntax clusters"));
5606 vim_free(name);
5607 return 0;
5608 }
5609
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610 /*
5611 * Make room for at least one other cluster entry.
5612 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005613 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 {
5615 vim_free(name);
5616 return 0;
5617 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618
Bram Moolenaar860cae12010-06-05 23:22:07 +02005619 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5620 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5621 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5622 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5623 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005624
Bram Moolenaar217ad922005-03-20 22:37:15 +00005625 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005626 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005627 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005628 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005629
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 return len + SYNID_CLUSTER;
5631}
5632
5633/*
5634 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5635 * [add={groupname},..] [remove={groupname},..]".
5636 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005638syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639{
5640 char_u *arg = eap->arg;
5641 char_u *group_name_end;
5642 char_u *rest;
5643 int scl_id;
5644 short *clstr_list;
5645 int got_clstr = FALSE;
5646 int opt_len;
5647 int list_op;
5648
5649 eap->nextcmd = find_nextcmd(arg);
5650 if (eap->skip)
5651 return;
5652
5653 rest = get_group_name(arg, &group_name_end);
5654
5655 if (rest != NULL)
5656 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005657 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5658 if (scl_id == 0)
5659 return;
5660 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005661
5662 for (;;)
5663 {
5664 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005665 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005666 {
5667 opt_len = 3;
5668 list_op = CLUSTER_ADD;
5669 }
5670 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005671 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 {
5673 opt_len = 6;
5674 list_op = CLUSTER_SUBTRACT;
5675 }
5676 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005677 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678 {
5679 opt_len = 8;
5680 list_op = CLUSTER_REPLACE;
5681 }
5682 else
5683 break;
5684
5685 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005686 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687 {
5688 EMSG2(_(e_invarg2), rest);
5689 break;
5690 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005691 if (scl_id >= 0)
5692 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005694 else
5695 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005696 got_clstr = TRUE;
5697 }
5698
5699 if (got_clstr)
5700 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005701 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005702 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 }
5704 }
5705
5706 if (!got_clstr)
5707 EMSG(_("E400: No cluster specified"));
5708 if (rest == NULL || !ends_excmd(*rest))
5709 EMSG2(_(e_invarg2), arg);
5710}
5711
5712/*
5713 * On first call for current buffer: Init growing array.
5714 */
5715 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005716init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005717{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005718 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5719 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720}
5721
5722/*
5723 * Get one pattern for a ":syntax match" or ":syntax region" command.
5724 * Stores the pattern and program in a synpat_T.
5725 * Returns a pointer to the next argument, or NULL in case of an error.
5726 */
5727 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005728get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005729{
5730 char_u *end;
5731 int *p;
5732 int idx;
5733 char_u *cpo_save;
5734
5735 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005736 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737 return NULL;
5738
5739 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5740 if (*end != *arg) /* end delimiter not found */
5741 {
5742 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5743 return NULL;
5744 }
5745 /* store the pattern and compiled regexp program */
5746 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5747 return NULL;
5748
5749 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5750 cpo_save = p_cpo;
5751 p_cpo = (char_u *)"";
5752 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5753 p_cpo = cpo_save;
5754
5755 if (ci->sp_prog == NULL)
5756 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005757 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005758#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005759 syn_clear_time(&ci->sp_time);
5760#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005761
5762 /*
5763 * Check for a match, highlight or region offset.
5764 */
5765 ++end;
5766 do
5767 {
5768 for (idx = SPO_COUNT; --idx >= 0; )
5769 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5770 break;
5771 if (idx >= 0)
5772 {
5773 p = &(ci->sp_offsets[idx]);
5774 if (idx != SPO_LC_OFF)
5775 switch (end[3])
5776 {
5777 case 's': break;
5778 case 'b': break;
5779 case 'e': idx += SPO_COUNT; break;
5780 default: idx = -1; break;
5781 }
5782 if (idx >= 0)
5783 {
5784 ci->sp_off_flags |= (1 << idx);
5785 if (idx == SPO_LC_OFF) /* lc=99 */
5786 {
5787 end += 3;
5788 *p = getdigits(&end);
5789
5790 /* "lc=" offset automatically sets "ms=" offset */
5791 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5792 {
5793 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5794 ci->sp_offsets[SPO_MS_OFF] = *p;
5795 }
5796 }
5797 else /* yy=x+99 */
5798 {
5799 end += 4;
5800 if (*end == '+')
5801 {
5802 ++end;
5803 *p = getdigits(&end); /* positive offset */
5804 }
5805 else if (*end == '-')
5806 {
5807 ++end;
5808 *p = -getdigits(&end); /* negative offset */
5809 }
5810 }
5811 if (*end != ',')
5812 break;
5813 ++end;
5814 }
5815 }
5816 } while (idx >= 0);
5817
Bram Moolenaar1c465442017-03-12 20:10:05 +01005818 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005819 {
5820 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5821 return NULL;
5822 }
5823 return skipwhite(end);
5824}
5825
5826/*
5827 * Handle ":syntax sync .." command.
5828 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005830syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831{
5832 char_u *arg_start = eap->arg;
5833 char_u *arg_end;
5834 char_u *key = NULL;
5835 char_u *next_arg;
5836 int illegal = FALSE;
5837 int finished = FALSE;
5838 long n;
5839 char_u *cpo_save;
5840
5841 if (ends_excmd(*arg_start))
5842 {
5843 syn_cmd_list(eap, TRUE);
5844 return;
5845 }
5846
5847 while (!ends_excmd(*arg_start))
5848 {
5849 arg_end = skiptowhite(arg_start);
5850 next_arg = skipwhite(arg_end);
5851 vim_free(key);
5852 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5853 if (STRCMP(key, "CCOMMENT") == 0)
5854 {
5855 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005856 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005857 if (!ends_excmd(*next_arg))
5858 {
5859 arg_end = skiptowhite(next_arg);
5860 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005861 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005862 (int)(arg_end - next_arg));
5863 next_arg = skipwhite(arg_end);
5864 }
5865 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005866 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 }
5868 else if ( STRNCMP(key, "LINES", 5) == 0
5869 || STRNCMP(key, "MINLINES", 8) == 0
5870 || STRNCMP(key, "MAXLINES", 8) == 0
5871 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5872 {
5873 if (key[4] == 'S')
5874 arg_end = key + 6;
5875 else if (key[0] == 'L')
5876 arg_end = key + 11;
5877 else
5878 arg_end = key + 9;
5879 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5880 {
5881 illegal = TRUE;
5882 break;
5883 }
5884 n = getdigits(&arg_end);
5885 if (!eap->skip)
5886 {
5887 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005888 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005890 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005891 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005892 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005893 }
5894 }
5895 else if (STRCMP(key, "FROMSTART") == 0)
5896 {
5897 if (!eap->skip)
5898 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005899 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5900 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005901 }
5902 }
5903 else if (STRCMP(key, "LINECONT") == 0)
5904 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005905 if (*next_arg == NUL) /* missing pattern */
5906 {
5907 illegal = TRUE;
5908 break;
5909 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005910 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911 {
5912 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5913 finished = TRUE;
5914 break;
5915 }
5916 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5917 if (*arg_end != *next_arg) /* end delimiter not found */
5918 {
5919 illegal = TRUE;
5920 break;
5921 }
5922
5923 if (!eap->skip)
5924 {
5925 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005926 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005927 (int)(arg_end - next_arg - 1))) == NULL)
5928 {
5929 finished = TRUE;
5930 break;
5931 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005932 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005933
5934 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5935 cpo_save = p_cpo;
5936 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005937 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005938 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005939 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005940#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005941 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5942#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005943
Bram Moolenaar860cae12010-06-05 23:22:07 +02005944 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005945 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005946 vim_free(curwin->w_s->b_syn_linecont_pat);
5947 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005948 finished = TRUE;
5949 break;
5950 }
5951 }
5952 next_arg = skipwhite(arg_end + 1);
5953 }
5954 else
5955 {
5956 eap->arg = next_arg;
5957 if (STRCMP(key, "MATCH") == 0)
5958 syn_cmd_match(eap, TRUE);
5959 else if (STRCMP(key, "REGION") == 0)
5960 syn_cmd_region(eap, TRUE);
5961 else if (STRCMP(key, "CLEAR") == 0)
5962 syn_cmd_clear(eap, TRUE);
5963 else
5964 illegal = TRUE;
5965 finished = TRUE;
5966 break;
5967 }
5968 arg_start = next_arg;
5969 }
5970 vim_free(key);
5971 if (illegal)
5972 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5973 else if (!finished)
5974 {
5975 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005976 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005977 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005978 }
5979}
5980
5981/*
5982 * Convert a line of highlight group names into a list of group ID numbers.
5983 * "arg" should point to the "contains" or "nextgroup" keyword.
5984 * "arg" is advanced to after the last group name.
5985 * Careful: the argument is modified (NULs added).
5986 * returns FAIL for some error, OK for success.
5987 */
5988 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005989get_id_list(
5990 char_u **arg,
5991 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005992 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005993 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005994 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005995{
5996 char_u *p = NULL;
5997 char_u *end;
5998 int round;
5999 int count;
6000 int total_count = 0;
6001 short *retval = NULL;
6002 char_u *name;
6003 regmatch_T regmatch;
6004 int id;
6005 int i;
6006 int failed = FALSE;
6007
6008 /*
6009 * We parse the list twice:
6010 * round == 1: count the number of items, allocate the array.
6011 * round == 2: fill the array with the items.
6012 * In round 1 new groups may be added, causing the number of items to
6013 * grow when a regexp is used. In that case round 1 is done once again.
6014 */
6015 for (round = 1; round <= 2; ++round)
6016 {
6017 /*
6018 * skip "contains"
6019 */
6020 p = skipwhite(*arg + keylen);
6021 if (*p != '=')
6022 {
6023 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6024 break;
6025 }
6026 p = skipwhite(p + 1);
6027 if (ends_excmd(*p))
6028 {
6029 EMSG2(_("E406: Empty argument: %s"), *arg);
6030 break;
6031 }
6032
6033 /*
6034 * parse the arguments after "contains"
6035 */
6036 count = 0;
6037 while (!ends_excmd(*p))
6038 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006039 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006040 ;
6041 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6042 if (name == NULL)
6043 {
6044 failed = TRUE;
6045 break;
6046 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006047 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006048 if ( STRCMP(name + 1, "ALLBUT") == 0
6049 || STRCMP(name + 1, "ALL") == 0
6050 || STRCMP(name + 1, "TOP") == 0
6051 || STRCMP(name + 1, "CONTAINED") == 0)
6052 {
6053 if (TOUPPER_ASC(**arg) != 'C')
6054 {
6055 EMSG2(_("E407: %s not allowed here"), name + 1);
6056 failed = TRUE;
6057 vim_free(name);
6058 break;
6059 }
6060 if (count != 0)
6061 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006062 EMSG2(_("E408: %s must be first in contains list"),
6063 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006064 failed = TRUE;
6065 vim_free(name);
6066 break;
6067 }
6068 if (name[1] == 'A')
6069 id = SYNID_ALLBUT;
6070 else if (name[1] == 'T')
6071 id = SYNID_TOP;
6072 else
6073 id = SYNID_CONTAINED;
6074 id += current_syn_inc_tag;
6075 }
6076 else if (name[1] == '@')
6077 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006078 if (skip)
6079 id = -1;
6080 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006081 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006082 }
6083 else
6084 {
6085 /*
6086 * Handle full group name.
6087 */
6088 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6089 id = syn_check_group(name + 1, (int)(end - p));
6090 else
6091 {
6092 /*
6093 * Handle match of regexp with group names.
6094 */
6095 *name = '^';
6096 STRCAT(name, "$");
6097 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6098 if (regmatch.regprog == NULL)
6099 {
6100 failed = TRUE;
6101 vim_free(name);
6102 break;
6103 }
6104
6105 regmatch.rm_ic = TRUE;
6106 id = 0;
6107 for (i = highlight_ga.ga_len; --i >= 0; )
6108 {
6109 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6110 (colnr_T)0))
6111 {
6112 if (round == 2)
6113 {
6114 /* Got more items than expected; can happen
6115 * when adding items that match:
6116 * "contains=a.*b,axb".
6117 * Go back to first round */
6118 if (count >= total_count)
6119 {
6120 vim_free(retval);
6121 round = 1;
6122 }
6123 else
6124 retval[count] = i + 1;
6125 }
6126 ++count;
6127 id = -1; /* remember that we found one */
6128 }
6129 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006130 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006131 }
6132 }
6133 vim_free(name);
6134 if (id == 0)
6135 {
6136 EMSG2(_("E409: Unknown group name: %s"), p);
6137 failed = TRUE;
6138 break;
6139 }
6140 if (id > 0)
6141 {
6142 if (round == 2)
6143 {
6144 /* Got more items than expected, go back to first round */
6145 if (count >= total_count)
6146 {
6147 vim_free(retval);
6148 round = 1;
6149 }
6150 else
6151 retval[count] = id;
6152 }
6153 ++count;
6154 }
6155 p = skipwhite(end);
6156 if (*p != ',')
6157 break;
6158 p = skipwhite(p + 1); /* skip comma in between arguments */
6159 }
6160 if (failed)
6161 break;
6162 if (round == 1)
6163 {
6164 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6165 if (retval == NULL)
6166 break;
6167 retval[count] = 0; /* zero means end of the list */
6168 total_count = count;
6169 }
6170 }
6171
6172 *arg = p;
6173 if (failed || retval == NULL)
6174 {
6175 vim_free(retval);
6176 return FAIL;
6177 }
6178
6179 if (*list == NULL)
6180 *list = retval;
6181 else
6182 vim_free(retval); /* list already found, don't overwrite it */
6183
6184 return OK;
6185}
6186
6187/*
6188 * Make a copy of an ID list.
6189 */
6190 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006191copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006192{
6193 int len;
6194 int count;
6195 short *retval;
6196
6197 if (list == NULL)
6198 return NULL;
6199
6200 for (count = 0; list[count]; ++count)
6201 ;
6202 len = (count + 1) * sizeof(short);
6203 retval = (short *)alloc((unsigned)len);
6204 if (retval != NULL)
6205 mch_memmove(retval, list, (size_t)len);
6206
6207 return retval;
6208}
6209
6210/*
6211 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6212 * "cur_si" can be NULL if not checking the "containedin" list.
6213 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6214 * the current item.
6215 * This function is called very often, keep it fast!!
6216 */
6217 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006218in_id_list(
6219 stateitem_T *cur_si, /* current item or NULL */
6220 short *list, /* id list */
6221 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6222 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006223{
6224 int retval;
6225 short *scl_list;
6226 short item;
6227 short id = ssp->id;
6228 static int depth = 0;
6229 int r;
6230
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006231 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006232 if (cur_si != NULL && ssp->cont_in_list != NULL
6233 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006234 {
6235 /* Ignore transparent items without a contains argument. Double check
6236 * that we don't go back past the first one. */
6237 while ((cur_si->si_flags & HL_TRANS_CONT)
6238 && cur_si > (stateitem_T *)(current_state.ga_data))
6239 --cur_si;
6240 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6241 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006242 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6243 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006244 return TRUE;
6245 }
6246
6247 if (list == NULL)
6248 return FALSE;
6249
6250 /*
6251 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6252 * inside anything. Only allow not-contained groups.
6253 */
6254 if (list == ID_LIST_ALL)
6255 return !contained;
6256
6257 /*
6258 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6259 * contains list. We also require that "id" is at the same ":syn include"
6260 * level as the list.
6261 */
6262 item = *list;
6263 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6264 {
6265 if (item < SYNID_TOP)
6266 {
6267 /* ALL or ALLBUT: accept all groups in the same file */
6268 if (item - SYNID_ALLBUT != ssp->inc_tag)
6269 return FALSE;
6270 }
6271 else if (item < SYNID_CONTAINED)
6272 {
6273 /* TOP: accept all not-contained groups in the same file */
6274 if (item - SYNID_TOP != ssp->inc_tag || contained)
6275 return FALSE;
6276 }
6277 else
6278 {
6279 /* CONTAINED: accept all contained groups in the same file */
6280 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6281 return FALSE;
6282 }
6283 item = *++list;
6284 retval = FALSE;
6285 }
6286 else
6287 retval = TRUE;
6288
6289 /*
6290 * Return "retval" if id is in the contains list.
6291 */
6292 while (item != 0)
6293 {
6294 if (item == id)
6295 return retval;
6296 if (item >= SYNID_CLUSTER)
6297 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006298 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006299 /* restrict recursiveness to 30 to avoid an endless loop for a
6300 * cluster that includes itself (indirectly) */
6301 if (scl_list != NULL && depth < 30)
6302 {
6303 ++depth;
6304 r = in_id_list(NULL, scl_list, ssp, contained);
6305 --depth;
6306 if (r)
6307 return retval;
6308 }
6309 }
6310 item = *++list;
6311 }
6312 return !retval;
6313}
6314
6315struct subcommand
6316{
Bram Moolenaard99df422016-01-29 23:20:40 +01006317 char *name; /* subcommand name */
6318 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006319};
6320
6321static struct subcommand subcommands[] =
6322{
6323 {"case", syn_cmd_case},
6324 {"clear", syn_cmd_clear},
6325 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006326 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327 {"enable", syn_cmd_enable},
6328 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006329 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330 {"keyword", syn_cmd_keyword},
6331 {"list", syn_cmd_list},
6332 {"manual", syn_cmd_manual},
6333 {"match", syn_cmd_match},
6334 {"on", syn_cmd_on},
6335 {"off", syn_cmd_off},
6336 {"region", syn_cmd_region},
6337 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006338 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006339 {"sync", syn_cmd_sync},
6340 {"", syn_cmd_list},
6341 {NULL, NULL}
6342};
6343
6344/*
6345 * ":syntax".
6346 * This searches the subcommands[] table for the subcommand name, and calls a
6347 * syntax_subcommand() function to do the rest.
6348 */
6349 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006350ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006351{
6352 char_u *arg = eap->arg;
6353 char_u *subcmd_end;
6354 char_u *subcmd_name;
6355 int i;
6356
6357 syn_cmdlinep = eap->cmdlinep;
6358
6359 /* isolate subcommand name */
6360 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6361 ;
6362 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6363 if (subcmd_name != NULL)
6364 {
6365 if (eap->skip) /* skip error messages for all subcommands */
6366 ++emsg_skip;
6367 for (i = 0; ; ++i)
6368 {
6369 if (subcommands[i].name == NULL)
6370 {
6371 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6372 break;
6373 }
6374 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6375 {
6376 eap->arg = skipwhite(subcmd_end);
6377 (subcommands[i].func)(eap, FALSE);
6378 break;
6379 }
6380 }
6381 vim_free(subcmd_name);
6382 if (eap->skip)
6383 --emsg_skip;
6384 }
6385}
6386
Bram Moolenaar860cae12010-06-05 23:22:07 +02006387 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006388ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006390 char_u *old_value;
6391 char_u *new_value;
6392
Bram Moolenaar860cae12010-06-05 23:22:07 +02006393 if (curwin->w_s == &curwin->w_buffer->b_s)
6394 {
6395 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6396 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006397 hash_init(&curwin->w_s->b_keywtab);
6398 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006399#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006400 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006401 curwin->w_p_spell = FALSE; /* No spell checking */
6402 clear_string_option(&curwin->w_s->b_p_spc);
6403 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006404 clear_string_option(&curwin->w_s->b_p_spl);
6405#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006406 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006407 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006408
6409 /* save value of b:current_syntax */
6410 old_value = get_var_value((char_u *)"b:current_syntax");
6411 if (old_value != NULL)
6412 old_value = vim_strsave(old_value);
6413
Bram Moolenaard1413d92016-03-02 21:51:56 +01006414#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006415 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6416 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006417 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006418#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006419
6420 /* move value of b:current_syntax to w:current_syntax */
6421 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006422 if (new_value != NULL)
6423 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006424
6425 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006426 if (old_value == NULL)
6427 do_unlet((char_u *)"b:current_syntax", TRUE);
6428 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006429 {
6430 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6431 vim_free(old_value);
6432 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006433}
6434
6435 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006436syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006437{
6438 return (win->w_s->b_syn_patterns.ga_len != 0
6439 || win->w_s->b_syn_clusters.ga_len != 0
6440 || win->w_s->b_keywtab.ht_used > 0
6441 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442}
6443
6444#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6445
6446static enum
6447{
6448 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006449 EXP_CASE, /* expand ":syn case" arguments */
6450 EXP_SPELL, /* expand ":syn spell" arguments */
6451 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006452} expand_what;
6453
Bram Moolenaar4f688582007-07-24 12:34:30 +00006454/*
6455 * Reset include_link, include_default, include_none to 0.
6456 * Called when we are done expanding.
6457 */
6458 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006459reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006460{
6461 include_link = include_default = include_none = 0;
6462}
6463
6464/*
6465 * Handle command line completion for :match and :echohl command: Add "None"
6466 * as highlight group.
6467 */
6468 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006469set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006470{
6471 xp->xp_context = EXPAND_HIGHLIGHT;
6472 xp->xp_pattern = arg;
6473 include_none = 1;
6474}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006475
6476/*
6477 * Handle command line completion for :syntax command.
6478 */
6479 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006480set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006481{
6482 char_u *p;
6483
6484 /* Default: expand subcommands */
6485 xp->xp_context = EXPAND_SYNTAX;
6486 expand_what = EXP_SUBCMD;
6487 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006488 include_link = 0;
6489 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490
6491 /* (part of) subcommand already typed */
6492 if (*arg != NUL)
6493 {
6494 p = skiptowhite(arg);
6495 if (*p != NUL) /* past first word */
6496 {
6497 xp->xp_pattern = skipwhite(p);
6498 if (*skiptowhite(xp->xp_pattern) != NUL)
6499 xp->xp_context = EXPAND_NOTHING;
6500 else if (STRNICMP(arg, "case", p - arg) == 0)
6501 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006502 else if (STRNICMP(arg, "spell", p - arg) == 0)
6503 expand_what = EXP_SPELL;
6504 else if (STRNICMP(arg, "sync", p - arg) == 0)
6505 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6507 || STRNICMP(arg, "region", p - arg) == 0
6508 || STRNICMP(arg, "match", p - arg) == 0
6509 || STRNICMP(arg, "list", p - arg) == 0)
6510 xp->xp_context = EXPAND_HIGHLIGHT;
6511 else
6512 xp->xp_context = EXPAND_NOTHING;
6513 }
6514 }
6515}
6516
Bram Moolenaar071d4272004-06-13 20:20:40 +00006517/*
6518 * Function given to ExpandGeneric() to obtain the list syntax names for
6519 * expansion.
6520 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006521 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006522get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006524 switch (expand_what)
6525 {
6526 case EXP_SUBCMD:
6527 return (char_u *)subcommands[idx].name;
6528 case EXP_CASE:
6529 {
6530 static char *case_args[] = {"match", "ignore", NULL};
6531 return (char_u *)case_args[idx];
6532 }
6533 case EXP_SPELL:
6534 {
6535 static char *spell_args[] =
6536 {"toplevel", "notoplevel", "default", NULL};
6537 return (char_u *)spell_args[idx];
6538 }
6539 case EXP_SYNC:
6540 {
6541 static char *sync_args[] =
6542 {"ccomment", "clear", "fromstart",
6543 "linebreaks=", "linecont", "lines=", "match",
6544 "maxlines=", "minlines=", "region", NULL};
6545 return (char_u *)sync_args[idx];
6546 }
6547 }
6548 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006549}
6550
6551#endif /* FEAT_CMDL_COMPL */
6552
Bram Moolenaar071d4272004-06-13 20:20:40 +00006553/*
6554 * Function called for expression evaluation: get syntax ID at file position.
6555 */
6556 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006557syn_get_id(
6558 win_T *wp,
6559 long lnum,
6560 colnr_T col,
6561 int trans, /* remove transparency */
6562 int *spellp, /* return: can do spell checking */
6563 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006564{
6565 /* When the position is not after the current position and in the same
6566 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006567 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006568 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006569 || col < current_col)
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006570 syntax_start(wp, lnum, NULL);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006571 else if (wp->w_buffer == syn_buf
6572 && lnum == current_lnum
6573 && col > current_col)
6574 /* next_match may not be correct when moving around, e.g. with the
6575 * "skip" expression in searchpair() */
6576 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006577
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006578 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579
6580 return (trans ? current_trans_id : current_id);
6581}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582
Bram Moolenaar860cae12010-06-05 23:22:07 +02006583#if defined(FEAT_CONCEAL) || defined(PROTO)
6584/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006585 * Get extra information about the syntax item. Must be called right after
6586 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006587 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006588 * Returns the current flags.
6589 */
6590 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006591get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006592{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006593 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006594 return current_flags;
6595}
6596
6597/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006598 * Return conceal substitution character
6599 */
6600 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006601syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006602{
6603 return current_sub_char;
6604}
6605#endif
6606
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006607#if defined(FEAT_EVAL) || defined(PROTO)
6608/*
6609 * Return the syntax ID at position "i" in the current stack.
6610 * The caller must have called syn_get_id() before to fill the stack.
6611 * Returns -1 when "i" is out of range.
6612 */
6613 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006614syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006615{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006616 if (i >= current_state.ga_len)
6617 {
6618 /* Need to invalidate the state, because we didn't properly finish it
6619 * for the last character, "keep_state" was TRUE. */
6620 invalidate_current_state();
6621 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006622 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006623 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006624 return CUR_STATE(i).si_id;
6625}
6626#endif
6627
Bram Moolenaar071d4272004-06-13 20:20:40 +00006628#if defined(FEAT_FOLDING) || defined(PROTO)
6629/*
6630 * Function called to get folding level for line "lnum" in window "wp".
6631 */
6632 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006633syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006634{
6635 int level = 0;
6636 int i;
6637
6638 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006639 if (wp->w_s->b_syn_folditems != 0
6640 && !wp->w_s->b_syn_error
6641# ifdef SYN_TIME_LIMIT
6642 && !wp->w_s->b_syn_slow
6643# endif
6644 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006645 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006646 syntax_start(wp, lnum, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006647
6648 for (i = 0; i < current_state.ga_len; ++i)
6649 if (CUR_STATE(i).si_flags & HL_FOLD)
6650 ++level;
6651 }
6652 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006653 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006654 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006655 if (level < 0)
6656 level = 0;
6657 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006658 return level;
6659}
6660#endif
6661
Bram Moolenaar01615492015-02-03 13:00:38 +01006662#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006663/*
6664 * ":syntime".
6665 */
6666 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006667ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006668{
6669 if (STRCMP(eap->arg, "on") == 0)
6670 syn_time_on = TRUE;
6671 else if (STRCMP(eap->arg, "off") == 0)
6672 syn_time_on = FALSE;
6673 else if (STRCMP(eap->arg, "clear") == 0)
6674 syntime_clear();
6675 else if (STRCMP(eap->arg, "report") == 0)
6676 syntime_report();
6677 else
6678 EMSG2(_(e_invarg2), eap->arg);
6679}
6680
6681 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006682syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006683{
6684 profile_zero(&st->total);
6685 profile_zero(&st->slowest);
6686 st->count = 0;
6687 st->match = 0;
6688}
6689
6690/*
6691 * Clear the syntax timing for the current buffer.
6692 */
6693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006694syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006695{
6696 int idx;
6697 synpat_T *spp;
6698
6699 if (!syntax_present(curwin))
6700 {
6701 MSG(_(msg_no_items));
6702 return;
6703 }
6704 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6705 {
6706 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6707 syn_clear_time(&spp->sp_time);
6708 }
6709}
6710
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006711#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6712/*
6713 * Function given to ExpandGeneric() to obtain the possible arguments of the
6714 * ":syntime {on,off,clear,report}" command.
6715 */
6716 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006717get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006718{
6719 switch (idx)
6720 {
6721 case 0: return (char_u *)"on";
6722 case 1: return (char_u *)"off";
6723 case 2: return (char_u *)"clear";
6724 case 3: return (char_u *)"report";
6725 }
6726 return NULL;
6727}
6728#endif
6729
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006730typedef struct
6731{
6732 proftime_T total;
6733 int count;
6734 int match;
6735 proftime_T slowest;
6736 proftime_T average;
6737 int id;
6738 char_u *pattern;
6739} time_entry_T;
6740
6741 static int
6742#ifdef __BORLANDC__
6743_RTLENTRYF
6744#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006745syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006746{
6747 const time_entry_T *s1 = v1;
6748 const time_entry_T *s2 = v2;
6749
6750 return profile_cmp(&s1->total, &s2->total);
6751}
6752
6753/*
6754 * Clear the syntax timing for the current buffer.
6755 */
6756 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006757syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006758{
6759 int idx;
6760 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006761# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006762 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006763# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006764 int len;
6765 proftime_T total_total;
6766 int total_count = 0;
6767 garray_T ga;
6768 time_entry_T *p;
6769
6770 if (!syntax_present(curwin))
6771 {
6772 MSG(_(msg_no_items));
6773 return;
6774 }
6775
6776 ga_init2(&ga, sizeof(time_entry_T), 50);
6777 profile_zero(&total_total);
6778 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6779 {
6780 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6781 if (spp->sp_time.count > 0)
6782 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006783 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006784 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6785 p->total = spp->sp_time.total;
6786 profile_add(&total_total, &spp->sp_time.total);
6787 p->count = spp->sp_time.count;
6788 p->match = spp->sp_time.match;
6789 total_count += spp->sp_time.count;
6790 p->slowest = spp->sp_time.slowest;
6791# ifdef FEAT_FLOAT
6792 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6793 p->average = tm;
6794# endif
6795 p->id = spp->sp_syn.id;
6796 p->pattern = spp->sp_pattern;
6797 ++ga.ga_len;
6798 }
6799 }
6800
Bram Moolenaara2162552017-01-08 17:46:20 +01006801 /* Sort on total time. Skip if there are no items to avoid passing NULL
6802 * pointer to qsort(). */
6803 if (ga.ga_len > 1)
6804 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006805 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006806
6807 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6808 MSG_PUTS("\n");
6809 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6810 {
6811 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6812 p = ((time_entry_T *)ga.ga_data) + idx;
6813
6814 MSG_PUTS(profile_msg(&p->total));
6815 MSG_PUTS(" "); /* make sure there is always a separating space */
6816 msg_advance(13);
6817 msg_outnum(p->count);
6818 MSG_PUTS(" ");
6819 msg_advance(20);
6820 msg_outnum(p->match);
6821 MSG_PUTS(" ");
6822 msg_advance(26);
6823 MSG_PUTS(profile_msg(&p->slowest));
6824 MSG_PUTS(" ");
6825 msg_advance(38);
6826# ifdef FEAT_FLOAT
6827 MSG_PUTS(profile_msg(&p->average));
6828 MSG_PUTS(" ");
6829# endif
6830 msg_advance(50);
6831 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6832 MSG_PUTS(" ");
6833
6834 msg_advance(69);
6835 if (Columns < 80)
6836 len = 20; /* will wrap anyway */
6837 else
6838 len = Columns - 70;
6839 if (len > (int)STRLEN(p->pattern))
6840 len = (int)STRLEN(p->pattern);
6841 msg_outtrans_len(p->pattern, len);
6842 MSG_PUTS("\n");
6843 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006844 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006845 if (!got_int)
6846 {
6847 MSG_PUTS("\n");
6848 MSG_PUTS(profile_msg(&total_total));
6849 msg_advance(13);
6850 msg_outnum(total_count);
6851 MSG_PUTS("\n");
6852 }
6853}
6854#endif
6855
Bram Moolenaar071d4272004-06-13 20:20:40 +00006856#endif /* FEAT_SYN_HL */
6857
Bram Moolenaar071d4272004-06-13 20:20:40 +00006858/**************************************
6859 * Highlighting stuff *
6860 **************************************/
6861
6862/*
6863 * The default highlight groups. These are compiled-in for fast startup and
6864 * they still work when the runtime files can't be found.
6865 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006866 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6867 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006868 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006869#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006870# define CENT(a, b) b
6871#else
6872# define CENT(a, b) a
6873#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006874static char *(highlight_init_both[]) =
6875 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006876 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6877 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6878 CENT("IncSearch term=reverse cterm=reverse",
6879 "IncSearch term=reverse cterm=reverse gui=reverse"),
6880 CENT("ModeMsg term=bold cterm=bold",
6881 "ModeMsg term=bold cterm=bold gui=bold"),
6882 CENT("NonText term=bold ctermfg=Blue",
6883 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6884 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6885 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6886 CENT("StatusLineNC term=reverse cterm=reverse",
6887 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006888 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006889#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006890 CENT("VertSplit term=reverse cterm=reverse",
6891 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006892#endif
6893#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006894 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6895 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006896#endif
6897#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006898 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6899 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006900#endif
6901#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006902 CENT("PmenuSbar ctermbg=Grey",
6903 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006904#endif
6905#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006906 CENT("TabLineSel term=bold cterm=bold",
6907 "TabLineSel term=bold cterm=bold gui=bold"),
6908 CENT("TabLineFill term=reverse cterm=reverse",
6909 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006910#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006911#ifdef FEAT_GUI
6912 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006913 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006914#endif
Bram Moolenaarc768a202017-06-22 16:04:27 +02006915 "default link QuickFixLine Search",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006916 NULL
6917 };
6918
6919static char *(highlight_init_light[]) =
6920 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006921 CENT("Directory term=bold ctermfg=DarkBlue",
6922 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6923 CENT("LineNr term=underline ctermfg=Brown",
6924 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006925 CENT("CursorLineNr term=bold ctermfg=Brown",
6926 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006927 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6928 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6929 CENT("Question term=standout ctermfg=DarkGreen",
6930 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6931 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6932 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006933#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006934 CENT("SpellBad term=reverse ctermbg=LightRed",
6935 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6936 CENT("SpellCap term=reverse ctermbg=LightBlue",
6937 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6938 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6939 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6940 CENT("SpellLocal term=underline ctermbg=Cyan",
6941 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006942#endif
6943#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006944 CENT("PmenuThumb ctermbg=Black",
6945 "PmenuThumb ctermbg=Black guibg=Black"),
6946 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6947 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6948 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6949 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006950#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006951 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6952 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6953 CENT("Title term=bold ctermfg=DarkMagenta",
6954 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6955 CENT("WarningMsg term=standout ctermfg=DarkRed",
6956 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006957#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006958 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6959 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006960#endif
6961#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006962 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6963 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6964 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6965 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
6967#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006968 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6969 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006970#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006971 CENT("Visual term=reverse",
6972 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006973#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006974 CENT("DiffAdd term=bold ctermbg=LightBlue",
6975 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6976 CENT("DiffChange term=bold ctermbg=LightMagenta",
6977 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6978 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6979 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006980#endif
6981#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006982 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#endif
6985#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006986 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006987 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006988 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006989 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006990 CENT("ColorColumn term=reverse ctermbg=LightRed",
6991 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006992#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006993#ifdef FEAT_CONCEAL
6994 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6995 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6996#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006997#ifdef FEAT_AUTOCMD
6998 CENT("MatchParen term=reverse ctermbg=Cyan",
6999 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
7000#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007001#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007002 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007003#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007004 NULL
7005 };
7006
7007static char *(highlight_init_dark[]) =
7008 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007009 CENT("Directory term=bold ctermfg=LightCyan",
7010 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7011 CENT("LineNr term=underline ctermfg=Yellow",
7012 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01007013 CENT("CursorLineNr term=bold ctermfg=Yellow",
7014 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007015 CENT("MoreMsg term=bold ctermfg=LightGreen",
7016 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7017 CENT("Question term=standout ctermfg=LightGreen",
7018 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7019 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7020 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7021 CENT("SpecialKey term=bold ctermfg=LightBlue",
7022 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007023#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007024 CENT("SpellBad term=reverse ctermbg=Red",
7025 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7026 CENT("SpellCap term=reverse ctermbg=Blue",
7027 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7028 CENT("SpellRare term=reverse ctermbg=Magenta",
7029 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7030 CENT("SpellLocal term=underline ctermbg=Cyan",
7031 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007032#endif
7033#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007034 CENT("PmenuThumb ctermbg=White",
7035 "PmenuThumb ctermbg=White guibg=White"),
7036 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7037 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007038 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7039 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007040#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007041 CENT("Title term=bold ctermfg=LightMagenta",
7042 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7043 CENT("WarningMsg term=standout ctermfg=LightRed",
7044 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007045#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007046 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7047 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007048#endif
7049#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007050 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7051 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7052 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7053 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007054#endif
7055#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007056 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7057 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007058#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007059 CENT("Visual term=reverse",
7060 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007061#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007062 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7063 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7064 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7065 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7066 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7067 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007068#endif
7069#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007070 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7071 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007072#endif
7073#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007074 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007075 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007076 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007077 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007078 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7079 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007080#endif
7081#ifdef FEAT_AUTOCMD
7082 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7083 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007084#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007085#ifdef FEAT_CONCEAL
7086 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7087 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7088#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007089#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007090 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007091#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007092 NULL
7093 };
7094
7095 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007096init_highlight(
7097 int both, /* include groups where 'bg' doesn't matter */
7098 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007099{
7100 int i;
7101 char **pp;
7102 static int had_both = FALSE;
7103#ifdef FEAT_EVAL
7104 char_u *p;
7105
7106 /*
7107 * Try finding the color scheme file. Used when a color file was loaded
7108 * and 'background' or 't_Co' is changed.
7109 */
7110 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007111 if (p != NULL)
7112 {
7113 /* The value of g:colors_name could be freed when sourcing the script,
7114 * making "p" invalid, so copy it. */
7115 char_u *copy_p = vim_strsave(p);
7116 int r;
7117
7118 if (copy_p != NULL)
7119 {
7120 r = load_colors(copy_p);
7121 vim_free(copy_p);
7122 if (r == OK)
7123 return;
7124 }
7125 }
7126
Bram Moolenaar071d4272004-06-13 20:20:40 +00007127#endif
7128
7129 /*
7130 * Didn't use a color file, use the compiled-in colors.
7131 */
7132 if (both)
7133 {
7134 had_both = TRUE;
7135 pp = highlight_init_both;
7136 for (i = 0; pp[i] != NULL; ++i)
7137 do_highlight((char_u *)pp[i], reset, TRUE);
7138 }
7139 else if (!had_both)
7140 /* Don't do anything before the call with both == TRUE from main().
7141 * Not everything has been setup then, and that call will overrule
7142 * everything anyway. */
7143 return;
7144
7145 if (*p_bg == 'l')
7146 pp = highlight_init_light;
7147 else
7148 pp = highlight_init_dark;
7149 for (i = 0; pp[i] != NULL; ++i)
7150 do_highlight((char_u *)pp[i], reset, TRUE);
7151
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007152 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007153 * depend on the number of colors available.
7154 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007155 * to avoid Statement highlighted text disappears.
7156 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007157 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007158 do_highlight((char_u *)(*p_bg == 'l'
7159 ? "Visual cterm=NONE ctermbg=LightGrey"
7160 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007161 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007162 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007163 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7164 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007165 if (*p_bg == 'l')
7166 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7167 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007168
Bram Moolenaar071d4272004-06-13 20:20:40 +00007169#ifdef FEAT_SYN_HL
7170 /*
7171 * If syntax highlighting is enabled load the highlighting for it.
7172 */
7173 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007174 {
7175 static int recursive = 0;
7176
7177 if (recursive >= 5)
7178 EMSG(_("E679: recursive loop loading syncolor.vim"));
7179 else
7180 {
7181 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007182 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007183 --recursive;
7184 }
7185 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007186#endif
7187}
7188
7189/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007190 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191 * Return OK for success, FAIL for failure.
7192 */
7193 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007194load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007195{
7196 char_u *buf;
7197 int retval = FAIL;
7198 static int recursive = FALSE;
7199
7200 /* When being called recursively, this is probably because setting
7201 * 'background' caused the highlighting to be reloaded. This means it is
7202 * working, thus we should return OK. */
7203 if (recursive)
7204 return OK;
7205
7206 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007207 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208 if (buf != NULL)
7209 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007210 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007211 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007213#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007214 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007215#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007216 }
7217 recursive = FALSE;
7218
7219 return retval;
7220}
7221
7222/*
7223 * Handle the ":highlight .." command.
7224 * When using ":hi clear" this is called recursively for each group with
7225 * "forceit" and "init" both TRUE.
7226 */
7227 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007228do_highlight(
7229 char_u *line,
7230 int forceit,
7231 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007232{
7233 char_u *name_end;
7234 char_u *p;
7235 char_u *linep;
7236 char_u *key_start;
7237 char_u *arg_start;
7238 char_u *key = NULL, *arg = NULL;
7239 long i;
7240 int off;
7241 int len;
7242 int attr;
7243 int id;
7244 int idx;
7245 int dodefault = FALSE;
7246 int doclear = FALSE;
7247 int dolink = FALSE;
7248 int error = FALSE;
7249 int color;
7250 int is_normal_group = FALSE; /* "Normal" group */
7251#ifdef FEAT_GUI_X11
7252 int is_menu_group = FALSE; /* "Menu" group */
7253 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7254 int is_tooltip_group = FALSE; /* "Tooltip" group */
7255 int do_colors = FALSE; /* need to update colors? */
7256#else
7257# define is_menu_group 0
7258# define is_tooltip_group 0
7259#endif
7260
7261 /*
7262 * If no argument, list current highlighting.
7263 */
7264 if (ends_excmd(*line))
7265 {
7266 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7267 /* TODO: only call when the group has attributes set */
7268 highlight_list_one((int)i);
7269 return;
7270 }
7271
7272 /*
7273 * Isolate the name.
7274 */
7275 name_end = skiptowhite(line);
7276 linep = skipwhite(name_end);
7277
7278 /*
7279 * Check for "default" argument.
7280 */
7281 if (STRNCMP(line, "default", name_end - line) == 0)
7282 {
7283 dodefault = TRUE;
7284 line = linep;
7285 name_end = skiptowhite(line);
7286 linep = skipwhite(name_end);
7287 }
7288
7289 /*
7290 * Check for "clear" or "link" argument.
7291 */
7292 if (STRNCMP(line, "clear", name_end - line) == 0)
7293 doclear = TRUE;
7294 if (STRNCMP(line, "link", name_end - line) == 0)
7295 dolink = TRUE;
7296
7297 /*
7298 * ":highlight {group-name}": list highlighting for one group.
7299 */
7300 if (!doclear && !dolink && ends_excmd(*linep))
7301 {
7302 id = syn_namen2id(line, (int)(name_end - line));
7303 if (id == 0)
7304 EMSG2(_("E411: highlight group not found: %s"), line);
7305 else
7306 highlight_list_one(id);
7307 return;
7308 }
7309
7310 /*
7311 * Handle ":highlight link {from} {to}" command.
7312 */
7313 if (dolink)
7314 {
7315 char_u *from_start = linep;
7316 char_u *from_end;
7317 char_u *to_start;
7318 char_u *to_end;
7319 int from_id;
7320 int to_id;
7321
7322 from_end = skiptowhite(from_start);
7323 to_start = skipwhite(from_end);
7324 to_end = skiptowhite(to_start);
7325
7326 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7327 {
7328 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7329 from_start);
7330 return;
7331 }
7332
7333 if (!ends_excmd(*skipwhite(to_end)))
7334 {
7335 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7336 return;
7337 }
7338
7339 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7340 if (STRNCMP(to_start, "NONE", 4) == 0)
7341 to_id = 0;
7342 else
7343 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7344
7345 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7346 {
7347 /*
7348 * Don't allow a link when there already is some highlighting
7349 * for the group, unless '!' is used
7350 */
7351 if (to_id > 0 && !forceit && !init
7352 && hl_has_settings(from_id - 1, dodefault))
7353 {
7354 if (sourcing_name == NULL && !dodefault)
7355 EMSG(_("E414: group has settings, highlight link ignored"));
7356 }
7357 else
7358 {
7359 if (!init)
7360 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7361 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007362#ifdef FEAT_EVAL
7363 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7364#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01007365 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007366 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007367 }
7368 }
7369
7370 /* Only call highlight_changed() once, after sourcing a syntax file */
7371 need_highlight_changed = TRUE;
7372
7373 return;
7374 }
7375
7376 if (doclear)
7377 {
7378 /*
7379 * ":highlight clear [group]" command.
7380 */
7381 line = linep;
7382 if (ends_excmd(*line))
7383 {
7384#ifdef FEAT_GUI
7385 /* First, we do not destroy the old values, but allocate the new
7386 * ones and update the display. THEN we destroy the old values.
7387 * If we destroy the old values first, then the old values
7388 * (such as GuiFont's or GuiFontset's) will still be displayed but
7389 * invalid because they were free'd.
7390 */
7391 if (gui.in_use)
7392 {
7393# ifdef FEAT_BEVAL_TIP
7394 gui_init_tooltip_font();
7395# endif
7396# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7397 gui_init_menu_font();
7398# endif
7399 }
7400# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7401 gui_mch_def_colors();
7402# endif
7403# ifdef FEAT_GUI_X11
7404# ifdef FEAT_MENU
7405
7406 /* This only needs to be done when there is no Menu highlight
7407 * group defined by default, which IS currently the case.
7408 */
7409 gui_mch_new_menu_colors();
7410# endif
7411 if (gui.in_use)
7412 {
7413 gui_new_scrollbar_colors();
7414# ifdef FEAT_BEVAL
7415 gui_mch_new_tooltip_colors();
7416# endif
7417# ifdef FEAT_MENU
7418 gui_mch_new_menu_font();
7419# endif
7420 }
7421# endif
7422
7423 /* Ok, we're done allocating the new default graphics items.
7424 * The screen should already be refreshed at this point.
7425 * It is now Ok to clear out the old data.
7426 */
7427#endif
7428#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007429 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007430#endif
7431 restore_cterm_colors();
7432
7433 /*
7434 * Clear all default highlight groups and load the defaults.
7435 */
7436 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7437 highlight_clear(idx);
7438 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007439#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007440 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007441 highlight_gui_started();
7442#endif
7443 highlight_changed();
7444 redraw_later_clear();
7445 return;
7446 }
7447 name_end = skiptowhite(line);
7448 linep = skipwhite(name_end);
7449 }
7450
7451 /*
7452 * Find the group name in the table. If it does not exist yet, add it.
7453 */
7454 id = syn_check_group(line, (int)(name_end - line));
7455 if (id == 0) /* failed (out of memory) */
7456 return;
7457 idx = id - 1; /* index is ID minus one */
7458
7459 /* Return if "default" was used and the group already has settings. */
7460 if (dodefault && hl_has_settings(idx, TRUE))
7461 return;
7462
7463 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7464 is_normal_group = TRUE;
7465#ifdef FEAT_GUI_X11
7466 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7467 is_menu_group = TRUE;
7468 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7469 is_scrollbar_group = TRUE;
7470 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7471 is_tooltip_group = TRUE;
7472#endif
7473
7474 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7475 if (doclear || (forceit && init))
7476 {
7477 highlight_clear(idx);
7478 if (!doclear)
7479 HL_TABLE()[idx].sg_set = 0;
7480 }
7481
7482 if (!doclear)
7483 while (!ends_excmd(*linep))
7484 {
7485 key_start = linep;
7486 if (*linep == '=')
7487 {
7488 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7489 error = TRUE;
7490 break;
7491 }
7492
7493 /*
7494 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7495 * "guibg").
7496 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007497 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007498 ++linep;
7499 vim_free(key);
7500 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7501 if (key == NULL)
7502 {
7503 error = TRUE;
7504 break;
7505 }
7506 linep = skipwhite(linep);
7507
7508 if (STRCMP(key, "NONE") == 0)
7509 {
7510 if (!init || HL_TABLE()[idx].sg_set == 0)
7511 {
7512 if (!init)
7513 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7514 highlight_clear(idx);
7515 }
7516 continue;
7517 }
7518
7519 /*
7520 * Check for the equal sign.
7521 */
7522 if (*linep != '=')
7523 {
7524 EMSG2(_("E416: missing equal sign: %s"), key_start);
7525 error = TRUE;
7526 break;
7527 }
7528 ++linep;
7529
7530 /*
7531 * Isolate the argument.
7532 */
7533 linep = skipwhite(linep);
7534 if (*linep == '\'') /* guifg='color name' */
7535 {
7536 arg_start = ++linep;
7537 linep = vim_strchr(linep, '\'');
7538 if (linep == NULL)
7539 {
7540 EMSG2(_(e_invarg2), key_start);
7541 error = TRUE;
7542 break;
7543 }
7544 }
7545 else
7546 {
7547 arg_start = linep;
7548 linep = skiptowhite(linep);
7549 }
7550 if (linep == arg_start)
7551 {
7552 EMSG2(_("E417: missing argument: %s"), key_start);
7553 error = TRUE;
7554 break;
7555 }
7556 vim_free(arg);
7557 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7558 if (arg == NULL)
7559 {
7560 error = TRUE;
7561 break;
7562 }
7563 if (*linep == '\'')
7564 ++linep;
7565
7566 /*
7567 * Store the argument.
7568 */
7569 if ( STRCMP(key, "TERM") == 0
7570 || STRCMP(key, "CTERM") == 0
7571 || STRCMP(key, "GUI") == 0)
7572 {
7573 attr = 0;
7574 off = 0;
7575 while (arg[off] != NUL)
7576 {
7577 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7578 {
7579 len = (int)STRLEN(hl_name_table[i]);
7580 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7581 {
7582 attr |= hl_attr_table[i];
7583 off += len;
7584 break;
7585 }
7586 }
7587 if (i < 0)
7588 {
7589 EMSG2(_("E418: Illegal value: %s"), arg);
7590 error = TRUE;
7591 break;
7592 }
7593 if (arg[off] == ',') /* another one follows */
7594 ++off;
7595 }
7596 if (error)
7597 break;
7598 if (*key == 'T')
7599 {
7600 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7601 {
7602 if (!init)
7603 HL_TABLE()[idx].sg_set |= SG_TERM;
7604 HL_TABLE()[idx].sg_term = attr;
7605 }
7606 }
7607 else if (*key == 'C')
7608 {
7609 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7610 {
7611 if (!init)
7612 HL_TABLE()[idx].sg_set |= SG_CTERM;
7613 HL_TABLE()[idx].sg_cterm = attr;
7614 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7615 }
7616 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007617#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007618 else
7619 {
7620 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7621 {
7622 if (!init)
7623 HL_TABLE()[idx].sg_set |= SG_GUI;
7624 HL_TABLE()[idx].sg_gui = attr;
7625 }
7626 }
7627#endif
7628 }
7629 else if (STRCMP(key, "FONT") == 0)
7630 {
7631 /* in non-GUI fonts are simply ignored */
7632#ifdef FEAT_GUI
7633 if (!gui.shell_created)
7634 {
7635 /* GUI not started yet, always accept the name. */
7636 vim_free(HL_TABLE()[idx].sg_font_name);
7637 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7638 }
7639 else
7640 {
7641 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7642# ifdef FEAT_XFONTSET
7643 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7644# endif
7645 /* First, save the current font/fontset.
7646 * Then try to allocate the font/fontset.
7647 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7648 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7649 */
7650
7651 HL_TABLE()[idx].sg_font = NOFONT;
7652# ifdef FEAT_XFONTSET
7653 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7654# endif
7655 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007656 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007657
7658# ifdef FEAT_XFONTSET
7659 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7660 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007661 /* New fontset was accepted. Free the old one, if there
7662 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007663 gui_mch_free_fontset(temp_sg_fontset);
7664 vim_free(HL_TABLE()[idx].sg_font_name);
7665 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7666 }
7667 else
7668 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7669# endif
7670 if (HL_TABLE()[idx].sg_font != NOFONT)
7671 {
7672 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007673 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007674 gui_mch_free_font(temp_sg_font);
7675 vim_free(HL_TABLE()[idx].sg_font_name);
7676 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7677 }
7678 else
7679 HL_TABLE()[idx].sg_font = temp_sg_font;
7680 }
7681#endif
7682 }
7683 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7684 {
7685 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7686 {
7687 if (!init)
7688 HL_TABLE()[idx].sg_set |= SG_CTERM;
7689
7690 /* When setting the foreground color, and previously the "bold"
7691 * flag was set for a light color, reset it now */
7692 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7693 {
7694 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7695 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7696 }
7697
7698 if (VIM_ISDIGIT(*arg))
7699 color = atoi((char *)arg);
7700 else if (STRICMP(arg, "fg") == 0)
7701 {
7702 if (cterm_normal_fg_color)
7703 color = cterm_normal_fg_color - 1;
7704 else
7705 {
7706 EMSG(_("E419: FG color unknown"));
7707 error = TRUE;
7708 break;
7709 }
7710 }
7711 else if (STRICMP(arg, "bg") == 0)
7712 {
7713 if (cterm_normal_bg_color > 0)
7714 color = cterm_normal_bg_color - 1;
7715 else
7716 {
7717 EMSG(_("E420: BG color unknown"));
7718 error = TRUE;
7719 break;
7720 }
7721 }
7722 else
7723 {
7724 static char *(color_names[28]) = {
7725 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7726 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7727 "Gray", "Grey",
7728 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7729 "Blue", "LightBlue", "Green", "LightGreen",
7730 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7731 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7732 static int color_numbers_16[28] = {0, 1, 2, 3,
7733 4, 5, 6, 6,
7734 7, 7,
7735 7, 7, 8, 8,
7736 9, 9, 10, 10,
7737 11, 11, 12, 12, 13,
7738 13, 14, 14, 15, -1};
7739 /* for xterm with 88 colors... */
7740 static int color_numbers_88[28] = {0, 4, 2, 6,
7741 1, 5, 32, 72,
7742 84, 84,
7743 7, 7, 82, 82,
7744 12, 43, 10, 61,
7745 14, 63, 9, 74, 13,
7746 75, 11, 78, 15, -1};
7747 /* for xterm with 256 colors... */
7748 static int color_numbers_256[28] = {0, 4, 2, 6,
7749 1, 5, 130, 130,
7750 248, 248,
7751 7, 7, 242, 242,
7752 12, 81, 10, 121,
7753 14, 159, 9, 224, 13,
7754 225, 11, 229, 15, -1};
7755 /* for terminals with less than 16 colors... */
7756 static int color_numbers_8[28] = {0, 4, 2, 6,
7757 1, 5, 3, 3,
7758 7, 7,
7759 7, 7, 0+8, 0+8,
7760 4+8, 4+8, 2+8, 2+8,
7761 6+8, 6+8, 1+8, 1+8, 5+8,
7762 5+8, 3+8, 3+8, 7+8, -1};
7763#if defined(__QNXNTO__)
7764 static int *color_numbers_8_qansi = color_numbers_8;
7765 /* On qnx, the 8 & 16 color arrays are the same */
7766 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7767 color_numbers_8_qansi = color_numbers_16;
7768#endif
7769
7770 /* reduce calls to STRICMP a bit, it can be slow */
7771 off = TOUPPER_ASC(*arg);
7772 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7773 if (off == color_names[i][0]
7774 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7775 break;
7776 if (i < 0)
7777 {
7778 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7779 error = TRUE;
7780 break;
7781 }
7782
Bram Moolenaaraab93b12017-03-18 21:37:28 +01007783 /* Use the _16 table to check if it's a valid color name. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007784 color = color_numbers_16[i];
7785 if (color >= 0)
7786 {
7787 if (t_colors == 8)
7788 {
7789 /* t_Co is 8: use the 8 colors table */
7790#if defined(__QNXNTO__)
7791 color = color_numbers_8_qansi[i];
7792#else
7793 color = color_numbers_8[i];
7794#endif
7795 if (key[5] == 'F')
7796 {
7797 /* set/reset bold attribute to get light foreground
7798 * colors (on some terminals, e.g. "linux") */
7799 if (color & 8)
7800 {
7801 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7802 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7803 }
7804 else
7805 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7806 }
7807 color &= 7; /* truncate to 8 colors */
7808 }
7809 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007810 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 {
7812 /*
7813 * Guess: if the termcap entry ends in 'm', it is
7814 * probably an xterm-like terminal. Use the changed
7815 * order for colors.
7816 */
7817 if (*T_CAF != NUL)
7818 p = T_CAF;
7819 else
7820 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007821 if (*p != NUL && (t_colors > 256
7822 || *(p + STRLEN(p) - 1) == 'm'))
7823 {
7824 if (t_colors == 88)
7825 color = color_numbers_88[i];
7826 else if (t_colors >= 256)
7827 color = color_numbers_256[i];
7828 else
7829 color = color_numbers_8[i];
7830 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007831 }
7832 }
7833 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007834 /* Add one to the argument, to avoid zero. Zero is used for
7835 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836 if (key[5] == 'F')
7837 {
7838 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7839 if (is_normal_group)
7840 {
7841 cterm_normal_fg_color = color + 1;
7842 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7843#ifdef FEAT_GUI
7844 /* Don't do this if the GUI is used. */
7845 if (!gui.in_use && !gui.starting)
7846#endif
7847 {
7848 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007849 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 term_fg_color(color);
7851 }
7852 }
7853 }
7854 else
7855 {
7856 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7857 if (is_normal_group)
7858 {
7859 cterm_normal_bg_color = color + 1;
7860#ifdef FEAT_GUI
7861 /* Don't mess with 'background' if the GUI is used. */
7862 if (!gui.in_use && !gui.starting)
7863#endif
7864 {
7865 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007866 if (color >= 0)
7867 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007868 int dark = -1;
7869
Bram Moolenaarccbab932010-05-13 15:40:30 +02007870 if (termcap_active)
7871 term_bg_color(color);
7872 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007873 dark = (color == 0 || color == 4);
7874 /* Limit the heuristic to the standard 16 colors */
7875 else if (color < 16)
7876 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007877 /* Set the 'background' option if the value is
7878 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007879 if (dark != -1
7880 && dark != (*p_bg == 'd')
7881 && !option_was_set((char_u *)"bg"))
7882 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007883 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007884 (char_u *)(dark ? "dark" : "light"), 0);
7885 reset_option_was_set((char_u *)"bg");
7886 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007887 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007888 }
7889 }
7890 }
7891 }
7892 }
7893 else if (STRCMP(key, "GUIFG") == 0)
7894 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007895#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007896 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007897 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007898 if (!init)
7899 HL_TABLE()[idx].sg_set |= SG_GUI;
7900
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007901# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007902 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007903 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007904 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007905 {
7906 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007907# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007908 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7909 if (STRCMP(arg, "NONE"))
7910 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7911 else
7912 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007913# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007914# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007915 if (is_menu_group)
7916 gui.menu_fg_pixel = i;
7917 if (is_scrollbar_group)
7918 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007919# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007920 if (is_tooltip_group)
7921 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007922# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007923 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007924# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007925 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007926# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007927 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007928#endif
7929 }
7930 else if (STRCMP(key, "GUIBG") == 0)
7931 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007932#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007933 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007934 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007935 if (!init)
7936 HL_TABLE()[idx].sg_set |= SG_GUI;
7937
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007938# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007939 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007940 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007941 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007942 {
7943 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007944# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007945 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7946 if (STRCMP(arg, "NONE") != 0)
7947 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7948 else
7949 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007950# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007951# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007952 if (is_menu_group)
7953 gui.menu_bg_pixel = i;
7954 if (is_scrollbar_group)
7955 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007956# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007957 if (is_tooltip_group)
7958 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007959# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007960 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007961# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007962 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007963# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007964 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007965#endif
7966 }
7967 else if (STRCMP(key, "GUISP") == 0)
7968 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007969#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007970 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7971 {
7972 if (!init)
7973 HL_TABLE()[idx].sg_set |= SG_GUI;
7974
Bram Moolenaar61623362010-07-14 22:04:22 +02007975# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007976 i = color_name2handle(arg);
7977 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7978 {
7979 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007980# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007981 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7982 if (STRCMP(arg, "NONE") != 0)
7983 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7984 else
7985 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007986# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007987 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007988# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007989 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007990#endif
7991 }
7992 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7993 {
7994 char_u buf[100];
7995 char_u *tname;
7996
7997 if (!init)
7998 HL_TABLE()[idx].sg_set |= SG_TERM;
7999
8000 /*
8001 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008002 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008003 */
8004 if (STRNCMP(arg, "t_", 2) == 0)
8005 {
8006 off = 0;
8007 buf[0] = 0;
8008 while (arg[off] != NUL)
8009 {
8010 /* Isolate one termcap name */
8011 for (len = 0; arg[off + len] &&
8012 arg[off + len] != ','; ++len)
8013 ;
8014 tname = vim_strnsave(arg + off, len);
8015 if (tname == NULL) /* out of memory */
8016 {
8017 error = TRUE;
8018 break;
8019 }
8020 /* lookup the escape sequence for the item */
8021 p = get_term_code(tname);
8022 vim_free(tname);
8023 if (p == NULL) /* ignore non-existing things */
8024 p = (char_u *)"";
8025
8026 /* Append it to the already found stuff */
8027 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8028 {
8029 EMSG2(_("E422: terminal code too long: %s"), arg);
8030 error = TRUE;
8031 break;
8032 }
8033 STRCAT(buf, p);
8034
8035 /* Advance to the next item */
8036 off += len;
8037 if (arg[off] == ',') /* another one follows */
8038 ++off;
8039 }
8040 }
8041 else
8042 {
8043 /*
8044 * Copy characters from arg[] to buf[], translating <> codes.
8045 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008046 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008047 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008048 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008049 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008050 off += len;
8051 else /* copy as normal char */
8052 buf[off++] = *p++;
8053 }
8054 buf[off] = NUL;
8055 }
8056 if (error)
8057 break;
8058
8059 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8060 p = NULL;
8061 else
8062 p = vim_strsave(buf);
8063 if (key[2] == 'A')
8064 {
8065 vim_free(HL_TABLE()[idx].sg_start);
8066 HL_TABLE()[idx].sg_start = p;
8067 }
8068 else
8069 {
8070 vim_free(HL_TABLE()[idx].sg_stop);
8071 HL_TABLE()[idx].sg_stop = p;
8072 }
8073 }
8074 else
8075 {
8076 EMSG2(_("E423: Illegal argument: %s"), key_start);
8077 error = TRUE;
8078 break;
8079 }
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008080 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008081
8082 /*
8083 * When highlighting has been given for a group, don't link it.
8084 */
8085 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8086 HL_TABLE()[idx].sg_link = 0;
8087
8088 /*
8089 * Continue with next argument.
8090 */
8091 linep = skipwhite(linep);
8092 }
8093
8094 /*
8095 * If there is an error, and it's a new entry, remove it from the table.
8096 */
8097 if (error && idx == highlight_ga.ga_len)
8098 syn_unadd_group();
8099 else
8100 {
8101 if (is_normal_group)
8102 {
8103 HL_TABLE()[idx].sg_term_attr = 0;
8104 HL_TABLE()[idx].sg_cterm_attr = 0;
8105#ifdef FEAT_GUI
8106 HL_TABLE()[idx].sg_gui_attr = 0;
8107 /*
8108 * Need to update all groups, because they might be using "bg"
8109 * and/or "fg", which have been changed now.
8110 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008111#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008112#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008113 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114 highlight_gui_started();
8115#endif
8116 }
8117#ifdef FEAT_GUI_X11
8118# ifdef FEAT_MENU
8119 else if (is_menu_group)
8120 {
8121 if (gui.in_use && do_colors)
8122 gui_mch_new_menu_colors();
8123 }
8124# endif
8125 else if (is_scrollbar_group)
8126 {
8127 if (gui.in_use && do_colors)
8128 gui_new_scrollbar_colors();
8129 }
8130# ifdef FEAT_BEVAL
8131 else if (is_tooltip_group)
8132 {
8133 if (gui.in_use && do_colors)
8134 gui_mch_new_tooltip_colors();
8135 }
8136# endif
8137#endif
8138 else
8139 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008140#ifdef FEAT_EVAL
8141 HL_TABLE()[idx].sg_scriptID = current_SID;
8142#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008143 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144 }
8145 vim_free(key);
8146 vim_free(arg);
8147
8148 /* Only call highlight_changed() once, after sourcing a syntax file */
8149 need_highlight_changed = TRUE;
8150}
8151
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008152#if defined(EXITFREE) || defined(PROTO)
8153 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008154free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008155{
8156 int i;
8157
8158 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008159 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008160 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008161 vim_free(HL_TABLE()[i].sg_name);
8162 vim_free(HL_TABLE()[i].sg_name_u);
8163 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008164 ga_clear(&highlight_ga);
8165}
8166#endif
8167
Bram Moolenaar071d4272004-06-13 20:20:40 +00008168/*
8169 * Reset the cterm colors to what they were before Vim was started, if
8170 * possible. Otherwise reset them to zero.
8171 */
8172 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008173restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008175#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008176 /* Since t_me has been set, this probably means that the user
8177 * wants to use this as default colors. Need to reset default
8178 * background/foreground colors. */
8179 mch_set_normal_colors();
8180#else
8181 cterm_normal_fg_color = 0;
8182 cterm_normal_fg_bold = 0;
8183 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008184# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008185 cterm_normal_fg_gui_color = INVALCOLOR;
8186 cterm_normal_bg_gui_color = INVALCOLOR;
8187# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008188#endif
8189}
8190
8191/*
8192 * Return TRUE if highlight group "idx" has any settings.
8193 * When "check_link" is TRUE also check for an existing link.
8194 */
8195 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008196hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197{
8198 return ( HL_TABLE()[idx].sg_term_attr != 0
8199 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008200 || HL_TABLE()[idx].sg_cterm_fg != 0
8201 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008202#ifdef FEAT_GUI
8203 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008204 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8205 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8206 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008207 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208#endif
8209 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8210}
8211
8212/*
8213 * Clear highlighting for one group.
8214 */
8215 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008216highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008218 HL_TABLE()[idx].sg_cleared = TRUE;
8219
Bram Moolenaar071d4272004-06-13 20:20:40 +00008220 HL_TABLE()[idx].sg_term = 0;
8221 vim_free(HL_TABLE()[idx].sg_start);
8222 HL_TABLE()[idx].sg_start = NULL;
8223 vim_free(HL_TABLE()[idx].sg_stop);
8224 HL_TABLE()[idx].sg_stop = NULL;
8225 HL_TABLE()[idx].sg_term_attr = 0;
8226 HL_TABLE()[idx].sg_cterm = 0;
8227 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8228 HL_TABLE()[idx].sg_cterm_fg = 0;
8229 HL_TABLE()[idx].sg_cterm_bg = 0;
8230 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008231#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008232 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8234 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008235 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8236 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008237 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8238 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008239#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008240#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008241 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8242 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008243#endif
8244#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008245 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008246 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8247 HL_TABLE()[idx].sg_font = NOFONT;
8248# ifdef FEAT_XFONTSET
8249 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8250 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8251# endif
8252 vim_free(HL_TABLE()[idx].sg_font_name);
8253 HL_TABLE()[idx].sg_font_name = NULL;
8254 HL_TABLE()[idx].sg_gui_attr = 0;
8255#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008256#ifdef FEAT_EVAL
8257 /* Clear the script ID only when there is no link, since that is not
8258 * cleared. */
8259 if (HL_TABLE()[idx].sg_link == 0)
8260 HL_TABLE()[idx].sg_scriptID = 0;
8261#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008262}
8263
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008264#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008265/*
8266 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008267 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008268 * "Tooltip" colors.
8269 */
8270 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008271set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008272{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008273#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008274# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008275 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008276# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008277 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008278 if (set_group_colors((char_u *)"Normal",
8279 &gui.norm_pixel, &gui.back_pixel,
8280 FALSE, TRUE, FALSE))
8281 {
8282 gui_mch_new_colors();
8283 must_redraw = CLEAR;
8284 }
8285# ifdef FEAT_GUI_X11
8286 if (set_group_colors((char_u *)"Menu",
8287 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8288 TRUE, FALSE, FALSE))
8289 {
8290# ifdef FEAT_MENU
8291 gui_mch_new_menu_colors();
8292# endif
8293 must_redraw = CLEAR;
8294 }
8295# ifdef FEAT_BEVAL
8296 if (set_group_colors((char_u *)"Tooltip",
8297 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8298 FALSE, FALSE, TRUE))
8299 {
8300# ifdef FEAT_TOOLBAR
8301 gui_mch_new_tooltip_colors();
8302# endif
8303 must_redraw = CLEAR;
8304 }
8305# endif
8306 if (set_group_colors((char_u *)"Scrollbar",
8307 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8308 FALSE, FALSE, FALSE))
8309 {
8310 gui_new_scrollbar_colors();
8311 must_redraw = CLEAR;
8312 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008314 }
8315#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008316#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008317# ifdef FEAT_GUI
8318 else
8319# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008320 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008321 int idx;
8322
8323 idx = syn_name2id((char_u *)"Normal") - 1;
8324 if (idx >= 0)
8325 {
8326 gui_do_one_color(idx, FALSE, FALSE);
8327
8328 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8329 {
8330 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8331 must_redraw = CLEAR;
8332 }
8333 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8334 {
8335 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8336 must_redraw = CLEAR;
8337 }
8338 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339 }
8340#endif
8341}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008342#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008343
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008344#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008345/*
8346 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8347 */
8348 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008349set_group_colors(
8350 char_u *name,
8351 guicolor_T *fgp,
8352 guicolor_T *bgp,
8353 int do_menu,
8354 int use_norm,
8355 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008356{
8357 int idx;
8358
8359 idx = syn_name2id(name) - 1;
8360 if (idx >= 0)
8361 {
8362 gui_do_one_color(idx, do_menu, do_tooltip);
8363
8364 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8365 *fgp = HL_TABLE()[idx].sg_gui_fg;
8366 else if (use_norm)
8367 *fgp = gui.def_norm_pixel;
8368 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8369 *bgp = HL_TABLE()[idx].sg_gui_bg;
8370 else if (use_norm)
8371 *bgp = gui.def_back_pixel;
8372 return TRUE;
8373 }
8374 return FALSE;
8375}
8376
8377/*
8378 * Get the font of the "Normal" group.
8379 * Returns "" when it's not found or not set.
8380 */
8381 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008382hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383{
8384 int id;
8385 char_u *s;
8386
8387 id = syn_name2id((char_u *)"Normal");
8388 if (id > 0)
8389 {
8390 s = HL_TABLE()[id - 1].sg_font_name;
8391 if (s != NULL)
8392 return s;
8393 }
8394 return (char_u *)"";
8395}
8396
8397/*
8398 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8399 * actually chosen to be used.
8400 */
8401 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008402hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008403{
8404 int id;
8405
8406 id = syn_name2id((char_u *)"Normal");
8407 if (id > 0)
8408 {
8409 vim_free(HL_TABLE()[id - 1].sg_font_name);
8410 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8411 }
8412}
8413
8414/*
8415 * Set background color for "Normal" group. Called by gui_set_bg_color()
8416 * when the color is known.
8417 */
8418 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008419hl_set_bg_color_name(
8420 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008421{
8422 int id;
8423
8424 if (name != NULL)
8425 {
8426 id = syn_name2id((char_u *)"Normal");
8427 if (id > 0)
8428 {
8429 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8430 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8431 }
8432 }
8433}
8434
8435/*
8436 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8437 * when the color is known.
8438 */
8439 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008440hl_set_fg_color_name(
8441 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008442{
8443 int id;
8444
8445 if (name != NULL)
8446 {
8447 id = syn_name2id((char_u *)"Normal");
8448 if (id > 0)
8449 {
8450 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8451 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8452 }
8453 }
8454}
8455
8456/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008457 * Return the handle for a font name.
8458 * Returns NOFONT when failed.
8459 */
8460 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008461font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008462{
8463 if (STRCMP(name, "NONE") == 0)
8464 return NOFONT;
8465
8466 return gui_mch_get_font(name, TRUE);
8467}
8468
8469# ifdef FEAT_XFONTSET
8470/*
8471 * Return the handle for a fontset name.
8472 * Returns NOFONTSET when failed.
8473 */
8474 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008475fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008476{
8477 if (STRCMP(name, "NONE") == 0)
8478 return NOFONTSET;
8479
8480 return gui_mch_get_fontset(name, TRUE, fixed_width);
8481}
8482# endif
8483
8484/*
8485 * Get the font or fontset for one highlight group.
8486 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008487 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008488hl_do_font(
8489 int idx,
8490 char_u *arg,
8491 int do_normal, /* set normal font */
8492 int do_menu UNUSED, /* set menu font */
8493 int do_tooltip UNUSED, /* set tooltip font */
8494 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008495{
8496# ifdef FEAT_XFONTSET
8497 /* If 'guifontset' is not empty, first try using the name as a
8498 * fontset. If that doesn't work, use it as a font name. */
8499 if (*p_guifontset != NUL
8500# ifdef FONTSET_ALWAYS
8501 || do_menu
8502# endif
8503# ifdef FEAT_BEVAL_TIP
8504 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8505 || do_tooltip
8506# endif
8507 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008508 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008509 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008510 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008511 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8512# ifdef FONTSET_ALWAYS
8513 || do_menu
8514# endif
8515# ifdef FEAT_BEVAL_TIP
8516 || do_tooltip
8517# endif
8518 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008519 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008520 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8521 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008522 /* If it worked and it's the Normal group, use it as the normal
8523 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524 if (do_normal)
8525 gui_init_font(arg, TRUE);
8526# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8527 if (do_menu)
8528 {
8529# ifdef FONTSET_ALWAYS
8530 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8531# else
8532 /* YIKES! This is a bug waiting to crash the program */
8533 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8534# endif
8535 gui_mch_new_menu_font();
8536 }
8537# ifdef FEAT_BEVAL
8538 if (do_tooltip)
8539 {
8540 /* The Athena widget set cannot currently handle switching between
8541 * displaying a single font and a fontset.
8542 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008543 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008544 * XFontStruct is used.
8545 */
8546 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8547 gui_mch_new_tooltip_font();
8548 }
8549# endif
8550# endif
8551 }
8552 else
8553# endif
8554 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008555 if (free_font)
8556 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008557 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8558 /* If it worked and it's the Normal group, use it as the
8559 * normal font. Same for the Menu group. */
8560 if (HL_TABLE()[idx].sg_font != NOFONT)
8561 {
8562 if (do_normal)
8563 gui_init_font(arg, FALSE);
8564#ifndef FONTSET_ALWAYS
8565# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8566 if (do_menu)
8567 {
8568 gui.menu_font = HL_TABLE()[idx].sg_font;
8569 gui_mch_new_menu_font();
8570 }
8571# endif
8572#endif
8573 }
8574 }
8575}
8576
8577#endif /* FEAT_GUI */
8578
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008579#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008580/*
8581 * Return the handle for a color name.
8582 * Returns INVALCOLOR when failed.
8583 */
8584 static guicolor_T
8585color_name2handle(char_u *name)
8586{
8587 if (STRCMP(name, "NONE") == 0)
8588 return INVALCOLOR;
8589
8590 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8591 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008592#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008593 if (gui.in_use)
8594#endif
8595#ifdef FEAT_GUI
8596 return gui.norm_pixel;
8597#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008598#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008599 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008600 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008601 /* Guess that the foreground is black or white. */
8602 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008603#endif
8604 }
8605 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8606 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008607#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008608 if (gui.in_use)
8609#endif
8610#ifdef FEAT_GUI
8611 return gui.back_pixel;
8612#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008613#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008614 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008615 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008616 /* Guess that the background is white or black. */
8617 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008618#endif
8619 }
8620
8621 return GUI_GET_COLOR(name);
8622}
8623#endif
8624
Bram Moolenaar071d4272004-06-13 20:20:40 +00008625/*
8626 * Table with the specifications for an attribute number.
8627 * Note that this table is used by ALL buffers. This is required because the
8628 * GUI can redraw at any time for any buffer.
8629 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008630static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008631
8632#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8633
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008634static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008635
8636#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8637
8638#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008639static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008640
8641#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8642#endif
8643
8644/*
8645 * Return the attr number for a set of colors and font.
8646 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8647 * if the combination is new.
8648 * Return 0 for error (no more room).
8649 */
8650 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008651get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008652{
8653 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008654 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008655 static int recursive = FALSE;
8656
8657 /*
8658 * Init the table, in case it wasn't done yet.
8659 */
8660 table->ga_itemsize = sizeof(attrentry_T);
8661 table->ga_growsize = 7;
8662
8663 /*
8664 * Try to find an entry with the same specifications.
8665 */
8666 for (i = 0; i < table->ga_len; ++i)
8667 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008668 taep = &(((attrentry_T *)table->ga_data)[i]);
8669 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008670 && (
8671#ifdef FEAT_GUI
8672 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008673 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8674 && aep->ae_u.gui.bg_color
8675 == taep->ae_u.gui.bg_color
8676 && aep->ae_u.gui.sp_color
8677 == taep->ae_u.gui.sp_color
8678 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008679# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008680 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681# endif
8682 ))
8683 ||
8684#endif
8685 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008686 && (aep->ae_u.term.start == NULL)
8687 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008688 && (aep->ae_u.term.start == NULL
8689 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008690 taep->ae_u.term.start) == 0)
8691 && (aep->ae_u.term.stop == NULL)
8692 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008693 && (aep->ae_u.term.stop == NULL
8694 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008695 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008697 && aep->ae_u.cterm.fg_color
8698 == taep->ae_u.cterm.fg_color
8699 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008700 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008701#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008702 && aep->ae_u.cterm.fg_rgb
8703 == taep->ae_u.cterm.fg_rgb
8704 && aep->ae_u.cterm.bg_rgb
8705 == taep->ae_u.cterm.bg_rgb
8706#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008707 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008708
8709 return i + ATTR_OFF;
8710 }
8711
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008712 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008713 {
8714 /*
8715 * Running out of attribute entries! remove all attributes, and
8716 * compute new ones for all groups.
8717 * When called recursively, we are really out of numbers.
8718 */
8719 if (recursive)
8720 {
8721 EMSG(_("E424: Too many different highlighting attributes in use"));
8722 return 0;
8723 }
8724 recursive = TRUE;
8725
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008726 clear_hl_tables();
8727
Bram Moolenaar071d4272004-06-13 20:20:40 +00008728 must_redraw = CLEAR;
8729
8730 for (i = 0; i < highlight_ga.ga_len; ++i)
8731 set_hl_attr(i);
8732
8733 recursive = FALSE;
8734 }
8735
8736 /*
8737 * This is a new combination of colors and font, add an entry.
8738 */
8739 if (ga_grow(table, 1) == FAIL)
8740 return 0;
8741
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008742 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8743 vim_memset(taep, 0, sizeof(attrentry_T));
8744 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008745#ifdef FEAT_GUI
8746 if (table == &gui_attr_table)
8747 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008748 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8749 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8750 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8751 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008752# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008753 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008754# endif
8755 }
8756#endif
8757 if (table == &term_attr_table)
8758 {
8759 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008760 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008761 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008762 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008763 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008764 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008765 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008766 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008767 }
8768 else if (table == &cterm_attr_table)
8769 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008770 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8771 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008772#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008773 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8774 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8775#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008776 }
8777 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008778 return (table->ga_len - 1 + ATTR_OFF);
8779}
8780
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008781/*
8782 * Clear all highlight tables.
8783 */
8784 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008785clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008786{
8787 int i;
8788 attrentry_T *taep;
8789
8790#ifdef FEAT_GUI
8791 ga_clear(&gui_attr_table);
8792#endif
8793 for (i = 0; i < term_attr_table.ga_len; ++i)
8794 {
8795 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8796 vim_free(taep->ae_u.term.start);
8797 vim_free(taep->ae_u.term.stop);
8798 }
8799 ga_clear(&term_attr_table);
8800 ga_clear(&cterm_attr_table);
8801}
8802
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008803#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008804/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008805 * Combine special attributes (e.g., for spelling) with other attributes
8806 * (e.g., for syntax highlighting).
8807 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008808 * This creates a new group when required.
8809 * Since we expect there to be few spelling mistakes we don't cache the
8810 * result.
8811 * Return the resulting attributes.
8812 */
8813 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008814hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008815{
8816 attrentry_T *char_aep = NULL;
8817 attrentry_T *spell_aep;
8818 attrentry_T new_en;
8819
8820 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008821 return prim_attr;
8822 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8823 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008824#ifdef FEAT_GUI
8825 if (gui.in_use)
8826 {
8827 if (char_attr > HL_ALL)
8828 char_aep = syn_gui_attr2entry(char_attr);
8829 if (char_aep != NULL)
8830 new_en = *char_aep;
8831 else
8832 {
8833 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008834 new_en.ae_u.gui.fg_color = INVALCOLOR;
8835 new_en.ae_u.gui.bg_color = INVALCOLOR;
8836 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008837 if (char_attr <= HL_ALL)
8838 new_en.ae_attr = char_attr;
8839 }
8840
Bram Moolenaar30abd282005-06-22 22:35:10 +00008841 if (prim_attr <= HL_ALL)
8842 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008843 else
8844 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008845 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008846 if (spell_aep != NULL)
8847 {
8848 new_en.ae_attr |= spell_aep->ae_attr;
8849 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8850 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8851 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8852 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8853 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8854 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8855 if (spell_aep->ae_u.gui.font != NOFONT)
8856 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8857# ifdef FEAT_XFONTSET
8858 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8859 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8860# endif
8861 }
8862 }
8863 return get_attr_entry(&gui_attr_table, &new_en);
8864 }
8865#endif
8866
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008867 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008868 {
8869 if (char_attr > HL_ALL)
8870 char_aep = syn_cterm_attr2entry(char_attr);
8871 if (char_aep != NULL)
8872 new_en = *char_aep;
8873 else
8874 {
8875 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008876#ifdef FEAT_TERMGUICOLORS
8877 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8878 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8879#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008880 if (char_attr <= HL_ALL)
8881 new_en.ae_attr = char_attr;
8882 }
8883
Bram Moolenaar30abd282005-06-22 22:35:10 +00008884 if (prim_attr <= HL_ALL)
8885 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008886 else
8887 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008888 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008889 if (spell_aep != NULL)
8890 {
8891 new_en.ae_attr |= spell_aep->ae_attr;
8892 if (spell_aep->ae_u.cterm.fg_color > 0)
8893 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8894 if (spell_aep->ae_u.cterm.bg_color > 0)
8895 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008896#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008897 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008898 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008899 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008900 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8901#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008902 }
8903 }
8904 return get_attr_entry(&cterm_attr_table, &new_en);
8905 }
8906
8907 if (char_attr > HL_ALL)
8908 char_aep = syn_term_attr2entry(char_attr);
8909 if (char_aep != NULL)
8910 new_en = *char_aep;
8911 else
8912 {
8913 vim_memset(&new_en, 0, sizeof(new_en));
8914 if (char_attr <= HL_ALL)
8915 new_en.ae_attr = char_attr;
8916 }
8917
Bram Moolenaar30abd282005-06-22 22:35:10 +00008918 if (prim_attr <= HL_ALL)
8919 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008920 else
8921 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008922 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008923 if (spell_aep != NULL)
8924 {
8925 new_en.ae_attr |= spell_aep->ae_attr;
8926 if (spell_aep->ae_u.term.start != NULL)
8927 {
8928 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8929 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8930 }
8931 }
8932 }
8933 return get_attr_entry(&term_attr_table, &new_en);
8934}
8935#endif
8936
Bram Moolenaar071d4272004-06-13 20:20:40 +00008937#ifdef FEAT_GUI
8938
8939 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008940syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008941{
8942 attr -= ATTR_OFF;
8943 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8944 return NULL;
8945 return &(GUI_ATTR_ENTRY(attr));
8946}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008947#endif /* FEAT_GUI */
8948
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008949/*
8950 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8951 * Only to be used when "attr" > HL_ALL.
8952 */
8953 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008954syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008955{
8956 attrentry_T *aep;
8957
8958#ifdef FEAT_GUI
8959 if (gui.in_use)
8960 aep = syn_gui_attr2entry(attr);
8961 else
8962#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008963 if (IS_CTERM)
8964 aep = syn_cterm_attr2entry(attr);
8965 else
8966 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008967
8968 if (aep == NULL) /* highlighting not set */
8969 return 0;
8970 return aep->ae_attr;
8971}
8972
8973
Bram Moolenaar071d4272004-06-13 20:20:40 +00008974 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008975syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008976{
8977 attr -= ATTR_OFF;
8978 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8979 return NULL;
8980 return &(TERM_ATTR_ENTRY(attr));
8981}
8982
8983 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008984syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008985{
8986 attr -= ATTR_OFF;
8987 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8988 return NULL;
8989 return &(CTERM_ATTR_ENTRY(attr));
8990}
8991
8992#define LIST_ATTR 1
8993#define LIST_STRING 2
8994#define LIST_INT 3
8995
8996 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008997highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008998{
8999 struct hl_group *sgp;
9000 int didh = FALSE;
9001
9002 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9003
9004 didh = highlight_list_arg(id, didh, LIST_ATTR,
9005 sgp->sg_term, NULL, "term");
9006 didh = highlight_list_arg(id, didh, LIST_STRING,
9007 0, sgp->sg_start, "start");
9008 didh = highlight_list_arg(id, didh, LIST_STRING,
9009 0, sgp->sg_stop, "stop");
9010
9011 didh = highlight_list_arg(id, didh, LIST_ATTR,
9012 sgp->sg_cterm, NULL, "cterm");
9013 didh = highlight_list_arg(id, didh, LIST_INT,
9014 sgp->sg_cterm_fg, NULL, "ctermfg");
9015 didh = highlight_list_arg(id, didh, LIST_INT,
9016 sgp->sg_cterm_bg, NULL, "ctermbg");
9017
Bram Moolenaar61623362010-07-14 22:04:22 +02009018#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009019 didh = highlight_list_arg(id, didh, LIST_ATTR,
9020 sgp->sg_gui, NULL, "gui");
9021 didh = highlight_list_arg(id, didh, LIST_STRING,
9022 0, sgp->sg_gui_fg_name, "guifg");
9023 didh = highlight_list_arg(id, didh, LIST_STRING,
9024 0, sgp->sg_gui_bg_name, "guibg");
9025 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009026 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009027#endif
9028#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009029 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009030 0, sgp->sg_font_name, "font");
9031#endif
9032
Bram Moolenaar661b1822005-07-28 22:36:45 +00009033 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009034 {
9035 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009036 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009037 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009038 msg_putchar(' ');
9039 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9040 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009041
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009042 if (!didh)
9043 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009044#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009045 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009046 last_set_msg(sgp->sg_scriptID);
9047#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009048}
9049
9050 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009051highlight_list_arg(
9052 int id,
9053 int didh,
9054 int type,
9055 int iarg,
9056 char_u *sarg,
9057 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009058{
9059 char_u buf[100];
9060 char_u *ts;
9061 int i;
9062
Bram Moolenaar661b1822005-07-28 22:36:45 +00009063 if (got_int)
9064 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009065 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9066 {
9067 ts = buf;
9068 if (type == LIST_INT)
9069 sprintf((char *)buf, "%d", iarg - 1);
9070 else if (type == LIST_STRING)
9071 ts = sarg;
9072 else /* type == LIST_ATTR */
9073 {
9074 buf[0] = NUL;
9075 for (i = 0; hl_attr_table[i] != 0; ++i)
9076 {
9077 if (iarg & hl_attr_table[i])
9078 {
9079 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009080 vim_strcat(buf, (char_u *)",", 100);
9081 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009082 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9083 }
9084 }
9085 }
9086
9087 (void)syn_list_header(didh,
9088 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9089 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009090 if (!got_int)
9091 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009092 if (*name != NUL)
9093 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009094 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9095 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009096 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009097 msg_outtrans(ts);
9098 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009099 }
9100 return didh;
9101}
9102
9103#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9104/*
9105 * Return "1" if highlight group "id" has attribute "flag".
9106 * Return NULL otherwise.
9107 */
9108 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009109highlight_has_attr(
9110 int id,
9111 int flag,
9112 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009113{
9114 int attr;
9115
9116 if (id <= 0 || id > highlight_ga.ga_len)
9117 return NULL;
9118
Bram Moolenaar61623362010-07-14 22:04:22 +02009119#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009120 if (modec == 'g')
9121 attr = HL_TABLE()[id - 1].sg_gui;
9122 else
9123#endif
9124 if (modec == 'c')
9125 attr = HL_TABLE()[id - 1].sg_cterm;
9126 else
9127 attr = HL_TABLE()[id - 1].sg_term;
9128
9129 if (attr & flag)
9130 return (char_u *)"1";
9131 return NULL;
9132}
9133#endif
9134
9135#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9136/*
9137 * Return color name of highlight group "id".
9138 */
9139 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009140highlight_color(
9141 int id,
9142 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9143 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009144{
9145 static char_u name[20];
9146 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009147 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009148 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009149 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009150
9151 if (id <= 0 || id > highlight_ga.ga_len)
9152 return NULL;
9153
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009154 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009155 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009156 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009157 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009158 font = TRUE;
9159 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009160 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009161 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9162 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009163 if (modec == 'g')
9164 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009165# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009166# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009167 /* return font name */
9168 if (font)
9169 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009170# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009171
Bram Moolenaar071d4272004-06-13 20:20:40 +00009172 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009173 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009174 {
9175 guicolor_T color;
9176 long_u rgb;
9177 static char_u buf[10];
9178
9179 if (fg)
9180 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009181 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009182# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009183 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009184# else
9185 color = INVALCOLOR;
9186# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009187 else
9188 color = HL_TABLE()[id - 1].sg_gui_bg;
9189 if (color == INVALCOLOR)
9190 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009191 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009192 sprintf((char *)buf, "#%02x%02x%02x",
9193 (unsigned)(rgb >> 16),
9194 (unsigned)(rgb >> 8) & 255,
9195 (unsigned)rgb & 255);
9196 return buf;
9197 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009198# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009199 if (fg)
9200 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009201 if (sp)
9202 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009203 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9204 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009205 if (font || sp)
9206 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009207 if (modec == 'c')
9208 {
9209 if (fg)
9210 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9211 else
9212 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009213 if (n < 0)
9214 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009215 sprintf((char *)name, "%d", n);
9216 return name;
9217 }
9218 /* term doesn't have color */
9219 return NULL;
9220}
9221#endif
9222
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009223#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009224 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009225 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009226/*
9227 * Return color name of highlight group "id" as RGB value.
9228 */
9229 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009230highlight_gui_color_rgb(
9231 int id,
9232 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009233{
9234 guicolor_T color;
9235
9236 if (id <= 0 || id > highlight_ga.ga_len)
9237 return 0L;
9238
9239 if (fg)
9240 color = HL_TABLE()[id - 1].sg_gui_fg;
9241 else
9242 color = HL_TABLE()[id - 1].sg_gui_bg;
9243
9244 if (color == INVALCOLOR)
9245 return 0L;
9246
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009247 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009248}
9249#endif
9250
9251/*
9252 * Output the syntax list header.
9253 * Return TRUE when started a new line.
9254 */
9255 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009256syn_list_header(
9257 int did_header, /* did header already */
9258 int outlen, /* length of string that comes */
9259 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009260{
9261 int endcol = 19;
9262 int newline = TRUE;
9263
9264 if (!did_header)
9265 {
9266 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009267 if (got_int)
9268 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009269 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9270 endcol = 15;
9271 }
9272 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009273 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009274 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009275 if (got_int)
9276 return TRUE;
9277 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009278 else
9279 {
9280 if (msg_col >= endcol) /* wrap around is like starting a new line */
9281 newline = FALSE;
9282 }
9283
9284 if (msg_col >= endcol) /* output at least one space */
9285 endcol = msg_col + 1;
9286 if (Columns <= endcol) /* avoid hang for tiny window */
9287 endcol = Columns - 1;
9288
9289 msg_advance(endcol);
9290
9291 /* Show "xxx" with the attributes. */
9292 if (!did_header)
9293 {
9294 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9295 msg_putchar(' ');
9296 }
9297
9298 return newline;
9299}
9300
9301/*
9302 * Set the attribute numbers for a highlight group.
9303 * Called after one of the attributes has changed.
9304 */
9305 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009306set_hl_attr(
9307 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009308{
9309 attrentry_T at_en;
9310 struct hl_group *sgp = HL_TABLE() + idx;
9311
9312 /* The "Normal" group doesn't need an attribute number */
9313 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9314 return;
9315
9316#ifdef FEAT_GUI
9317 /*
9318 * For the GUI mode: If there are other than "normal" highlighting
9319 * attributes, need to allocate an attr number.
9320 */
9321 if (sgp->sg_gui_fg == INVALCOLOR
9322 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009323 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009324 && sgp->sg_font == NOFONT
9325# ifdef FEAT_XFONTSET
9326 && sgp->sg_fontset == NOFONTSET
9327# endif
9328 )
9329 {
9330 sgp->sg_gui_attr = sgp->sg_gui;
9331 }
9332 else
9333 {
9334 at_en.ae_attr = sgp->sg_gui;
9335 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9336 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009337 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009338 at_en.ae_u.gui.font = sgp->sg_font;
9339# ifdef FEAT_XFONTSET
9340 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9341# endif
9342 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9343 }
9344#endif
9345 /*
9346 * For the term mode: If there are other than "normal" highlighting
9347 * attributes, need to allocate an attr number.
9348 */
9349 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9350 sgp->sg_term_attr = sgp->sg_term;
9351 else
9352 {
9353 at_en.ae_attr = sgp->sg_term;
9354 at_en.ae_u.term.start = sgp->sg_start;
9355 at_en.ae_u.term.stop = sgp->sg_stop;
9356 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9357 }
9358
9359 /*
9360 * For the color term mode: If there are other than "normal"
9361 * highlighting attributes, need to allocate an attr number.
9362 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009363 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009364# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009365 && sgp->sg_gui_fg == INVALCOLOR
9366 && sgp->sg_gui_bg == INVALCOLOR
9367# endif
9368 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009369 sgp->sg_cterm_attr = sgp->sg_cterm;
9370 else
9371 {
9372 at_en.ae_attr = sgp->sg_cterm;
9373 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9374 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009375# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009376 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9377 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009378# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009379 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9380 }
9381}
9382
9383/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009384 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009385 * If it is not found, 0 is returned.
9386 */
9387 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009388syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009389{
9390 int i;
9391 char_u name_u[200];
9392
9393 /* Avoid using stricmp() too much, it's slow on some systems */
9394 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9395 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009396 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009397 vim_strup(name_u);
9398 for (i = highlight_ga.ga_len; --i >= 0; )
9399 if (HL_TABLE()[i].sg_name_u != NULL
9400 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9401 break;
9402 return i + 1;
9403}
9404
9405#if defined(FEAT_EVAL) || defined(PROTO)
9406/*
9407 * Return TRUE if highlight group "name" exists.
9408 */
9409 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009410highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009411{
9412 return (syn_name2id(name) > 0);
9413}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009414
9415# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9416/*
9417 * Return the name of highlight group "id".
9418 * When not a valid ID return an empty string.
9419 */
9420 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009421syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009422{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009423 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009424 return (char_u *)"";
9425 return HL_TABLE()[id - 1].sg_name;
9426}
9427# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009428#endif
9429
9430/*
9431 * Like syn_name2id(), but take a pointer + length argument.
9432 */
9433 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009434syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009435{
9436 char_u *name;
9437 int id = 0;
9438
9439 name = vim_strnsave(linep, len);
9440 if (name != NULL)
9441 {
9442 id = syn_name2id(name);
9443 vim_free(name);
9444 }
9445 return id;
9446}
9447
9448/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009449 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009450 * The argument is a pointer to the name and the length of the name.
9451 * If it doesn't exist yet, a new entry is created.
9452 * Return 0 for failure.
9453 */
9454 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009455syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009456{
9457 int id;
9458 char_u *name;
9459
9460 name = vim_strnsave(pp, len);
9461 if (name == NULL)
9462 return 0;
9463
9464 id = syn_name2id(name);
9465 if (id == 0) /* doesn't exist yet */
9466 id = syn_add_group(name);
9467 else
9468 vim_free(name);
9469 return id;
9470}
9471
9472/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009473 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009474 * "name" must be an allocated string, it will be consumed.
9475 * Return 0 for failure.
9476 */
9477 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009478syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009479{
9480 char_u *p;
9481
9482 /* Check that the name is ASCII letters, digits and underscore. */
9483 for (p = name; *p != NUL; ++p)
9484 {
9485 if (!vim_isprintc(*p))
9486 {
9487 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009488 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009489 return 0;
9490 }
9491 else if (!ASCII_ISALNUM(*p) && *p != '_')
9492 {
9493 /* This is an error, but since there previously was no check only
9494 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009495 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009496 MSG(_("W18: Invalid character in group name"));
9497 break;
9498 }
9499 }
9500
9501 /*
9502 * First call for this growarray: init growing array.
9503 */
9504 if (highlight_ga.ga_data == NULL)
9505 {
9506 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9507 highlight_ga.ga_growsize = 10;
9508 }
9509
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009510 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009511 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009512 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009513 vim_free(name);
9514 return 0;
9515 }
9516
Bram Moolenaar071d4272004-06-13 20:20:40 +00009517 /*
9518 * Make room for at least one other syntax_highlight entry.
9519 */
9520 if (ga_grow(&highlight_ga, 1) == FAIL)
9521 {
9522 vim_free(name);
9523 return 0;
9524 }
9525
9526 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9527 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9528 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009529#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009530 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9531 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009532# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009533 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009534# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009535#endif
9536 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009537
9538 return highlight_ga.ga_len; /* ID is index plus one */
9539}
9540
9541/*
9542 * When, just after calling syn_add_group(), an error is discovered, this
9543 * function deletes the new name.
9544 */
9545 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009546syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009547{
9548 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009549 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9550 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9551}
9552
9553/*
9554 * Translate a group ID to highlight attributes.
9555 */
9556 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009557syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009558{
9559 int attr;
9560 struct hl_group *sgp;
9561
9562 hl_id = syn_get_final_id(hl_id);
9563 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9564
9565#ifdef FEAT_GUI
9566 /*
9567 * Only use GUI attr when the GUI is being used.
9568 */
9569 if (gui.in_use)
9570 attr = sgp->sg_gui_attr;
9571 else
9572#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009573 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009574 attr = sgp->sg_cterm_attr;
9575 else
9576 attr = sgp->sg_term_attr;
9577
9578 return attr;
9579}
9580
9581#ifdef FEAT_GUI
9582/*
9583 * Get the GUI colors and attributes for a group ID.
9584 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9585 */
9586 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009587syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009588{
9589 struct hl_group *sgp;
9590
9591 hl_id = syn_get_final_id(hl_id);
9592 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9593
9594 *fgp = sgp->sg_gui_fg;
9595 *bgp = sgp->sg_gui_bg;
9596 return sgp->sg_gui;
9597}
9598#endif
9599
9600/*
9601 * Translate a group ID to the final group ID (following links).
9602 */
9603 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009604syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009605{
9606 int count;
9607 struct hl_group *sgp;
9608
9609 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9610 return 0; /* Can be called from eval!! */
9611
9612 /*
9613 * Follow links until there is no more.
9614 * Look out for loops! Break after 100 links.
9615 */
9616 for (count = 100; --count >= 0; )
9617 {
9618 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9619 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9620 break;
9621 hl_id = sgp->sg_link;
9622 }
9623
9624 return hl_id;
9625}
9626
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009627#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009628/*
9629 * Call this function just after the GUI has started.
9630 * It finds the font and color handles for the highlighting groups.
9631 */
9632 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009633highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009634{
9635 int idx;
9636
9637 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009638# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9639# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009640 if (USE_24BIT)
9641# endif
9642 set_normal_colors();
9643# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009644
9645 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9646 gui_do_one_color(idx, FALSE, FALSE);
9647
9648 highlight_changed();
9649}
9650
9651 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009652gui_do_one_color(
9653 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009654 int do_menu UNUSED, /* TRUE: might set the menu font */
9655 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009656{
9657 int didit = FALSE;
9658
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009659# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009660# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009661 if (gui.in_use)
9662# endif
9663 if (HL_TABLE()[idx].sg_font_name != NULL)
9664 {
9665 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009666 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009667 didit = TRUE;
9668 }
9669# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009670 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9671 {
9672 HL_TABLE()[idx].sg_gui_fg =
9673 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9674 didit = TRUE;
9675 }
9676 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9677 {
9678 HL_TABLE()[idx].sg_gui_bg =
9679 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9680 didit = TRUE;
9681 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009682# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009683 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9684 {
9685 HL_TABLE()[idx].sg_gui_sp =
9686 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9687 didit = TRUE;
9688 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009689# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009690 if (didit) /* need to get a new attr number */
9691 set_hl_attr(idx);
9692}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009693#endif
9694
9695/*
9696 * Translate the 'highlight' option into attributes in highlight_attr[] and
9697 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9698 * corresponding highlights to use on top of HLF_SNC is computed.
9699 * Called only when the 'highlight' option has been changed and upon first
9700 * screen redraw after any :highlight command.
9701 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9702 */
9703 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009704highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009705{
9706 int hlf;
9707 int i;
9708 char_u *p;
9709 int attr;
9710 char_u *end;
9711 int id;
9712#ifdef USER_HIGHLIGHT
9713 char_u userhl[10];
9714# ifdef FEAT_STL_OPT
9715 int id_SNC = -1;
9716 int id_S = -1;
9717 int hlcnt;
9718# endif
9719#endif
9720 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9721
9722 need_highlight_changed = FALSE;
9723
9724 /*
9725 * Clear all attributes.
9726 */
9727 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9728 highlight_attr[hlf] = 0;
9729
9730 /*
9731 * First set all attributes to their default value.
9732 * Then use the attributes from the 'highlight' option.
9733 */
9734 for (i = 0; i < 2; ++i)
9735 {
9736 if (i)
9737 p = p_hl;
9738 else
9739 p = get_highlight_default();
9740 if (p == NULL) /* just in case */
9741 continue;
9742
9743 while (*p)
9744 {
9745 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9746 if (hl_flags[hlf] == *p)
9747 break;
9748 ++p;
9749 if (hlf == (int)HLF_COUNT || *p == NUL)
9750 return FAIL;
9751
9752 /*
9753 * Allow several hl_flags to be combined, like "bu" for
9754 * bold-underlined.
9755 */
9756 attr = 0;
9757 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9758 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01009759 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009760 continue;
9761
9762 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9763 return FAIL;
9764
9765 switch (*p)
9766 {
9767 case 'b': attr |= HL_BOLD;
9768 break;
9769 case 'i': attr |= HL_ITALIC;
9770 break;
9771 case '-':
9772 case 'n': /* no highlighting */
9773 break;
9774 case 'r': attr |= HL_INVERSE;
9775 break;
9776 case 's': attr |= HL_STANDOUT;
9777 break;
9778 case 'u': attr |= HL_UNDERLINE;
9779 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009780 case 'c': attr |= HL_UNDERCURL;
9781 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009782 case ':': ++p; /* highlight group name */
9783 if (attr || *p == NUL) /* no combinations */
9784 return FAIL;
9785 end = vim_strchr(p, ',');
9786 if (end == NULL)
9787 end = p + STRLEN(p);
9788 id = syn_check_group(p, (int)(end - p));
9789 if (id == 0)
9790 return FAIL;
9791 attr = syn_id2attr(id);
9792 p = end - 1;
9793#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9794 if (hlf == (int)HLF_SNC)
9795 id_SNC = syn_get_final_id(id);
9796 else if (hlf == (int)HLF_S)
9797 id_S = syn_get_final_id(id);
9798#endif
9799 break;
9800 default: return FAIL;
9801 }
9802 }
9803 highlight_attr[hlf] = attr;
9804
9805 p = skip_to_option_part(p); /* skip comma and spaces */
9806 }
9807 }
9808
9809#ifdef USER_HIGHLIGHT
9810 /* Setup the user highlights
9811 *
9812 * Temporarily utilize 10 more hl entries. Have to be in there
9813 * simultaneously in case of table overflows in get_attr_entry()
9814 */
9815# ifdef FEAT_STL_OPT
9816 if (ga_grow(&highlight_ga, 10) == FAIL)
9817 return FAIL;
9818 hlcnt = highlight_ga.ga_len;
9819 if (id_S == 0)
9820 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009821 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009822 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9823 id_S = hlcnt + 10;
9824 }
9825# endif
9826 for (i = 0; i < 9; i++)
9827 {
9828 sprintf((char *)userhl, "User%d", i + 1);
9829 id = syn_name2id(userhl);
9830 if (id == 0)
9831 {
9832 highlight_user[i] = 0;
9833# ifdef FEAT_STL_OPT
9834 highlight_stlnc[i] = 0;
9835# endif
9836 }
9837 else
9838 {
9839# ifdef FEAT_STL_OPT
9840 struct hl_group *hlt = HL_TABLE();
9841# endif
9842
9843 highlight_user[i] = syn_id2attr(id);
9844# ifdef FEAT_STL_OPT
9845 if (id_SNC == 0)
9846 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009847 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009848 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9849 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009850# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009851 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9852# endif
9853 }
9854 else
9855 mch_memmove(&hlt[hlcnt + i],
9856 &hlt[id_SNC - 1],
9857 sizeof(struct hl_group));
9858 hlt[hlcnt + i].sg_link = 0;
9859
9860 /* Apply difference between UserX and HLF_S to HLF_SNC */
9861 hlt[hlcnt + i].sg_term ^=
9862 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9863 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9864 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9865 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9866 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9867 hlt[hlcnt + i].sg_cterm ^=
9868 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9869 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9870 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9871 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9872 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009873# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009874 hlt[hlcnt + i].sg_gui ^=
9875 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009876# endif
9877# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009878 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9879 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9880 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9881 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009882 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9883 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009884 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9885 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9886# ifdef FEAT_XFONTSET
9887 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9888 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9889# endif
9890# endif
9891 highlight_ga.ga_len = hlcnt + i + 1;
9892 set_hl_attr(hlcnt + i); /* At long last we can apply */
9893 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9894# endif
9895 }
9896 }
9897# ifdef FEAT_STL_OPT
9898 highlight_ga.ga_len = hlcnt;
9899# endif
9900
9901#endif /* USER_HIGHLIGHT */
9902
9903 return OK;
9904}
9905
Bram Moolenaar4f688582007-07-24 12:34:30 +00009906#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009907
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009908static void highlight_list(void);
9909static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009910
9911/*
9912 * Handle command line completion for :highlight command.
9913 */
9914 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009915set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009916{
9917 char_u *p;
9918
9919 /* Default: expand group names */
9920 xp->xp_context = EXPAND_HIGHLIGHT;
9921 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009922 include_link = 2;
9923 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009924
9925 /* (part of) subcommand already typed */
9926 if (*arg != NUL)
9927 {
9928 p = skiptowhite(arg);
9929 if (*p != NUL) /* past "default" or group name */
9930 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009931 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009932 if (STRNCMP("default", arg, p - arg) == 0)
9933 {
9934 arg = skipwhite(p);
9935 xp->xp_pattern = arg;
9936 p = skiptowhite(arg);
9937 }
9938 if (*p != NUL) /* past group name */
9939 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009940 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009941 if (arg[1] == 'i' && arg[0] == 'N')
9942 highlight_list();
9943 if (STRNCMP("link", arg, p - arg) == 0
9944 || STRNCMP("clear", arg, p - arg) == 0)
9945 {
9946 xp->xp_pattern = skipwhite(p);
9947 p = skiptowhite(xp->xp_pattern);
9948 if (*p != NUL) /* past first group name */
9949 {
9950 xp->xp_pattern = skipwhite(p);
9951 p = skiptowhite(xp->xp_pattern);
9952 }
9953 }
9954 if (*p != NUL) /* past group name(s) */
9955 xp->xp_context = EXPAND_NOTHING;
9956 }
9957 }
9958 }
9959}
9960
9961/*
9962 * List highlighting matches in a nice way.
9963 */
9964 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009965highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009966{
9967 int i;
9968
9969 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +01009970 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009971 for (i = 40; --i >= 0; )
9972 highlight_list_two(99, 0);
9973}
9974
9975 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009976highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009977{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009978 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009979 msg_clr_eos();
9980 out_flush();
9981 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9982}
9983
9984#endif /* FEAT_CMDL_COMPL */
9985
9986#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9987 || defined(FEAT_SIGNS) || defined(PROTO)
9988/*
9989 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009990 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009991 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009992get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009993{
Bram Moolenaarc96272e2017-03-26 13:50:09 +02009994 return get_highlight_name_ext(xp, idx, TRUE);
9995}
9996
9997/*
9998 * Obtain a highlight group name.
9999 * When "skip_cleared" is TRUE don't return a cleared entry.
10000 */
10001 char_u *
10002get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10003{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010004 if (idx < 0)
10005 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010006
10007 /* Items are never removed from the table, skip the ones that were
10008 * cleared. */
10009 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10010 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010011
Bram Moolenaar071d4272004-06-13 20:20:40 +000010012#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010013 if (idx == highlight_ga.ga_len && include_none != 0)
10014 return (char_u *)"none";
10015 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010016 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010017 if (idx == highlight_ga.ga_len + include_none + include_default
10018 && include_link != 0)
10019 return (char_u *)"link";
10020 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10021 && include_link != 0)
10022 return (char_u *)"clear";
10023#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010024 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010025 return NULL;
10026 return HL_TABLE()[idx].sg_name;
10027}
10028#endif
10029
Bram Moolenaar4f688582007-07-24 12:34:30 +000010030#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010031/*
10032 * Free all the highlight group fonts.
10033 * Used when quitting for systems which need it.
10034 */
10035 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010036free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010037{
10038 int idx;
10039
10040 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10041 {
10042 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10043 HL_TABLE()[idx].sg_font = NOFONT;
10044# ifdef FEAT_XFONTSET
10045 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10046 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10047# endif
10048 }
10049
10050 gui_mch_free_font(gui.norm_font);
10051# ifdef FEAT_XFONTSET
10052 gui_mch_free_fontset(gui.fontset);
10053# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010054# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010055 gui_mch_free_font(gui.bold_font);
10056 gui_mch_free_font(gui.ital_font);
10057 gui_mch_free_font(gui.boldital_font);
10058# endif
10059}
10060#endif
10061
10062/**************************************
10063 * End of Highlighting stuff *
10064 **************************************/