blob: 6aec63d32dc5db2416925d6e30187cd37e12f470 [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;
1064}
1065
1066/*
1067 * Check for items in the stack that need their end updated.
1068 * When "startofline" is TRUE the last item is always updated.
1069 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1070 */
1071 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001072syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073{
1074 stateitem_T *cur_si;
1075 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001076 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001077
1078 if (startofline)
1079 {
1080 /* Check for a match carried over from a previous line with a
1081 * contained region. The match ends as soon as the region ends. */
1082 for (i = 0; i < current_state.ga_len; ++i)
1083 {
1084 cur_si = &CUR_STATE(i);
1085 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001086 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087 == SPTYPE_MATCH
1088 && cur_si->si_m_endpos.lnum < current_lnum)
1089 {
1090 cur_si->si_flags |= HL_MATCHCONT;
1091 cur_si->si_m_endpos.lnum = 0;
1092 cur_si->si_m_endpos.col = 0;
1093 cur_si->si_h_endpos = cur_si->si_m_endpos;
1094 cur_si->si_ends = TRUE;
1095 }
1096 }
1097 }
1098
1099 /*
1100 * Need to update the end of a start/skip/end that continues from the
1101 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001102 * influence contained items. If we've just removed "extend"
1103 * (startofline == 0) then we should update ends of normal regions
1104 * contained inside "keepend" because "extend" could have extended
1105 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106 * Then check for items ending in column 0.
1107 */
1108 i = current_state.ga_len - 1;
1109 if (keepend_level >= 0)
1110 for ( ; i > keepend_level; --i)
1111 if (CUR_STATE(i).si_flags & HL_EXTEND)
1112 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001113
1114 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001115 for ( ; i < current_state.ga_len; ++i)
1116 {
1117 cur_si = &CUR_STATE(i);
1118 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001119 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 || (i == current_state.ga_len - 1 && startofline))
1121 {
1122 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1123 cur_si->si_h_startpos.lnum = current_lnum;
1124
1125 if (!(cur_si->si_flags & HL_MATCHCONT))
1126 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001127
1128 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1129 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130 }
1131 }
1132 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133}
1134
1135/****************************************
1136 * Handling of the state stack cache.
1137 */
1138
1139/*
1140 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1141 *
1142 * To speed up syntax highlighting, the state stack for the start of some
1143 * lines is cached. These entries can be used to start parsing at that point.
1144 *
1145 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1146 * valid entries. b_sst_first points to the first one, then follow sst_next.
1147 * The entries are sorted on line number. The first entry is often for line 2
1148 * (line 1 always starts with an empty stack).
1149 * There is also a list for free entries. This construction is used to avoid
1150 * having to allocate and free memory blocks too often.
1151 *
1152 * When making changes to the buffer, this is logged in b_mod_*. When calling
1153 * update_screen() to update the display, it will call
1154 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1155 * entries. The entries which are inside the changed area are removed,
1156 * because they must be recomputed. Entries below the changed have their line
1157 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1158 * set to indicate that a check must be made if the changed lines would change
1159 * the cached entry.
1160 *
1161 * When later displaying lines, an entry is stored for each line. Displayed
1162 * lines are likely to be displayed again, in which case the state at the
1163 * start of the line is needed.
1164 * For not displayed lines, an entry is stored for every so many lines. These
1165 * entries will be used e.g., when scrolling backwards. The distance between
1166 * entries depends on the number of lines in the buffer. For small buffers
1167 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1168 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1169 */
1170
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001172syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173{
1174 synstate_T *p;
1175
1176 if (block->b_sst_array != NULL)
1177 {
1178 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1179 clear_syn_state(p);
1180 vim_free(block->b_sst_array);
1181 block->b_sst_array = NULL;
1182 block->b_sst_len = 0;
1183 }
1184}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185/*
1186 * Free b_sst_array[] for buffer "buf".
1187 * Used when syntax items changed to force resyncing everywhere.
1188 */
1189 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001190syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001192#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001193 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001194#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195
Bram Moolenaar860cae12010-06-05 23:22:07 +02001196 syn_stack_free_block(block);
1197
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198#ifdef FEAT_FOLDING
1199 /* When using "syntax" fold method, must update all folds. */
1200 FOR_ALL_WINDOWS(wp)
1201 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001203 foldUpdateAll(wp);
1204 }
1205#endif
1206}
1207
1208/*
1209 * Allocate the syntax state stack for syn_buf when needed.
1210 * If the number of entries in b_sst_array[] is much too big or a bit too
1211 * small, reallocate it.
1212 * Also used to allocate b_sst_array[] for the first time.
1213 */
1214 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001215syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216{
1217 long len;
1218 synstate_T *to, *from;
1219 synstate_T *sstp;
1220
1221 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1222 if (len < SST_MIN_ENTRIES)
1223 len = SST_MIN_ENTRIES;
1224 else if (len > SST_MAX_ENTRIES)
1225 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001226 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227 {
1228 /* Allocate 50% too much, to avoid reallocating too often. */
1229 len = syn_buf->b_ml.ml_line_count;
1230 len = (len + len / 2) / SST_DIST + Rows * 2;
1231 if (len < SST_MIN_ENTRIES)
1232 len = SST_MIN_ENTRIES;
1233 else if (len > SST_MAX_ENTRIES)
1234 len = SST_MAX_ENTRIES;
1235
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 {
1238 /* When shrinking the array, cleanup the existing stack.
1239 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 && syn_stack_cleanup())
1242 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1244 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 }
1246
1247 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1248 if (sstp == NULL) /* out of memory! */
1249 return;
1250
1251 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 {
1254 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001255 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 from = from->sst_next)
1257 {
1258 ++to;
1259 *to = *from;
1260 to->sst_next = to + 1;
1261 }
1262 }
1263 if (to != sstp - 1)
1264 {
1265 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001266 syn_block->b_sst_first = sstp;
1267 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001268 }
1269 else
1270 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001271 syn_block->b_sst_first = NULL;
1272 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001273 }
1274
1275 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001276 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 while (++to < sstp + len)
1278 to->sst_next = to + 1;
1279 (sstp + len - 1)->sst_next = NULL;
1280
Bram Moolenaar860cae12010-06-05 23:22:07 +02001281 vim_free(syn_block->b_sst_array);
1282 syn_block->b_sst_array = sstp;
1283 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 }
1285}
1286
1287/*
1288 * Check for changes in a buffer to affect stored syntax states. Uses the
1289 * b_mod_* fields.
1290 * Called from update_screen(), before screen is being updated, once for each
1291 * displayed buffer.
1292 */
1293 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001294syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 win_T *wp;
1297
1298 syn_stack_apply_changes_block(&buf->b_s, buf);
1299
1300 FOR_ALL_WINDOWS(wp)
1301 {
1302 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1303 syn_stack_apply_changes_block(wp->w_s, buf);
1304 }
1305}
1306
1307 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001308syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 synstate_T *p, *prev, *np;
1311 linenr_T n;
1312
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 return;
1315
1316 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001317 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001319 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 {
1321 n = p->sst_lnum + buf->b_mod_xlines;
1322 if (n <= buf->b_mod_bot)
1323 {
1324 /* this state is inside the changed area, remove it */
1325 np = p->sst_next;
1326 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001327 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 else
1329 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 p = np;
1332 continue;
1333 }
1334 /* This state is below the changed area. Remember the line
1335 * that needs to be parsed before this entry can be made valid
1336 * again. */
1337 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1338 {
1339 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1340 p->sst_change_lnum += buf->b_mod_xlines;
1341 else
1342 p->sst_change_lnum = buf->b_mod_top;
1343 }
1344 if (p->sst_change_lnum == 0
1345 || p->sst_change_lnum < buf->b_mod_bot)
1346 p->sst_change_lnum = buf->b_mod_bot;
1347
1348 p->sst_lnum = n;
1349 }
1350 prev = p;
1351 p = p->sst_next;
1352 }
1353}
1354
1355/*
1356 * Reduce the number of entries in the state stack for syn_buf.
1357 * Returns TRUE if at least one entry was freed.
1358 */
1359 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001360syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361{
1362 synstate_T *p, *prev;
1363 disptick_T tick;
1364 int above;
1365 int dist;
1366 int retval = FALSE;
1367
Bram Moolenaar860cae12010-06-05 23:22:07 +02001368 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369 return retval;
1370
1371 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001372 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001373 dist = 999999;
1374 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376
1377 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001378 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 * be removed. Set "above" when the "tick" for the oldest entry is above
1380 * "b_sst_lasttick" (the display tick wraps around).
1381 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001382 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001384 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1386 {
1387 if (prev->sst_lnum + dist > p->sst_lnum)
1388 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001389 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001390 {
1391 if (!above || p->sst_tick < tick)
1392 tick = p->sst_tick;
1393 above = TRUE;
1394 }
1395 else if (!above && p->sst_tick < tick)
1396 tick = p->sst_tick;
1397 }
1398 }
1399
1400 /*
1401 * Go through the list to make the entries for the oldest tick at an
1402 * interval of several lines.
1403 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001404 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001405 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1406 {
1407 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1408 {
1409 /* Move this entry from used list to free list */
1410 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001411 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 p = prev;
1413 retval = TRUE;
1414 }
1415 }
1416 return retval;
1417}
1418
1419/*
1420 * Free the allocated memory for a syn_state item.
1421 * Move the entry into the free list.
1422 */
1423 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001424syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001425{
1426 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001427 p->sst_next = block->b_sst_firstfree;
1428 block->b_sst_firstfree = p;
1429 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430}
1431
1432/*
1433 * Find an entry in the list of state stacks at or before "lnum".
1434 * Returns NULL when there is no entry or the first entry is after "lnum".
1435 */
1436 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001437syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438{
1439 synstate_T *p, *prev;
1440
1441 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001442 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443 {
1444 if (p->sst_lnum == lnum)
1445 return p;
1446 if (p->sst_lnum > lnum)
1447 break;
1448 }
1449 return prev;
1450}
1451
1452/*
1453 * Try saving the current state in b_sst_array[].
1454 * The current state must be valid for the start of the current_lnum line!
1455 */
1456 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001457store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458{
1459 int i;
1460 synstate_T *p;
1461 bufstate_T *bp;
1462 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001463 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464
1465 /*
1466 * If the current state contains a start or end pattern that continues
1467 * from the previous line, we can't use it. Don't store it then.
1468 */
1469 for (i = current_state.ga_len - 1; i >= 0; --i)
1470 {
1471 cur_si = &CUR_STATE(i);
1472 if (cur_si->si_h_startpos.lnum >= current_lnum
1473 || cur_si->si_m_endpos.lnum >= current_lnum
1474 || cur_si->si_h_endpos.lnum >= current_lnum
1475 || (cur_si->si_end_idx
1476 && cur_si->si_eoe_pos.lnum >= current_lnum))
1477 break;
1478 }
1479 if (i >= 0)
1480 {
1481 if (sp != NULL)
1482 {
1483 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001484 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001486 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 else
1488 {
1489 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001490 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 if (p->sst_next == sp)
1492 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001493 if (p != NULL) /* just in case */
1494 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001496 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497 sp = NULL;
1498 }
1499 }
1500 else if (sp == NULL || sp->sst_lnum != current_lnum)
1501 {
1502 /*
1503 * Add a new entry
1504 */
1505 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001506 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 {
1508 (void)syn_stack_cleanup();
1509 /* "sp" may have been moved to the freelist now */
1510 sp = syn_stack_find_entry(current_lnum);
1511 }
1512 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001513 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514 sp = NULL;
1515 else
1516 {
1517 /* Take the first item from the free list and put it in the used
1518 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001519 p = syn_block->b_sst_firstfree;
1520 syn_block->b_sst_firstfree = p->sst_next;
1521 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001522 if (sp == NULL)
1523 {
1524 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001525 p->sst_next = syn_block->b_sst_first;
1526 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 }
1528 else
1529 {
1530 /* insert in list after *sp */
1531 p->sst_next = sp->sst_next;
1532 sp->sst_next = p;
1533 }
1534 sp = p;
1535 sp->sst_stacksize = 0;
1536 sp->sst_lnum = current_lnum;
1537 }
1538 }
1539 if (sp != NULL)
1540 {
1541 /* When overwriting an existing state stack, clear it first */
1542 clear_syn_state(sp);
1543 sp->sst_stacksize = current_state.ga_len;
1544 if (current_state.ga_len > SST_FIX_STATES)
1545 {
1546 /* Need to clear it, might be something remaining from when the
1547 * length was less than SST_FIX_STATES. */
1548 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1549 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1550 sp->sst_stacksize = 0;
1551 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001552 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1554 }
1555 else
1556 bp = sp->sst_union.sst_stack;
1557 for (i = 0; i < sp->sst_stacksize; ++i)
1558 {
1559 bp[i].bs_idx = CUR_STATE(i).si_idx;
1560 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001561#ifdef FEAT_CONCEAL
1562 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1563 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1564#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1566 }
1567 sp->sst_next_flags = current_next_flags;
1568 sp->sst_next_list = current_next_list;
1569 sp->sst_tick = display_tick;
1570 sp->sst_change_lnum = 0;
1571 }
1572 current_state_stored = TRUE;
1573 return sp;
1574}
1575
1576/*
1577 * Copy a state stack from "from" in b_sst_array[] to current_state;
1578 */
1579 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001580load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001581{
1582 int i;
1583 bufstate_T *bp;
1584
1585 clear_current_state();
1586 validate_current_state();
1587 keepend_level = -1;
1588 if (from->sst_stacksize
1589 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1590 {
1591 if (from->sst_stacksize > SST_FIX_STATES)
1592 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1593 else
1594 bp = from->sst_union.sst_stack;
1595 for (i = 0; i < from->sst_stacksize; ++i)
1596 {
1597 CUR_STATE(i).si_idx = bp[i].bs_idx;
1598 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001599#ifdef FEAT_CONCEAL
1600 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1601 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1602#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1604 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1605 keepend_level = i;
1606 CUR_STATE(i).si_ends = FALSE;
1607 CUR_STATE(i).si_m_lnum = 0;
1608 if (CUR_STATE(i).si_idx >= 0)
1609 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001610 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001611 else
1612 CUR_STATE(i).si_next_list = NULL;
1613 update_si_attr(i);
1614 }
1615 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 }
1617 current_next_list = from->sst_next_list;
1618 current_next_flags = from->sst_next_flags;
1619 current_lnum = from->sst_lnum;
1620}
1621
1622/*
1623 * Compare saved state stack "*sp" with the current state.
1624 * Return TRUE when they are equal.
1625 */
1626 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001627syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001628{
1629 int i, j;
1630 bufstate_T *bp;
1631 reg_extmatch_T *six, *bsx;
1632
1633 /* First a quick check if the stacks have the same size end nextlist. */
1634 if (sp->sst_stacksize == current_state.ga_len
1635 && sp->sst_next_list == current_next_list)
1636 {
1637 /* Need to compare all states on both stacks. */
1638 if (sp->sst_stacksize > SST_FIX_STATES)
1639 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1640 else
1641 bp = sp->sst_union.sst_stack;
1642
1643 for (i = current_state.ga_len; --i >= 0; )
1644 {
1645 /* If the item has another index the state is different. */
1646 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1647 break;
1648 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1649 {
1650 /* When the extmatch pointers are different, the strings in
1651 * them can still be the same. Check if the extmatch
1652 * references are equal. */
1653 bsx = bp[i].bs_extmatch;
1654 six = CUR_STATE(i).si_extmatch;
1655 /* If one of the extmatch pointers is NULL the states are
1656 * different. */
1657 if (bsx == NULL || six == NULL)
1658 break;
1659 for (j = 0; j < NSUBEXP; ++j)
1660 {
1661 /* Check each referenced match string. They must all be
1662 * equal. */
1663 if (bsx->matches[j] != six->matches[j])
1664 {
1665 /* If the pointer is different it can still be the
1666 * same text. Compare the strings, ignore case when
1667 * the start item has the sp_ic flag set. */
1668 if (bsx->matches[j] == NULL
1669 || six->matches[j] == NULL)
1670 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001671 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001672 ? MB_STRICMP(bsx->matches[j],
1673 six->matches[j]) != 0
1674 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1675 break;
1676 }
1677 }
1678 if (j != NSUBEXP)
1679 break;
1680 }
1681 }
1682 if (i < 0)
1683 return TRUE;
1684 }
1685 return FALSE;
1686}
1687
1688/*
1689 * We stop parsing syntax above line "lnum". If the stored state at or below
1690 * this line depended on a change before it, it now depends on the line below
1691 * the last parsed line.
1692 * The window looks like this:
1693 * line which changed
1694 * displayed line
1695 * displayed line
1696 * lnum -> line below window
1697 */
1698 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001699syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001700{
1701 synstate_T *sp;
1702
1703 sp = syn_stack_find_entry(lnum);
1704 if (sp != NULL && sp->sst_lnum < lnum)
1705 sp = sp->sst_next;
1706
1707 if (sp != NULL && sp->sst_change_lnum != 0)
1708 sp->sst_change_lnum = lnum;
1709}
1710
1711/*
1712 * End of handling of the state stack.
1713 ****************************************/
1714
1715 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001716invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717{
1718 clear_current_state();
1719 current_state.ga_itemsize = 0; /* mark current_state invalid */
1720 current_next_list = NULL;
1721 keepend_level = -1;
1722}
1723
1724 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001725validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001726{
1727 current_state.ga_itemsize = sizeof(stateitem_T);
1728 current_state.ga_growsize = 3;
1729}
1730
1731/*
1732 * Return TRUE if the syntax at start of lnum changed since last time.
1733 * This will only be called just after get_syntax_attr() for the previous
1734 * line, to check if the next line needs to be redrawn too.
1735 */
1736 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001737syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738{
1739 int retval = TRUE;
1740 synstate_T *sp;
1741
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742 /*
1743 * Check the state stack when:
1744 * - lnum is just below the previously syntaxed line.
1745 * - lnum is not before the lines with saved states.
1746 * - lnum is not past the lines with saved states.
1747 * - lnum is at or before the last changed line.
1748 */
1749 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1750 {
1751 sp = syn_stack_find_entry(lnum);
1752 if (sp != NULL && sp->sst_lnum == lnum)
1753 {
1754 /*
1755 * finish the previous line (needed when not all of the line was
1756 * drawn)
1757 */
1758 (void)syn_finish_line(FALSE);
1759
1760 /*
1761 * Compare the current state with the previously saved state of
1762 * the line.
1763 */
1764 if (syn_stack_equal(sp))
1765 retval = FALSE;
1766
1767 /*
1768 * Store the current state in b_sst_array[] for later use.
1769 */
1770 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001771 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772 }
1773 }
1774
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 return retval;
1776}
1777
1778/*
1779 * Finish the current line.
1780 * This doesn't return any attributes, it only gets the state at the end of
1781 * the line. It can start anywhere in the line, as long as the current state
1782 * is valid.
1783 */
1784 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001785syn_finish_line(
1786 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001787{
1788 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001789 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001791 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001792 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001793 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1794 /*
1795 * When syncing, and found some item, need to check the item.
1796 */
1797 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001800 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001802 cur_si = &CUR_STATE(current_state.ga_len - 1);
1803 if (cur_si->si_idx >= 0
1804 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1805 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1806 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001807
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001808 /* syn_current_attr() will have skipped the check for an item
1809 * that ends here, need to do that now. Be careful not to go
1810 * past the NUL. */
1811 prev_current_col = current_col;
1812 if (syn_getcurline()[current_col] != NUL)
1813 ++current_col;
1814 check_state_ends();
1815 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001816 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001817 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001818 }
1819 return FALSE;
1820}
1821
1822/*
1823 * Return highlight attributes for next character.
1824 * Must first call syntax_start() once for the line.
1825 * "col" is normally 0 for the first use in a line, and increments by one each
1826 * time. It's allowed to skip characters and to stop before the end of the
1827 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001828 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1829 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 */
1831 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001832get_syntax_attr(
1833 colnr_T col,
1834 int *can_spell,
1835 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001836{
1837 int attr = 0;
1838
Bram Moolenaar349955a2007-08-14 21:07:36 +00001839 if (can_spell != NULL)
1840 /* Default: Only do spelling when there is no @Spell cluster or when
1841 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001842 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1843 ? (syn_block->b_spell_cluster_id == 0)
1844 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001845
Bram Moolenaar071d4272004-06-13 20:20:40 +00001846 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001847 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001848 return 0;
1849
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001850 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001851 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001852 {
1853 clear_current_state();
1854#ifdef FEAT_EVAL
1855 current_id = 0;
1856 current_trans_id = 0;
1857#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001858#ifdef FEAT_CONCEAL
1859 current_flags = 0;
1860#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001861 return 0;
1862 }
1863
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864 /* Make sure current_state is valid */
1865 if (INVALID_STATE(&current_state))
1866 validate_current_state();
1867
1868 /*
1869 * Skip from the current column to "col", get the attributes for "col".
1870 */
1871 while (current_col <= col)
1872 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001873 attr = syn_current_attr(FALSE, TRUE, can_spell,
1874 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 ++current_col;
1876 }
1877
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 return attr;
1879}
1880
1881/*
1882 * Get syntax attributes for current_lnum, current_col.
1883 */
1884 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001885syn_current_attr(
1886 int syncing, /* When 1: called for syncing */
1887 int displaying, /* result will be displayed */
1888 int *can_spell, /* return: do spell checking */
1889 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890{
1891 int syn_id;
1892 lpos_T endpos; /* was: char_u *endp; */
1893 lpos_T hl_startpos; /* was: int hl_startcol; */
1894 lpos_T hl_endpos;
1895 lpos_T eos_pos; /* end-of-start match (start region) */
1896 lpos_T eoe_pos; /* end-of-end pattern */
1897 int end_idx; /* group ID for end pattern */
1898 int idx;
1899 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001900 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 int startcol;
1902 int endcol;
1903 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001904 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905 short *next_list;
1906 int found_match; /* found usable match */
1907 static int try_next_column = FALSE; /* must try in next col */
1908 int do_keywords;
1909 regmmatch_T regmatch;
1910 lpos_T pos;
1911 int lc_col;
1912 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001913 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914 char_u *line; /* current line. NOTE: becomes invalid after
1915 looking for a pattern match! */
1916
1917 /* variables for zero-width matches that have a "nextgroup" argument */
1918 int keep_next_list;
1919 int zero_width_next_list = FALSE;
1920 garray_T zero_width_next_ga;
1921
1922 /*
1923 * No character, no attributes! Past end of line?
1924 * Do try matching with an empty line (could be the start of a region).
1925 */
1926 line = syn_getcurline();
1927 if (line[current_col] == NUL && current_col != 0)
1928 {
1929 /*
1930 * If we found a match after the last column, use it.
1931 */
1932 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1933 && next_match_col != MAXCOL)
1934 (void)push_next_match(NULL);
1935
1936 current_finished = TRUE;
1937 current_state_stored = FALSE;
1938 return 0;
1939 }
1940
1941 /* if the current or next character is NUL, we will finish the line now */
1942 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1943 {
1944 current_finished = TRUE;
1945 current_state_stored = FALSE;
1946 }
1947
1948 /*
1949 * When in the previous column there was a match but it could not be used
1950 * (empty match or already matched in this column) need to try again in
1951 * the next column.
1952 */
1953 if (try_next_column)
1954 {
1955 next_match_idx = -1;
1956 try_next_column = FALSE;
1957 }
1958
1959 /* Only check for keywords when not syncing and there are some. */
1960 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001961 && (syn_block->b_keywtab.ht_used > 0
1962 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963
1964 /* Init the list of zero-width matches with a nextlist. This is used to
1965 * avoid matching the same item in the same position twice. */
1966 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1967
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001968 /* use syntax iskeyword option */
1969 save_chartab(buf_chartab);
1970
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 /*
1972 * Repeat matching keywords and patterns, to find contained items at the
1973 * same column. This stops when there are no extra matches at the current
1974 * column.
1975 */
1976 do
1977 {
1978 found_match = FALSE;
1979 keep_next_list = FALSE;
1980 syn_id = 0;
1981
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001982
Bram Moolenaar071d4272004-06-13 20:20:40 +00001983 /*
1984 * 1. Check for a current state.
1985 * Only when there is no current state, or if the current state may
1986 * contain other things, we need to check for keywords and patterns.
1987 * Always need to check for contained items if some item has the
1988 * "containedin" argument (takes extra time!).
1989 */
1990 if (current_state.ga_len)
1991 cur_si = &CUR_STATE(current_state.ga_len - 1);
1992 else
1993 cur_si = NULL;
1994
Bram Moolenaar860cae12010-06-05 23:22:07 +02001995 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996 || cur_si->si_cont_list != NULL)
1997 {
1998 /*
1999 * 2. Check for keywords, if on a keyword char after a non-keyword
2000 * char. Don't do this when syncing.
2001 */
2002 if (do_keywords)
2003 {
2004 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002005 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002006 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002007 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008#ifdef FEAT_MBYTE
2009 - (has_mbyte
2010 ? (*mb_head_off)(line, line + current_col - 1)
2011 : 0)
2012#endif
2013 , syn_buf)))
2014 {
2015 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002016 &endcol, &flags, &next_list, cur_si,
2017 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002018 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019 {
2020 if (push_current_state(KEYWORD_IDX) == OK)
2021 {
2022 cur_si = &CUR_STATE(current_state.ga_len - 1);
2023 cur_si->si_m_startcol = current_col;
2024 cur_si->si_h_startpos.lnum = current_lnum;
2025 cur_si->si_h_startpos.col = 0; /* starts right away */
2026 cur_si->si_m_endpos.lnum = current_lnum;
2027 cur_si->si_m_endpos.col = endcol;
2028 cur_si->si_h_endpos.lnum = current_lnum;
2029 cur_si->si_h_endpos.col = endcol;
2030 cur_si->si_ends = TRUE;
2031 cur_si->si_end_idx = 0;
2032 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002033#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002034 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002035 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002036 if (current_state.ga_len > 1)
2037 cur_si->si_flags |=
2038 CUR_STATE(current_state.ga_len - 2).si_flags
2039 & HL_CONCEAL;
2040#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002041 cur_si->si_id = syn_id;
2042 cur_si->si_trans_id = syn_id;
2043 if (flags & HL_TRANSP)
2044 {
2045 if (current_state.ga_len < 2)
2046 {
2047 cur_si->si_attr = 0;
2048 cur_si->si_trans_id = 0;
2049 }
2050 else
2051 {
2052 cur_si->si_attr = CUR_STATE(
2053 current_state.ga_len - 2).si_attr;
2054 cur_si->si_trans_id = CUR_STATE(
2055 current_state.ga_len - 2).si_trans_id;
2056 }
2057 }
2058 else
2059 cur_si->si_attr = syn_id2attr(syn_id);
2060 cur_si->si_cont_list = NULL;
2061 cur_si->si_next_list = next_list;
2062 check_keepend();
2063 }
2064 else
2065 vim_free(next_list);
2066 }
2067 }
2068 }
2069
2070 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002071 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002072 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002073 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074 {
2075 /*
2076 * If we didn't check for a match yet, or we are past it, check
2077 * for any match with a pattern.
2078 */
2079 if (next_match_idx < 0 || next_match_col < (int)current_col)
2080 {
2081 /*
2082 * Check all relevant patterns for a match at this
2083 * position. This is complicated, because matching with a
2084 * pattern takes quite a bit of time, thus we want to
2085 * avoid doing it when it's not needed.
2086 */
2087 next_match_idx = 0; /* no match in this line yet */
2088 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002089 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002090 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002091 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002092 if ( spp->sp_syncing == syncing
2093 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2094 && (spp->sp_type == SPTYPE_MATCH
2095 || spp->sp_type == SPTYPE_START)
2096 && (current_next_list != NULL
2097 ? in_id_list(NULL, current_next_list,
2098 &spp->sp_syn, 0)
2099 : (cur_si == NULL
2100 ? !(spp->sp_flags & HL_CONTAINED)
2101 : in_id_list(cur_si,
2102 cur_si->si_cont_list, &spp->sp_syn,
2103 spp->sp_flags & HL_CONTAINED))))
2104 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002105 int r;
2106
Bram Moolenaar071d4272004-06-13 20:20:40 +00002107 /* If we already tried matching in this line, and
2108 * there isn't a match before next_match_col, skip
2109 * this item. */
2110 if (spp->sp_line_id == current_line_id
2111 && spp->sp_startcol >= next_match_col)
2112 continue;
2113 spp->sp_line_id = current_line_id;
2114
2115 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2116 if (lc_col < 0)
2117 lc_col = 0;
2118
2119 regmatch.rmm_ic = spp->sp_ic;
2120 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002121 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002122 current_lnum,
2123 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002124 IF_SYN_TIME(&spp->sp_time));
2125 spp->sp_prog = regmatch.regprog;
2126 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002127 {
2128 /* no match in this line, try another one */
2129 spp->sp_startcol = MAXCOL;
2130 continue;
2131 }
2132
2133 /*
2134 * Compute the first column of the match.
2135 */
2136 syn_add_start_off(&pos, &regmatch,
2137 spp, SPO_MS_OFF, -1);
2138 if (pos.lnum > current_lnum)
2139 {
2140 /* must have used end of match in a next line,
2141 * we can't handle that */
2142 spp->sp_startcol = MAXCOL;
2143 continue;
2144 }
2145 startcol = pos.col;
2146
2147 /* remember the next column where this pattern
2148 * matches in the current line */
2149 spp->sp_startcol = startcol;
2150
2151 /*
2152 * If a previously found match starts at a lower
2153 * column number, don't use this one.
2154 */
2155 if (startcol >= next_match_col)
2156 continue;
2157
2158 /*
2159 * If we matched this pattern at this position
2160 * before, skip it. Must retry in the next
2161 * column, because it may match from there.
2162 */
2163 if (did_match_already(idx, &zero_width_next_ga))
2164 {
2165 try_next_column = TRUE;
2166 continue;
2167 }
2168
2169 endpos.lnum = regmatch.endpos[0].lnum;
2170 endpos.col = regmatch.endpos[0].col;
2171
2172 /* Compute the highlight start. */
2173 syn_add_start_off(&hl_startpos, &regmatch,
2174 spp, SPO_HS_OFF, -1);
2175
2176 /* Compute the region start. */
2177 /* Default is to use the end of the match. */
2178 syn_add_end_off(&eos_pos, &regmatch,
2179 spp, SPO_RS_OFF, 0);
2180
2181 /*
2182 * Grab the external submatches before they get
2183 * overwritten. Reference count doesn't change.
2184 */
2185 unref_extmatch(cur_extmatch);
2186 cur_extmatch = re_extmatch_out;
2187 re_extmatch_out = NULL;
2188
2189 flags = 0;
2190 eoe_pos.lnum = 0; /* avoid warning */
2191 eoe_pos.col = 0;
2192 end_idx = 0;
2193 hl_endpos.lnum = 0;
2194
2195 /*
2196 * For a "oneline" the end must be found in the
2197 * same line too. Search for it after the end of
2198 * the match with the start pattern. Set the
2199 * resulting end positions at the same time.
2200 */
2201 if (spp->sp_type == SPTYPE_START
2202 && (spp->sp_flags & HL_ONELINE))
2203 {
2204 lpos_T startpos;
2205
2206 startpos = endpos;
2207 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2208 &flags, &eoe_pos, &end_idx, cur_extmatch);
2209 if (endpos.lnum == 0)
2210 continue; /* not found */
2211 }
2212
2213 /*
2214 * For a "match" the size must be > 0 after the
2215 * end offset needs has been added. Except when
2216 * syncing.
2217 */
2218 else if (spp->sp_type == SPTYPE_MATCH)
2219 {
2220 syn_add_end_off(&hl_endpos, &regmatch, spp,
2221 SPO_HE_OFF, 0);
2222 syn_add_end_off(&endpos, &regmatch, spp,
2223 SPO_ME_OFF, 0);
2224 if (endpos.lnum == current_lnum
2225 && (int)endpos.col + syncing < startcol)
2226 {
2227 /*
2228 * If an empty string is matched, may need
2229 * to try matching again at next column.
2230 */
2231 if (regmatch.startpos[0].col
2232 == regmatch.endpos[0].col)
2233 try_next_column = TRUE;
2234 continue;
2235 }
2236 }
2237
2238 /*
2239 * keep the best match so far in next_match_*
2240 */
2241 /* Highlighting must start after startpos and end
2242 * before endpos. */
2243 if (hl_startpos.lnum == current_lnum
2244 && (int)hl_startpos.col < startcol)
2245 hl_startpos.col = startcol;
2246 limit_pos_zero(&hl_endpos, &endpos);
2247
2248 next_match_idx = idx;
2249 next_match_col = startcol;
2250 next_match_m_endpos = endpos;
2251 next_match_h_endpos = hl_endpos;
2252 next_match_h_startpos = hl_startpos;
2253 next_match_flags = flags;
2254 next_match_eos_pos = eos_pos;
2255 next_match_eoe_pos = eoe_pos;
2256 next_match_end_idx = end_idx;
2257 unref_extmatch(next_match_extmatch);
2258 next_match_extmatch = cur_extmatch;
2259 cur_extmatch = NULL;
2260 }
2261 }
2262 }
2263
2264 /*
2265 * If we found a match at the current column, use it.
2266 */
2267 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2268 {
2269 synpat_T *lspp;
2270
2271 /* When a zero-width item matched which has a nextgroup,
2272 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002273 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 if (next_match_m_endpos.lnum == current_lnum
2275 && next_match_m_endpos.col == current_col
2276 && lspp->sp_next_list != NULL)
2277 {
2278 current_next_list = lspp->sp_next_list;
2279 current_next_flags = lspp->sp_flags;
2280 keep_next_list = TRUE;
2281 zero_width_next_list = TRUE;
2282
2283 /* Add the index to a list, so that we can check
2284 * later that we don't match it again (and cause an
2285 * endless loop). */
2286 if (ga_grow(&zero_width_next_ga, 1) == OK)
2287 {
2288 ((int *)(zero_width_next_ga.ga_data))
2289 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002290 }
2291 next_match_idx = -1;
2292 }
2293 else
2294 cur_si = push_next_match(cur_si);
2295 found_match = TRUE;
2296 }
2297 }
2298 }
2299
2300 /*
2301 * Handle searching for nextgroup match.
2302 */
2303 if (current_next_list != NULL && !keep_next_list)
2304 {
2305 /*
2306 * If a nextgroup was not found, continue looking for one if:
2307 * - this is an empty line and the "skipempty" option was given
2308 * - we are on white space and the "skipwhite" option was given
2309 */
2310 if (!found_match)
2311 {
2312 line = syn_getcurline();
2313 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002314 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002315 || ((current_next_flags & HL_SKIPEMPTY)
2316 && *line == NUL))
2317 break;
2318 }
2319
2320 /*
2321 * If a nextgroup was found: Use it, and continue looking for
2322 * contained matches.
2323 * If a nextgroup was not found: Continue looking for a normal
2324 * match.
2325 * When did set current_next_list for a zero-width item and no
2326 * match was found don't loop (would get stuck).
2327 */
2328 current_next_list = NULL;
2329 next_match_idx = -1;
2330 if (!zero_width_next_list)
2331 found_match = TRUE;
2332 }
2333
2334 } while (found_match);
2335
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002336 restore_chartab(buf_chartab);
2337
Bram Moolenaar071d4272004-06-13 20:20:40 +00002338 /*
2339 * Use attributes from the current state, if within its highlighting.
2340 * If not, use attributes from the current-but-one state, etc.
2341 */
2342 current_attr = 0;
2343#ifdef FEAT_EVAL
2344 current_id = 0;
2345 current_trans_id = 0;
2346#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002347#ifdef FEAT_CONCEAL
2348 current_flags = 0;
2349#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 if (cur_si != NULL)
2351 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002352#ifndef FEAT_EVAL
2353 int current_trans_id = 0;
2354#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2356 {
2357 sip = &CUR_STATE(idx);
2358 if ((current_lnum > sip->si_h_startpos.lnum
2359 || (current_lnum == sip->si_h_startpos.lnum
2360 && current_col >= sip->si_h_startpos.col))
2361 && (sip->si_h_endpos.lnum == 0
2362 || current_lnum < sip->si_h_endpos.lnum
2363 || (current_lnum == sip->si_h_endpos.lnum
2364 && current_col < sip->si_h_endpos.col)))
2365 {
2366 current_attr = sip->si_attr;
2367#ifdef FEAT_EVAL
2368 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002369#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002370 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002371#ifdef FEAT_CONCEAL
2372 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002373 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002374 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002375#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002376 break;
2377 }
2378 }
2379
Bram Moolenaar217ad922005-03-20 22:37:15 +00002380 if (can_spell != NULL)
2381 {
2382 struct sp_syn sps;
2383
2384 /*
2385 * set "can_spell" to TRUE if spell checking is supposed to be
2386 * done in the current item.
2387 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002389 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002390 /* There is no @Spell cluster: Do spelling for items without
2391 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002392 if (syn_block->b_nospell_cluster_id == 0
2393 || current_trans_id == 0)
2394 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002395 else
2396 {
2397 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002398 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002399 sps.cont_in_list = NULL;
2400 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2401 }
2402 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002403 else
2404 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002405 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002406 * the @Spell cluster. But not when @NoSpell is also there.
2407 * At the toplevel only spell check when ":syn spell toplevel"
2408 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002409 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002410 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002411 else
2412 {
2413 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002414 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002415 sps.cont_in_list = NULL;
2416 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2417
Bram Moolenaar860cae12010-06-05 23:22:07 +02002418 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002419 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002420 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002421 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2422 *can_spell = FALSE;
2423 }
2424 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002425 }
2426 }
2427
2428
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429 /*
2430 * Check for end of current state (and the states before it) at the
2431 * next column. Don't do this for syncing, because we would miss a
2432 * single character match.
2433 * First check if the current state ends at the current column. It
2434 * may be for an empty match and a containing item might end in the
2435 * current column.
2436 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002437 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002438 {
2439 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002440 if (current_state.ga_len > 0
2441 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002442 {
2443 ++current_col;
2444 check_state_ends();
2445 --current_col;
2446 }
2447 }
2448 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002449 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002450 /* Default: Only do spelling when there is no @Spell cluster or when
2451 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002452 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2453 ? (syn_block->b_spell_cluster_id == 0)
2454 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002456 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 if (current_next_list != NULL
2458 && syn_getcurline()[current_col + 1] == NUL
2459 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2460 current_next_list = NULL;
2461
2462 if (zero_width_next_ga.ga_len > 0)
2463 ga_clear(&zero_width_next_ga);
2464
2465 /* No longer need external matches. But keep next_match_extmatch. */
2466 unref_extmatch(re_extmatch_out);
2467 re_extmatch_out = NULL;
2468 unref_extmatch(cur_extmatch);
2469
2470 return current_attr;
2471}
2472
2473
2474/*
2475 * Check if we already matched pattern "idx" at the current column.
2476 */
2477 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002478did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002479{
2480 int i;
2481
2482 for (i = current_state.ga_len; --i >= 0; )
2483 if (CUR_STATE(i).si_m_startcol == (int)current_col
2484 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2485 && CUR_STATE(i).si_idx == idx)
2486 return TRUE;
2487
2488 /* Zero-width matches with a nextgroup argument are not put on the syntax
2489 * stack, and can only be matched once anyway. */
2490 for (i = gap->ga_len; --i >= 0; )
2491 if (((int *)(gap->ga_data))[i] == idx)
2492 return TRUE;
2493
2494 return FALSE;
2495}
2496
2497/*
2498 * Push the next match onto the stack.
2499 */
2500 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002501push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502{
2503 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002504#ifdef FEAT_CONCEAL
2505 int save_flags;
2506#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507
Bram Moolenaar860cae12010-06-05 23:22:07 +02002508 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002509
2510 /*
2511 * Push the item in current_state stack;
2512 */
2513 if (push_current_state(next_match_idx) == OK)
2514 {
2515 /*
2516 * If it's a start-skip-end type that crosses lines, figure out how
2517 * much it continues in this line. Otherwise just fill in the length.
2518 */
2519 cur_si = &CUR_STATE(current_state.ga_len - 1);
2520 cur_si->si_h_startpos = next_match_h_startpos;
2521 cur_si->si_m_startcol = current_col;
2522 cur_si->si_m_lnum = current_lnum;
2523 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002524#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002525 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002526 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002527 if (current_state.ga_len > 1)
2528 cur_si->si_flags |=
2529 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2530#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531 cur_si->si_next_list = spp->sp_next_list;
2532 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2533 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2534 {
2535 /* Try to find the end pattern in the current line */
2536 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2537 check_keepend();
2538 }
2539 else
2540 {
2541 cur_si->si_m_endpos = next_match_m_endpos;
2542 cur_si->si_h_endpos = next_match_h_endpos;
2543 cur_si->si_ends = TRUE;
2544 cur_si->si_flags |= next_match_flags;
2545 cur_si->si_eoe_pos = next_match_eoe_pos;
2546 cur_si->si_end_idx = next_match_end_idx;
2547 }
2548 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2549 keepend_level = current_state.ga_len - 1;
2550 check_keepend();
2551 update_si_attr(current_state.ga_len - 1);
2552
Bram Moolenaar860cae12010-06-05 23:22:07 +02002553#ifdef FEAT_CONCEAL
2554 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2555#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002556 /*
2557 * If the start pattern has another highlight group, push another item
2558 * on the stack for the start pattern.
2559 */
2560 if ( spp->sp_type == SPTYPE_START
2561 && spp->sp_syn_match_id != 0
2562 && push_current_state(next_match_idx) == OK)
2563 {
2564 cur_si = &CUR_STATE(current_state.ga_len - 1);
2565 cur_si->si_h_startpos = next_match_h_startpos;
2566 cur_si->si_m_startcol = current_col;
2567 cur_si->si_m_lnum = current_lnum;
2568 cur_si->si_m_endpos = next_match_eos_pos;
2569 cur_si->si_h_endpos = next_match_eos_pos;
2570 cur_si->si_ends = TRUE;
2571 cur_si->si_end_idx = 0;
2572 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002573#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002574 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002575 cur_si->si_flags |= save_flags;
2576 if (cur_si->si_flags & HL_CONCEALENDS)
2577 cur_si->si_flags |= HL_CONCEAL;
2578#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002579 cur_si->si_next_list = NULL;
2580 check_keepend();
2581 update_si_attr(current_state.ga_len - 1);
2582 }
2583 }
2584
2585 next_match_idx = -1; /* try other match next time */
2586
2587 return cur_si;
2588}
2589
2590/*
2591 * Check for end of current state (and the states before it).
2592 */
2593 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002594check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595{
2596 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002597 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598
2599 cur_si = &CUR_STATE(current_state.ga_len - 1);
2600 for (;;)
2601 {
2602 if (cur_si->si_ends
2603 && (cur_si->si_m_endpos.lnum < current_lnum
2604 || (cur_si->si_m_endpos.lnum == current_lnum
2605 && cur_si->si_m_endpos.col <= current_col)))
2606 {
2607 /*
2608 * If there is an end pattern group ID, highlight the end pattern
2609 * now. No need to pop the current item from the stack.
2610 * Only do this if the end pattern continues beyond the current
2611 * position.
2612 */
2613 if (cur_si->si_end_idx
2614 && (cur_si->si_eoe_pos.lnum > current_lnum
2615 || (cur_si->si_eoe_pos.lnum == current_lnum
2616 && cur_si->si_eoe_pos.col > current_col)))
2617 {
2618 cur_si->si_idx = cur_si->si_end_idx;
2619 cur_si->si_end_idx = 0;
2620 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2621 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2622 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002623#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002624 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002625 if (cur_si->si_flags & HL_CONCEALENDS)
2626 cur_si->si_flags |= HL_CONCEAL;
2627#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002629
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002630 /* nextgroup= should not match in the end pattern */
2631 current_next_list = NULL;
2632
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002633 /* what matches next may be different now, clear it */
2634 next_match_idx = 0;
2635 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002636 break;
2637 }
2638 else
2639 {
2640 /* handle next_list, unless at end of line and no "skipnl" or
2641 * "skipempty" */
2642 current_next_list = cur_si->si_next_list;
2643 current_next_flags = cur_si->si_flags;
2644 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2645 && syn_getcurline()[current_col] == NUL)
2646 current_next_list = NULL;
2647
2648 /* When the ended item has "extend", another item with
2649 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002650 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651
2652 pop_current_state();
2653
2654 if (current_state.ga_len == 0)
2655 break;
2656
Bram Moolenaar81993f42008-01-11 20:27:45 +00002657 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002658 {
2659 syn_update_ends(FALSE);
2660 if (current_state.ga_len == 0)
2661 break;
2662 }
2663
2664 cur_si = &CUR_STATE(current_state.ga_len - 1);
2665
2666 /*
2667 * Only for a region the search for the end continues after
2668 * the end of the contained item. If the contained match
2669 * included the end-of-line, break here, the region continues.
2670 * Don't do this when:
2671 * - "keepend" is used for the contained item
2672 * - not at the end of the line (could be end="x$"me=e-1).
2673 * - "excludenl" is used (HL_HAS_EOL won't be set)
2674 */
2675 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002676 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 == SPTYPE_START
2678 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2679 {
2680 update_si_end(cur_si, (int)current_col, TRUE);
2681 check_keepend();
2682 if ((current_next_flags & HL_HAS_EOL)
2683 && keepend_level < 0
2684 && syn_getcurline()[current_col] == NUL)
2685 break;
2686 }
2687 }
2688 }
2689 else
2690 break;
2691 }
2692}
2693
2694/*
2695 * Update an entry in the current_state stack for a match or region. This
2696 * fills in si_attr, si_next_list and si_cont_list.
2697 */
2698 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002699update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700{
2701 stateitem_T *sip = &CUR_STATE(idx);
2702 synpat_T *spp;
2703
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002704 /* This should not happen... */
2705 if (sip->si_idx < 0)
2706 return;
2707
Bram Moolenaar860cae12010-06-05 23:22:07 +02002708 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002709 if (sip->si_flags & HL_MATCH)
2710 sip->si_id = spp->sp_syn_match_id;
2711 else
2712 sip->si_id = spp->sp_syn.id;
2713 sip->si_attr = syn_id2attr(sip->si_id);
2714 sip->si_trans_id = sip->si_id;
2715 if (sip->si_flags & HL_MATCH)
2716 sip->si_cont_list = NULL;
2717 else
2718 sip->si_cont_list = spp->sp_cont_list;
2719
2720 /*
2721 * For transparent items, take attr from outer item.
2722 * Also take cont_list, if there is none.
2723 * Don't do this for the matchgroup of a start or end pattern.
2724 */
2725 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2726 {
2727 if (idx == 0)
2728 {
2729 sip->si_attr = 0;
2730 sip->si_trans_id = 0;
2731 if (sip->si_cont_list == NULL)
2732 sip->si_cont_list = ID_LIST_ALL;
2733 }
2734 else
2735 {
2736 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2737 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002738 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2739 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002740 if (sip->si_cont_list == NULL)
2741 {
2742 sip->si_flags |= HL_TRANS_CONT;
2743 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2744 }
2745 }
2746 }
2747}
2748
2749/*
2750 * Check the current stack for patterns with "keepend" flag.
2751 * Propagate the match-end to contained items, until a "skipend" item is found.
2752 */
2753 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002754check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755{
2756 int i;
2757 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002758 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759 stateitem_T *sip;
2760
2761 /*
2762 * This check can consume a lot of time; only do it from the level where
2763 * there really is a keepend.
2764 */
2765 if (keepend_level < 0)
2766 return;
2767
2768 /*
2769 * Find the last index of an "extend" item. "keepend" items before that
2770 * won't do anything. If there is no "extend" item "i" will be
2771 * "keepend_level" and all "keepend" items will work normally.
2772 */
2773 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2774 if (CUR_STATE(i).si_flags & HL_EXTEND)
2775 break;
2776
2777 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002778 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002779 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002780 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002781 for ( ; i < current_state.ga_len; ++i)
2782 {
2783 sip = &CUR_STATE(i);
2784 if (maxpos.lnum != 0)
2785 {
2786 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002787 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2789 sip->si_ends = TRUE;
2790 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002791 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2792 {
2793 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794 || maxpos.lnum > sip->si_m_endpos.lnum
2795 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002796 && maxpos.col > sip->si_m_endpos.col))
2797 maxpos = sip->si_m_endpos;
2798 if (maxpos_h.lnum == 0
2799 || maxpos_h.lnum > sip->si_h_endpos.lnum
2800 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2801 && maxpos_h.col > sip->si_h_endpos.col))
2802 maxpos_h = sip->si_h_endpos;
2803 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 }
2805}
2806
2807/*
2808 * Update an entry in the current_state stack for a start-skip-end pattern.
2809 * This finds the end of the current item, if it's in the current line.
2810 *
2811 * Return the flags for the matched END.
2812 */
2813 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002814update_si_end(
2815 stateitem_T *sip,
2816 int startcol, /* where to start searching for the end */
2817 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818{
2819 lpos_T startpos;
2820 lpos_T endpos;
2821 lpos_T hl_endpos;
2822 lpos_T end_endpos;
2823 int end_idx;
2824
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002825 /* return quickly for a keyword */
2826 if (sip->si_idx < 0)
2827 return;
2828
Bram Moolenaar071d4272004-06-13 20:20:40 +00002829 /* Don't update when it's already done. Can be a match of an end pattern
2830 * that started in a previous line. Watch out: can also be a "keepend"
2831 * from a containing item. */
2832 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2833 return;
2834
2835 /*
2836 * We need to find the end of the region. It may continue in the next
2837 * line.
2838 */
2839 end_idx = 0;
2840 startpos.lnum = current_lnum;
2841 startpos.col = startcol;
2842 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2843 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2844
2845 if (endpos.lnum == 0)
2846 {
2847 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002848 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002849 {
2850 /* a "oneline" never continues in the next line */
2851 sip->si_ends = TRUE;
2852 sip->si_m_endpos.lnum = current_lnum;
2853 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2854 }
2855 else
2856 {
2857 /* continues in the next line */
2858 sip->si_ends = FALSE;
2859 sip->si_m_endpos.lnum = 0;
2860 }
2861 sip->si_h_endpos = sip->si_m_endpos;
2862 }
2863 else
2864 {
2865 /* match within this line */
2866 sip->si_m_endpos = endpos;
2867 sip->si_h_endpos = hl_endpos;
2868 sip->si_eoe_pos = end_endpos;
2869 sip->si_ends = TRUE;
2870 sip->si_end_idx = end_idx;
2871 }
2872}
2873
2874/*
2875 * Add a new state to the current state stack.
2876 * It is cleared and the index set to "idx".
2877 * Return FAIL if it's not possible (out of memory).
2878 */
2879 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002880push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881{
2882 if (ga_grow(&current_state, 1) == FAIL)
2883 return FAIL;
2884 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2885 CUR_STATE(current_state.ga_len).si_idx = idx;
2886 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 return OK;
2888}
2889
2890/*
2891 * Remove a state from the current_state stack.
2892 */
2893 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002894pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895{
2896 if (current_state.ga_len)
2897 {
2898 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2899 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 }
2901 /* after the end of a pattern, try matching a keyword or pattern */
2902 next_match_idx = -1;
2903
2904 /* if first state with "keepend" is popped, reset keepend_level */
2905 if (keepend_level >= current_state.ga_len)
2906 keepend_level = -1;
2907}
2908
2909/*
2910 * Find the end of a start/skip/end syntax region after "startpos".
2911 * Only checks one line.
2912 * Also handles a match item that continued from a previous line.
2913 * If not found, the syntax item continues in the next line. m_endpos->lnum
2914 * will be 0.
2915 * If found, the end of the region and the end of the highlighting is
2916 * computed.
2917 */
2918 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002919find_endpos(
2920 int idx, /* index of the pattern */
2921 lpos_T *startpos, /* where to start looking for an END match */
2922 lpos_T *m_endpos, /* return: end of match */
2923 lpos_T *hl_endpos, /* return: end of highlighting */
2924 long *flagsp, /* return: flags of matching END */
2925 lpos_T *end_endpos, /* return: end of end pattern match */
2926 int *end_idx, /* return: group ID for end pat. match, or 0 */
2927 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928{
2929 colnr_T matchcol;
2930 synpat_T *spp, *spp_skip;
2931 int start_idx;
2932 int best_idx;
2933 regmmatch_T regmatch;
2934 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2935 lpos_T pos;
2936 char_u *line;
2937 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002938 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002940 /* just in case we are invoked for a keyword */
2941 if (idx < 0)
2942 return;
2943
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 /*
2945 * Check for being called with a START pattern.
2946 * Can happen with a match that continues to the next line, because it
2947 * contained a region.
2948 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002949 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 if (spp->sp_type != SPTYPE_START)
2951 {
2952 *hl_endpos = *startpos;
2953 return;
2954 }
2955
2956 /*
2957 * Find the SKIP or first END pattern after the last START pattern.
2958 */
2959 for (;;)
2960 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002961 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002962 if (spp->sp_type != SPTYPE_START)
2963 break;
2964 ++idx;
2965 }
2966
2967 /*
2968 * Lookup the SKIP pattern (if present)
2969 */
2970 if (spp->sp_type == SPTYPE_SKIP)
2971 {
2972 spp_skip = spp;
2973 ++idx;
2974 }
2975 else
2976 spp_skip = NULL;
2977
2978 /* Setup external matches for syn_regexec(). */
2979 unref_extmatch(re_extmatch_in);
2980 re_extmatch_in = ref_extmatch(start_ext);
2981
2982 matchcol = startpos->col; /* start looking for a match at sstart */
2983 start_idx = idx; /* remember the first END pattern. */
2984 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002985
2986 /* use syntax iskeyword option */
2987 save_chartab(buf_chartab);
2988
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989 for (;;)
2990 {
2991 /*
2992 * Find end pattern that matches first after "matchcol".
2993 */
2994 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002995 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996 {
2997 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002998 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999
Bram Moolenaar860cae12010-06-05 23:22:07 +02003000 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3002 break;
3003 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3004 if (lc_col < 0)
3005 lc_col = 0;
3006
3007 regmatch.rmm_ic = spp->sp_ic;
3008 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003009 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3010 IF_SYN_TIME(&spp->sp_time));
3011 spp->sp_prog = regmatch.regprog;
3012 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 {
3014 if (best_idx == -1 || regmatch.startpos[0].col
3015 < best_regmatch.startpos[0].col)
3016 {
3017 best_idx = idx;
3018 best_regmatch.startpos[0] = regmatch.startpos[0];
3019 best_regmatch.endpos[0] = regmatch.endpos[0];
3020 }
3021 }
3022 }
3023
3024 /*
3025 * If all end patterns have been tried, and there is no match, the
3026 * item continues until end-of-line.
3027 */
3028 if (best_idx == -1)
3029 break;
3030
3031 /*
3032 * If the skip pattern matches before the end pattern,
3033 * continue searching after the skip pattern.
3034 */
3035 if (spp_skip != NULL)
3036 {
3037 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003038 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039
3040 if (lc_col < 0)
3041 lc_col = 0;
3042 regmatch.rmm_ic = spp_skip->sp_ic;
3043 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003044 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3045 IF_SYN_TIME(&spp_skip->sp_time));
3046 spp_skip->sp_prog = regmatch.regprog;
3047 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003048 <= best_regmatch.startpos[0].col)
3049 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003050 int line_len;
3051
Bram Moolenaar071d4272004-06-13 20:20:40 +00003052 /* Add offset to skip pattern match */
3053 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3054
3055 /* If the skip pattern goes on to the next line, there is no
3056 * match with an end pattern in this line. */
3057 if (pos.lnum > startpos->lnum)
3058 break;
3059
3060 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003061 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062
3063 /* take care of an empty match or negative offset */
3064 if (pos.col <= matchcol)
3065 ++matchcol;
3066 else if (pos.col <= regmatch.endpos[0].col)
3067 matchcol = pos.col;
3068 else
3069 /* Be careful not to jump over the NUL at the end-of-line */
3070 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003071 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 ++matchcol)
3073 ;
3074
3075 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003076 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077 break;
3078
3079 continue; /* start with first end pattern again */
3080 }
3081 }
3082
3083 /*
3084 * Match from start pattern to end pattern.
3085 * Correct for match and highlight offset of end pattern.
3086 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003087 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3089 /* can't end before the start */
3090 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3091 m_endpos->col = startpos->col;
3092
3093 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3094 /* can't end before the start */
3095 if (end_endpos->lnum == startpos->lnum
3096 && end_endpos->col < startpos->col)
3097 end_endpos->col = startpos->col;
3098 /* can't end after the match */
3099 limit_pos(end_endpos, m_endpos);
3100
3101 /*
3102 * If the end group is highlighted differently, adjust the pointers.
3103 */
3104 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3105 {
3106 *end_idx = best_idx;
3107 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3108 {
3109 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3110 hl_endpos->col = best_regmatch.endpos[0].col;
3111 }
3112 else
3113 {
3114 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3115 hl_endpos->col = best_regmatch.startpos[0].col;
3116 }
3117 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3118
3119 /* can't end before the start */
3120 if (hl_endpos->lnum == startpos->lnum
3121 && hl_endpos->col < startpos->col)
3122 hl_endpos->col = startpos->col;
3123 limit_pos(hl_endpos, m_endpos);
3124
3125 /* now the match ends where the highlighting ends, it is turned
3126 * into the matchgroup for the end */
3127 *m_endpos = *hl_endpos;
3128 }
3129 else
3130 {
3131 *end_idx = 0;
3132 *hl_endpos = *end_endpos;
3133 }
3134
3135 *flagsp = spp->sp_flags;
3136
3137 had_match = TRUE;
3138 break;
3139 }
3140
3141 /* no match for an END pattern in this line */
3142 if (!had_match)
3143 m_endpos->lnum = 0;
3144
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003145 restore_chartab(buf_chartab);
3146
Bram Moolenaar071d4272004-06-13 20:20:40 +00003147 /* Remove external matches. */
3148 unref_extmatch(re_extmatch_in);
3149 re_extmatch_in = NULL;
3150}
3151
3152/*
3153 * Limit "pos" not to be after "limit".
3154 */
3155 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003156limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157{
3158 if (pos->lnum > limit->lnum)
3159 *pos = *limit;
3160 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3161 pos->col = limit->col;
3162}
3163
3164/*
3165 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3166 */
3167 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003168limit_pos_zero(
3169 lpos_T *pos,
3170 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171{
3172 if (pos->lnum == 0)
3173 *pos = *limit;
3174 else
3175 limit_pos(pos, limit);
3176}
3177
3178/*
3179 * Add offset to matched text for end of match or highlight.
3180 */
3181 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003182syn_add_end_off(
3183 lpos_T *result, /* returned position */
3184 regmmatch_T *regmatch, /* start/end of match */
3185 synpat_T *spp, /* matched pattern */
3186 int idx, /* index of offset */
3187 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188{
3189 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003190 int off;
3191 char_u *base;
3192 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193
3194 if (spp->sp_off_flags & (1 << idx))
3195 {
3196 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003197 col = regmatch->startpos[0].col;
3198 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199 }
3200 else
3201 {
3202 result->lnum = regmatch->endpos[0].lnum;
3203 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003204 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003206 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3207 * is a matchgroup. Watch out for match with last NL in the buffer. */
3208 if (result->lnum > syn_buf->b_ml.ml_line_count)
3209 col = 0;
3210 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003211 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003212 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3213 p = base + col;
3214 if (off > 0)
3215 {
3216 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003217 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003218 }
3219 else if (off < 0)
3220 {
3221 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003222 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003223 }
3224 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003225 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003226 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227}
3228
3229/*
3230 * Add offset to matched text for start of match or highlight.
3231 * Avoid resulting column to become negative.
3232 */
3233 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003234syn_add_start_off(
3235 lpos_T *result, /* returned position */
3236 regmmatch_T *regmatch, /* start/end of match */
3237 synpat_T *spp,
3238 int idx,
3239 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240{
3241 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003242 int off;
3243 char_u *base;
3244 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245
3246 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3247 {
3248 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003249 col = regmatch->endpos[0].col;
3250 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 }
3252 else
3253 {
3254 result->lnum = regmatch->startpos[0].lnum;
3255 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003256 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003258 if (result->lnum > syn_buf->b_ml.ml_line_count)
3259 {
3260 /* a "\n" at the end of the pattern may take us below the last line */
3261 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003262 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003263 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003264 if (off != 0)
3265 {
3266 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3267 p = base + col;
3268 if (off > 0)
3269 {
3270 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003271 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003272 }
3273 else if (off < 0)
3274 {
3275 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003276 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003277 }
3278 col = (int)(p - base);
3279 }
3280 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003281}
3282
3283/*
3284 * Get current line in syntax buffer.
3285 */
3286 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003287syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288{
3289 return ml_get_buf(syn_buf, current_lnum, FALSE);
3290}
3291
3292/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003293 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294 * Returns TRUE when there is a match.
3295 */
3296 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003297syn_regexec(
3298 regmmatch_T *rmp,
3299 linenr_T lnum,
3300 colnr_T col,
3301 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003303 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003304#ifdef FEAT_RELTIME
3305 int timed_out = FALSE;
3306#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003307#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003308 proftime_T pt;
3309
3310 if (syn_time_on)
3311 profile_start(&pt);
3312#endif
3313
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003314 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003315 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3316#ifdef FEAT_RELTIME
3317 syn_tm, &timed_out
3318#else
3319 NULL, NULL
3320#endif
3321 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003322
Bram Moolenaarf7512552013-06-06 14:55:19 +02003323#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003324 if (syn_time_on)
3325 {
3326 profile_end(&pt);
3327 profile_add(&st->total, &pt);
3328 if (profile_cmp(&pt, &st->slowest) < 0)
3329 st->slowest = pt;
3330 ++st->count;
3331 if (r > 0)
3332 ++st->match;
3333 }
3334#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003335#ifdef FEAT_RELTIME
3336 if (timed_out)
3337 syn_win->w_s->b_syn_slow = TRUE;
3338#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003339
3340 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 {
3342 rmp->startpos[0].lnum += lnum;
3343 rmp->endpos[0].lnum += lnum;
3344 return TRUE;
3345 }
3346 return FALSE;
3347}
3348
3349/*
3350 * Check one position in a line for a matching keyword.
3351 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003352 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353 */
3354 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003355check_keyword_id(
3356 char_u *line,
3357 int startcol, /* position in line to check for keyword */
3358 int *endcolp, /* return: character after found keyword */
3359 long *flagsp, /* return: flags of matching keyword */
3360 short **next_listp, /* return: next_list of matching keyword */
3361 stateitem_T *cur_si, /* item at the top of the stack */
3362 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003364 keyentry_T *kp;
3365 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003367 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003369 hashtab_T *ht;
3370 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371
3372 /* Find first character after the keyword. First character was already
3373 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003374 kwp = line + startcol;
3375 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376 do
3377 {
3378#ifdef FEAT_MBYTE
3379 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003380 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 else
3382#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003383 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003385 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386
Bram Moolenaardad6b692005-01-25 22:14:34 +00003387 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 return 0;
3389
3390 /*
3391 * Must make a copy of the keyword, so we can add a NUL and make it
3392 * lowercase.
3393 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003394 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395
3396 /*
3397 * Try twice:
3398 * 1. matching case
3399 * 2. ignoring case
3400 */
3401 for (round = 1; round <= 2; ++round)
3402 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003403 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003404 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003405 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003406 if (round == 2) /* ignore case */
3407 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003408
3409 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003410 * Find keywords that match. There can be several with different
3411 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412 * When current_next_list is non-zero accept only that group, otherwise:
3413 * Accept a not-contained keyword at toplevel.
3414 * Accept a keyword at other levels only if it is in the contains list.
3415 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003416 hi = hash_find(ht, keyword);
3417 if (!HASHITEM_EMPTY(hi))
3418 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003419 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003420 if (current_next_list != 0
3421 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3422 : (cur_si == NULL
3423 ? !(kp->flags & HL_CONTAINED)
3424 : in_id_list(cur_si, cur_si->si_cont_list,
3425 &kp->k_syn, kp->flags & HL_CONTAINED)))
3426 {
3427 *endcolp = startcol + kwlen;
3428 *flagsp = kp->flags;
3429 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003430#ifdef FEAT_CONCEAL
3431 *ccharp = kp->k_char;
3432#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003433 return kp->k_syn.id;
3434 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003435 }
3436 }
3437 return 0;
3438}
3439
3440/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003441 * Handle ":syntax conceal" command.
3442 */
3443 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003444syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003445{
3446#ifdef FEAT_CONCEAL
3447 char_u *arg = eap->arg;
3448 char_u *next;
3449
3450 eap->nextcmd = find_nextcmd(arg);
3451 if (eap->skip)
3452 return;
3453
3454 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003455 if (*arg == NUL)
3456 {
3457 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003458 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003459 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003460 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003461 }
3462 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003463 curwin->w_s->b_syn_conceal = TRUE;
3464 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3465 curwin->w_s->b_syn_conceal = FALSE;
3466 else
3467 EMSG2(_("E390: Illegal argument: %s"), arg);
3468#endif
3469}
3470
3471/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003472 * Handle ":syntax case" command.
3473 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003475syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476{
3477 char_u *arg = eap->arg;
3478 char_u *next;
3479
3480 eap->nextcmd = find_nextcmd(arg);
3481 if (eap->skip)
3482 return;
3483
3484 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003485 if (*arg == NUL)
3486 {
3487 if (curwin->w_s->b_syn_ic)
3488 MSG(_("syntax case ignore"));
3489 else
3490 MSG(_("syntax case match"));
3491 }
3492 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003495 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 else
3497 EMSG2(_("E390: Illegal argument: %s"), arg);
3498}
3499
3500/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003501 * Handle ":syntax spell" command.
3502 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003503 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003504syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003505{
3506 char_u *arg = eap->arg;
3507 char_u *next;
3508
3509 eap->nextcmd = find_nextcmd(arg);
3510 if (eap->skip)
3511 return;
3512
3513 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003514 if (*arg == NUL)
3515 {
3516 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3517 MSG(_("syntax spell toplevel"));
3518 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3519 MSG(_("syntax spell notoplevel"));
3520 else
3521 MSG(_("syntax spell default"));
3522 }
3523 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003524 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003525 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003526 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003527 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003528 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003529 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003530 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003531 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003532 return;
3533 }
3534
3535 /* assume spell checking changed, force a redraw */
3536 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003537}
3538
3539/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003540 * Handle ":syntax iskeyword" command.
3541 */
3542 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003543syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003544{
3545 char_u *arg = eap->arg;
3546 char_u save_chartab[32];
3547 char_u *save_isk;
3548
3549 if (eap->skip)
3550 return;
3551
3552 arg = skipwhite(arg);
3553 if (*arg == NUL)
3554 {
3555 MSG_PUTS("\n");
3556 MSG_PUTS(_("syntax iskeyword "));
3557 if (curwin->w_s->b_syn_isk != empty_option)
3558 msg_outtrans(curwin->w_s->b_syn_isk);
3559 else
3560 msg_outtrans((char_u *)"not set");
3561 }
3562 else
3563 {
3564 if (STRNICMP(arg, "clear", 5) == 0)
3565 {
3566 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3567 (size_t)32);
3568 clear_string_option(&curwin->w_s->b_syn_isk);
3569 }
3570 else
3571 {
3572 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3573 save_isk = curbuf->b_p_isk;
3574 curbuf->b_p_isk = vim_strsave(arg);
3575
3576 buf_init_chartab(curbuf, FALSE);
3577 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3578 (size_t)32);
3579 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3580 clear_string_option(&curwin->w_s->b_syn_isk);
3581 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3582 curbuf->b_p_isk = save_isk;
3583 }
3584 }
3585 redraw_win_later(curwin, NOT_VALID);
3586}
3587
3588/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589 * Clear all syntax info for one buffer.
3590 */
3591 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003592syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003593{
3594 int i;
3595
Bram Moolenaar860cae12010-06-05 23:22:07 +02003596 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003597#ifdef FEAT_RELTIME
3598 block->b_syn_slow = FALSE; /* clear previous timeout */
3599#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003600 block->b_syn_ic = FALSE; /* Use case, by default */
3601 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3602 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003603#ifdef FEAT_CONCEAL
3604 block->b_syn_conceal = FALSE;
3605#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606
3607 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003608 clear_keywtab(&block->b_keywtab);
3609 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610
3611 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003612 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3613 syn_clear_pattern(block, i);
3614 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615
3616 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3618 syn_clear_cluster(block, i);
3619 ga_clear(&block->b_syn_clusters);
3620 block->b_spell_cluster_id = 0;
3621 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003622
Bram Moolenaar860cae12010-06-05 23:22:07 +02003623 block->b_syn_sync_flags = 0;
3624 block->b_syn_sync_minlines = 0;
3625 block->b_syn_sync_maxlines = 0;
3626 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627
Bram Moolenaar473de612013-06-08 18:19:48 +02003628 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003629 block->b_syn_linecont_prog = NULL;
3630 vim_free(block->b_syn_linecont_pat);
3631 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003633 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003635 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636
3637 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003640
3641 /* Reset the counter for ":syn include" */
3642 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643}
3644
3645/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003646 * Get rid of ownsyntax for window "wp".
3647 */
3648 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003649reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003650{
3651 if (wp->w_s != &wp->w_buffer->b_s)
3652 {
3653 syntax_clear(wp->w_s);
3654 vim_free(wp->w_s);
3655 wp->w_s = &wp->w_buffer->b_s;
3656 }
3657}
3658
3659/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660 * Clear syncing info for one buffer.
3661 */
3662 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003663syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664{
3665 int i;
3666
3667 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003668 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3669 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3670 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671
Bram Moolenaar860cae12010-06-05 23:22:07 +02003672 curwin->w_s->b_syn_sync_flags = 0;
3673 curwin->w_s->b_syn_sync_minlines = 0;
3674 curwin->w_s->b_syn_sync_maxlines = 0;
3675 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676
Bram Moolenaar473de612013-06-08 18:19:48 +02003677 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003678 curwin->w_s->b_syn_linecont_prog = NULL;
3679 vim_free(curwin->w_s->b_syn_linecont_pat);
3680 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003681 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003683 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684}
3685
3686/*
3687 * Remove one pattern from the buffer's pattern list.
3688 */
3689 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003690syn_remove_pattern(
3691 synblock_T *block,
3692 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693{
3694 synpat_T *spp;
3695
Bram Moolenaar860cae12010-06-05 23:22:07 +02003696 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697#ifdef FEAT_FOLDING
3698 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003699 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003701 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003703 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3704 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705}
3706
3707/*
3708 * Clear and free one syntax pattern. When clearing all, must be called from
3709 * last to first!
3710 */
3711 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003712syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003714 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003715 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003719 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3720 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3721 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722 }
3723}
3724
3725/*
3726 * Clear and free one syntax cluster.
3727 */
3728 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003729syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003731 vim_free(SYN_CLSTR(block)[i].scl_name);
3732 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3733 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734}
3735
3736/*
3737 * Handle ":syntax clear" command.
3738 */
3739 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003740syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741{
3742 char_u *arg = eap->arg;
3743 char_u *arg_end;
3744 int id;
3745
3746 eap->nextcmd = find_nextcmd(arg);
3747 if (eap->skip)
3748 return;
3749
3750 /*
3751 * We have to disable this within ":syn include @group filename",
3752 * because otherwise @group would get deleted.
3753 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3754 * clear".
3755 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003756 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003757 return;
3758
3759 if (ends_excmd(*arg))
3760 {
3761 /*
3762 * No argument: Clear all syntax items.
3763 */
3764 if (syncing)
3765 syntax_sync_clear();
3766 else
3767 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003768 syntax_clear(curwin->w_s);
3769 if (curwin->w_s == &curwin->w_buffer->b_s)
3770 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003771 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772 }
3773 }
3774 else
3775 {
3776 /*
3777 * Clear the group IDs that are in the argument.
3778 */
3779 while (!ends_excmd(*arg))
3780 {
3781 arg_end = skiptowhite(arg);
3782 if (*arg == '@')
3783 {
3784 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3785 if (id == 0)
3786 {
3787 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3788 break;
3789 }
3790 else
3791 {
3792 /*
3793 * We can't physically delete a cluster without changing
3794 * the IDs of other clusters, so we do the next best thing
3795 * and make it empty.
3796 */
3797 short scl_id = id - SYNID_CLUSTER;
3798
Bram Moolenaar860cae12010-06-05 23:22:07 +02003799 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3800 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801 }
3802 }
3803 else
3804 {
3805 id = syn_namen2id(arg, (int)(arg_end - arg));
3806 if (id == 0)
3807 {
3808 EMSG2(_(e_nogroup), arg);
3809 break;
3810 }
3811 else
3812 syn_clear_one(id, syncing);
3813 }
3814 arg = skipwhite(arg_end);
3815 }
3816 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003817 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003818 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819}
3820
3821/*
3822 * Clear one syntax group for the current buffer.
3823 */
3824 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003825syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826{
3827 synpat_T *spp;
3828 int idx;
3829
3830 /* Clear keywords only when not ":syn sync clear group-name" */
3831 if (!syncing)
3832 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003833 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3834 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835 }
3836
3837 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003838 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003840 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3842 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003843 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 }
3845}
3846
3847/*
3848 * Handle ":syntax on" command.
3849 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003851syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852{
3853 syn_cmd_onoff(eap, "syntax");
3854}
3855
3856/*
3857 * Handle ":syntax enable" command.
3858 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003860syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003861{
3862 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3863 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003864 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865}
3866
3867/*
3868 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003869 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003872syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873{
3874 eap->nextcmd = check_nextcmd(eap->arg);
3875 if (!eap->skip)
3876 {
3877 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3878 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003879 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880 }
3881}
3882
3883/*
3884 * Handle ":syntax manual" command.
3885 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003887syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888{
3889 syn_cmd_onoff(eap, "manual");
3890}
3891
3892/*
3893 * Handle ":syntax off" command.
3894 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003896syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003897{
3898 syn_cmd_onoff(eap, "nosyntax");
3899}
3900
3901 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003902syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903{
3904 char_u buf[100];
3905
3906 eap->nextcmd = check_nextcmd(eap->arg);
3907 if (!eap->skip)
3908 {
3909 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003910 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 do_cmdline_cmd(buf);
3912 }
3913}
3914
3915/*
3916 * Handle ":syntax [list]" command: list current syntax words.
3917 */
3918 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003919syn_cmd_list(
3920 exarg_T *eap,
3921 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922{
3923 char_u *arg = eap->arg;
3924 int id;
3925 char_u *arg_end;
3926
3927 eap->nextcmd = find_nextcmd(arg);
3928 if (eap->skip)
3929 return;
3930
Bram Moolenaar860cae12010-06-05 23:22:07 +02003931 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003933 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934 return;
3935 }
3936
3937 if (syncing)
3938 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003939 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003940 {
3941 MSG_PUTS(_("syncing on C-style comments"));
3942 syn_lines_msg();
3943 syn_match_msg();
3944 return;
3945 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003946 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003948 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 MSG_PUTS(_("no syncing"));
3950 else
3951 {
3952 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003953 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 MSG_PUTS(_(" lines before top line"));
3955 syn_match_msg();
3956 }
3957 return;
3958 }
3959 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003960 if (curwin->w_s->b_syn_sync_minlines > 0
3961 || curwin->w_s->b_syn_sync_maxlines > 0
3962 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 {
3964 MSG_PUTS(_("\nsyncing on items"));
3965 syn_lines_msg();
3966 syn_match_msg();
3967 }
3968 }
3969 else
3970 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3971 if (ends_excmd(*arg))
3972 {
3973 /*
3974 * No argument: List all group IDs and all syntax clusters.
3975 */
3976 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3977 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003978 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979 syn_list_cluster(id);
3980 }
3981 else
3982 {
3983 /*
3984 * List the group IDs and syntax clusters that are in the argument.
3985 */
3986 while (!ends_excmd(*arg) && !got_int)
3987 {
3988 arg_end = skiptowhite(arg);
3989 if (*arg == '@')
3990 {
3991 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3992 if (id == 0)
3993 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3994 else
3995 syn_list_cluster(id - SYNID_CLUSTER);
3996 }
3997 else
3998 {
3999 id = syn_namen2id(arg, (int)(arg_end - arg));
4000 if (id == 0)
4001 EMSG2(_(e_nogroup), arg);
4002 else
4003 syn_list_one(id, syncing, TRUE);
4004 }
4005 arg = skipwhite(arg_end);
4006 }
4007 }
4008 eap->nextcmd = check_nextcmd(arg);
4009}
4010
4011 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004012syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004013{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004014 if (curwin->w_s->b_syn_sync_maxlines > 0
4015 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 {
4017 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004018 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 {
4020 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004021 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4022 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023 MSG_PUTS(", ");
4024 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004025 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 {
4027 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004028 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 }
4030 MSG_PUTS(_(" lines before top line"));
4031 }
4032}
4033
4034 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004035syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004036{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004037 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 {
4039 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004040 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041 MSG_PUTS(_(" line breaks"));
4042 }
4043}
4044
4045static int last_matchgroup;
4046
4047struct name_list
4048{
4049 int flag;
4050 char *name;
4051};
4052
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004053static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054
4055/*
4056 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4057 */
4058 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004059syn_list_one(
4060 int id,
4061 int syncing, /* when TRUE: list syncing items */
4062 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063{
4064 int attr;
4065 int idx;
4066 int did_header = FALSE;
4067 synpat_T *spp;
4068 static struct name_list namelist1[] =
4069 {
4070 {HL_DISPLAY, "display"},
4071 {HL_CONTAINED, "contained"},
4072 {HL_ONELINE, "oneline"},
4073 {HL_KEEPEND, "keepend"},
4074 {HL_EXTEND, "extend"},
4075 {HL_EXCLUDENL, "excludenl"},
4076 {HL_TRANSP, "transparent"},
4077 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004078#ifdef FEAT_CONCEAL
4079 {HL_CONCEAL, "conceal"},
4080 {HL_CONCEALENDS, "concealends"},
4081#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004082 {0, NULL}
4083 };
4084 static struct name_list namelist2[] =
4085 {
4086 {HL_SKIPWHITE, "skipwhite"},
4087 {HL_SKIPNL, "skipnl"},
4088 {HL_SKIPEMPTY, "skipempty"},
4089 {0, NULL}
4090 };
4091
Bram Moolenaar8820b482017-03-16 17:23:31 +01004092 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093
4094 /* list the keywords for "id" */
4095 if (!syncing)
4096 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004097 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4098 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 did_header, attr);
4100 }
4101
4102 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004103 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004105 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4107 continue;
4108
4109 (void)syn_list_header(did_header, 999, id);
4110 did_header = TRUE;
4111 last_matchgroup = 0;
4112 if (spp->sp_type == SPTYPE_MATCH)
4113 {
4114 put_pattern("match", ' ', spp, attr);
4115 msg_putchar(' ');
4116 }
4117 else if (spp->sp_type == SPTYPE_START)
4118 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004119 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4120 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4121 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4122 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4123 while (idx < curwin->w_s->b_syn_patterns.ga_len
4124 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4125 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 --idx;
4127 msg_putchar(' ');
4128 }
4129 syn_list_flags(namelist1, spp->sp_flags, attr);
4130
4131 if (spp->sp_cont_list != NULL)
4132 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4133
4134 if (spp->sp_syn.cont_in_list != NULL)
4135 put_id_list((char_u *)"containedin",
4136 spp->sp_syn.cont_in_list, attr);
4137
4138 if (spp->sp_next_list != NULL)
4139 {
4140 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4141 syn_list_flags(namelist2, spp->sp_flags, attr);
4142 }
4143 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4144 {
4145 if (spp->sp_flags & HL_SYNC_HERE)
4146 msg_puts_attr((char_u *)"grouphere", attr);
4147 else
4148 msg_puts_attr((char_u *)"groupthere", attr);
4149 msg_putchar(' ');
4150 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004151 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4153 else
4154 MSG_PUTS("NONE");
4155 msg_putchar(' ');
4156 }
4157 }
4158
4159 /* list the link, if there is one */
4160 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4161 {
4162 (void)syn_list_header(did_header, 999, id);
4163 msg_puts_attr((char_u *)"links to", attr);
4164 msg_putchar(' ');
4165 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4166 }
4167}
4168
4169 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004170syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171{
4172 int i;
4173
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004174 for (i = 0; nlist[i].flag != 0; ++i)
4175 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004177 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 msg_putchar(' ');
4179 }
4180}
4181
4182/*
4183 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4184 */
4185 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004186syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187{
4188 int endcol = 15;
4189
4190 /* slight hack: roughly duplicate the guts of syn_list_header() */
4191 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004192 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193
4194 if (msg_col >= endcol) /* output at least one space */
4195 endcol = msg_col + 1;
4196 if (Columns <= endcol) /* avoid hang for tiny window */
4197 endcol = Columns - 1;
4198
4199 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004200 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004202 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004203 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204 }
4205 else
4206 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004207 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004208 msg_puts((char_u *)"=NONE");
4209 }
4210}
4211
4212 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004213put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214{
4215 short *p;
4216
4217 msg_puts_attr(name, attr);
4218 msg_putchar('=');
4219 for (p = list; *p; ++p)
4220 {
4221 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4222 {
4223 if (p[1])
4224 MSG_PUTS("ALLBUT");
4225 else
4226 MSG_PUTS("ALL");
4227 }
4228 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4229 {
4230 MSG_PUTS("TOP");
4231 }
4232 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4233 {
4234 MSG_PUTS("CONTAINED");
4235 }
4236 else if (*p >= SYNID_CLUSTER)
4237 {
4238 short scl_id = *p - SYNID_CLUSTER;
4239
4240 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004241 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 }
4243 else
4244 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4245 if (p[1])
4246 msg_putchar(',');
4247 }
4248 msg_putchar(' ');
4249}
4250
4251 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004252put_pattern(
4253 char *s,
4254 int c,
4255 synpat_T *spp,
4256 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257{
4258 long n;
4259 int mask;
4260 int first;
4261 static char *sepchars = "/+=-#@\"|'^&";
4262 int i;
4263
4264 /* May have to write "matchgroup=group" */
4265 if (last_matchgroup != spp->sp_syn_match_id)
4266 {
4267 last_matchgroup = spp->sp_syn_match_id;
4268 msg_puts_attr((char_u *)"matchgroup", attr);
4269 msg_putchar('=');
4270 if (last_matchgroup == 0)
4271 msg_outtrans((char_u *)"NONE");
4272 else
4273 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4274 msg_putchar(' ');
4275 }
4276
4277 /* output the name of the pattern and an '=' or ' ' */
4278 msg_puts_attr((char_u *)s, attr);
4279 msg_putchar(c);
4280
4281 /* output the pattern, in between a char that is not in the pattern */
4282 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4283 if (sepchars[++i] == NUL)
4284 {
4285 i = 0; /* no good char found, just use the first one */
4286 break;
4287 }
4288 msg_putchar(sepchars[i]);
4289 msg_outtrans(spp->sp_pattern);
4290 msg_putchar(sepchars[i]);
4291
4292 /* output any pattern options */
4293 first = TRUE;
4294 for (i = 0; i < SPO_COUNT; ++i)
4295 {
4296 mask = (1 << i);
4297 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4298 {
4299 if (!first)
4300 msg_putchar(','); /* separate with commas */
4301 msg_puts((char_u *)spo_name_tab[i]);
4302 n = spp->sp_offsets[i];
4303 if (i != SPO_LC_OFF)
4304 {
4305 if (spp->sp_off_flags & mask)
4306 msg_putchar('s');
4307 else
4308 msg_putchar('e');
4309 if (n > 0)
4310 msg_putchar('+');
4311 }
4312 if (n || i == SPO_LC_OFF)
4313 msg_outnum(n);
4314 first = FALSE;
4315 }
4316 }
4317 msg_putchar(' ');
4318}
4319
4320/*
4321 * List or clear the keywords for one syntax group.
4322 * Return TRUE if the header has been printed.
4323 */
4324 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004325syn_list_keywords(
4326 int id,
4327 hashtab_T *ht,
4328 int did_header, /* header has already been printed */
4329 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 hashitem_T *hi;
4333 keyentry_T *kp;
4334 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 int prev_contained = 0;
4336 short *prev_next_list = NULL;
4337 short *prev_cont_in_list = NULL;
4338 int prev_skipnl = 0;
4339 int prev_skipwhite = 0;
4340 int prev_skipempty = 0;
4341
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342 /*
4343 * Unfortunately, this list of keywords is not sorted on alphabet but on
4344 * hash value...
4345 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004346 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004347 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004350 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004351 --todo;
4352 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 if (prev_contained != (kp->flags & HL_CONTAINED)
4357 || prev_skipnl != (kp->flags & HL_SKIPNL)
4358 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4359 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4360 || prev_cont_in_list != kp->k_syn.cont_in_list
4361 || prev_next_list != kp->next_list)
4362 outlen = 9999;
4363 else
4364 outlen = (int)STRLEN(kp->keyword);
4365 /* output "contained" and "nextgroup" on each line */
4366 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 prev_contained = 0;
4369 prev_next_list = NULL;
4370 prev_cont_in_list = NULL;
4371 prev_skipnl = 0;
4372 prev_skipwhite = 0;
4373 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004375 did_header = TRUE;
4376 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004382 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 put_id_list((char_u *)"containedin",
4385 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 if (kp->next_list != prev_next_list)
4390 {
4391 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4392 msg_putchar(' ');
4393 prev_next_list = kp->next_list;
4394 if (kp->flags & HL_SKIPNL)
4395 {
4396 msg_puts_attr((char_u *)"skipnl", attr);
4397 msg_putchar(' ');
4398 prev_skipnl = (kp->flags & HL_SKIPNL);
4399 }
4400 if (kp->flags & HL_SKIPWHITE)
4401 {
4402 msg_puts_attr((char_u *)"skipwhite", attr);
4403 msg_putchar(' ');
4404 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4405 }
4406 if (kp->flags & HL_SKIPEMPTY)
4407 {
4408 msg_puts_attr((char_u *)"skipempty", attr);
4409 msg_putchar(' ');
4410 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4411 }
4412 }
4413 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004414 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 }
4416 }
4417 }
4418
4419 return did_header;
4420}
4421
4422 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004423syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004424{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004425 hashitem_T *hi;
4426 keyentry_T *kp;
4427 keyentry_T *kp_prev;
4428 keyentry_T *kp_next;
4429 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430
Bram Moolenaardad6b692005-01-25 22:14:34 +00004431 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004432 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004433 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004435 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004437 --todo;
4438 kp_prev = NULL;
4439 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004441 if (kp->k_syn.id == id)
4442 {
4443 kp_next = kp->ke_next;
4444 if (kp_prev == NULL)
4445 {
4446 if (kp_next == NULL)
4447 hash_remove(ht, hi);
4448 else
4449 hi->hi_key = KE2HIKEY(kp_next);
4450 }
4451 else
4452 kp_prev->ke_next = kp_next;
4453 vim_free(kp->next_list);
4454 vim_free(kp->k_syn.cont_in_list);
4455 vim_free(kp);
4456 kp = kp_next;
4457 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004459 {
4460 kp_prev = kp;
4461 kp = kp->ke_next;
4462 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 }
4464 }
4465 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004466 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467}
4468
4469/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004470 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 */
4472 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004473clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004475 hashitem_T *hi;
4476 int todo;
4477 keyentry_T *kp;
4478 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004480 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004481 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004483 if (!HASHITEM_EMPTY(hi))
4484 {
4485 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004486 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004488 kp_next = kp->ke_next;
4489 vim_free(kp->next_list);
4490 vim_free(kp->k_syn.cont_in_list);
4491 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004495 hash_clear(ht);
4496 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497}
4498
4499/*
4500 * Add a keyword to the list of keywords.
4501 */
4502 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004503add_keyword(
4504 char_u *name, /* name of keyword */
4505 int id, /* group ID for this keyword */
4506 int flags, /* flags for this keyword */
4507 short *cont_in_list, /* containedin for this keyword */
4508 short *next_list, /* nextgroup for this keyword */
4509 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004511 keyentry_T *kp;
4512 hashtab_T *ht;
4513 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004514 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004515 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004516 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517
Bram Moolenaar860cae12010-06-05 23:22:07 +02004518 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 name_ic = str_foldcase(name, (int)STRLEN(name),
4520 name_folded, MAXKEYWLEN + 1);
4521 else
4522 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004523 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4524 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004526 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004527 kp->k_syn.id = id;
4528 kp->k_syn.inc_tag = current_syn_inc_tag;
4529 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004530 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004531 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004533 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004534 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535
Bram Moolenaar860cae12010-06-05 23:22:07 +02004536 if (curwin->w_s->b_syn_ic)
4537 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004539 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540
Bram Moolenaardad6b692005-01-25 22:14:34 +00004541 hash = hash_hash(kp->keyword);
4542 hi = hash_lookup(ht, kp->keyword, hash);
4543 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004545 /* new keyword, add to hashtable */
4546 kp->ke_next = NULL;
4547 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004549 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004551 /* keyword already exists, prepend to list */
4552 kp->ke_next = HI2KE(hi);
4553 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555}
4556
4557/*
4558 * Get the start and end of the group name argument.
4559 * Return a pointer to the first argument.
4560 * Return NULL if the end of the command was found instead of further args.
4561 */
4562 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004563get_group_name(
4564 char_u *arg, /* start of the argument */
4565 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566{
4567 char_u *rest;
4568
4569 *name_end = skiptowhite(arg);
4570 rest = skipwhite(*name_end);
4571
4572 /*
4573 * Check if there are enough arguments. The first argument may be a
4574 * pattern, where '|' is allowed, so only check for NUL.
4575 */
4576 if (ends_excmd(*arg) || *rest == NUL)
4577 return NULL;
4578 return rest;
4579}
4580
4581/*
4582 * Check for syntax command option arguments.
4583 * This can be called at any place in the list of arguments, and just picks
4584 * out the arguments that are known. Can be called several times in a row to
4585 * collect all options in between other arguments.
4586 * Return a pointer to the next argument (which isn't an option).
4587 * Return NULL for any error;
4588 */
4589 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004590get_syn_options(
4591 char_u *arg, /* next argument to be checked */
4592 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004593 int *conceal_char UNUSED,
4594 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004595{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596 char_u *gname_start, *gname;
4597 int syn_id;
4598 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004599 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600 int i;
4601 int fidx;
4602 static struct flag
4603 {
4604 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004605 int argtype;
4606 int flags;
4607 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4608 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4609 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4610 {"eExXtTeEnNdD", 0, HL_EXTEND},
4611 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4612 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4613 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4614 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4615 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4616 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4617 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4618 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4619 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004620 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4621 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4622 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004623 {"cCoOnNtTaAiInNsS", 1, 0},
4624 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4625 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004627 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628
4629 if (arg == NULL) /* already detected error */
4630 return NULL;
4631
Bram Moolenaar860cae12010-06-05 23:22:07 +02004632#ifdef FEAT_CONCEAL
4633 if (curwin->w_s->b_syn_conceal)
4634 opt->flags |= HL_CONCEAL;
4635#endif
4636
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 for (;;)
4638 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004639 /*
4640 * This is used very often when a large number of keywords is defined.
4641 * Need to skip quickly when no option name is found.
4642 * Also avoid tolower(), it's slow.
4643 */
4644 if (strchr(first_letters, *arg) == NULL)
4645 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646
4647 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4648 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004649 p = flagtab[fidx].name;
4650 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4651 if (arg[len] != p[i] && arg[len] != p[i + 1])
4652 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004653 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004654 || (flagtab[fidx].argtype > 0
4655 ? arg[len] == '='
4656 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004658 if (opt->keyword
4659 && (flagtab[fidx].flags == HL_DISPLAY
4660 || flagtab[fidx].flags == HL_FOLD
4661 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 /* treat "display", "fold" and "extend" as a keyword */
4663 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664 break;
4665 }
4666 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004667 if (fidx < 0) /* no match found */
4668 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004670 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004673 {
4674 EMSG(_("E395: contains argument not accepted here"));
4675 return NULL;
4676 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004677 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 return NULL;
4679 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004680 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004682 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 return NULL;
4684 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004685 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004687 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 return NULL;
4689 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004690 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4691 {
4692#ifdef FEAT_MBYTE
4693 /* cchar=? */
4694 if (has_mbyte)
4695 {
4696# ifdef FEAT_CONCEAL
4697 *conceal_char = mb_ptr2char(arg + 6);
4698# endif
4699 arg += mb_ptr2len(arg + 6) - 1;
4700 }
4701 else
4702#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004703 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004704#ifdef FEAT_CONCEAL
4705 *conceal_char = arg[6];
4706#else
4707 ;
4708#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004709 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004710#ifdef FEAT_CONCEAL
4711 if (!vim_isprintc_strict(*conceal_char))
4712 {
4713 EMSG(_("E844: invalid cchar value"));
4714 return NULL;
4715 }
4716#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004717 arg = skipwhite(arg + 7);
4718 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004719 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004720 {
4721 opt->flags |= flagtab[fidx].flags;
4722 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004724 if (flagtab[fidx].flags == HL_SYNC_HERE
4725 || flagtab[fidx].flags == HL_SYNC_THERE)
4726 {
4727 if (opt->sync_idx == NULL)
4728 {
4729 EMSG(_("E393: group[t]here not accepted here"));
4730 return NULL;
4731 }
4732 gname_start = arg;
4733 arg = skiptowhite(arg);
4734 if (gname_start == arg)
4735 return NULL;
4736 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4737 if (gname == NULL)
4738 return NULL;
4739 if (STRCMP(gname, "NONE") == 0)
4740 *opt->sync_idx = NONE_IDX;
4741 else
4742 {
4743 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004744 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4745 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4746 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004747 {
4748 *opt->sync_idx = i;
4749 break;
4750 }
4751 if (i < 0)
4752 {
4753 EMSG2(_("E394: Didn't find region item for %s"), gname);
4754 vim_free(gname);
4755 return NULL;
4756 }
4757 }
4758
4759 vim_free(gname);
4760 arg = skipwhite(arg);
4761 }
4762#ifdef FEAT_FOLDING
4763 else if (flagtab[fidx].flags == HL_FOLD
4764 && foldmethodIsSyntax(curwin))
4765 /* Need to update folds later. */
4766 foldUpdateAll(curwin);
4767#endif
4768 }
4769 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770
4771 return arg;
4772}
4773
4774/*
4775 * Adjustments to syntax item when declared in a ":syn include"'d file.
4776 * Set the contained flag, and if the item is not already contained, add it
4777 * to the specified top-level group, if any.
4778 */
4779 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004780syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004781{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004782 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783 return;
4784 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004785 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786 {
4787 /* We have to alloc this, because syn_combine_list() will free it. */
4788 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004789 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004790
4791 if (grp_list != NULL)
4792 {
4793 grp_list[0] = id;
4794 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004795 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 CLUSTER_ADD);
4797 }
4798 }
4799}
4800
4801/*
4802 * Handle ":syntax include [@{group-name}] filename" command.
4803 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004805syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806{
4807 char_u *arg = eap->arg;
4808 int sgl_id = 1;
4809 char_u *group_name_end;
4810 char_u *rest;
4811 char_u *errormsg = NULL;
4812 int prev_toplvl_grp;
4813 int prev_syn_inc_tag;
4814 int source = FALSE;
4815
4816 eap->nextcmd = find_nextcmd(arg);
4817 if (eap->skip)
4818 return;
4819
4820 if (arg[0] == '@')
4821 {
4822 ++arg;
4823 rest = get_group_name(arg, &group_name_end);
4824 if (rest == NULL)
4825 {
4826 EMSG((char_u *)_("E397: Filename required"));
4827 return;
4828 }
4829 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004830 if (sgl_id == 0)
4831 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832 /* separate_nextcmd() and expand_filename() depend on this */
4833 eap->arg = rest;
4834 }
4835
4836 /*
4837 * Everything that's left, up to the next command, should be the
4838 * filename to include.
4839 */
4840 eap->argt |= (XFILE | NOSPC);
4841 separate_nextcmd(eap);
4842 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4843 {
4844 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4845 * file. Need to expand the file name first. In other cases
4846 * ":runtime!" is used. */
4847 source = TRUE;
4848 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4849 {
4850 if (errormsg != NULL)
4851 EMSG(errormsg);
4852 return;
4853 }
4854 }
4855
4856 /*
4857 * Save and restore the existing top-level grouplist id and ":syn
4858 * include" tag around the actual inclusion.
4859 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004860 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4861 {
4862 EMSG((char_u *)_("E847: Too many syntax includes"));
4863 return;
4864 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004865 prev_syn_inc_tag = current_syn_inc_tag;
4866 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004867 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4868 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004869 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004870 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004872 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873 current_syn_inc_tag = prev_syn_inc_tag;
4874}
4875
4876/*
4877 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4878 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004880syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881{
4882 char_u *arg = eap->arg;
4883 char_u *group_name_end;
4884 int syn_id;
4885 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004886 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004888 char_u *kw;
4889 syn_opt_arg_T syn_opt_arg;
4890 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892
4893 rest = get_group_name(arg, &group_name_end);
4894
4895 if (rest != NULL)
4896 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004897 if (eap->skip)
4898 syn_id = -1;
4899 else
4900 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004901 if (syn_id != 0)
4902 /* allocate a buffer, for removing backslashes in the keyword */
4903 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904 if (keyword_copy != NULL)
4905 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004906 syn_opt_arg.flags = 0;
4907 syn_opt_arg.keyword = TRUE;
4908 syn_opt_arg.sync_idx = NULL;
4909 syn_opt_arg.has_cont_list = FALSE;
4910 syn_opt_arg.cont_in_list = NULL;
4911 syn_opt_arg.next_list = NULL;
4912
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 /*
4914 * The options given apply to ALL keywords, so all options must be
4915 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004916 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004918 cnt = 0;
4919 p = keyword_copy;
4920 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004922 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4923 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004924 if (rest == NULL || ends_excmd(*rest))
4925 break;
4926 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004927 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004929 if (*rest == '\\' && rest[1] != NUL)
4930 ++rest;
4931 *p++ = *rest++;
4932 }
4933 *p++ = NUL;
4934 ++cnt;
4935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004936
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004937 if (!eap->skip)
4938 {
4939 /* Adjust flags for use of ":syn include". */
4940 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4941
4942 /*
4943 * 2: Add an entry for each keyword.
4944 */
4945 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4946 {
4947 for (p = vim_strchr(kw, '['); ; )
4948 {
4949 if (p != NULL)
4950 *p = NUL;
4951 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004952 syn_opt_arg.cont_in_list,
4953 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004954 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004955 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004956 if (p[1] == NUL)
4957 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004958 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004959 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004960 }
4961 if (p[1] == ']')
4962 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004963 if (p[2] != NUL)
4964 {
4965 EMSG3(_("E890: trailing char after ']': %s]%s"),
4966 kw, &p[2]);
4967 goto error;
4968 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004969 kw = p + 1; /* skip over the "]" */
4970 break;
4971 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004972#ifdef FEAT_MBYTE
4973 if (has_mbyte)
4974 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004975 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004976
4977 mch_memmove(p, p + 1, l);
4978 p += l;
4979 }
4980 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004982 {
4983 p[0] = p[1];
4984 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985 }
4986 }
4987 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004988 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004989error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004991 vim_free(syn_opt_arg.cont_in_list);
4992 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 }
4994 }
4995
4996 if (rest != NULL)
4997 eap->nextcmd = check_nextcmd(rest);
4998 else
4999 EMSG2(_(e_invarg2), arg);
5000
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005001 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005002 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003}
5004
5005/*
5006 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5007 *
5008 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5009 */
5010 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005011syn_cmd_match(
5012 exarg_T *eap,
5013 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005014{
5015 char_u *arg = eap->arg;
5016 char_u *group_name_end;
5017 char_u *rest;
5018 synpat_T item; /* the item found in the line */
5019 int syn_id;
5020 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005021 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005023 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024
5025 /* Isolate the group name, check for validity */
5026 rest = get_group_name(arg, &group_name_end);
5027
5028 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005029 syn_opt_arg.flags = 0;
5030 syn_opt_arg.keyword = FALSE;
5031 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5032 syn_opt_arg.has_cont_list = TRUE;
5033 syn_opt_arg.cont_list = NULL;
5034 syn_opt_arg.cont_in_list = NULL;
5035 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005036 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037
5038 /* get the pattern. */
5039 init_syn_patterns();
5040 vim_memset(&item, 0, sizeof(item));
5041 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005042 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5043 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005044
5045 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005046 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005047
5048 if (rest != NULL) /* all arguments are valid */
5049 {
5050 /*
5051 * Check for trailing command and illegal trailing arguments.
5052 */
5053 eap->nextcmd = check_nextcmd(rest);
5054 if (!ends_excmd(*rest) || eap->skip)
5055 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005056 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057 && (syn_id = syn_check_group(arg,
5058 (int)(group_name_end - arg))) != 0)
5059 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005060 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061 /*
5062 * Store the pattern in the syn_items list
5063 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005064 idx = curwin->w_s->b_syn_patterns.ga_len;
5065 SYN_ITEMS(curwin->w_s)[idx] = item;
5066 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5067 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5068 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5069 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5070 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5071 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5072 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5073 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005074 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005075#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005076 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005077#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005078 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005079 curwin->w_s->b_syn_containedin = TRUE;
5080 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5081 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005082
5083 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005084 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005085 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005086#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005087 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005088 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005089#endif
5090
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005091 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005092 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005093 return; /* don't free the progs and patterns now */
5094 }
5095 }
5096
5097 /*
5098 * Something failed, free the allocated memory.
5099 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005100 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005101 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005102 vim_free(syn_opt_arg.cont_list);
5103 vim_free(syn_opt_arg.cont_in_list);
5104 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005105
5106 if (rest == NULL)
5107 EMSG2(_(e_invarg2), arg);
5108}
5109
5110/*
5111 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5112 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5113 */
5114 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005115syn_cmd_region(
5116 exarg_T *eap,
5117 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118{
5119 char_u *arg = eap->arg;
5120 char_u *group_name_end;
5121 char_u *rest; /* next arg, NULL on error */
5122 char_u *key_end;
5123 char_u *key = NULL;
5124 char_u *p;
5125 int item;
5126#define ITEM_START 0
5127#define ITEM_SKIP 1
5128#define ITEM_END 2
5129#define ITEM_MATCHGROUP 3
5130 struct pat_ptr
5131 {
5132 synpat_T *pp_synp; /* pointer to syn_pattern */
5133 int pp_matchgroup_id; /* matchgroup ID */
5134 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5135 } *(pat_ptrs[3]);
5136 /* patterns found in the line */
5137 struct pat_ptr *ppp;
5138 struct pat_ptr *ppp_next;
5139 int pat_count = 0; /* nr of syn_patterns found */
5140 int syn_id;
5141 int matchgroup_id = 0;
5142 int not_enough = FALSE; /* not enough arguments */
5143 int illegal = FALSE; /* illegal arguments */
5144 int success = FALSE;
5145 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005146 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005147 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005148
5149 /* Isolate the group name, check for validity */
5150 rest = get_group_name(arg, &group_name_end);
5151
5152 pat_ptrs[0] = NULL;
5153 pat_ptrs[1] = NULL;
5154 pat_ptrs[2] = NULL;
5155
5156 init_syn_patterns();
5157
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005158 syn_opt_arg.flags = 0;
5159 syn_opt_arg.keyword = FALSE;
5160 syn_opt_arg.sync_idx = NULL;
5161 syn_opt_arg.has_cont_list = TRUE;
5162 syn_opt_arg.cont_list = NULL;
5163 syn_opt_arg.cont_in_list = NULL;
5164 syn_opt_arg.next_list = NULL;
5165
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166 /*
5167 * get the options, patterns and matchgroup.
5168 */
5169 while (rest != NULL && !ends_excmd(*rest))
5170 {
5171 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005172 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005173 if (rest == NULL || ends_excmd(*rest))
5174 break;
5175
5176 /* must be a pattern or matchgroup then */
5177 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005178 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 ++key_end;
5180 vim_free(key);
5181 key = vim_strnsave_up(rest, (int)(key_end - rest));
5182 if (key == NULL) /* out of memory */
5183 {
5184 rest = NULL;
5185 break;
5186 }
5187 if (STRCMP(key, "MATCHGROUP") == 0)
5188 item = ITEM_MATCHGROUP;
5189 else if (STRCMP(key, "START") == 0)
5190 item = ITEM_START;
5191 else if (STRCMP(key, "END") == 0)
5192 item = ITEM_END;
5193 else if (STRCMP(key, "SKIP") == 0)
5194 {
5195 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5196 {
5197 illegal = TRUE;
5198 break;
5199 }
5200 item = ITEM_SKIP;
5201 }
5202 else
5203 break;
5204 rest = skipwhite(key_end);
5205 if (*rest != '=')
5206 {
5207 rest = NULL;
5208 EMSG2(_("E398: Missing '=': %s"), arg);
5209 break;
5210 }
5211 rest = skipwhite(rest + 1);
5212 if (*rest == NUL)
5213 {
5214 not_enough = TRUE;
5215 break;
5216 }
5217
5218 if (item == ITEM_MATCHGROUP)
5219 {
5220 p = skiptowhite(rest);
5221 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5222 matchgroup_id = 0;
5223 else
5224 {
5225 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5226 if (matchgroup_id == 0)
5227 {
5228 illegal = TRUE;
5229 break;
5230 }
5231 }
5232 rest = skipwhite(p);
5233 }
5234 else
5235 {
5236 /*
5237 * Allocate room for a syn_pattern, and link it in the list of
5238 * syn_patterns for this item, at the start (because the list is
5239 * used from end to start).
5240 */
5241 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5242 if (ppp == NULL)
5243 {
5244 rest = NULL;
5245 break;
5246 }
5247 ppp->pp_next = pat_ptrs[item];
5248 pat_ptrs[item] = ppp;
5249 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5250 if (ppp->pp_synp == NULL)
5251 {
5252 rest = NULL;
5253 break;
5254 }
5255
5256 /*
5257 * Get the syntax pattern and the following offset(s).
5258 */
5259 /* Enable the appropriate \z specials. */
5260 if (item == ITEM_START)
5261 reg_do_extmatch = REX_SET;
5262 else if (item == ITEM_SKIP || item == ITEM_END)
5263 reg_do_extmatch = REX_USE;
5264 rest = get_syn_pattern(rest, ppp->pp_synp);
5265 reg_do_extmatch = 0;
5266 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005267 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005268 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5269 ppp->pp_matchgroup_id = matchgroup_id;
5270 ++pat_count;
5271 }
5272 }
5273 vim_free(key);
5274 if (illegal || not_enough)
5275 rest = NULL;
5276
5277 /*
5278 * Must have a "start" and "end" pattern.
5279 */
5280 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5281 pat_ptrs[ITEM_END] == NULL))
5282 {
5283 not_enough = TRUE;
5284 rest = NULL;
5285 }
5286
5287 if (rest != NULL)
5288 {
5289 /*
5290 * Check for trailing garbage or command.
5291 * If OK, add the item.
5292 */
5293 eap->nextcmd = check_nextcmd(rest);
5294 if (!ends_excmd(*rest) || eap->skip)
5295 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005296 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005297 && (syn_id = syn_check_group(arg,
5298 (int)(group_name_end - arg))) != 0)
5299 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005300 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301 /*
5302 * Store the start/skip/end in the syn_items list
5303 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005304 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005305 for (item = ITEM_START; item <= ITEM_END; ++item)
5306 {
5307 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5308 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5310 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5311 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005312 (item == ITEM_START) ? SPTYPE_START :
5313 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005314 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5315 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005316 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5317 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005318 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005320#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005321 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005322#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005323 if (item == ITEM_START)
5324 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005325 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005326 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005327 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005328 syn_opt_arg.cont_in_list;
5329 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005330 curwin->w_s->b_syn_containedin = TRUE;
5331 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005332 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005333 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005334 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005335 ++idx;
5336#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005337 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005338 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005339#endif
5340 }
5341 }
5342
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005343 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005344 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345 success = TRUE; /* don't free the progs and patterns now */
5346 }
5347 }
5348
5349 /*
5350 * Free the allocated memory.
5351 */
5352 for (item = ITEM_START; item <= ITEM_END; ++item)
5353 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5354 {
5355 if (!success)
5356 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005357 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005358 vim_free(ppp->pp_synp->sp_pattern);
5359 }
5360 vim_free(ppp->pp_synp);
5361 ppp_next = ppp->pp_next;
5362 vim_free(ppp);
5363 }
5364
5365 if (!success)
5366 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005367 vim_free(syn_opt_arg.cont_list);
5368 vim_free(syn_opt_arg.cont_in_list);
5369 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005370 if (not_enough)
5371 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5372 else if (illegal || rest == NULL)
5373 EMSG2(_(e_invarg2), arg);
5374 }
5375}
5376
5377/*
5378 * A simple syntax group ID comparison function suitable for use in qsort()
5379 */
5380 static int
5381#ifdef __BORLANDC__
5382_RTLENTRYF
5383#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005384syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005385{
5386 const short *s1 = v1;
5387 const short *s2 = v2;
5388
5389 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5390}
5391
5392/*
5393 * Combines lists of syntax clusters.
5394 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5395 */
5396 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005397syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005398{
5399 int count1 = 0;
5400 int count2 = 0;
5401 short *g1;
5402 short *g2;
5403 short *clstr = NULL;
5404 int count;
5405 int round;
5406
5407 /*
5408 * Handle degenerate cases.
5409 */
5410 if (*clstr2 == NULL)
5411 return;
5412 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5413 {
5414 if (list_op == CLUSTER_REPLACE)
5415 vim_free(*clstr1);
5416 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5417 *clstr1 = *clstr2;
5418 else
5419 vim_free(*clstr2);
5420 return;
5421 }
5422
5423 for (g1 = *clstr1; *g1; g1++)
5424 ++count1;
5425 for (g2 = *clstr2; *g2; g2++)
5426 ++count2;
5427
5428 /*
5429 * For speed purposes, sort both lists.
5430 */
5431 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5432 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5433
5434 /*
5435 * We proceed in two passes; in round 1, we count the elements to place
5436 * in the new list, and in round 2, we allocate and populate the new
5437 * list. For speed, we use a mergesort-like method, adding the smaller
5438 * of the current elements in each list to the new list.
5439 */
5440 for (round = 1; round <= 2; round++)
5441 {
5442 g1 = *clstr1;
5443 g2 = *clstr2;
5444 count = 0;
5445
5446 /*
5447 * First, loop through the lists until one of them is empty.
5448 */
5449 while (*g1 && *g2)
5450 {
5451 /*
5452 * We always want to add from the first list.
5453 */
5454 if (*g1 < *g2)
5455 {
5456 if (round == 2)
5457 clstr[count] = *g1;
5458 count++;
5459 g1++;
5460 continue;
5461 }
5462 /*
5463 * We only want to add from the second list if we're adding the
5464 * lists.
5465 */
5466 if (list_op == CLUSTER_ADD)
5467 {
5468 if (round == 2)
5469 clstr[count] = *g2;
5470 count++;
5471 }
5472 if (*g1 == *g2)
5473 g1++;
5474 g2++;
5475 }
5476
5477 /*
5478 * Now add the leftovers from whichever list didn't get finished
5479 * first. As before, we only want to add from the second list if
5480 * we're adding the lists.
5481 */
5482 for (; *g1; g1++, count++)
5483 if (round == 2)
5484 clstr[count] = *g1;
5485 if (list_op == CLUSTER_ADD)
5486 for (; *g2; g2++, count++)
5487 if (round == 2)
5488 clstr[count] = *g2;
5489
5490 if (round == 1)
5491 {
5492 /*
5493 * If the group ended up empty, we don't need to allocate any
5494 * space for it.
5495 */
5496 if (count == 0)
5497 {
5498 clstr = NULL;
5499 break;
5500 }
5501 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5502 if (clstr == NULL)
5503 break;
5504 clstr[count] = 0;
5505 }
5506 }
5507
5508 /*
5509 * Finally, put the new list in place.
5510 */
5511 vim_free(*clstr1);
5512 vim_free(*clstr2);
5513 *clstr1 = clstr;
5514}
5515
5516/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005517 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005518 * If it is not found, 0 is returned.
5519 */
5520 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005521syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522{
5523 int i;
5524 char_u *name_u;
5525
5526 /* Avoid using stricmp() too much, it's slow on some systems */
5527 name_u = vim_strsave_up(name);
5528 if (name_u == NULL)
5529 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005530 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5531 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5532 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005533 break;
5534 vim_free(name_u);
5535 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5536}
5537
5538/*
5539 * Like syn_scl_name2id(), but take a pointer + length argument.
5540 */
5541 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005542syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543{
5544 char_u *name;
5545 int id = 0;
5546
5547 name = vim_strnsave(linep, len);
5548 if (name != NULL)
5549 {
5550 id = syn_scl_name2id(name);
5551 vim_free(name);
5552 }
5553 return id;
5554}
5555
5556/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005557 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558 * The argument is a pointer to the name and the length of the name.
5559 * If it doesn't exist yet, a new entry is created.
5560 * Return 0 for failure.
5561 */
5562 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005563syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005564{
5565 int id;
5566 char_u *name;
5567
5568 name = vim_strnsave(pp, len);
5569 if (name == NULL)
5570 return 0;
5571
5572 id = syn_scl_name2id(name);
5573 if (id == 0) /* doesn't exist yet */
5574 id = syn_add_cluster(name);
5575 else
5576 vim_free(name);
5577 return id;
5578}
5579
5580/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005581 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 * "name" must be an allocated string, it will be consumed.
5583 * Return 0 for failure.
5584 */
5585 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005586syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005588 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589
5590 /*
5591 * First call for this growarray: init growing array.
5592 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005593 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005595 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5596 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005597 }
5598
Bram Moolenaar42431a72011-04-01 14:44:59 +02005599 len = curwin->w_s->b_syn_clusters.ga_len;
5600 if (len >= MAX_CLUSTER_ID)
5601 {
5602 EMSG((char_u *)_("E848: Too many syntax clusters"));
5603 vim_free(name);
5604 return 0;
5605 }
5606
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 /*
5608 * Make room for at least one other cluster entry.
5609 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005610 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005611 {
5612 vim_free(name);
5613 return 0;
5614 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005615
Bram Moolenaar860cae12010-06-05 23:22:07 +02005616 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5617 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5618 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5619 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5620 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621
Bram Moolenaar217ad922005-03-20 22:37:15 +00005622 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005623 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005624 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005625 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005626
Bram Moolenaar071d4272004-06-13 20:20:40 +00005627 return len + SYNID_CLUSTER;
5628}
5629
5630/*
5631 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5632 * [add={groupname},..] [remove={groupname},..]".
5633 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005634 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005635syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636{
5637 char_u *arg = eap->arg;
5638 char_u *group_name_end;
5639 char_u *rest;
5640 int scl_id;
5641 short *clstr_list;
5642 int got_clstr = FALSE;
5643 int opt_len;
5644 int list_op;
5645
5646 eap->nextcmd = find_nextcmd(arg);
5647 if (eap->skip)
5648 return;
5649
5650 rest = get_group_name(arg, &group_name_end);
5651
5652 if (rest != NULL)
5653 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005654 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5655 if (scl_id == 0)
5656 return;
5657 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658
5659 for (;;)
5660 {
5661 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005662 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663 {
5664 opt_len = 3;
5665 list_op = CLUSTER_ADD;
5666 }
5667 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005668 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005669 {
5670 opt_len = 6;
5671 list_op = CLUSTER_SUBTRACT;
5672 }
5673 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005674 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 {
5676 opt_len = 8;
5677 list_op = CLUSTER_REPLACE;
5678 }
5679 else
5680 break;
5681
5682 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005683 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684 {
5685 EMSG2(_(e_invarg2), rest);
5686 break;
5687 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005688 if (scl_id >= 0)
5689 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005691 else
5692 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 got_clstr = TRUE;
5694 }
5695
5696 if (got_clstr)
5697 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005698 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005699 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 }
5701 }
5702
5703 if (!got_clstr)
5704 EMSG(_("E400: No cluster specified"));
5705 if (rest == NULL || !ends_excmd(*rest))
5706 EMSG2(_(e_invarg2), arg);
5707}
5708
5709/*
5710 * On first call for current buffer: Init growing array.
5711 */
5712 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005713init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005715 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5716 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005717}
5718
5719/*
5720 * Get one pattern for a ":syntax match" or ":syntax region" command.
5721 * Stores the pattern and program in a synpat_T.
5722 * Returns a pointer to the next argument, or NULL in case of an error.
5723 */
5724 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005725get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726{
5727 char_u *end;
5728 int *p;
5729 int idx;
5730 char_u *cpo_save;
5731
5732 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005733 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734 return NULL;
5735
5736 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5737 if (*end != *arg) /* end delimiter not found */
5738 {
5739 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5740 return NULL;
5741 }
5742 /* store the pattern and compiled regexp program */
5743 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5744 return NULL;
5745
5746 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5747 cpo_save = p_cpo;
5748 p_cpo = (char_u *)"";
5749 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5750 p_cpo = cpo_save;
5751
5752 if (ci->sp_prog == NULL)
5753 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005754 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005755#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005756 syn_clear_time(&ci->sp_time);
5757#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005758
5759 /*
5760 * Check for a match, highlight or region offset.
5761 */
5762 ++end;
5763 do
5764 {
5765 for (idx = SPO_COUNT; --idx >= 0; )
5766 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5767 break;
5768 if (idx >= 0)
5769 {
5770 p = &(ci->sp_offsets[idx]);
5771 if (idx != SPO_LC_OFF)
5772 switch (end[3])
5773 {
5774 case 's': break;
5775 case 'b': break;
5776 case 'e': idx += SPO_COUNT; break;
5777 default: idx = -1; break;
5778 }
5779 if (idx >= 0)
5780 {
5781 ci->sp_off_flags |= (1 << idx);
5782 if (idx == SPO_LC_OFF) /* lc=99 */
5783 {
5784 end += 3;
5785 *p = getdigits(&end);
5786
5787 /* "lc=" offset automatically sets "ms=" offset */
5788 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5789 {
5790 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5791 ci->sp_offsets[SPO_MS_OFF] = *p;
5792 }
5793 }
5794 else /* yy=x+99 */
5795 {
5796 end += 4;
5797 if (*end == '+')
5798 {
5799 ++end;
5800 *p = getdigits(&end); /* positive offset */
5801 }
5802 else if (*end == '-')
5803 {
5804 ++end;
5805 *p = -getdigits(&end); /* negative offset */
5806 }
5807 }
5808 if (*end != ',')
5809 break;
5810 ++end;
5811 }
5812 }
5813 } while (idx >= 0);
5814
Bram Moolenaar1c465442017-03-12 20:10:05 +01005815 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005816 {
5817 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5818 return NULL;
5819 }
5820 return skipwhite(end);
5821}
5822
5823/*
5824 * Handle ":syntax sync .." command.
5825 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005826 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005827syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005828{
5829 char_u *arg_start = eap->arg;
5830 char_u *arg_end;
5831 char_u *key = NULL;
5832 char_u *next_arg;
5833 int illegal = FALSE;
5834 int finished = FALSE;
5835 long n;
5836 char_u *cpo_save;
5837
5838 if (ends_excmd(*arg_start))
5839 {
5840 syn_cmd_list(eap, TRUE);
5841 return;
5842 }
5843
5844 while (!ends_excmd(*arg_start))
5845 {
5846 arg_end = skiptowhite(arg_start);
5847 next_arg = skipwhite(arg_end);
5848 vim_free(key);
5849 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5850 if (STRCMP(key, "CCOMMENT") == 0)
5851 {
5852 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005853 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005854 if (!ends_excmd(*next_arg))
5855 {
5856 arg_end = skiptowhite(next_arg);
5857 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005858 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005859 (int)(arg_end - next_arg));
5860 next_arg = skipwhite(arg_end);
5861 }
5862 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864 }
5865 else if ( STRNCMP(key, "LINES", 5) == 0
5866 || STRNCMP(key, "MINLINES", 8) == 0
5867 || STRNCMP(key, "MAXLINES", 8) == 0
5868 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5869 {
5870 if (key[4] == 'S')
5871 arg_end = key + 6;
5872 else if (key[0] == 'L')
5873 arg_end = key + 11;
5874 else
5875 arg_end = key + 9;
5876 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5877 {
5878 illegal = TRUE;
5879 break;
5880 }
5881 n = getdigits(&arg_end);
5882 if (!eap->skip)
5883 {
5884 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005885 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005887 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005888 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005889 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005890 }
5891 }
5892 else if (STRCMP(key, "FROMSTART") == 0)
5893 {
5894 if (!eap->skip)
5895 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005896 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5897 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005898 }
5899 }
5900 else if (STRCMP(key, "LINECONT") == 0)
5901 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005902 if (*next_arg == NUL) /* missing pattern */
5903 {
5904 illegal = TRUE;
5905 break;
5906 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005907 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005908 {
5909 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5910 finished = TRUE;
5911 break;
5912 }
5913 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5914 if (*arg_end != *next_arg) /* end delimiter not found */
5915 {
5916 illegal = TRUE;
5917 break;
5918 }
5919
5920 if (!eap->skip)
5921 {
5922 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005923 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005924 (int)(arg_end - next_arg - 1))) == NULL)
5925 {
5926 finished = TRUE;
5927 break;
5928 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005929 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005930
5931 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5932 cpo_save = p_cpo;
5933 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005934 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005935 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005936 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005937#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005938 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5939#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005940
Bram Moolenaar860cae12010-06-05 23:22:07 +02005941 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005942 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005943 vim_free(curwin->w_s->b_syn_linecont_pat);
5944 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005945 finished = TRUE;
5946 break;
5947 }
5948 }
5949 next_arg = skipwhite(arg_end + 1);
5950 }
5951 else
5952 {
5953 eap->arg = next_arg;
5954 if (STRCMP(key, "MATCH") == 0)
5955 syn_cmd_match(eap, TRUE);
5956 else if (STRCMP(key, "REGION") == 0)
5957 syn_cmd_region(eap, TRUE);
5958 else if (STRCMP(key, "CLEAR") == 0)
5959 syn_cmd_clear(eap, TRUE);
5960 else
5961 illegal = TRUE;
5962 finished = TRUE;
5963 break;
5964 }
5965 arg_start = next_arg;
5966 }
5967 vim_free(key);
5968 if (illegal)
5969 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5970 else if (!finished)
5971 {
5972 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005973 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005974 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005975 }
5976}
5977
5978/*
5979 * Convert a line of highlight group names into a list of group ID numbers.
5980 * "arg" should point to the "contains" or "nextgroup" keyword.
5981 * "arg" is advanced to after the last group name.
5982 * Careful: the argument is modified (NULs added).
5983 * returns FAIL for some error, OK for success.
5984 */
5985 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005986get_id_list(
5987 char_u **arg,
5988 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005989 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005990 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005991 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005992{
5993 char_u *p = NULL;
5994 char_u *end;
5995 int round;
5996 int count;
5997 int total_count = 0;
5998 short *retval = NULL;
5999 char_u *name;
6000 regmatch_T regmatch;
6001 int id;
6002 int i;
6003 int failed = FALSE;
6004
6005 /*
6006 * We parse the list twice:
6007 * round == 1: count the number of items, allocate the array.
6008 * round == 2: fill the array with the items.
6009 * In round 1 new groups may be added, causing the number of items to
6010 * grow when a regexp is used. In that case round 1 is done once again.
6011 */
6012 for (round = 1; round <= 2; ++round)
6013 {
6014 /*
6015 * skip "contains"
6016 */
6017 p = skipwhite(*arg + keylen);
6018 if (*p != '=')
6019 {
6020 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6021 break;
6022 }
6023 p = skipwhite(p + 1);
6024 if (ends_excmd(*p))
6025 {
6026 EMSG2(_("E406: Empty argument: %s"), *arg);
6027 break;
6028 }
6029
6030 /*
6031 * parse the arguments after "contains"
6032 */
6033 count = 0;
6034 while (!ends_excmd(*p))
6035 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006036 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006037 ;
6038 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6039 if (name == NULL)
6040 {
6041 failed = TRUE;
6042 break;
6043 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006044 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006045 if ( STRCMP(name + 1, "ALLBUT") == 0
6046 || STRCMP(name + 1, "ALL") == 0
6047 || STRCMP(name + 1, "TOP") == 0
6048 || STRCMP(name + 1, "CONTAINED") == 0)
6049 {
6050 if (TOUPPER_ASC(**arg) != 'C')
6051 {
6052 EMSG2(_("E407: %s not allowed here"), name + 1);
6053 failed = TRUE;
6054 vim_free(name);
6055 break;
6056 }
6057 if (count != 0)
6058 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006059 EMSG2(_("E408: %s must be first in contains list"),
6060 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006061 failed = TRUE;
6062 vim_free(name);
6063 break;
6064 }
6065 if (name[1] == 'A')
6066 id = SYNID_ALLBUT;
6067 else if (name[1] == 'T')
6068 id = SYNID_TOP;
6069 else
6070 id = SYNID_CONTAINED;
6071 id += current_syn_inc_tag;
6072 }
6073 else if (name[1] == '@')
6074 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006075 if (skip)
6076 id = -1;
6077 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006078 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006079 }
6080 else
6081 {
6082 /*
6083 * Handle full group name.
6084 */
6085 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6086 id = syn_check_group(name + 1, (int)(end - p));
6087 else
6088 {
6089 /*
6090 * Handle match of regexp with group names.
6091 */
6092 *name = '^';
6093 STRCAT(name, "$");
6094 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6095 if (regmatch.regprog == NULL)
6096 {
6097 failed = TRUE;
6098 vim_free(name);
6099 break;
6100 }
6101
6102 regmatch.rm_ic = TRUE;
6103 id = 0;
6104 for (i = highlight_ga.ga_len; --i >= 0; )
6105 {
6106 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6107 (colnr_T)0))
6108 {
6109 if (round == 2)
6110 {
6111 /* Got more items than expected; can happen
6112 * when adding items that match:
6113 * "contains=a.*b,axb".
6114 * Go back to first round */
6115 if (count >= total_count)
6116 {
6117 vim_free(retval);
6118 round = 1;
6119 }
6120 else
6121 retval[count] = i + 1;
6122 }
6123 ++count;
6124 id = -1; /* remember that we found one */
6125 }
6126 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006127 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006128 }
6129 }
6130 vim_free(name);
6131 if (id == 0)
6132 {
6133 EMSG2(_("E409: Unknown group name: %s"), p);
6134 failed = TRUE;
6135 break;
6136 }
6137 if (id > 0)
6138 {
6139 if (round == 2)
6140 {
6141 /* Got more items than expected, go back to first round */
6142 if (count >= total_count)
6143 {
6144 vim_free(retval);
6145 round = 1;
6146 }
6147 else
6148 retval[count] = id;
6149 }
6150 ++count;
6151 }
6152 p = skipwhite(end);
6153 if (*p != ',')
6154 break;
6155 p = skipwhite(p + 1); /* skip comma in between arguments */
6156 }
6157 if (failed)
6158 break;
6159 if (round == 1)
6160 {
6161 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6162 if (retval == NULL)
6163 break;
6164 retval[count] = 0; /* zero means end of the list */
6165 total_count = count;
6166 }
6167 }
6168
6169 *arg = p;
6170 if (failed || retval == NULL)
6171 {
6172 vim_free(retval);
6173 return FAIL;
6174 }
6175
6176 if (*list == NULL)
6177 *list = retval;
6178 else
6179 vim_free(retval); /* list already found, don't overwrite it */
6180
6181 return OK;
6182}
6183
6184/*
6185 * Make a copy of an ID list.
6186 */
6187 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006188copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006189{
6190 int len;
6191 int count;
6192 short *retval;
6193
6194 if (list == NULL)
6195 return NULL;
6196
6197 for (count = 0; list[count]; ++count)
6198 ;
6199 len = (count + 1) * sizeof(short);
6200 retval = (short *)alloc((unsigned)len);
6201 if (retval != NULL)
6202 mch_memmove(retval, list, (size_t)len);
6203
6204 return retval;
6205}
6206
6207/*
6208 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6209 * "cur_si" can be NULL if not checking the "containedin" list.
6210 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6211 * the current item.
6212 * This function is called very often, keep it fast!!
6213 */
6214 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006215in_id_list(
6216 stateitem_T *cur_si, /* current item or NULL */
6217 short *list, /* id list */
6218 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6219 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006220{
6221 int retval;
6222 short *scl_list;
6223 short item;
6224 short id = ssp->id;
6225 static int depth = 0;
6226 int r;
6227
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006228 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006229 if (cur_si != NULL && ssp->cont_in_list != NULL
6230 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006231 {
6232 /* Ignore transparent items without a contains argument. Double check
6233 * that we don't go back past the first one. */
6234 while ((cur_si->si_flags & HL_TRANS_CONT)
6235 && cur_si > (stateitem_T *)(current_state.ga_data))
6236 --cur_si;
6237 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6238 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006239 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6240 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006241 return TRUE;
6242 }
6243
6244 if (list == NULL)
6245 return FALSE;
6246
6247 /*
6248 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6249 * inside anything. Only allow not-contained groups.
6250 */
6251 if (list == ID_LIST_ALL)
6252 return !contained;
6253
6254 /*
6255 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6256 * contains list. We also require that "id" is at the same ":syn include"
6257 * level as the list.
6258 */
6259 item = *list;
6260 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6261 {
6262 if (item < SYNID_TOP)
6263 {
6264 /* ALL or ALLBUT: accept all groups in the same file */
6265 if (item - SYNID_ALLBUT != ssp->inc_tag)
6266 return FALSE;
6267 }
6268 else if (item < SYNID_CONTAINED)
6269 {
6270 /* TOP: accept all not-contained groups in the same file */
6271 if (item - SYNID_TOP != ssp->inc_tag || contained)
6272 return FALSE;
6273 }
6274 else
6275 {
6276 /* CONTAINED: accept all contained groups in the same file */
6277 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6278 return FALSE;
6279 }
6280 item = *++list;
6281 retval = FALSE;
6282 }
6283 else
6284 retval = TRUE;
6285
6286 /*
6287 * Return "retval" if id is in the contains list.
6288 */
6289 while (item != 0)
6290 {
6291 if (item == id)
6292 return retval;
6293 if (item >= SYNID_CLUSTER)
6294 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006295 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006296 /* restrict recursiveness to 30 to avoid an endless loop for a
6297 * cluster that includes itself (indirectly) */
6298 if (scl_list != NULL && depth < 30)
6299 {
6300 ++depth;
6301 r = in_id_list(NULL, scl_list, ssp, contained);
6302 --depth;
6303 if (r)
6304 return retval;
6305 }
6306 }
6307 item = *++list;
6308 }
6309 return !retval;
6310}
6311
6312struct subcommand
6313{
Bram Moolenaard99df422016-01-29 23:20:40 +01006314 char *name; /* subcommand name */
6315 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006316};
6317
6318static struct subcommand subcommands[] =
6319{
6320 {"case", syn_cmd_case},
6321 {"clear", syn_cmd_clear},
6322 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006323 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006324 {"enable", syn_cmd_enable},
6325 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006326 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327 {"keyword", syn_cmd_keyword},
6328 {"list", syn_cmd_list},
6329 {"manual", syn_cmd_manual},
6330 {"match", syn_cmd_match},
6331 {"on", syn_cmd_on},
6332 {"off", syn_cmd_off},
6333 {"region", syn_cmd_region},
6334 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006335 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 {"sync", syn_cmd_sync},
6337 {"", syn_cmd_list},
6338 {NULL, NULL}
6339};
6340
6341/*
6342 * ":syntax".
6343 * This searches the subcommands[] table for the subcommand name, and calls a
6344 * syntax_subcommand() function to do the rest.
6345 */
6346 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006347ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348{
6349 char_u *arg = eap->arg;
6350 char_u *subcmd_end;
6351 char_u *subcmd_name;
6352 int i;
6353
6354 syn_cmdlinep = eap->cmdlinep;
6355
6356 /* isolate subcommand name */
6357 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6358 ;
6359 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6360 if (subcmd_name != NULL)
6361 {
6362 if (eap->skip) /* skip error messages for all subcommands */
6363 ++emsg_skip;
6364 for (i = 0; ; ++i)
6365 {
6366 if (subcommands[i].name == NULL)
6367 {
6368 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6369 break;
6370 }
6371 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6372 {
6373 eap->arg = skipwhite(subcmd_end);
6374 (subcommands[i].func)(eap, FALSE);
6375 break;
6376 }
6377 }
6378 vim_free(subcmd_name);
6379 if (eap->skip)
6380 --emsg_skip;
6381 }
6382}
6383
Bram Moolenaar860cae12010-06-05 23:22:07 +02006384 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006385ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006387 char_u *old_value;
6388 char_u *new_value;
6389
Bram Moolenaar860cae12010-06-05 23:22:07 +02006390 if (curwin->w_s == &curwin->w_buffer->b_s)
6391 {
6392 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6393 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006394 hash_init(&curwin->w_s->b_keywtab);
6395 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006396#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006397 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006398 curwin->w_p_spell = FALSE; /* No spell checking */
6399 clear_string_option(&curwin->w_s->b_p_spc);
6400 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006401 clear_string_option(&curwin->w_s->b_p_spl);
6402#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006403 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006404 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006405
6406 /* save value of b:current_syntax */
6407 old_value = get_var_value((char_u *)"b:current_syntax");
6408 if (old_value != NULL)
6409 old_value = vim_strsave(old_value);
6410
Bram Moolenaard1413d92016-03-02 21:51:56 +01006411#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006412 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6413 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006414 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006415#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006416
6417 /* move value of b:current_syntax to w:current_syntax */
6418 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006419 if (new_value != NULL)
6420 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006421
6422 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006423 if (old_value == NULL)
6424 do_unlet((char_u *)"b:current_syntax", TRUE);
6425 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006426 {
6427 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6428 vim_free(old_value);
6429 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006430}
6431
6432 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006433syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006434{
6435 return (win->w_s->b_syn_patterns.ga_len != 0
6436 || win->w_s->b_syn_clusters.ga_len != 0
6437 || win->w_s->b_keywtab.ht_used > 0
6438 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006439}
6440
6441#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6442
6443static enum
6444{
6445 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006446 EXP_CASE, /* expand ":syn case" arguments */
6447 EXP_SPELL, /* expand ":syn spell" arguments */
6448 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006449} expand_what;
6450
Bram Moolenaar4f688582007-07-24 12:34:30 +00006451/*
6452 * Reset include_link, include_default, include_none to 0.
6453 * Called when we are done expanding.
6454 */
6455 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006456reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006457{
6458 include_link = include_default = include_none = 0;
6459}
6460
6461/*
6462 * Handle command line completion for :match and :echohl command: Add "None"
6463 * as highlight group.
6464 */
6465 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006466set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006467{
6468 xp->xp_context = EXPAND_HIGHLIGHT;
6469 xp->xp_pattern = arg;
6470 include_none = 1;
6471}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006472
6473/*
6474 * Handle command line completion for :syntax command.
6475 */
6476 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006477set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006478{
6479 char_u *p;
6480
6481 /* Default: expand subcommands */
6482 xp->xp_context = EXPAND_SYNTAX;
6483 expand_what = EXP_SUBCMD;
6484 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006485 include_link = 0;
6486 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487
6488 /* (part of) subcommand already typed */
6489 if (*arg != NUL)
6490 {
6491 p = skiptowhite(arg);
6492 if (*p != NUL) /* past first word */
6493 {
6494 xp->xp_pattern = skipwhite(p);
6495 if (*skiptowhite(xp->xp_pattern) != NUL)
6496 xp->xp_context = EXPAND_NOTHING;
6497 else if (STRNICMP(arg, "case", p - arg) == 0)
6498 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006499 else if (STRNICMP(arg, "spell", p - arg) == 0)
6500 expand_what = EXP_SPELL;
6501 else if (STRNICMP(arg, "sync", p - arg) == 0)
6502 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006503 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6504 || STRNICMP(arg, "region", p - arg) == 0
6505 || STRNICMP(arg, "match", p - arg) == 0
6506 || STRNICMP(arg, "list", p - arg) == 0)
6507 xp->xp_context = EXPAND_HIGHLIGHT;
6508 else
6509 xp->xp_context = EXPAND_NOTHING;
6510 }
6511 }
6512}
6513
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514/*
6515 * Function given to ExpandGeneric() to obtain the list syntax names for
6516 * expansion.
6517 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006518 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006519get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006520{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006521 switch (expand_what)
6522 {
6523 case EXP_SUBCMD:
6524 return (char_u *)subcommands[idx].name;
6525 case EXP_CASE:
6526 {
6527 static char *case_args[] = {"match", "ignore", NULL};
6528 return (char_u *)case_args[idx];
6529 }
6530 case EXP_SPELL:
6531 {
6532 static char *spell_args[] =
6533 {"toplevel", "notoplevel", "default", NULL};
6534 return (char_u *)spell_args[idx];
6535 }
6536 case EXP_SYNC:
6537 {
6538 static char *sync_args[] =
6539 {"ccomment", "clear", "fromstart",
6540 "linebreaks=", "linecont", "lines=", "match",
6541 "maxlines=", "minlines=", "region", NULL};
6542 return (char_u *)sync_args[idx];
6543 }
6544 }
6545 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006546}
6547
6548#endif /* FEAT_CMDL_COMPL */
6549
Bram Moolenaar071d4272004-06-13 20:20:40 +00006550/*
6551 * Function called for expression evaluation: get syntax ID at file position.
6552 */
6553 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006554syn_get_id(
6555 win_T *wp,
6556 long lnum,
6557 colnr_T col,
6558 int trans, /* remove transparency */
6559 int *spellp, /* return: can do spell checking */
6560 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006561{
6562 /* When the position is not after the current position and in the same
6563 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006564 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006566 || col < current_col)
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006567 syntax_start(wp, lnum, NULL);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006568 else if (wp->w_buffer == syn_buf
6569 && lnum == current_lnum
6570 && col > current_col)
6571 /* next_match may not be correct when moving around, e.g. with the
6572 * "skip" expression in searchpair() */
6573 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006575 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006576
6577 return (trans ? current_trans_id : current_id);
6578}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579
Bram Moolenaar860cae12010-06-05 23:22:07 +02006580#if defined(FEAT_CONCEAL) || defined(PROTO)
6581/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006582 * Get extra information about the syntax item. Must be called right after
6583 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006584 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006585 * Returns the current flags.
6586 */
6587 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006588get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006589{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006590 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006591 return current_flags;
6592}
6593
6594/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006595 * Return conceal substitution character
6596 */
6597 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006598syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006599{
6600 return current_sub_char;
6601}
6602#endif
6603
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006604#if defined(FEAT_EVAL) || defined(PROTO)
6605/*
6606 * Return the syntax ID at position "i" in the current stack.
6607 * The caller must have called syn_get_id() before to fill the stack.
6608 * Returns -1 when "i" is out of range.
6609 */
6610 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006611syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006612{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006613 if (i >= current_state.ga_len)
6614 {
6615 /* Need to invalidate the state, because we didn't properly finish it
6616 * for the last character, "keep_state" was TRUE. */
6617 invalidate_current_state();
6618 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006619 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006620 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006621 return CUR_STATE(i).si_id;
6622}
6623#endif
6624
Bram Moolenaar071d4272004-06-13 20:20:40 +00006625#if defined(FEAT_FOLDING) || defined(PROTO)
6626/*
6627 * Function called to get folding level for line "lnum" in window "wp".
6628 */
6629 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006630syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006631{
6632 int level = 0;
6633 int i;
6634
6635 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006636 if (wp->w_s->b_syn_folditems != 0
6637 && !wp->w_s->b_syn_error
6638# ifdef SYN_TIME_LIMIT
6639 && !wp->w_s->b_syn_slow
6640# endif
6641 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006642 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006643 syntax_start(wp, lnum, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006644
6645 for (i = 0; i < current_state.ga_len; ++i)
6646 if (CUR_STATE(i).si_flags & HL_FOLD)
6647 ++level;
6648 }
6649 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006650 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006651 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006652 if (level < 0)
6653 level = 0;
6654 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006655 return level;
6656}
6657#endif
6658
Bram Moolenaar01615492015-02-03 13:00:38 +01006659#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006660/*
6661 * ":syntime".
6662 */
6663 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006664ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006665{
6666 if (STRCMP(eap->arg, "on") == 0)
6667 syn_time_on = TRUE;
6668 else if (STRCMP(eap->arg, "off") == 0)
6669 syn_time_on = FALSE;
6670 else if (STRCMP(eap->arg, "clear") == 0)
6671 syntime_clear();
6672 else if (STRCMP(eap->arg, "report") == 0)
6673 syntime_report();
6674 else
6675 EMSG2(_(e_invarg2), eap->arg);
6676}
6677
6678 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006679syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006680{
6681 profile_zero(&st->total);
6682 profile_zero(&st->slowest);
6683 st->count = 0;
6684 st->match = 0;
6685}
6686
6687/*
6688 * Clear the syntax timing for the current buffer.
6689 */
6690 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006691syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006692{
6693 int idx;
6694 synpat_T *spp;
6695
6696 if (!syntax_present(curwin))
6697 {
6698 MSG(_(msg_no_items));
6699 return;
6700 }
6701 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6702 {
6703 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6704 syn_clear_time(&spp->sp_time);
6705 }
6706}
6707
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006708#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6709/*
6710 * Function given to ExpandGeneric() to obtain the possible arguments of the
6711 * ":syntime {on,off,clear,report}" command.
6712 */
6713 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006714get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006715{
6716 switch (idx)
6717 {
6718 case 0: return (char_u *)"on";
6719 case 1: return (char_u *)"off";
6720 case 2: return (char_u *)"clear";
6721 case 3: return (char_u *)"report";
6722 }
6723 return NULL;
6724}
6725#endif
6726
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006727typedef struct
6728{
6729 proftime_T total;
6730 int count;
6731 int match;
6732 proftime_T slowest;
6733 proftime_T average;
6734 int id;
6735 char_u *pattern;
6736} time_entry_T;
6737
6738 static int
6739#ifdef __BORLANDC__
6740_RTLENTRYF
6741#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006742syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006743{
6744 const time_entry_T *s1 = v1;
6745 const time_entry_T *s2 = v2;
6746
6747 return profile_cmp(&s1->total, &s2->total);
6748}
6749
6750/*
6751 * Clear the syntax timing for the current buffer.
6752 */
6753 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006754syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006755{
6756 int idx;
6757 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006758# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006759 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006760# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006761 int len;
6762 proftime_T total_total;
6763 int total_count = 0;
6764 garray_T ga;
6765 time_entry_T *p;
6766
6767 if (!syntax_present(curwin))
6768 {
6769 MSG(_(msg_no_items));
6770 return;
6771 }
6772
6773 ga_init2(&ga, sizeof(time_entry_T), 50);
6774 profile_zero(&total_total);
6775 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6776 {
6777 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6778 if (spp->sp_time.count > 0)
6779 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006780 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006781 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6782 p->total = spp->sp_time.total;
6783 profile_add(&total_total, &spp->sp_time.total);
6784 p->count = spp->sp_time.count;
6785 p->match = spp->sp_time.match;
6786 total_count += spp->sp_time.count;
6787 p->slowest = spp->sp_time.slowest;
6788# ifdef FEAT_FLOAT
6789 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6790 p->average = tm;
6791# endif
6792 p->id = spp->sp_syn.id;
6793 p->pattern = spp->sp_pattern;
6794 ++ga.ga_len;
6795 }
6796 }
6797
Bram Moolenaara2162552017-01-08 17:46:20 +01006798 /* Sort on total time. Skip if there are no items to avoid passing NULL
6799 * pointer to qsort(). */
6800 if (ga.ga_len > 1)
6801 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006802 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006803
6804 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6805 MSG_PUTS("\n");
6806 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6807 {
6808 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6809 p = ((time_entry_T *)ga.ga_data) + idx;
6810
6811 MSG_PUTS(profile_msg(&p->total));
6812 MSG_PUTS(" "); /* make sure there is always a separating space */
6813 msg_advance(13);
6814 msg_outnum(p->count);
6815 MSG_PUTS(" ");
6816 msg_advance(20);
6817 msg_outnum(p->match);
6818 MSG_PUTS(" ");
6819 msg_advance(26);
6820 MSG_PUTS(profile_msg(&p->slowest));
6821 MSG_PUTS(" ");
6822 msg_advance(38);
6823# ifdef FEAT_FLOAT
6824 MSG_PUTS(profile_msg(&p->average));
6825 MSG_PUTS(" ");
6826# endif
6827 msg_advance(50);
6828 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6829 MSG_PUTS(" ");
6830
6831 msg_advance(69);
6832 if (Columns < 80)
6833 len = 20; /* will wrap anyway */
6834 else
6835 len = Columns - 70;
6836 if (len > (int)STRLEN(p->pattern))
6837 len = (int)STRLEN(p->pattern);
6838 msg_outtrans_len(p->pattern, len);
6839 MSG_PUTS("\n");
6840 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006841 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006842 if (!got_int)
6843 {
6844 MSG_PUTS("\n");
6845 MSG_PUTS(profile_msg(&total_total));
6846 msg_advance(13);
6847 msg_outnum(total_count);
6848 MSG_PUTS("\n");
6849 }
6850}
6851#endif
6852
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853#endif /* FEAT_SYN_HL */
6854
Bram Moolenaar071d4272004-06-13 20:20:40 +00006855/**************************************
6856 * Highlighting stuff *
6857 **************************************/
6858
6859/*
6860 * The default highlight groups. These are compiled-in for fast startup and
6861 * they still work when the runtime files can't be found.
6862 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006863 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6864 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006865 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006866#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006867# define CENT(a, b) b
6868#else
6869# define CENT(a, b) a
6870#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871static char *(highlight_init_both[]) =
6872 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006873 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6874 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6875 CENT("IncSearch term=reverse cterm=reverse",
6876 "IncSearch term=reverse cterm=reverse gui=reverse"),
6877 CENT("ModeMsg term=bold cterm=bold",
6878 "ModeMsg term=bold cterm=bold gui=bold"),
6879 CENT("NonText term=bold ctermfg=Blue",
6880 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6881 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6882 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6883 CENT("StatusLineNC term=reverse cterm=reverse",
6884 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006885 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006886#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006887 CENT("VertSplit term=reverse cterm=reverse",
6888 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006889#endif
6890#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006891 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6892 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006893#endif
6894#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006895 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6896 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006897#endif
6898#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006899 CENT("PmenuSbar ctermbg=Grey",
6900 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#endif
6902#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006903 CENT("TabLineSel term=bold cterm=bold",
6904 "TabLineSel term=bold cterm=bold gui=bold"),
6905 CENT("TabLineFill term=reverse cterm=reverse",
6906 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006907#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006908#ifdef FEAT_GUI
6909 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006910 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006911#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006912 NULL
6913 };
6914
6915static char *(highlight_init_light[]) =
6916 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006917 CENT("Directory term=bold ctermfg=DarkBlue",
6918 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6919 CENT("LineNr term=underline ctermfg=Brown",
6920 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006921 CENT("CursorLineNr term=bold ctermfg=Brown",
6922 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006923 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6924 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6925 CENT("Question term=standout ctermfg=DarkGreen",
6926 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6927 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6928 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006929#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006930 CENT("SpellBad term=reverse ctermbg=LightRed",
6931 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6932 CENT("SpellCap term=reverse ctermbg=LightBlue",
6933 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6934 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6935 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6936 CENT("SpellLocal term=underline ctermbg=Cyan",
6937 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006938#endif
6939#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006940 CENT("PmenuThumb ctermbg=Black",
6941 "PmenuThumb ctermbg=Black guibg=Black"),
6942 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6943 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6944 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6945 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006946#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006947 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6948 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6949 CENT("Title term=bold ctermfg=DarkMagenta",
6950 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6951 CENT("WarningMsg term=standout ctermfg=DarkRed",
6952 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006953#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006954 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6955 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006956#endif
6957#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006958 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6959 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6960 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6961 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006962#endif
6963#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006964 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6965 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006967 CENT("Visual term=reverse",
6968 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006969#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006970 CENT("DiffAdd term=bold ctermbg=LightBlue",
6971 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6972 CENT("DiffChange term=bold ctermbg=LightMagenta",
6973 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6974 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6975 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006976#endif
6977#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006978 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6979 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006980#endif
6981#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006982 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006983 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006984 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006985 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006986 CENT("ColorColumn term=reverse ctermbg=LightRed",
6987 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006988#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006989#ifdef FEAT_CONCEAL
6990 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6991 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6992#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006993#ifdef FEAT_AUTOCMD
6994 CENT("MatchParen term=reverse ctermbg=Cyan",
6995 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6996#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006997#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006998 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006999#endif
Bram Moolenaar21020352017-06-13 17:21:04 +02007000 CENT("QuickFixLine term=reverse ctermbg=Cyan",
7001 "QuickFixLine term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00007002 NULL
7003 };
7004
7005static char *(highlight_init_dark[]) =
7006 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007007 CENT("Directory term=bold ctermfg=LightCyan",
7008 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7009 CENT("LineNr term=underline ctermfg=Yellow",
7010 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01007011 CENT("CursorLineNr term=bold ctermfg=Yellow",
7012 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007013 CENT("MoreMsg term=bold ctermfg=LightGreen",
7014 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7015 CENT("Question term=standout ctermfg=LightGreen",
7016 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7017 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7018 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7019 CENT("SpecialKey term=bold ctermfg=LightBlue",
7020 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007021#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007022 CENT("SpellBad term=reverse ctermbg=Red",
7023 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7024 CENT("SpellCap term=reverse ctermbg=Blue",
7025 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7026 CENT("SpellRare term=reverse ctermbg=Magenta",
7027 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7028 CENT("SpellLocal term=underline ctermbg=Cyan",
7029 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007030#endif
7031#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007032 CENT("PmenuThumb ctermbg=White",
7033 "PmenuThumb ctermbg=White guibg=White"),
7034 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7035 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007036 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7037 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007038#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007039 CENT("Title term=bold ctermfg=LightMagenta",
7040 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7041 CENT("WarningMsg term=standout ctermfg=LightRed",
7042 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007043#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007044 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7045 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007046#endif
7047#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007048 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7049 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7050 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7051 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007052#endif
7053#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007054 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7055 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007056#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007057 CENT("Visual term=reverse",
7058 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007059#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007060 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7061 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7062 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7063 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7064 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7065 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007066#endif
7067#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007068 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7069 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007070#endif
7071#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007072 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007073 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007074 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007075 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007076 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7077 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007078#endif
7079#ifdef FEAT_AUTOCMD
7080 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7081 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007082#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007083#ifdef FEAT_CONCEAL
7084 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7085 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7086#endif
Bram Moolenaar21020352017-06-13 17:21:04 +02007087 CENT("QuickFixLine term=reverse ctermbg=Cyan",
7088 "QuickFixLine term=reverse ctermbg=Cyan guibg=Cyan"),
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 **************************************/