blob: e026c440031263ec2614da3cdd0dbe09b2d181a8 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010025 int sg_cleared; /* "hi clear" was used */
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/* for normal terminals */
27 int sg_term; /* "term=" highlighting attributes */
28 char_u *sg_start; /* terminal string for start highl */
29 char_u *sg_stop; /* terminal string for stop highl */
30 int sg_term_attr; /* Screen attr for term mode */
31/* for color terminals */
32 int sg_cterm; /* "cterm=" highlighting attr */
33 int sg_cterm_bold; /* bold attr was set for light color */
34 int sg_cterm_fg; /* terminal fg color number + 1 */
35 int sg_cterm_bg; /* terminal bg color number + 1 */
36 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000037/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000040 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020041#endif
42#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 GuiFont sg_font; /* GUI font handle */
45#ifdef FEAT_XFONTSET
46 GuiFontset sg_fontset; /* GUI fontset handle */
47#endif
48 char_u *sg_font_name; /* GUI font or fontset name */
49 int sg_gui_attr; /* Screen attr for GUI mode */
50#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020051#if defined(FEAT_GUI) || defined(FEAT_EVAL)
52/* Store the sp color name for the GUI or synIDattr() */
53 int sg_gui; /* "gui=" highlighting attributes */
54 char_u *sg_gui_fg_name;/* GUI foreground color name */
55 char_u *sg_gui_bg_name;/* GUI background color name */
56 char_u *sg_gui_sp_name;/* GUI special color name */
57#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 int sg_link; /* link to this highlight group ID */
59 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000060#ifdef FEAT_EVAL
61 scid_T sg_scriptID; /* script in which the group was last set */
62#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000063};
64
65#define SG_TERM 1 /* term has been set */
66#define SG_CTERM 2 /* cterm has been set */
67#define SG_GUI 4 /* gui has been set */
68#define SG_LINK 8 /* link has been set */
69
70static garray_T highlight_ga; /* highlight groups for 'highlight' option */
71
72#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
73
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020074#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000077/* Flags to indicate an additional string for highlight name completion. */
78static int include_none = 0; /* when 1 include "None" */
79static int include_default = 0; /* when 1 include "default" */
80static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000081#endif
82
83/*
84 * The "term", "cterm" and "gui" arguments can be any combination of the
85 * following names, separated by commas (but no spaces!).
86 */
87static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {"bold", "standout", "underline", "undercurl",
89 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000092
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010093static int get_attr_entry(garray_T *table, attrentry_T *aep);
94static void syn_unadd_group(void);
95static void set_hl_attr(int idx);
96static void highlight_list_one(int id);
97static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
98static int syn_add_group(char_u *name);
99static int syn_list_header(int did_header, int outlen, int id);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100105static guicolor_T color_name2handle(char_u *name);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200106#endif
107#ifdef FEAT_GUI
108static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100109static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100111static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100113static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#endif
115
116/*
117 * An attribute number is the index in attr_table plus ATTR_OFF.
118 */
119#define ATTR_OFF (HL_ALL + 1)
120
121#if defined(FEAT_SYN_HL) || defined(PROTO)
122
123#define SYN_NAMELEN 50 /* maximum length of a syntax name */
124
125/* different types of offsets that are possible */
126#define SPO_MS_OFF 0 /* match start offset */
127#define SPO_ME_OFF 1 /* match end offset */
128#define SPO_HS_OFF 2 /* highl. start offset */
129#define SPO_HE_OFF 3 /* highl. end offset */
130#define SPO_RS_OFF 4 /* region start offset */
131#define SPO_RE_OFF 5 /* region end offset */
132#define SPO_LC_OFF 6 /* leading context offset */
133#define SPO_COUNT 7
134
135static char *(spo_name_tab[SPO_COUNT]) =
136 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
137
138/*
139 * The patterns that are being searched for are stored in a syn_pattern.
140 * A match item consists of one pattern.
141 * A start/end item consists of n start patterns and m end patterns.
142 * A start/skip/end item consists of n start patterns, one skip pattern and m
143 * end patterns.
144 * For the latter two, the patterns are always consecutive: start-skip-end.
145 *
146 * A character offset can be given for the matched text (_m_start and _m_end)
147 * and for the actually highlighted text (_h_start and _h_end).
148 */
149typedef struct syn_pattern
150{
151 char sp_type; /* see SPTYPE_ defines below */
152 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200153 int sp_flags; /* see HL_ defines below */
154#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200155 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200156#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 struct sp_syn sp_syn; /* struct passed to in_id_list() */
158 short sp_syn_match_id; /* highlight group ID of pattern */
159 char_u *sp_pattern; /* regexp to match, pattern */
160 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200161#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200162 syn_time_T sp_time;
163#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 int sp_ic; /* ignore-case flag for sp_prog */
165 short sp_off_flags; /* see below */
166 int sp_offsets[SPO_COUNT]; /* offsets */
167 short *sp_cont_list; /* cont. group IDs, if non-zero */
168 short *sp_next_list; /* next group IDs, if non-zero */
169 int sp_sync_idx; /* sync item index (syncing only) */
170 int sp_line_id; /* ID of last line where tried */
171 int sp_startcol; /* next match in sp_line_id line */
172} synpat_T;
173
174/* The sp_off_flags are computed like this:
175 * offset from the start of the matched text: (1 << SPO_XX_OFF)
176 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
177 * When both are present, only one is used.
178 */
179
180#define SPTYPE_MATCH 1 /* match keyword with this group ID */
181#define SPTYPE_START 2 /* match a regexp, start of item */
182#define SPTYPE_END 3 /* match a regexp, end of item */
183#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
184
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185
186#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
187
188#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
189
190/*
191 * Flags for b_syn_sync_flags:
192 */
193#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
194#define SF_MATCH 0x02 /* sync by matching a pattern */
195
196#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
197
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198#define MAXKEYWLEN 80 /* maximum length of a keyword */
199
200/*
201 * The attributes of the syntax item that has been recognized.
202 */
203static int current_attr = 0; /* attr of current syntax word */
204#ifdef FEAT_EVAL
205static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000206static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200208#ifdef FEAT_CONCEAL
209static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200210static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200211static int current_sub_char = 0;
212#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215{
216 char_u *scl_name; /* syntax cluster name */
217 char_u *scl_name_u; /* uppercase of scl_name */
218 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000219} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220
221/*
222 * Methods of combining two clusters
223 */
224#define CLUSTER_REPLACE 1 /* replace first list with second */
225#define CLUSTER_ADD 2 /* add second list to first */
226#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
227
Bram Moolenaar217ad922005-03-20 22:37:15 +0000228#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229
230/*
231 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200232 * 0 - 19999 normal syntax groups
233 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
234 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
235 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
236 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200238#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200239#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
240#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
241#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
242
Bram Moolenaar42431a72011-04-01 14:44:59 +0200243#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
244#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245
246/*
247 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
248 * expand_filename(). Most of the other syntax commands don't need it, so
249 * instead of passing it to them, we stow it here.
250 */
251static char_u **syn_cmdlinep;
252
253/*
254 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200255 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 * rules in each ":syn include"'d file.
257 */
258static int current_syn_inc_tag = 0;
259static int running_syn_inc_tag = 0;
260
261/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000262 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
263 * This avoids adding a pointer to the hashtable item.
264 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
265 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
266 * HI2KE() converts a hashitem pointer to a var pointer.
267 */
268static keyentry_T dumkey;
269#define KE2HIKEY(kp) ((kp)->keyword)
270#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
271#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
272
273/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 * To reduce the time spent in keepend(), remember at which level in the state
275 * stack the first item with "keepend" is present. When "-1", there is no
276 * "keepend" on the stack.
277 */
278static int keepend_level = -1;
279
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200280static char msg_no_items[] = N_("No Syntax items defined for this buffer");
281
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282/*
283 * For the current state we need to remember more than just the idx.
284 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
285 * (The end positions have the column number of the next char)
286 */
287typedef struct state_item
288{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000289 int si_idx; /* index of syntax pattern or
290 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000292 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_m_lnum; /* lnum of the match */
294 int si_m_startcol; /* starting column of the match */
295 lpos_T si_m_endpos; /* just after end posn of the match */
296 lpos_T si_h_startpos; /* start position of the highlighting */
297 lpos_T si_h_endpos; /* end position of the highlighting */
298 lpos_T si_eoe_pos; /* end position of end pattern */
299 int si_end_idx; /* group ID for end pattern or zero */
300 int si_ends; /* if match ends before si_m_endpos */
301 int si_attr; /* attributes in this state */
302 long si_flags; /* HL_HAS_EOL flag in this state, and
303 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200304#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200305 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200306 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200307#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 short *si_cont_list; /* list of contained groups */
309 short *si_next_list; /* nextgroup IDs after this item ends */
310 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
311 * pattern */
312} stateitem_T;
313
314#define KEYWORD_IDX -1 /* value of si_idx for keywords */
315#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
316 but contained groups */
317
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200318#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100319static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#endif
321
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000323 * Struct to reduce the number of arguments to get_syn_options(), it's used
324 * very often.
325 */
326typedef struct
327{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000328 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000329 int keyword; /* TRUE for ":syn keyword" */
330 int *sync_idx; /* syntax item for "grouphere" argument, NULL
331 if not allowed */
332 char has_cont_list; /* TRUE if "cont_list" can be used */
333 short *cont_list; /* group IDs for "contains" argument */
334 short *cont_in_list; /* group IDs for "containedin" argument */
335 short *next_list; /* group IDs for "nextgroup" argument */
336} syn_opt_arg_T;
337
338/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339 * The next possible match in the current line for any pattern is remembered,
340 * to avoid having to try for a match in each column.
341 * If next_match_idx == -1, not tried (in this line) yet.
342 * If next_match_col == MAXCOL, no match found in this line.
343 * (All end positions have the column of the char after the end)
344 */
345static int next_match_col; /* column for start of next match */
346static lpos_T next_match_m_endpos; /* position for end of next match */
347static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
348static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
349static int next_match_idx; /* index of matched item */
350static long next_match_flags; /* flags for next match */
351static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
352static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
353static int next_match_end_idx; /* ID of group for end pattn or zero */
354static reg_extmatch_T *next_match_extmatch = NULL;
355
356/*
357 * A state stack is an array of integers or stateitem_T, stored in a
358 * garray_T. A state stack is invalid if it's itemsize entry is zero.
359 */
360#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
361#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
362
363/*
364 * The current state (within the line) of the recognition engine.
365 * When current_state.ga_itemsize is 0 the current state is invalid.
366 */
367static win_T *syn_win; /* current window for highlighting */
368static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200369static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200370#ifdef FEAT_RELTIME
371static proftime_T *syn_tm;
372#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373static linenr_T current_lnum = 0; /* lnum of current state */
374static colnr_T current_col = 0; /* column of current state */
375static int current_state_stored = 0; /* TRUE if stored current state
376 * after setting current_finished */
377static int current_finished = 0; /* current line has been finished */
378static garray_T current_state /* current stack of state_items */
379 = {0, 0, 0, 0, NULL};
380static short *current_next_list = NULL; /* when non-zero, nextgroup list */
381static int current_next_flags = 0; /* flags for current_next_list */
382static int current_line_id = 0; /* unique number for current line */
383
384#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
385
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100386static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100387static void save_chartab(char_u *chartab);
388static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100389static int syn_match_linecont(linenr_T lnum);
390static void syn_start_line(void);
391static void syn_update_ends(int startofline);
392static void syn_stack_alloc(void);
393static int syn_stack_cleanup(void);
394static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
395static synstate_T *syn_stack_find_entry(linenr_T lnum);
396static synstate_T *store_current_state(void);
397static void load_current_state(synstate_T *from);
398static void invalidate_current_state(void);
399static int syn_stack_equal(synstate_T *sp);
400static void validate_current_state(void);
401static int syn_finish_line(int syncing);
402static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
403static int did_match_already(int idx, garray_T *gap);
404static stateitem_T *push_next_match(stateitem_T *cur_si);
405static void check_state_ends(void);
406static void update_si_attr(int idx);
407static void check_keepend(void);
408static void update_si_end(stateitem_T *sip, int startcol, int force);
409static short *copy_id_list(short *list);
410static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
411static int push_current_state(int idx);
412static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200413#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100414static void syn_clear_time(syn_time_T *tt);
415static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200416#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100417static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200420#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200422static int syn_time_on = FALSE;
423# define IF_SYN_TIME(p) (p)
424#else
425# define IF_SYN_TIME(p) NULL
426typedef int syn_time_T;
427#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100429static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
430static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
431static void clear_syn_state(synstate_T *p);
432static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000433
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100434static void limit_pos(lpos_T *pos, lpos_T *limit);
435static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
436static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
437static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
438static char_u *syn_getcurline(void);
439static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
440static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
441static void syn_cmd_case(exarg_T *eap, int syncing);
442static void syn_cmd_spell(exarg_T *eap, int syncing);
443static void syntax_sync_clear(void);
444static void syn_remove_pattern(synblock_T *block, int idx);
445static void syn_clear_pattern(synblock_T *block, int i);
446static void syn_clear_cluster(synblock_T *block, int i);
447static void syn_cmd_clear(exarg_T *eap, int syncing);
448static void syn_cmd_conceal(exarg_T *eap, int syncing);
449static void syn_clear_one(int id, int syncing);
450static void syn_cmd_on(exarg_T *eap, int syncing);
451static void syn_cmd_enable(exarg_T *eap, int syncing);
452static void syn_cmd_reset(exarg_T *eap, int syncing);
453static void syn_cmd_manual(exarg_T *eap, int syncing);
454static void syn_cmd_off(exarg_T *eap, int syncing);
455static void syn_cmd_onoff(exarg_T *eap, char *name);
456static void syn_cmd_list(exarg_T *eap, int syncing);
457static void syn_lines_msg(void);
458static void syn_match_msg(void);
459static void syn_stack_free_block(synblock_T *block);
460static void syn_list_one(int id, int syncing, int link_only);
461static void syn_list_cluster(int id);
462static void put_id_list(char_u *name, short *list, int attr);
463static void put_pattern(char *s, int c, synpat_T *spp, int attr);
464static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
465static void syn_clear_keyword(int id, hashtab_T *ht);
466static void clear_keywtab(hashtab_T *ht);
467static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
468static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100469static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100470static void syn_cmd_include(exarg_T *eap, int syncing);
471static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
472static void syn_cmd_keyword(exarg_T *eap, int syncing);
473static void syn_cmd_match(exarg_T *eap, int syncing);
474static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100476static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100478static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100480static void syn_cmd_cluster(exarg_T *eap, int syncing);
481static int syn_scl_name2id(char_u *name);
482static int syn_scl_namen2id(char_u *linep, int len);
483static int syn_check_cluster(char_u *pp, int len);
484static int syn_add_cluster(char_u *name);
485static void init_syn_patterns(void);
486static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
487static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100488static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100489static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
490static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491
492/*
493 * Start the syntax recognition for a line. This function is normally called
494 * from the screen updating, once for each displayed line.
495 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
496 * it. Careful: curbuf and curwin are likely to point to another buffer and
497 * window.
498 */
499 void
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200500syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501{
502 synstate_T *p;
503 synstate_T *last_valid = NULL;
504 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000505 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 linenr_T parsed_lnum;
507 linenr_T first_stored;
508 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100509 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200511#ifdef FEAT_CONCEAL
512 current_sub_char = NUL;
513#endif
514
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 /*
516 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000517 * Also do this when a change was made, the current state may be invalid
518 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200520 if (syn_block != wp->w_s
521 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100522 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 invalidate_current_state();
525 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200526 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100528 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 syn_win = wp;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200530#ifdef FEAT_RELTIME
531 syn_tm = syntax_tm;
532#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533
534 /*
535 * Allocate syntax stack when needed.
536 */
537 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200538 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000539 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541
542 /*
543 * If the state of the end of the previous line is useful, store it.
544 */
545 if (VALID_STATE(&current_state)
546 && current_lnum < lnum
547 && current_lnum < syn_buf->b_ml.ml_line_count)
548 {
549 (void)syn_finish_line(FALSE);
550 if (!current_state_stored)
551 {
552 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000553 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000554 }
555
556 /*
557 * If the current_lnum is now the same as "lnum", keep the current
558 * state (this happens very often!). Otherwise invalidate
559 * current_state and figure it out below.
560 */
561 if (current_lnum != lnum)
562 invalidate_current_state();
563 }
564 else
565 invalidate_current_state();
566
567 /*
568 * Try to synchronize from a saved state in b_sst_array[].
569 * Only do this if lnum is not before and not to far beyond a saved state.
570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200571 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572 {
573 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 {
576 if (p->sst_lnum > lnum)
577 break;
578 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
579 {
580 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200581 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000582 last_min_valid = p;
583 }
584 }
585 if (last_min_valid != NULL)
586 load_current_state(last_min_valid);
587 }
588
589 /*
590 * If "lnum" is before or far beyond a line with a saved state, need to
591 * re-synchronize.
592 */
593 if (INVALID_STATE(&current_state))
594 {
595 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200596 if (current_lnum == 1)
597 /* First line is always valid, no matter "minlines". */
598 first_stored = 1;
599 else
600 /* Need to parse "minlines" lines before state can be considered
601 * valid to store. */
602 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603 }
604 else
605 first_stored = current_lnum;
606
607 /*
608 * Advance from the sync point or saved state until the current line.
609 * Save some entries for syncing with later on.
610 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200611 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000612 dist = 999999;
613 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200614 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615 while (current_lnum < lnum)
616 {
617 syn_start_line();
618 (void)syn_finish_line(FALSE);
619 ++current_lnum;
620
621 /* If we parsed at least "minlines" lines or started at a valid
622 * state, the current state is considered valid. */
623 if (current_lnum >= first_stored)
624 {
625 /* Check if the saved state entry is for the current line and is
626 * equal to the current state. If so, then validate all saved
627 * states that depended on a change before the parsed line. */
628 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000629 prev = syn_stack_find_entry(current_lnum - 1);
630 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200631 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000632 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000633 sp = prev;
634 while (sp != NULL && sp->sst_lnum < current_lnum)
635 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636 if (sp != NULL
637 && sp->sst_lnum == current_lnum
638 && syn_stack_equal(sp))
639 {
640 parsed_lnum = current_lnum;
641 prev = sp;
642 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
643 {
644 if (sp->sst_lnum <= lnum)
645 /* valid state before desired line, use this one */
646 prev = sp;
647 else if (sp->sst_change_lnum == 0)
648 /* past saved states depending on change, break here. */
649 break;
650 sp->sst_change_lnum = 0;
651 sp = sp->sst_next;
652 }
653 load_current_state(prev);
654 }
655 /* Store the state at this line when it's the first one, the line
656 * where we start parsing, or some distance from the previously
657 * saved state. But only when parsed at least 'minlines'. */
658 else if (prev == NULL
659 || current_lnum == lnum
660 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000661 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662 }
663
664 /* This can take a long time: break when CTRL-C pressed. The current
665 * state will be wrong then. */
666 line_breakcheck();
667 if (got_int)
668 {
669 current_lnum = lnum;
670 break;
671 }
672 }
673
674 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000675}
676
677/*
678 * We cannot simply discard growarrays full of state_items or buf_states; we
679 * have to manually release their extmatch pointers first.
680 */
681 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100682clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683{
684 int i;
685 garray_T *gap;
686
687 if (p->sst_stacksize > SST_FIX_STATES)
688 {
689 gap = &(p->sst_union.sst_ga);
690 for (i = 0; i < gap->ga_len; i++)
691 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
692 ga_clear(gap);
693 }
694 else
695 {
696 for (i = 0; i < p->sst_stacksize; i++)
697 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
698 }
699}
700
701/*
702 * Cleanup the current_state stack.
703 */
704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100705clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706{
707 int i;
708 stateitem_T *sip;
709
710 sip = (stateitem_T *)(current_state.ga_data);
711 for (i = 0; i < current_state.ga_len; i++)
712 unref_extmatch(sip[i].si_extmatch);
713 ga_clear(&current_state);
714}
715
716/*
717 * Try to find a synchronisation point for line "lnum".
718 *
719 * This sets current_lnum and the current state. One of three methods is
720 * used:
721 * 1. Search backwards for the end of a C-comment.
722 * 2. Search backwards for given sync patterns.
723 * 3. Simply start on a given number of lines above "lnum".
724 */
725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100726syn_sync(
727 win_T *wp,
728 linenr_T start_lnum,
729 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730{
731 buf_T *curbuf_save;
732 win_T *curwin_save;
733 pos_T cursor_save;
734 int idx;
735 linenr_T lnum;
736 linenr_T end_lnum;
737 linenr_T break_lnum;
738 int had_sync_point;
739 stateitem_T *cur_si;
740 synpat_T *spp;
741 char_u *line;
742 int found_flags = 0;
743 int found_match_idx = 0;
744 linenr_T found_current_lnum = 0;
745 int found_current_col= 0;
746 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000747 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748
749 /*
750 * Clear any current state that might be hanging around.
751 */
752 invalidate_current_state();
753
754 /*
755 * Start at least "minlines" back. Default starting point for parsing is
756 * there.
757 * Start further back, to avoid that scrolling backwards will result in
758 * resyncing for every line. Now it resyncs only one out of N lines,
759 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
760 * Watch out for overflow when minlines is MAXLNUM.
761 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200762 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763 start_lnum = 1;
764 else
765 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200766 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200768 else if (syn_block->b_syn_sync_minlines < 10)
769 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
772 if (syn_block->b_syn_sync_maxlines != 0
773 && lnum > syn_block->b_syn_sync_maxlines)
774 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775 if (lnum >= start_lnum)
776 start_lnum = 1;
777 else
778 start_lnum -= lnum;
779 }
780 current_lnum = start_lnum;
781
782 /*
783 * 1. Search backwards for the end of a C-style comment.
784 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200785 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 {
787 /* Need to make syn_buf the current buffer for a moment, to be able to
788 * use find_start_comment(). */
789 curwin_save = curwin;
790 curwin = wp;
791 curbuf_save = curbuf;
792 curbuf = syn_buf;
793
794 /*
795 * Skip lines that end in a backslash.
796 */
797 for ( ; start_lnum > 1; --start_lnum)
798 {
799 line = ml_get(start_lnum - 1);
800 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
801 break;
802 }
803 current_lnum = start_lnum;
804
805 /* set cursor to start of search */
806 cursor_save = wp->w_cursor;
807 wp->w_cursor.lnum = start_lnum;
808 wp->w_cursor.col = 0;
809
810 /*
811 * If the line is inside a comment, need to find the syntax item that
812 * defines the comment.
813 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
814 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200815 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000816 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200817 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
818 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
819 == syn_block->b_syn_sync_id
820 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821 {
822 validate_current_state();
823 if (push_current_state(idx) == OK)
824 update_si_attr(current_state.ga_len - 1);
825 break;
826 }
827 }
828
829 /* restore cursor and buffer */
830 wp->w_cursor = cursor_save;
831 curwin = curwin_save;
832 curbuf = curbuf_save;
833 }
834
835 /*
836 * 2. Search backwards for given sync patterns.
837 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200838 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200840 if (syn_block->b_syn_sync_maxlines != 0
841 && start_lnum > syn_block->b_syn_sync_maxlines)
842 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000843 else
844 break_lnum = 0;
845
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000846 found_m_endpos.lnum = 0;
847 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000848 end_lnum = start_lnum;
849 lnum = start_lnum;
850 while (--lnum > break_lnum)
851 {
852 /* This can take a long time: break when CTRL-C pressed. */
853 line_breakcheck();
854 if (got_int)
855 {
856 invalidate_current_state();
857 current_lnum = start_lnum;
858 break;
859 }
860
861 /* Check if we have run into a valid saved state stack now. */
862 if (last_valid != NULL && lnum == last_valid->sst_lnum)
863 {
864 load_current_state(last_valid);
865 break;
866 }
867
868 /*
869 * Check if the previous line has the line-continuation pattern.
870 */
871 if (lnum > 1 && syn_match_linecont(lnum - 1))
872 continue;
873
874 /*
875 * Start with nothing on the state stack
876 */
877 validate_current_state();
878
879 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
880 {
881 syn_start_line();
882 for (;;)
883 {
884 had_sync_point = syn_finish_line(TRUE);
885 /*
886 * When a sync point has been found, remember where, and
887 * continue to look for another one, further on in the line.
888 */
889 if (had_sync_point && current_state.ga_len)
890 {
891 cur_si = &CUR_STATE(current_state.ga_len - 1);
892 if (cur_si->si_m_endpos.lnum > start_lnum)
893 {
894 /* ignore match that goes to after where started */
895 current_lnum = end_lnum;
896 break;
897 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000898 if (cur_si->si_idx < 0)
899 {
900 /* Cannot happen? */
901 found_flags = 0;
902 found_match_idx = KEYWORD_IDX;
903 }
904 else
905 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200906 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000907 found_flags = spp->sp_flags;
908 found_match_idx = spp->sp_sync_idx;
909 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 found_current_lnum = current_lnum;
911 found_current_col = current_col;
912 found_m_endpos = cur_si->si_m_endpos;
913 /*
914 * Continue after the match (be aware of a zero-length
915 * match).
916 */
917 if (found_m_endpos.lnum > current_lnum)
918 {
919 current_lnum = found_m_endpos.lnum;
920 current_col = found_m_endpos.col;
921 if (current_lnum >= end_lnum)
922 break;
923 }
924 else if (found_m_endpos.col > current_col)
925 current_col = found_m_endpos.col;
926 else
927 ++current_col;
928
929 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000930 * an item that ends here, need to do that now. Be
931 * careful not to go past the NUL. */
932 prev_current_col = current_col;
933 if (syn_getcurline()[current_col] != NUL)
934 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000936 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 }
938 else
939 break;
940 }
941 }
942
943 /*
944 * If a sync point was encountered, break here.
945 */
946 if (found_flags)
947 {
948 /*
949 * Put the item that was specified by the sync point on the
950 * state stack. If there was no item specified, make the
951 * state stack empty.
952 */
953 clear_current_state();
954 if (found_match_idx >= 0
955 && push_current_state(found_match_idx) == OK)
956 update_si_attr(current_state.ga_len - 1);
957
958 /*
959 * When using "grouphere", continue from the sync point
960 * match, until the end of the line. Parsing starts at
961 * the next line.
962 * For "groupthere" the parsing starts at start_lnum.
963 */
964 if (found_flags & HL_SYNC_HERE)
965 {
966 if (current_state.ga_len)
967 {
968 cur_si = &CUR_STATE(current_state.ga_len - 1);
969 cur_si->si_h_startpos.lnum = found_current_lnum;
970 cur_si->si_h_startpos.col = found_current_col;
971 update_si_end(cur_si, (int)current_col, TRUE);
972 check_keepend();
973 }
974 current_col = found_m_endpos.col;
975 current_lnum = found_m_endpos.lnum;
976 (void)syn_finish_line(FALSE);
977 ++current_lnum;
978 }
979 else
980 current_lnum = start_lnum;
981
982 break;
983 }
984
985 end_lnum = lnum;
986 invalidate_current_state();
987 }
988
989 /* Ran into start of the file or exceeded maximum number of lines */
990 if (lnum <= break_lnum)
991 {
992 invalidate_current_state();
993 current_lnum = break_lnum + 1;
994 }
995 }
996
997 validate_current_state();
998}
999
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001000 static void
1001save_chartab(char_u *chartab)
1002{
1003 if (syn_block->b_syn_isk != empty_option)
1004 {
1005 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1006 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1007 (size_t)32);
1008 }
1009}
1010
1011 static void
1012restore_chartab(char_u *chartab)
1013{
1014 if (syn_win->w_s->b_syn_isk != empty_option)
1015 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1016}
1017
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018/*
1019 * Return TRUE if the line-continuation pattern matches in line "lnum".
1020 */
1021 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001022syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023{
1024 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001025 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001026 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027
Bram Moolenaar860cae12010-06-05 23:22:07 +02001028 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001030 /* use syntax iskeyword option */
1031 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001032 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1033 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001034 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001035 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001036 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001037 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001038 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 }
1040 return FALSE;
1041}
1042
1043/*
1044 * Prepare the current state for the start of a line.
1045 */
1046 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001047syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048{
1049 current_finished = FALSE;
1050 current_col = 0;
1051
1052 /*
1053 * Need to update the end of a start/skip/end that continues from the
1054 * previous line and regions that have "keepend".
1055 */
1056 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001057 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001059 check_state_ends();
1060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001061
1062 next_match_idx = -1;
1063 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001064#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001065 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001066#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067}
1068
1069/*
1070 * Check for items in the stack that need their end updated.
1071 * When "startofline" is TRUE the last item is always updated.
1072 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1073 */
1074 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001075syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076{
1077 stateitem_T *cur_si;
1078 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001079 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080
1081 if (startofline)
1082 {
1083 /* Check for a match carried over from a previous line with a
1084 * contained region. The match ends as soon as the region ends. */
1085 for (i = 0; i < current_state.ga_len; ++i)
1086 {
1087 cur_si = &CUR_STATE(i);
1088 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001089 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001090 == SPTYPE_MATCH
1091 && cur_si->si_m_endpos.lnum < current_lnum)
1092 {
1093 cur_si->si_flags |= HL_MATCHCONT;
1094 cur_si->si_m_endpos.lnum = 0;
1095 cur_si->si_m_endpos.col = 0;
1096 cur_si->si_h_endpos = cur_si->si_m_endpos;
1097 cur_si->si_ends = TRUE;
1098 }
1099 }
1100 }
1101
1102 /*
1103 * Need to update the end of a start/skip/end that continues from the
1104 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001105 * influence contained items. If we've just removed "extend"
1106 * (startofline == 0) then we should update ends of normal regions
1107 * contained inside "keepend" because "extend" could have extended
1108 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001109 * Then check for items ending in column 0.
1110 */
1111 i = current_state.ga_len - 1;
1112 if (keepend_level >= 0)
1113 for ( ; i > keepend_level; --i)
1114 if (CUR_STATE(i).si_flags & HL_EXTEND)
1115 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001116
1117 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001118 for ( ; i < current_state.ga_len; ++i)
1119 {
1120 cur_si = &CUR_STATE(i);
1121 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001122 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123 || (i == current_state.ga_len - 1 && startofline))
1124 {
1125 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1126 cur_si->si_h_startpos.lnum = current_lnum;
1127
1128 if (!(cur_si->si_flags & HL_MATCHCONT))
1129 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001130
1131 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1132 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133 }
1134 }
1135 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001136}
1137
1138/****************************************
1139 * Handling of the state stack cache.
1140 */
1141
1142/*
1143 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1144 *
1145 * To speed up syntax highlighting, the state stack for the start of some
1146 * lines is cached. These entries can be used to start parsing at that point.
1147 *
1148 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1149 * valid entries. b_sst_first points to the first one, then follow sst_next.
1150 * The entries are sorted on line number. The first entry is often for line 2
1151 * (line 1 always starts with an empty stack).
1152 * There is also a list for free entries. This construction is used to avoid
1153 * having to allocate and free memory blocks too often.
1154 *
1155 * When making changes to the buffer, this is logged in b_mod_*. When calling
1156 * update_screen() to update the display, it will call
1157 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1158 * entries. The entries which are inside the changed area are removed,
1159 * because they must be recomputed. Entries below the changed have their line
1160 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1161 * set to indicate that a check must be made if the changed lines would change
1162 * the cached entry.
1163 *
1164 * When later displaying lines, an entry is stored for each line. Displayed
1165 * lines are likely to be displayed again, in which case the state at the
1166 * start of the line is needed.
1167 * For not displayed lines, an entry is stored for every so many lines. These
1168 * entries will be used e.g., when scrolling backwards. The distance between
1169 * entries depends on the number of lines in the buffer. For small buffers
1170 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1171 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1172 */
1173
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001175syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001176{
1177 synstate_T *p;
1178
1179 if (block->b_sst_array != NULL)
1180 {
1181 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1182 clear_syn_state(p);
1183 vim_free(block->b_sst_array);
1184 block->b_sst_array = NULL;
1185 block->b_sst_len = 0;
1186 }
1187}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188/*
1189 * Free b_sst_array[] for buffer "buf".
1190 * Used when syntax items changed to force resyncing everywhere.
1191 */
1192 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001193syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001195#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001197#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198
Bram Moolenaar860cae12010-06-05 23:22:07 +02001199 syn_stack_free_block(block);
1200
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201#ifdef FEAT_FOLDING
1202 /* When using "syntax" fold method, must update all folds. */
1203 FOR_ALL_WINDOWS(wp)
1204 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001205 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 foldUpdateAll(wp);
1207 }
1208#endif
1209}
1210
1211/*
1212 * Allocate the syntax state stack for syn_buf when needed.
1213 * If the number of entries in b_sst_array[] is much too big or a bit too
1214 * small, reallocate it.
1215 * Also used to allocate b_sst_array[] for the first time.
1216 */
1217 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001218syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219{
1220 long len;
1221 synstate_T *to, *from;
1222 synstate_T *sstp;
1223
1224 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1225 if (len < SST_MIN_ENTRIES)
1226 len = SST_MIN_ENTRIES;
1227 else if (len > SST_MAX_ENTRIES)
1228 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 {
1231 /* Allocate 50% too much, to avoid reallocating too often. */
1232 len = syn_buf->b_ml.ml_line_count;
1233 len = (len + len / 2) / SST_DIST + Rows * 2;
1234 if (len < SST_MIN_ENTRIES)
1235 len = SST_MIN_ENTRIES;
1236 else if (len > SST_MAX_ENTRIES)
1237 len = SST_MAX_ENTRIES;
1238
Bram Moolenaar860cae12010-06-05 23:22:07 +02001239 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240 {
1241 /* When shrinking the array, cleanup the existing stack.
1242 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 && syn_stack_cleanup())
1245 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001246 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1247 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 }
1249
1250 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1251 if (sstp == NULL) /* out of memory! */
1252 return;
1253
1254 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001255 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 {
1257 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 from = from->sst_next)
1260 {
1261 ++to;
1262 *to = *from;
1263 to->sst_next = to + 1;
1264 }
1265 }
1266 if (to != sstp - 1)
1267 {
1268 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 syn_block->b_sst_first = sstp;
1270 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 }
1272 else
1273 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001274 syn_block->b_sst_first = NULL;
1275 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001276 }
1277
1278 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280 while (++to < sstp + len)
1281 to->sst_next = to + 1;
1282 (sstp + len - 1)->sst_next = NULL;
1283
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 vim_free(syn_block->b_sst_array);
1285 syn_block->b_sst_array = sstp;
1286 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287 }
1288}
1289
1290/*
1291 * Check for changes in a buffer to affect stored syntax states. Uses the
1292 * b_mod_* fields.
1293 * Called from update_screen(), before screen is being updated, once for each
1294 * displayed buffer.
1295 */
1296 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001297syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001299 win_T *wp;
1300
1301 syn_stack_apply_changes_block(&buf->b_s, buf);
1302
1303 FOR_ALL_WINDOWS(wp)
1304 {
1305 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1306 syn_stack_apply_changes_block(wp->w_s, buf);
1307 }
1308}
1309
1310 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001311syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001312{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 synstate_T *p, *prev, *np;
1314 linenr_T n;
1315
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 return;
1318
1319 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001320 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 {
1324 n = p->sst_lnum + buf->b_mod_xlines;
1325 if (n <= buf->b_mod_bot)
1326 {
1327 /* this state is inside the changed area, remove it */
1328 np = p->sst_next;
1329 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 else
1332 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001333 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334 p = np;
1335 continue;
1336 }
1337 /* This state is below the changed area. Remember the line
1338 * that needs to be parsed before this entry can be made valid
1339 * again. */
1340 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1341 {
1342 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1343 p->sst_change_lnum += buf->b_mod_xlines;
1344 else
1345 p->sst_change_lnum = buf->b_mod_top;
1346 }
1347 if (p->sst_change_lnum == 0
1348 || p->sst_change_lnum < buf->b_mod_bot)
1349 p->sst_change_lnum = buf->b_mod_bot;
1350
1351 p->sst_lnum = n;
1352 }
1353 prev = p;
1354 p = p->sst_next;
1355 }
1356}
1357
1358/*
1359 * Reduce the number of entries in the state stack for syn_buf.
1360 * Returns TRUE if at least one entry was freed.
1361 */
1362 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001363syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364{
1365 synstate_T *p, *prev;
1366 disptick_T tick;
1367 int above;
1368 int dist;
1369 int retval = FALSE;
1370
Bram Moolenaar860cae12010-06-05 23:22:07 +02001371 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372 return retval;
1373
1374 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001376 dist = 999999;
1377 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001378 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379
1380 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001381 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 * be removed. Set "above" when the "tick" for the oldest entry is above
1383 * "b_sst_lasttick" (the display tick wraps around).
1384 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001387 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001388 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1389 {
1390 if (prev->sst_lnum + dist > p->sst_lnum)
1391 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001392 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 {
1394 if (!above || p->sst_tick < tick)
1395 tick = p->sst_tick;
1396 above = TRUE;
1397 }
1398 else if (!above && p->sst_tick < tick)
1399 tick = p->sst_tick;
1400 }
1401 }
1402
1403 /*
1404 * Go through the list to make the entries for the oldest tick at an
1405 * interval of several lines.
1406 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001407 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1409 {
1410 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1411 {
1412 /* Move this entry from used list to free list */
1413 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001414 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415 p = prev;
1416 retval = TRUE;
1417 }
1418 }
1419 return retval;
1420}
1421
1422/*
1423 * Free the allocated memory for a syn_state item.
1424 * Move the entry into the free list.
1425 */
1426 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001427syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428{
1429 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001430 p->sst_next = block->b_sst_firstfree;
1431 block->b_sst_firstfree = p;
1432 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433}
1434
1435/*
1436 * Find an entry in the list of state stacks at or before "lnum".
1437 * Returns NULL when there is no entry or the first entry is after "lnum".
1438 */
1439 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001440syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441{
1442 synstate_T *p, *prev;
1443
1444 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001445 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446 {
1447 if (p->sst_lnum == lnum)
1448 return p;
1449 if (p->sst_lnum > lnum)
1450 break;
1451 }
1452 return prev;
1453}
1454
1455/*
1456 * Try saving the current state in b_sst_array[].
1457 * The current state must be valid for the start of the current_lnum line!
1458 */
1459 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001460store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461{
1462 int i;
1463 synstate_T *p;
1464 bufstate_T *bp;
1465 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001466 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467
1468 /*
1469 * If the current state contains a start or end pattern that continues
1470 * from the previous line, we can't use it. Don't store it then.
1471 */
1472 for (i = current_state.ga_len - 1; i >= 0; --i)
1473 {
1474 cur_si = &CUR_STATE(i);
1475 if (cur_si->si_h_startpos.lnum >= current_lnum
1476 || cur_si->si_m_endpos.lnum >= current_lnum
1477 || cur_si->si_h_endpos.lnum >= current_lnum
1478 || (cur_si->si_end_idx
1479 && cur_si->si_eoe_pos.lnum >= current_lnum))
1480 break;
1481 }
1482 if (i >= 0)
1483 {
1484 if (sp != NULL)
1485 {
1486 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001487 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001489 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 else
1491 {
1492 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001493 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 if (p->sst_next == sp)
1495 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001496 if (p != NULL) /* just in case */
1497 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 sp = NULL;
1501 }
1502 }
1503 else if (sp == NULL || sp->sst_lnum != current_lnum)
1504 {
1505 /*
1506 * Add a new entry
1507 */
1508 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001509 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 {
1511 (void)syn_stack_cleanup();
1512 /* "sp" may have been moved to the freelist now */
1513 sp = syn_stack_find_entry(current_lnum);
1514 }
1515 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001516 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 sp = NULL;
1518 else
1519 {
1520 /* Take the first item from the free list and put it in the used
1521 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001522 p = syn_block->b_sst_firstfree;
1523 syn_block->b_sst_firstfree = p->sst_next;
1524 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 if (sp == NULL)
1526 {
1527 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001528 p->sst_next = syn_block->b_sst_first;
1529 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001530 }
1531 else
1532 {
1533 /* insert in list after *sp */
1534 p->sst_next = sp->sst_next;
1535 sp->sst_next = p;
1536 }
1537 sp = p;
1538 sp->sst_stacksize = 0;
1539 sp->sst_lnum = current_lnum;
1540 }
1541 }
1542 if (sp != NULL)
1543 {
1544 /* When overwriting an existing state stack, clear it first */
1545 clear_syn_state(sp);
1546 sp->sst_stacksize = current_state.ga_len;
1547 if (current_state.ga_len > SST_FIX_STATES)
1548 {
1549 /* Need to clear it, might be something remaining from when the
1550 * length was less than SST_FIX_STATES. */
1551 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1552 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1553 sp->sst_stacksize = 0;
1554 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001555 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1557 }
1558 else
1559 bp = sp->sst_union.sst_stack;
1560 for (i = 0; i < sp->sst_stacksize; ++i)
1561 {
1562 bp[i].bs_idx = CUR_STATE(i).si_idx;
1563 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001564#ifdef FEAT_CONCEAL
1565 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1566 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1567#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1569 }
1570 sp->sst_next_flags = current_next_flags;
1571 sp->sst_next_list = current_next_list;
1572 sp->sst_tick = display_tick;
1573 sp->sst_change_lnum = 0;
1574 }
1575 current_state_stored = TRUE;
1576 return sp;
1577}
1578
1579/*
1580 * Copy a state stack from "from" in b_sst_array[] to current_state;
1581 */
1582 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001583load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001584{
1585 int i;
1586 bufstate_T *bp;
1587
1588 clear_current_state();
1589 validate_current_state();
1590 keepend_level = -1;
1591 if (from->sst_stacksize
1592 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1593 {
1594 if (from->sst_stacksize > SST_FIX_STATES)
1595 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1596 else
1597 bp = from->sst_union.sst_stack;
1598 for (i = 0; i < from->sst_stacksize; ++i)
1599 {
1600 CUR_STATE(i).si_idx = bp[i].bs_idx;
1601 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001602#ifdef FEAT_CONCEAL
1603 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1604 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1605#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1607 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1608 keepend_level = i;
1609 CUR_STATE(i).si_ends = FALSE;
1610 CUR_STATE(i).si_m_lnum = 0;
1611 if (CUR_STATE(i).si_idx >= 0)
1612 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001613 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614 else
1615 CUR_STATE(i).si_next_list = NULL;
1616 update_si_attr(i);
1617 }
1618 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001619 }
1620 current_next_list = from->sst_next_list;
1621 current_next_flags = from->sst_next_flags;
1622 current_lnum = from->sst_lnum;
1623}
1624
1625/*
1626 * Compare saved state stack "*sp" with the current state.
1627 * Return TRUE when they are equal.
1628 */
1629 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001630syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001631{
1632 int i, j;
1633 bufstate_T *bp;
1634 reg_extmatch_T *six, *bsx;
1635
1636 /* First a quick check if the stacks have the same size end nextlist. */
1637 if (sp->sst_stacksize == current_state.ga_len
1638 && sp->sst_next_list == current_next_list)
1639 {
1640 /* Need to compare all states on both stacks. */
1641 if (sp->sst_stacksize > SST_FIX_STATES)
1642 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1643 else
1644 bp = sp->sst_union.sst_stack;
1645
1646 for (i = current_state.ga_len; --i >= 0; )
1647 {
1648 /* If the item has another index the state is different. */
1649 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1650 break;
1651 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1652 {
1653 /* When the extmatch pointers are different, the strings in
1654 * them can still be the same. Check if the extmatch
1655 * references are equal. */
1656 bsx = bp[i].bs_extmatch;
1657 six = CUR_STATE(i).si_extmatch;
1658 /* If one of the extmatch pointers is NULL the states are
1659 * different. */
1660 if (bsx == NULL || six == NULL)
1661 break;
1662 for (j = 0; j < NSUBEXP; ++j)
1663 {
1664 /* Check each referenced match string. They must all be
1665 * equal. */
1666 if (bsx->matches[j] != six->matches[j])
1667 {
1668 /* If the pointer is different it can still be the
1669 * same text. Compare the strings, ignore case when
1670 * the start item has the sp_ic flag set. */
1671 if (bsx->matches[j] == NULL
1672 || six->matches[j] == NULL)
1673 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001674 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675 ? MB_STRICMP(bsx->matches[j],
1676 six->matches[j]) != 0
1677 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1678 break;
1679 }
1680 }
1681 if (j != NSUBEXP)
1682 break;
1683 }
1684 }
1685 if (i < 0)
1686 return TRUE;
1687 }
1688 return FALSE;
1689}
1690
1691/*
1692 * We stop parsing syntax above line "lnum". If the stored state at or below
1693 * this line depended on a change before it, it now depends on the line below
1694 * the last parsed line.
1695 * The window looks like this:
1696 * line which changed
1697 * displayed line
1698 * displayed line
1699 * lnum -> line below window
1700 */
1701 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001702syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703{
1704 synstate_T *sp;
1705
1706 sp = syn_stack_find_entry(lnum);
1707 if (sp != NULL && sp->sst_lnum < lnum)
1708 sp = sp->sst_next;
1709
1710 if (sp != NULL && sp->sst_change_lnum != 0)
1711 sp->sst_change_lnum = lnum;
1712}
1713
1714/*
1715 * End of handling of the state stack.
1716 ****************************************/
1717
1718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001719invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720{
1721 clear_current_state();
1722 current_state.ga_itemsize = 0; /* mark current_state invalid */
1723 current_next_list = NULL;
1724 keepend_level = -1;
1725}
1726
1727 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001728validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729{
1730 current_state.ga_itemsize = sizeof(stateitem_T);
1731 current_state.ga_growsize = 3;
1732}
1733
1734/*
1735 * Return TRUE if the syntax at start of lnum changed since last time.
1736 * This will only be called just after get_syntax_attr() for the previous
1737 * line, to check if the next line needs to be redrawn too.
1738 */
1739 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001740syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741{
1742 int retval = TRUE;
1743 synstate_T *sp;
1744
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 /*
1746 * Check the state stack when:
1747 * - lnum is just below the previously syntaxed line.
1748 * - lnum is not before the lines with saved states.
1749 * - lnum is not past the lines with saved states.
1750 * - lnum is at or before the last changed line.
1751 */
1752 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1753 {
1754 sp = syn_stack_find_entry(lnum);
1755 if (sp != NULL && sp->sst_lnum == lnum)
1756 {
1757 /*
1758 * finish the previous line (needed when not all of the line was
1759 * drawn)
1760 */
1761 (void)syn_finish_line(FALSE);
1762
1763 /*
1764 * Compare the current state with the previously saved state of
1765 * the line.
1766 */
1767 if (syn_stack_equal(sp))
1768 retval = FALSE;
1769
1770 /*
1771 * Store the current state in b_sst_array[] for later use.
1772 */
1773 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001774 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 }
1776 }
1777
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 return retval;
1779}
1780
1781/*
1782 * Finish the current line.
1783 * This doesn't return any attributes, it only gets the state at the end of
1784 * the line. It can start anywhere in the line, as long as the current state
1785 * is valid.
1786 */
1787 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001788syn_finish_line(
1789 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790{
1791 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001792 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001794 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001796 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1797 /*
1798 * When syncing, and found some item, need to check the item.
1799 */
1800 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001803 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001805 cur_si = &CUR_STATE(current_state.ga_len - 1);
1806 if (cur_si->si_idx >= 0
1807 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1808 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1809 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001811 /* syn_current_attr() will have skipped the check for an item
1812 * that ends here, need to do that now. Be careful not to go
1813 * past the NUL. */
1814 prev_current_col = current_col;
1815 if (syn_getcurline()[current_col] != NUL)
1816 ++current_col;
1817 check_state_ends();
1818 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001820 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821 }
1822 return FALSE;
1823}
1824
1825/*
1826 * Return highlight attributes for next character.
1827 * Must first call syntax_start() once for the line.
1828 * "col" is normally 0 for the first use in a line, and increments by one each
1829 * time. It's allowed to skip characters and to stop before the end of the
1830 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001831 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1832 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 */
1834 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001835get_syntax_attr(
1836 colnr_T col,
1837 int *can_spell,
1838 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839{
1840 int attr = 0;
1841
Bram Moolenaar349955a2007-08-14 21:07:36 +00001842 if (can_spell != NULL)
1843 /* Default: Only do spelling when there is no @Spell cluster or when
1844 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001845 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1846 ? (syn_block->b_spell_cluster_id == 0)
1847 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001848
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001850 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001851 return 0;
1852
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001853 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001854 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001855 {
1856 clear_current_state();
1857#ifdef FEAT_EVAL
1858 current_id = 0;
1859 current_trans_id = 0;
1860#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001861#ifdef FEAT_CONCEAL
1862 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001863 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001864#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001865 return 0;
1866 }
1867
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868 /* Make sure current_state is valid */
1869 if (INVALID_STATE(&current_state))
1870 validate_current_state();
1871
1872 /*
1873 * Skip from the current column to "col", get the attributes for "col".
1874 */
1875 while (current_col <= col)
1876 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001877 attr = syn_current_attr(FALSE, TRUE, can_spell,
1878 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879 ++current_col;
1880 }
1881
Bram Moolenaar071d4272004-06-13 20:20:40 +00001882 return attr;
1883}
1884
1885/*
1886 * Get syntax attributes for current_lnum, current_col.
1887 */
1888 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001889syn_current_attr(
1890 int syncing, /* When 1: called for syncing */
1891 int displaying, /* result will be displayed */
1892 int *can_spell, /* return: do spell checking */
1893 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894{
1895 int syn_id;
1896 lpos_T endpos; /* was: char_u *endp; */
1897 lpos_T hl_startpos; /* was: int hl_startcol; */
1898 lpos_T hl_endpos;
1899 lpos_T eos_pos; /* end-of-start match (start region) */
1900 lpos_T eoe_pos; /* end-of-end pattern */
1901 int end_idx; /* group ID for end pattern */
1902 int idx;
1903 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001904 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905 int startcol;
1906 int endcol;
1907 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001908 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909 short *next_list;
1910 int found_match; /* found usable match */
1911 static int try_next_column = FALSE; /* must try in next col */
1912 int do_keywords;
1913 regmmatch_T regmatch;
1914 lpos_T pos;
1915 int lc_col;
1916 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001917 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 char_u *line; /* current line. NOTE: becomes invalid after
1919 looking for a pattern match! */
1920
1921 /* variables for zero-width matches that have a "nextgroup" argument */
1922 int keep_next_list;
1923 int zero_width_next_list = FALSE;
1924 garray_T zero_width_next_ga;
1925
1926 /*
1927 * No character, no attributes! Past end of line?
1928 * Do try matching with an empty line (could be the start of a region).
1929 */
1930 line = syn_getcurline();
1931 if (line[current_col] == NUL && current_col != 0)
1932 {
1933 /*
1934 * If we found a match after the last column, use it.
1935 */
1936 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1937 && next_match_col != MAXCOL)
1938 (void)push_next_match(NULL);
1939
1940 current_finished = TRUE;
1941 current_state_stored = FALSE;
1942 return 0;
1943 }
1944
1945 /* if the current or next character is NUL, we will finish the line now */
1946 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1947 {
1948 current_finished = TRUE;
1949 current_state_stored = FALSE;
1950 }
1951
1952 /*
1953 * When in the previous column there was a match but it could not be used
1954 * (empty match or already matched in this column) need to try again in
1955 * the next column.
1956 */
1957 if (try_next_column)
1958 {
1959 next_match_idx = -1;
1960 try_next_column = FALSE;
1961 }
1962
1963 /* Only check for keywords when not syncing and there are some. */
1964 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001965 && (syn_block->b_keywtab.ht_used > 0
1966 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967
1968 /* Init the list of zero-width matches with a nextlist. This is used to
1969 * avoid matching the same item in the same position twice. */
1970 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1971
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001972 /* use syntax iskeyword option */
1973 save_chartab(buf_chartab);
1974
Bram Moolenaar071d4272004-06-13 20:20:40 +00001975 /*
1976 * Repeat matching keywords and patterns, to find contained items at the
1977 * same column. This stops when there are no extra matches at the current
1978 * column.
1979 */
1980 do
1981 {
1982 found_match = FALSE;
1983 keep_next_list = FALSE;
1984 syn_id = 0;
1985
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001986
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987 /*
1988 * 1. Check for a current state.
1989 * Only when there is no current state, or if the current state may
1990 * contain other things, we need to check for keywords and patterns.
1991 * Always need to check for contained items if some item has the
1992 * "containedin" argument (takes extra time!).
1993 */
1994 if (current_state.ga_len)
1995 cur_si = &CUR_STATE(current_state.ga_len - 1);
1996 else
1997 cur_si = NULL;
1998
Bram Moolenaar860cae12010-06-05 23:22:07 +02001999 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002000 || cur_si->si_cont_list != NULL)
2001 {
2002 /*
2003 * 2. Check for keywords, if on a keyword char after a non-keyword
2004 * char. Don't do this when syncing.
2005 */
2006 if (do_keywords)
2007 {
2008 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002009 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002011 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012#ifdef FEAT_MBYTE
2013 - (has_mbyte
2014 ? (*mb_head_off)(line, line + current_col - 1)
2015 : 0)
2016#endif
2017 , syn_buf)))
2018 {
2019 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002020 &endcol, &flags, &next_list, cur_si,
2021 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002022 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 {
2024 if (push_current_state(KEYWORD_IDX) == OK)
2025 {
2026 cur_si = &CUR_STATE(current_state.ga_len - 1);
2027 cur_si->si_m_startcol = current_col;
2028 cur_si->si_h_startpos.lnum = current_lnum;
2029 cur_si->si_h_startpos.col = 0; /* starts right away */
2030 cur_si->si_m_endpos.lnum = current_lnum;
2031 cur_si->si_m_endpos.col = endcol;
2032 cur_si->si_h_endpos.lnum = current_lnum;
2033 cur_si->si_h_endpos.col = endcol;
2034 cur_si->si_ends = TRUE;
2035 cur_si->si_end_idx = 0;
2036 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002037#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002038 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002039 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002040 if (current_state.ga_len > 1)
2041 cur_si->si_flags |=
2042 CUR_STATE(current_state.ga_len - 2).si_flags
2043 & HL_CONCEAL;
2044#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045 cur_si->si_id = syn_id;
2046 cur_si->si_trans_id = syn_id;
2047 if (flags & HL_TRANSP)
2048 {
2049 if (current_state.ga_len < 2)
2050 {
2051 cur_si->si_attr = 0;
2052 cur_si->si_trans_id = 0;
2053 }
2054 else
2055 {
2056 cur_si->si_attr = CUR_STATE(
2057 current_state.ga_len - 2).si_attr;
2058 cur_si->si_trans_id = CUR_STATE(
2059 current_state.ga_len - 2).si_trans_id;
2060 }
2061 }
2062 else
2063 cur_si->si_attr = syn_id2attr(syn_id);
2064 cur_si->si_cont_list = NULL;
2065 cur_si->si_next_list = next_list;
2066 check_keepend();
2067 }
2068 else
2069 vim_free(next_list);
2070 }
2071 }
2072 }
2073
2074 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002075 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002077 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 {
2079 /*
2080 * If we didn't check for a match yet, or we are past it, check
2081 * for any match with a pattern.
2082 */
2083 if (next_match_idx < 0 || next_match_col < (int)current_col)
2084 {
2085 /*
2086 * Check all relevant patterns for a match at this
2087 * position. This is complicated, because matching with a
2088 * pattern takes quite a bit of time, thus we want to
2089 * avoid doing it when it's not needed.
2090 */
2091 next_match_idx = 0; /* no match in this line yet */
2092 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002093 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002094 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002095 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 if ( spp->sp_syncing == syncing
2097 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2098 && (spp->sp_type == SPTYPE_MATCH
2099 || spp->sp_type == SPTYPE_START)
2100 && (current_next_list != NULL
2101 ? in_id_list(NULL, current_next_list,
2102 &spp->sp_syn, 0)
2103 : (cur_si == NULL
2104 ? !(spp->sp_flags & HL_CONTAINED)
2105 : in_id_list(cur_si,
2106 cur_si->si_cont_list, &spp->sp_syn,
2107 spp->sp_flags & HL_CONTAINED))))
2108 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002109 int r;
2110
Bram Moolenaar071d4272004-06-13 20:20:40 +00002111 /* If we already tried matching in this line, and
2112 * there isn't a match before next_match_col, skip
2113 * this item. */
2114 if (spp->sp_line_id == current_line_id
2115 && spp->sp_startcol >= next_match_col)
2116 continue;
2117 spp->sp_line_id = current_line_id;
2118
2119 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2120 if (lc_col < 0)
2121 lc_col = 0;
2122
2123 regmatch.rmm_ic = spp->sp_ic;
2124 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002125 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002126 current_lnum,
2127 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002128 IF_SYN_TIME(&spp->sp_time));
2129 spp->sp_prog = regmatch.regprog;
2130 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002131 {
2132 /* no match in this line, try another one */
2133 spp->sp_startcol = MAXCOL;
2134 continue;
2135 }
2136
2137 /*
2138 * Compute the first column of the match.
2139 */
2140 syn_add_start_off(&pos, &regmatch,
2141 spp, SPO_MS_OFF, -1);
2142 if (pos.lnum > current_lnum)
2143 {
2144 /* must have used end of match in a next line,
2145 * we can't handle that */
2146 spp->sp_startcol = MAXCOL;
2147 continue;
2148 }
2149 startcol = pos.col;
2150
2151 /* remember the next column where this pattern
2152 * matches in the current line */
2153 spp->sp_startcol = startcol;
2154
2155 /*
2156 * If a previously found match starts at a lower
2157 * column number, don't use this one.
2158 */
2159 if (startcol >= next_match_col)
2160 continue;
2161
2162 /*
2163 * If we matched this pattern at this position
2164 * before, skip it. Must retry in the next
2165 * column, because it may match from there.
2166 */
2167 if (did_match_already(idx, &zero_width_next_ga))
2168 {
2169 try_next_column = TRUE;
2170 continue;
2171 }
2172
2173 endpos.lnum = regmatch.endpos[0].lnum;
2174 endpos.col = regmatch.endpos[0].col;
2175
2176 /* Compute the highlight start. */
2177 syn_add_start_off(&hl_startpos, &regmatch,
2178 spp, SPO_HS_OFF, -1);
2179
2180 /* Compute the region start. */
2181 /* Default is to use the end of the match. */
2182 syn_add_end_off(&eos_pos, &regmatch,
2183 spp, SPO_RS_OFF, 0);
2184
2185 /*
2186 * Grab the external submatches before they get
2187 * overwritten. Reference count doesn't change.
2188 */
2189 unref_extmatch(cur_extmatch);
2190 cur_extmatch = re_extmatch_out;
2191 re_extmatch_out = NULL;
2192
2193 flags = 0;
2194 eoe_pos.lnum = 0; /* avoid warning */
2195 eoe_pos.col = 0;
2196 end_idx = 0;
2197 hl_endpos.lnum = 0;
2198
2199 /*
2200 * For a "oneline" the end must be found in the
2201 * same line too. Search for it after the end of
2202 * the match with the start pattern. Set the
2203 * resulting end positions at the same time.
2204 */
2205 if (spp->sp_type == SPTYPE_START
2206 && (spp->sp_flags & HL_ONELINE))
2207 {
2208 lpos_T startpos;
2209
2210 startpos = endpos;
2211 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2212 &flags, &eoe_pos, &end_idx, cur_extmatch);
2213 if (endpos.lnum == 0)
2214 continue; /* not found */
2215 }
2216
2217 /*
2218 * For a "match" the size must be > 0 after the
2219 * end offset needs has been added. Except when
2220 * syncing.
2221 */
2222 else if (spp->sp_type == SPTYPE_MATCH)
2223 {
2224 syn_add_end_off(&hl_endpos, &regmatch, spp,
2225 SPO_HE_OFF, 0);
2226 syn_add_end_off(&endpos, &regmatch, spp,
2227 SPO_ME_OFF, 0);
2228 if (endpos.lnum == current_lnum
2229 && (int)endpos.col + syncing < startcol)
2230 {
2231 /*
2232 * If an empty string is matched, may need
2233 * to try matching again at next column.
2234 */
2235 if (regmatch.startpos[0].col
2236 == regmatch.endpos[0].col)
2237 try_next_column = TRUE;
2238 continue;
2239 }
2240 }
2241
2242 /*
2243 * keep the best match so far in next_match_*
2244 */
2245 /* Highlighting must start after startpos and end
2246 * before endpos. */
2247 if (hl_startpos.lnum == current_lnum
2248 && (int)hl_startpos.col < startcol)
2249 hl_startpos.col = startcol;
2250 limit_pos_zero(&hl_endpos, &endpos);
2251
2252 next_match_idx = idx;
2253 next_match_col = startcol;
2254 next_match_m_endpos = endpos;
2255 next_match_h_endpos = hl_endpos;
2256 next_match_h_startpos = hl_startpos;
2257 next_match_flags = flags;
2258 next_match_eos_pos = eos_pos;
2259 next_match_eoe_pos = eoe_pos;
2260 next_match_end_idx = end_idx;
2261 unref_extmatch(next_match_extmatch);
2262 next_match_extmatch = cur_extmatch;
2263 cur_extmatch = NULL;
2264 }
2265 }
2266 }
2267
2268 /*
2269 * If we found a match at the current column, use it.
2270 */
2271 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2272 {
2273 synpat_T *lspp;
2274
2275 /* When a zero-width item matched which has a nextgroup,
2276 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002277 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278 if (next_match_m_endpos.lnum == current_lnum
2279 && next_match_m_endpos.col == current_col
2280 && lspp->sp_next_list != NULL)
2281 {
2282 current_next_list = lspp->sp_next_list;
2283 current_next_flags = lspp->sp_flags;
2284 keep_next_list = TRUE;
2285 zero_width_next_list = TRUE;
2286
2287 /* Add the index to a list, so that we can check
2288 * later that we don't match it again (and cause an
2289 * endless loop). */
2290 if (ga_grow(&zero_width_next_ga, 1) == OK)
2291 {
2292 ((int *)(zero_width_next_ga.ga_data))
2293 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002294 }
2295 next_match_idx = -1;
2296 }
2297 else
2298 cur_si = push_next_match(cur_si);
2299 found_match = TRUE;
2300 }
2301 }
2302 }
2303
2304 /*
2305 * Handle searching for nextgroup match.
2306 */
2307 if (current_next_list != NULL && !keep_next_list)
2308 {
2309 /*
2310 * If a nextgroup was not found, continue looking for one if:
2311 * - this is an empty line and the "skipempty" option was given
2312 * - we are on white space and the "skipwhite" option was given
2313 */
2314 if (!found_match)
2315 {
2316 line = syn_getcurline();
2317 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002318 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002319 || ((current_next_flags & HL_SKIPEMPTY)
2320 && *line == NUL))
2321 break;
2322 }
2323
2324 /*
2325 * If a nextgroup was found: Use it, and continue looking for
2326 * contained matches.
2327 * If a nextgroup was not found: Continue looking for a normal
2328 * match.
2329 * When did set current_next_list for a zero-width item and no
2330 * match was found don't loop (would get stuck).
2331 */
2332 current_next_list = NULL;
2333 next_match_idx = -1;
2334 if (!zero_width_next_list)
2335 found_match = TRUE;
2336 }
2337
2338 } while (found_match);
2339
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002340 restore_chartab(buf_chartab);
2341
Bram Moolenaar071d4272004-06-13 20:20:40 +00002342 /*
2343 * Use attributes from the current state, if within its highlighting.
2344 * If not, use attributes from the current-but-one state, etc.
2345 */
2346 current_attr = 0;
2347#ifdef FEAT_EVAL
2348 current_id = 0;
2349 current_trans_id = 0;
2350#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002351#ifdef FEAT_CONCEAL
2352 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002353 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002354#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002355 if (cur_si != NULL)
2356 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002357#ifndef FEAT_EVAL
2358 int current_trans_id = 0;
2359#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2361 {
2362 sip = &CUR_STATE(idx);
2363 if ((current_lnum > sip->si_h_startpos.lnum
2364 || (current_lnum == sip->si_h_startpos.lnum
2365 && current_col >= sip->si_h_startpos.col))
2366 && (sip->si_h_endpos.lnum == 0
2367 || current_lnum < sip->si_h_endpos.lnum
2368 || (current_lnum == sip->si_h_endpos.lnum
2369 && current_col < sip->si_h_endpos.col)))
2370 {
2371 current_attr = sip->si_attr;
2372#ifdef FEAT_EVAL
2373 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002375 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002376#ifdef FEAT_CONCEAL
2377 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002378 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002379 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002380#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002381 break;
2382 }
2383 }
2384
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 if (can_spell != NULL)
2386 {
2387 struct sp_syn sps;
2388
2389 /*
2390 * set "can_spell" to TRUE if spell checking is supposed to be
2391 * done in the current item.
2392 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002393 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002394 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002395 /* There is no @Spell cluster: Do spelling for items without
2396 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002397 if (syn_block->b_nospell_cluster_id == 0
2398 || current_trans_id == 0)
2399 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002400 else
2401 {
2402 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002403 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002404 sps.cont_in_list = NULL;
2405 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2406 }
2407 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002408 else
2409 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002410 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002411 * the @Spell cluster. But not when @NoSpell is also there.
2412 * At the toplevel only spell check when ":syn spell toplevel"
2413 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002414 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002415 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002416 else
2417 {
2418 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002419 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002420 sps.cont_in_list = NULL;
2421 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2422
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002425 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002426 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2427 *can_spell = FALSE;
2428 }
2429 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002430 }
2431 }
2432
2433
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 /*
2435 * Check for end of current state (and the states before it) at the
2436 * next column. Don't do this for syncing, because we would miss a
2437 * single character match.
2438 * First check if the current state ends at the current column. It
2439 * may be for an empty match and a containing item might end in the
2440 * current column.
2441 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002442 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 {
2444 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002445 if (current_state.ga_len > 0
2446 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447 {
2448 ++current_col;
2449 check_state_ends();
2450 --current_col;
2451 }
2452 }
2453 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002454 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002455 /* Default: Only do spelling when there is no @Spell cluster or when
2456 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002457 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2458 ? (syn_block->b_spell_cluster_id == 0)
2459 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002461 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002462 if (current_next_list != NULL
2463 && syn_getcurline()[current_col + 1] == NUL
2464 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2465 current_next_list = NULL;
2466
2467 if (zero_width_next_ga.ga_len > 0)
2468 ga_clear(&zero_width_next_ga);
2469
2470 /* No longer need external matches. But keep next_match_extmatch. */
2471 unref_extmatch(re_extmatch_out);
2472 re_extmatch_out = NULL;
2473 unref_extmatch(cur_extmatch);
2474
2475 return current_attr;
2476}
2477
2478
2479/*
2480 * Check if we already matched pattern "idx" at the current column.
2481 */
2482 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002483did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484{
2485 int i;
2486
2487 for (i = current_state.ga_len; --i >= 0; )
2488 if (CUR_STATE(i).si_m_startcol == (int)current_col
2489 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2490 && CUR_STATE(i).si_idx == idx)
2491 return TRUE;
2492
2493 /* Zero-width matches with a nextgroup argument are not put on the syntax
2494 * stack, and can only be matched once anyway. */
2495 for (i = gap->ga_len; --i >= 0; )
2496 if (((int *)(gap->ga_data))[i] == idx)
2497 return TRUE;
2498
2499 return FALSE;
2500}
2501
2502/*
2503 * Push the next match onto the stack.
2504 */
2505 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002506push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507{
2508 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002509#ifdef FEAT_CONCEAL
2510 int save_flags;
2511#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512
Bram Moolenaar860cae12010-06-05 23:22:07 +02002513 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002514
2515 /*
2516 * Push the item in current_state stack;
2517 */
2518 if (push_current_state(next_match_idx) == OK)
2519 {
2520 /*
2521 * If it's a start-skip-end type that crosses lines, figure out how
2522 * much it continues in this line. Otherwise just fill in the length.
2523 */
2524 cur_si = &CUR_STATE(current_state.ga_len - 1);
2525 cur_si->si_h_startpos = next_match_h_startpos;
2526 cur_si->si_m_startcol = current_col;
2527 cur_si->si_m_lnum = current_lnum;
2528 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002529#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002530 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002531 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002532 if (current_state.ga_len > 1)
2533 cur_si->si_flags |=
2534 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536 cur_si->si_next_list = spp->sp_next_list;
2537 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2538 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2539 {
2540 /* Try to find the end pattern in the current line */
2541 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2542 check_keepend();
2543 }
2544 else
2545 {
2546 cur_si->si_m_endpos = next_match_m_endpos;
2547 cur_si->si_h_endpos = next_match_h_endpos;
2548 cur_si->si_ends = TRUE;
2549 cur_si->si_flags |= next_match_flags;
2550 cur_si->si_eoe_pos = next_match_eoe_pos;
2551 cur_si->si_end_idx = next_match_end_idx;
2552 }
2553 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2554 keepend_level = current_state.ga_len - 1;
2555 check_keepend();
2556 update_si_attr(current_state.ga_len - 1);
2557
Bram Moolenaar860cae12010-06-05 23:22:07 +02002558#ifdef FEAT_CONCEAL
2559 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2560#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 /*
2562 * If the start pattern has another highlight group, push another item
2563 * on the stack for the start pattern.
2564 */
2565 if ( spp->sp_type == SPTYPE_START
2566 && spp->sp_syn_match_id != 0
2567 && push_current_state(next_match_idx) == OK)
2568 {
2569 cur_si = &CUR_STATE(current_state.ga_len - 1);
2570 cur_si->si_h_startpos = next_match_h_startpos;
2571 cur_si->si_m_startcol = current_col;
2572 cur_si->si_m_lnum = current_lnum;
2573 cur_si->si_m_endpos = next_match_eos_pos;
2574 cur_si->si_h_endpos = next_match_eos_pos;
2575 cur_si->si_ends = TRUE;
2576 cur_si->si_end_idx = 0;
2577 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002578#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002579 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002580 cur_si->si_flags |= save_flags;
2581 if (cur_si->si_flags & HL_CONCEALENDS)
2582 cur_si->si_flags |= HL_CONCEAL;
2583#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584 cur_si->si_next_list = NULL;
2585 check_keepend();
2586 update_si_attr(current_state.ga_len - 1);
2587 }
2588 }
2589
2590 next_match_idx = -1; /* try other match next time */
2591
2592 return cur_si;
2593}
2594
2595/*
2596 * Check for end of current state (and the states before it).
2597 */
2598 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002599check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600{
2601 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002602 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603
2604 cur_si = &CUR_STATE(current_state.ga_len - 1);
2605 for (;;)
2606 {
2607 if (cur_si->si_ends
2608 && (cur_si->si_m_endpos.lnum < current_lnum
2609 || (cur_si->si_m_endpos.lnum == current_lnum
2610 && cur_si->si_m_endpos.col <= current_col)))
2611 {
2612 /*
2613 * If there is an end pattern group ID, highlight the end pattern
2614 * now. No need to pop the current item from the stack.
2615 * Only do this if the end pattern continues beyond the current
2616 * position.
2617 */
2618 if (cur_si->si_end_idx
2619 && (cur_si->si_eoe_pos.lnum > current_lnum
2620 || (cur_si->si_eoe_pos.lnum == current_lnum
2621 && cur_si->si_eoe_pos.col > current_col)))
2622 {
2623 cur_si->si_idx = cur_si->si_end_idx;
2624 cur_si->si_end_idx = 0;
2625 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2626 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2627 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002628#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002629 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002630 if (cur_si->si_flags & HL_CONCEALENDS)
2631 cur_si->si_flags |= HL_CONCEAL;
2632#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002633 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002634
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002635 /* nextgroup= should not match in the end pattern */
2636 current_next_list = NULL;
2637
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002638 /* what matches next may be different now, clear it */
2639 next_match_idx = 0;
2640 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 break;
2642 }
2643 else
2644 {
2645 /* handle next_list, unless at end of line and no "skipnl" or
2646 * "skipempty" */
2647 current_next_list = cur_si->si_next_list;
2648 current_next_flags = cur_si->si_flags;
2649 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2650 && syn_getcurline()[current_col] == NUL)
2651 current_next_list = NULL;
2652
2653 /* When the ended item has "extend", another item with
2654 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002655 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656
2657 pop_current_state();
2658
2659 if (current_state.ga_len == 0)
2660 break;
2661
Bram Moolenaar81993f42008-01-11 20:27:45 +00002662 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 {
2664 syn_update_ends(FALSE);
2665 if (current_state.ga_len == 0)
2666 break;
2667 }
2668
2669 cur_si = &CUR_STATE(current_state.ga_len - 1);
2670
2671 /*
2672 * Only for a region the search for the end continues after
2673 * the end of the contained item. If the contained match
2674 * included the end-of-line, break here, the region continues.
2675 * Don't do this when:
2676 * - "keepend" is used for the contained item
2677 * - not at the end of the line (could be end="x$"me=e-1).
2678 * - "excludenl" is used (HL_HAS_EOL won't be set)
2679 */
2680 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002681 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682 == SPTYPE_START
2683 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2684 {
2685 update_si_end(cur_si, (int)current_col, TRUE);
2686 check_keepend();
2687 if ((current_next_flags & HL_HAS_EOL)
2688 && keepend_level < 0
2689 && syn_getcurline()[current_col] == NUL)
2690 break;
2691 }
2692 }
2693 }
2694 else
2695 break;
2696 }
2697}
2698
2699/*
2700 * Update an entry in the current_state stack for a match or region. This
2701 * fills in si_attr, si_next_list and si_cont_list.
2702 */
2703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002704update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705{
2706 stateitem_T *sip = &CUR_STATE(idx);
2707 synpat_T *spp;
2708
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002709 /* This should not happen... */
2710 if (sip->si_idx < 0)
2711 return;
2712
Bram Moolenaar860cae12010-06-05 23:22:07 +02002713 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 if (sip->si_flags & HL_MATCH)
2715 sip->si_id = spp->sp_syn_match_id;
2716 else
2717 sip->si_id = spp->sp_syn.id;
2718 sip->si_attr = syn_id2attr(sip->si_id);
2719 sip->si_trans_id = sip->si_id;
2720 if (sip->si_flags & HL_MATCH)
2721 sip->si_cont_list = NULL;
2722 else
2723 sip->si_cont_list = spp->sp_cont_list;
2724
2725 /*
2726 * For transparent items, take attr from outer item.
2727 * Also take cont_list, if there is none.
2728 * Don't do this for the matchgroup of a start or end pattern.
2729 */
2730 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2731 {
2732 if (idx == 0)
2733 {
2734 sip->si_attr = 0;
2735 sip->si_trans_id = 0;
2736 if (sip->si_cont_list == NULL)
2737 sip->si_cont_list = ID_LIST_ALL;
2738 }
2739 else
2740 {
2741 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2742 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002743 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2744 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 if (sip->si_cont_list == NULL)
2746 {
2747 sip->si_flags |= HL_TRANS_CONT;
2748 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2749 }
2750 }
2751 }
2752}
2753
2754/*
2755 * Check the current stack for patterns with "keepend" flag.
2756 * Propagate the match-end to contained items, until a "skipend" item is found.
2757 */
2758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002759check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002760{
2761 int i;
2762 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002763 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002764 stateitem_T *sip;
2765
2766 /*
2767 * This check can consume a lot of time; only do it from the level where
2768 * there really is a keepend.
2769 */
2770 if (keepend_level < 0)
2771 return;
2772
2773 /*
2774 * Find the last index of an "extend" item. "keepend" items before that
2775 * won't do anything. If there is no "extend" item "i" will be
2776 * "keepend_level" and all "keepend" items will work normally.
2777 */
2778 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2779 if (CUR_STATE(i).si_flags & HL_EXTEND)
2780 break;
2781
2782 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002783 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002784 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002785 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 for ( ; i < current_state.ga_len; ++i)
2787 {
2788 sip = &CUR_STATE(i);
2789 if (maxpos.lnum != 0)
2790 {
2791 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002792 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2794 sip->si_ends = TRUE;
2795 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002796 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2797 {
2798 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799 || maxpos.lnum > sip->si_m_endpos.lnum
2800 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002801 && maxpos.col > sip->si_m_endpos.col))
2802 maxpos = sip->si_m_endpos;
2803 if (maxpos_h.lnum == 0
2804 || maxpos_h.lnum > sip->si_h_endpos.lnum
2805 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2806 && maxpos_h.col > sip->si_h_endpos.col))
2807 maxpos_h = sip->si_h_endpos;
2808 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809 }
2810}
2811
2812/*
2813 * Update an entry in the current_state stack for a start-skip-end pattern.
2814 * This finds the end of the current item, if it's in the current line.
2815 *
2816 * Return the flags for the matched END.
2817 */
2818 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002819update_si_end(
2820 stateitem_T *sip,
2821 int startcol, /* where to start searching for the end */
2822 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823{
2824 lpos_T startpos;
2825 lpos_T endpos;
2826 lpos_T hl_endpos;
2827 lpos_T end_endpos;
2828 int end_idx;
2829
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002830 /* return quickly for a keyword */
2831 if (sip->si_idx < 0)
2832 return;
2833
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 /* Don't update when it's already done. Can be a match of an end pattern
2835 * that started in a previous line. Watch out: can also be a "keepend"
2836 * from a containing item. */
2837 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2838 return;
2839
2840 /*
2841 * We need to find the end of the region. It may continue in the next
2842 * line.
2843 */
2844 end_idx = 0;
2845 startpos.lnum = current_lnum;
2846 startpos.col = startcol;
2847 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2848 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2849
2850 if (endpos.lnum == 0)
2851 {
2852 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002853 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 {
2855 /* a "oneline" never continues in the next line */
2856 sip->si_ends = TRUE;
2857 sip->si_m_endpos.lnum = current_lnum;
2858 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2859 }
2860 else
2861 {
2862 /* continues in the next line */
2863 sip->si_ends = FALSE;
2864 sip->si_m_endpos.lnum = 0;
2865 }
2866 sip->si_h_endpos = sip->si_m_endpos;
2867 }
2868 else
2869 {
2870 /* match within this line */
2871 sip->si_m_endpos = endpos;
2872 sip->si_h_endpos = hl_endpos;
2873 sip->si_eoe_pos = end_endpos;
2874 sip->si_ends = TRUE;
2875 sip->si_end_idx = end_idx;
2876 }
2877}
2878
2879/*
2880 * Add a new state to the current state stack.
2881 * It is cleared and the index set to "idx".
2882 * Return FAIL if it's not possible (out of memory).
2883 */
2884 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002885push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886{
2887 if (ga_grow(&current_state, 1) == FAIL)
2888 return FAIL;
2889 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2890 CUR_STATE(current_state.ga_len).si_idx = idx;
2891 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 return OK;
2893}
2894
2895/*
2896 * Remove a state from the current_state stack.
2897 */
2898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002899pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900{
2901 if (current_state.ga_len)
2902 {
2903 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2904 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 }
2906 /* after the end of a pattern, try matching a keyword or pattern */
2907 next_match_idx = -1;
2908
2909 /* if first state with "keepend" is popped, reset keepend_level */
2910 if (keepend_level >= current_state.ga_len)
2911 keepend_level = -1;
2912}
2913
2914/*
2915 * Find the end of a start/skip/end syntax region after "startpos".
2916 * Only checks one line.
2917 * Also handles a match item that continued from a previous line.
2918 * If not found, the syntax item continues in the next line. m_endpos->lnum
2919 * will be 0.
2920 * If found, the end of the region and the end of the highlighting is
2921 * computed.
2922 */
2923 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002924find_endpos(
2925 int idx, /* index of the pattern */
2926 lpos_T *startpos, /* where to start looking for an END match */
2927 lpos_T *m_endpos, /* return: end of match */
2928 lpos_T *hl_endpos, /* return: end of highlighting */
2929 long *flagsp, /* return: flags of matching END */
2930 lpos_T *end_endpos, /* return: end of end pattern match */
2931 int *end_idx, /* return: group ID for end pat. match, or 0 */
2932 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933{
2934 colnr_T matchcol;
2935 synpat_T *spp, *spp_skip;
2936 int start_idx;
2937 int best_idx;
2938 regmmatch_T regmatch;
2939 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2940 lpos_T pos;
2941 char_u *line;
2942 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002943 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002945 /* just in case we are invoked for a keyword */
2946 if (idx < 0)
2947 return;
2948
Bram Moolenaar071d4272004-06-13 20:20:40 +00002949 /*
2950 * Check for being called with a START pattern.
2951 * Can happen with a match that continues to the next line, because it
2952 * contained a region.
2953 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002954 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002955 if (spp->sp_type != SPTYPE_START)
2956 {
2957 *hl_endpos = *startpos;
2958 return;
2959 }
2960
2961 /*
2962 * Find the SKIP or first END pattern after the last START pattern.
2963 */
2964 for (;;)
2965 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002966 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002967 if (spp->sp_type != SPTYPE_START)
2968 break;
2969 ++idx;
2970 }
2971
2972 /*
2973 * Lookup the SKIP pattern (if present)
2974 */
2975 if (spp->sp_type == SPTYPE_SKIP)
2976 {
2977 spp_skip = spp;
2978 ++idx;
2979 }
2980 else
2981 spp_skip = NULL;
2982
2983 /* Setup external matches for syn_regexec(). */
2984 unref_extmatch(re_extmatch_in);
2985 re_extmatch_in = ref_extmatch(start_ext);
2986
2987 matchcol = startpos->col; /* start looking for a match at sstart */
2988 start_idx = idx; /* remember the first END pattern. */
2989 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002990
2991 /* use syntax iskeyword option */
2992 save_chartab(buf_chartab);
2993
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 for (;;)
2995 {
2996 /*
2997 * Find end pattern that matches first after "matchcol".
2998 */
2999 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003000 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 {
3002 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003003 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004
Bram Moolenaar860cae12010-06-05 23:22:07 +02003005 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003006 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3007 break;
3008 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3009 if (lc_col < 0)
3010 lc_col = 0;
3011
3012 regmatch.rmm_ic = spp->sp_ic;
3013 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003014 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3015 IF_SYN_TIME(&spp->sp_time));
3016 spp->sp_prog = regmatch.regprog;
3017 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018 {
3019 if (best_idx == -1 || regmatch.startpos[0].col
3020 < best_regmatch.startpos[0].col)
3021 {
3022 best_idx = idx;
3023 best_regmatch.startpos[0] = regmatch.startpos[0];
3024 best_regmatch.endpos[0] = regmatch.endpos[0];
3025 }
3026 }
3027 }
3028
3029 /*
3030 * If all end patterns have been tried, and there is no match, the
3031 * item continues until end-of-line.
3032 */
3033 if (best_idx == -1)
3034 break;
3035
3036 /*
3037 * If the skip pattern matches before the end pattern,
3038 * continue searching after the skip pattern.
3039 */
3040 if (spp_skip != NULL)
3041 {
3042 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003043 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044
3045 if (lc_col < 0)
3046 lc_col = 0;
3047 regmatch.rmm_ic = spp_skip->sp_ic;
3048 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003049 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3050 IF_SYN_TIME(&spp_skip->sp_time));
3051 spp_skip->sp_prog = regmatch.regprog;
3052 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 <= best_regmatch.startpos[0].col)
3054 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003055 int line_len;
3056
Bram Moolenaar071d4272004-06-13 20:20:40 +00003057 /* Add offset to skip pattern match */
3058 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3059
3060 /* If the skip pattern goes on to the next line, there is no
3061 * match with an end pattern in this line. */
3062 if (pos.lnum > startpos->lnum)
3063 break;
3064
3065 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003066 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067
3068 /* take care of an empty match or negative offset */
3069 if (pos.col <= matchcol)
3070 ++matchcol;
3071 else if (pos.col <= regmatch.endpos[0].col)
3072 matchcol = pos.col;
3073 else
3074 /* Be careful not to jump over the NUL at the end-of-line */
3075 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003076 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077 ++matchcol)
3078 ;
3079
3080 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003081 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 break;
3083
3084 continue; /* start with first end pattern again */
3085 }
3086 }
3087
3088 /*
3089 * Match from start pattern to end pattern.
3090 * Correct for match and highlight offset of end pattern.
3091 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003092 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3094 /* can't end before the start */
3095 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3096 m_endpos->col = startpos->col;
3097
3098 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3099 /* can't end before the start */
3100 if (end_endpos->lnum == startpos->lnum
3101 && end_endpos->col < startpos->col)
3102 end_endpos->col = startpos->col;
3103 /* can't end after the match */
3104 limit_pos(end_endpos, m_endpos);
3105
3106 /*
3107 * If the end group is highlighted differently, adjust the pointers.
3108 */
3109 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3110 {
3111 *end_idx = best_idx;
3112 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3113 {
3114 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3115 hl_endpos->col = best_regmatch.endpos[0].col;
3116 }
3117 else
3118 {
3119 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3120 hl_endpos->col = best_regmatch.startpos[0].col;
3121 }
3122 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3123
3124 /* can't end before the start */
3125 if (hl_endpos->lnum == startpos->lnum
3126 && hl_endpos->col < startpos->col)
3127 hl_endpos->col = startpos->col;
3128 limit_pos(hl_endpos, m_endpos);
3129
3130 /* now the match ends where the highlighting ends, it is turned
3131 * into the matchgroup for the end */
3132 *m_endpos = *hl_endpos;
3133 }
3134 else
3135 {
3136 *end_idx = 0;
3137 *hl_endpos = *end_endpos;
3138 }
3139
3140 *flagsp = spp->sp_flags;
3141
3142 had_match = TRUE;
3143 break;
3144 }
3145
3146 /* no match for an END pattern in this line */
3147 if (!had_match)
3148 m_endpos->lnum = 0;
3149
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003150 restore_chartab(buf_chartab);
3151
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152 /* Remove external matches. */
3153 unref_extmatch(re_extmatch_in);
3154 re_extmatch_in = NULL;
3155}
3156
3157/*
3158 * Limit "pos" not to be after "limit".
3159 */
3160 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003161limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162{
3163 if (pos->lnum > limit->lnum)
3164 *pos = *limit;
3165 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3166 pos->col = limit->col;
3167}
3168
3169/*
3170 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3171 */
3172 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003173limit_pos_zero(
3174 lpos_T *pos,
3175 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176{
3177 if (pos->lnum == 0)
3178 *pos = *limit;
3179 else
3180 limit_pos(pos, limit);
3181}
3182
3183/*
3184 * Add offset to matched text for end of match or highlight.
3185 */
3186 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003187syn_add_end_off(
3188 lpos_T *result, /* returned position */
3189 regmmatch_T *regmatch, /* start/end of match */
3190 synpat_T *spp, /* matched pattern */
3191 int idx, /* index of offset */
3192 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193{
3194 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003195 int off;
3196 char_u *base;
3197 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198
3199 if (spp->sp_off_flags & (1 << idx))
3200 {
3201 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003202 col = regmatch->startpos[0].col;
3203 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204 }
3205 else
3206 {
3207 result->lnum = regmatch->endpos[0].lnum;
3208 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003209 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003211 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3212 * is a matchgroup. Watch out for match with last NL in the buffer. */
3213 if (result->lnum > syn_buf->b_ml.ml_line_count)
3214 col = 0;
3215 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003216 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3218 p = base + col;
3219 if (off > 0)
3220 {
3221 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003222 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003223 }
3224 else if (off < 0)
3225 {
3226 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003227 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003228 }
3229 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003230 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003231 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232}
3233
3234/*
3235 * Add offset to matched text for start of match or highlight.
3236 * Avoid resulting column to become negative.
3237 */
3238 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003239syn_add_start_off(
3240 lpos_T *result, /* returned position */
3241 regmmatch_T *regmatch, /* start/end of match */
3242 synpat_T *spp,
3243 int idx,
3244 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245{
3246 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003247 int off;
3248 char_u *base;
3249 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250
3251 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3252 {
3253 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003254 col = regmatch->endpos[0].col;
3255 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 }
3257 else
3258 {
3259 result->lnum = regmatch->startpos[0].lnum;
3260 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003261 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003263 if (result->lnum > syn_buf->b_ml.ml_line_count)
3264 {
3265 /* a "\n" at the end of the pattern may take us below the last line */
3266 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003267 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003268 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003269 if (off != 0)
3270 {
3271 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3272 p = base + col;
3273 if (off > 0)
3274 {
3275 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003276 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003277 }
3278 else if (off < 0)
3279 {
3280 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003281 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003282 }
3283 col = (int)(p - base);
3284 }
3285 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286}
3287
3288/*
3289 * Get current line in syntax buffer.
3290 */
3291 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003292syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293{
3294 return ml_get_buf(syn_buf, current_lnum, FALSE);
3295}
3296
3297/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003298 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299 * Returns TRUE when there is a match.
3300 */
3301 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003302syn_regexec(
3303 regmmatch_T *rmp,
3304 linenr_T lnum,
3305 colnr_T col,
3306 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003308 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003309#ifdef FEAT_RELTIME
3310 int timed_out = FALSE;
3311#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003312#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003313 proftime_T pt;
3314
3315 if (syn_time_on)
3316 profile_start(&pt);
3317#endif
3318
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003319 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003320 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3321#ifdef FEAT_RELTIME
3322 syn_tm, &timed_out
3323#else
3324 NULL, NULL
3325#endif
3326 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003327
Bram Moolenaarf7512552013-06-06 14:55:19 +02003328#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003329 if (syn_time_on)
3330 {
3331 profile_end(&pt);
3332 profile_add(&st->total, &pt);
3333 if (profile_cmp(&pt, &st->slowest) < 0)
3334 st->slowest = pt;
3335 ++st->count;
3336 if (r > 0)
3337 ++st->match;
3338 }
3339#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003340#ifdef FEAT_RELTIME
3341 if (timed_out)
3342 syn_win->w_s->b_syn_slow = TRUE;
3343#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003344
3345 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 {
3347 rmp->startpos[0].lnum += lnum;
3348 rmp->endpos[0].lnum += lnum;
3349 return TRUE;
3350 }
3351 return FALSE;
3352}
3353
3354/*
3355 * Check one position in a line for a matching keyword.
3356 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003357 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 */
3359 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003360check_keyword_id(
3361 char_u *line,
3362 int startcol, /* position in line to check for keyword */
3363 int *endcolp, /* return: character after found keyword */
3364 long *flagsp, /* return: flags of matching keyword */
3365 short **next_listp, /* return: next_list of matching keyword */
3366 stateitem_T *cur_si, /* item at the top of the stack */
3367 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003369 keyentry_T *kp;
3370 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003372 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003374 hashtab_T *ht;
3375 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376
3377 /* Find first character after the keyword. First character was already
3378 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003379 kwp = line + startcol;
3380 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 do
3382 {
3383#ifdef FEAT_MBYTE
3384 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003385 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386 else
3387#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003390 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003393 return 0;
3394
3395 /*
3396 * Must make a copy of the keyword, so we can add a NUL and make it
3397 * lowercase.
3398 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003399 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400
3401 /*
3402 * Try twice:
3403 * 1. matching case
3404 * 2. ignoring case
3405 */
3406 for (round = 1; round <= 2; ++round)
3407 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003409 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003410 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003411 if (round == 2) /* ignore case */
3412 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413
3414 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003415 * Find keywords that match. There can be several with different
3416 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417 * When current_next_list is non-zero accept only that group, otherwise:
3418 * Accept a not-contained keyword at toplevel.
3419 * Accept a keyword at other levels only if it is in the contains list.
3420 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003421 hi = hash_find(ht, keyword);
3422 if (!HASHITEM_EMPTY(hi))
3423 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003425 if (current_next_list != 0
3426 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3427 : (cur_si == NULL
3428 ? !(kp->flags & HL_CONTAINED)
3429 : in_id_list(cur_si, cur_si->si_cont_list,
3430 &kp->k_syn, kp->flags & HL_CONTAINED)))
3431 {
3432 *endcolp = startcol + kwlen;
3433 *flagsp = kp->flags;
3434 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003435#ifdef FEAT_CONCEAL
3436 *ccharp = kp->k_char;
3437#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003438 return kp->k_syn.id;
3439 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440 }
3441 }
3442 return 0;
3443}
3444
3445/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003446 * Handle ":syntax conceal" command.
3447 */
3448 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003449syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003450{
3451#ifdef FEAT_CONCEAL
3452 char_u *arg = eap->arg;
3453 char_u *next;
3454
3455 eap->nextcmd = find_nextcmd(arg);
3456 if (eap->skip)
3457 return;
3458
3459 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003460 if (*arg == NUL)
3461 {
3462 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003463 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003464 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003465 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003466 }
3467 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468 curwin->w_s->b_syn_conceal = TRUE;
3469 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3470 curwin->w_s->b_syn_conceal = FALSE;
3471 else
3472 EMSG2(_("E390: Illegal argument: %s"), arg);
3473#endif
3474}
3475
3476/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 * Handle ":syntax case" command.
3478 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003480syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481{
3482 char_u *arg = eap->arg;
3483 char_u *next;
3484
3485 eap->nextcmd = find_nextcmd(arg);
3486 if (eap->skip)
3487 return;
3488
3489 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003490 if (*arg == NUL)
3491 {
3492 if (curwin->w_s->b_syn_ic)
3493 MSG(_("syntax case ignore"));
3494 else
3495 MSG(_("syntax case match"));
3496 }
3497 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 else
3502 EMSG2(_("E390: Illegal argument: %s"), arg);
3503}
3504
3505/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003506 * Handle ":syntax spell" command.
3507 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003508 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003509syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003510{
3511 char_u *arg = eap->arg;
3512 char_u *next;
3513
3514 eap->nextcmd = find_nextcmd(arg);
3515 if (eap->skip)
3516 return;
3517
3518 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003519 if (*arg == NUL)
3520 {
3521 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3522 MSG(_("syntax spell toplevel"));
3523 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3524 MSG(_("syntax spell notoplevel"));
3525 else
3526 MSG(_("syntax spell default"));
3527 }
3528 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003530 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003531 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003532 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003533 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003534 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003535 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003536 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003537 return;
3538 }
3539
3540 /* assume spell checking changed, force a redraw */
3541 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003542}
3543
3544/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003545 * Handle ":syntax iskeyword" command.
3546 */
3547 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003548syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003549{
3550 char_u *arg = eap->arg;
3551 char_u save_chartab[32];
3552 char_u *save_isk;
3553
3554 if (eap->skip)
3555 return;
3556
3557 arg = skipwhite(arg);
3558 if (*arg == NUL)
3559 {
3560 MSG_PUTS("\n");
3561 MSG_PUTS(_("syntax iskeyword "));
3562 if (curwin->w_s->b_syn_isk != empty_option)
3563 msg_outtrans(curwin->w_s->b_syn_isk);
3564 else
3565 msg_outtrans((char_u *)"not set");
3566 }
3567 else
3568 {
3569 if (STRNICMP(arg, "clear", 5) == 0)
3570 {
3571 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3572 (size_t)32);
3573 clear_string_option(&curwin->w_s->b_syn_isk);
3574 }
3575 else
3576 {
3577 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3578 save_isk = curbuf->b_p_isk;
3579 curbuf->b_p_isk = vim_strsave(arg);
3580
3581 buf_init_chartab(curbuf, FALSE);
3582 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3583 (size_t)32);
3584 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3585 clear_string_option(&curwin->w_s->b_syn_isk);
3586 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3587 curbuf->b_p_isk = save_isk;
3588 }
3589 }
3590 redraw_win_later(curwin, NOT_VALID);
3591}
3592
3593/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003594 * Clear all syntax info for one buffer.
3595 */
3596 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003597syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598{
3599 int i;
3600
Bram Moolenaar860cae12010-06-05 23:22:07 +02003601 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003602#ifdef FEAT_RELTIME
3603 block->b_syn_slow = FALSE; /* clear previous timeout */
3604#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003605 block->b_syn_ic = FALSE; /* Use case, by default */
3606 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3607 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003608#ifdef FEAT_CONCEAL
3609 block->b_syn_conceal = FALSE;
3610#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611
3612 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 clear_keywtab(&block->b_keywtab);
3614 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615
3616 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3618 syn_clear_pattern(block, i);
3619 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620
3621 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003622 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3623 syn_clear_cluster(block, i);
3624 ga_clear(&block->b_syn_clusters);
3625 block->b_spell_cluster_id = 0;
3626 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627
Bram Moolenaar860cae12010-06-05 23:22:07 +02003628 block->b_syn_sync_flags = 0;
3629 block->b_syn_sync_minlines = 0;
3630 block->b_syn_sync_maxlines = 0;
3631 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632
Bram Moolenaar473de612013-06-08 18:19:48 +02003633 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003634 block->b_syn_linecont_prog = NULL;
3635 vim_free(block->b_syn_linecont_pat);
3636 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003640 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641
3642 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003645
3646 /* Reset the counter for ":syn include" */
3647 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003648}
3649
3650/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003651 * Get rid of ownsyntax for window "wp".
3652 */
3653 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003654reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003655{
3656 if (wp->w_s != &wp->w_buffer->b_s)
3657 {
3658 syntax_clear(wp->w_s);
3659 vim_free(wp->w_s);
3660 wp->w_s = &wp->w_buffer->b_s;
3661 }
3662}
3663
3664/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 * Clear syncing info for one buffer.
3666 */
3667 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003668syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669{
3670 int i;
3671
3672 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003673 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3674 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3675 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676
Bram Moolenaar860cae12010-06-05 23:22:07 +02003677 curwin->w_s->b_syn_sync_flags = 0;
3678 curwin->w_s->b_syn_sync_minlines = 0;
3679 curwin->w_s->b_syn_sync_maxlines = 0;
3680 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681
Bram Moolenaar473de612013-06-08 18:19:48 +02003682 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 curwin->w_s->b_syn_linecont_prog = NULL;
3684 vim_free(curwin->w_s->b_syn_linecont_pat);
3685 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003686 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003688 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689}
3690
3691/*
3692 * Remove one pattern from the buffer's pattern list.
3693 */
3694 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003695syn_remove_pattern(
3696 synblock_T *block,
3697 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698{
3699 synpat_T *spp;
3700
Bram Moolenaar860cae12010-06-05 23:22:07 +02003701 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702#ifdef FEAT_FOLDING
3703 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003704 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003706 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003708 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3709 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710}
3711
3712/*
3713 * Clear and free one syntax pattern. When clearing all, must be called from
3714 * last to first!
3715 */
3716 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003717syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003719 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003720 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003722 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003724 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3725 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3726 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 }
3728}
3729
3730/*
3731 * Clear and free one syntax cluster.
3732 */
3733 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003734syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003736 vim_free(SYN_CLSTR(block)[i].scl_name);
3737 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3738 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739}
3740
3741/*
3742 * Handle ":syntax clear" command.
3743 */
3744 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003745syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746{
3747 char_u *arg = eap->arg;
3748 char_u *arg_end;
3749 int id;
3750
3751 eap->nextcmd = find_nextcmd(arg);
3752 if (eap->skip)
3753 return;
3754
3755 /*
3756 * We have to disable this within ":syn include @group filename",
3757 * because otherwise @group would get deleted.
3758 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3759 * clear".
3760 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003761 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762 return;
3763
3764 if (ends_excmd(*arg))
3765 {
3766 /*
3767 * No argument: Clear all syntax items.
3768 */
3769 if (syncing)
3770 syntax_sync_clear();
3771 else
3772 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003773 syntax_clear(curwin->w_s);
3774 if (curwin->w_s == &curwin->w_buffer->b_s)
3775 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003776 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777 }
3778 }
3779 else
3780 {
3781 /*
3782 * Clear the group IDs that are in the argument.
3783 */
3784 while (!ends_excmd(*arg))
3785 {
3786 arg_end = skiptowhite(arg);
3787 if (*arg == '@')
3788 {
3789 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3790 if (id == 0)
3791 {
3792 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3793 break;
3794 }
3795 else
3796 {
3797 /*
3798 * We can't physically delete a cluster without changing
3799 * the IDs of other clusters, so we do the next best thing
3800 * and make it empty.
3801 */
3802 short scl_id = id - SYNID_CLUSTER;
3803
Bram Moolenaar860cae12010-06-05 23:22:07 +02003804 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3805 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003806 }
3807 }
3808 else
3809 {
3810 id = syn_namen2id(arg, (int)(arg_end - arg));
3811 if (id == 0)
3812 {
3813 EMSG2(_(e_nogroup), arg);
3814 break;
3815 }
3816 else
3817 syn_clear_one(id, syncing);
3818 }
3819 arg = skipwhite(arg_end);
3820 }
3821 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003822 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003823 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824}
3825
3826/*
3827 * Clear one syntax group for the current buffer.
3828 */
3829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003830syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831{
3832 synpat_T *spp;
3833 int idx;
3834
3835 /* Clear keywords only when not ":syn sync clear group-name" */
3836 if (!syncing)
3837 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003838 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3839 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 }
3841
3842 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003843 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003845 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3847 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 }
3850}
3851
3852/*
3853 * Handle ":syntax on" command.
3854 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003856syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857{
3858 syn_cmd_onoff(eap, "syntax");
3859}
3860
3861/*
3862 * Handle ":syntax enable" command.
3863 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003865syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866{
3867 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3868 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003869 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870}
3871
3872/*
3873 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003874 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003877syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003878{
3879 eap->nextcmd = check_nextcmd(eap->arg);
3880 if (!eap->skip)
3881 {
3882 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3883 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003884 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 }
3886}
3887
3888/*
3889 * Handle ":syntax manual" command.
3890 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003892syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893{
3894 syn_cmd_onoff(eap, "manual");
3895}
3896
3897/*
3898 * Handle ":syntax off" command.
3899 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003901syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902{
3903 syn_cmd_onoff(eap, "nosyntax");
3904}
3905
3906 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003907syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908{
3909 char_u buf[100];
3910
3911 eap->nextcmd = check_nextcmd(eap->arg);
3912 if (!eap->skip)
3913 {
3914 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003915 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916 do_cmdline_cmd(buf);
3917 }
3918}
3919
3920/*
3921 * Handle ":syntax [list]" command: list current syntax words.
3922 */
3923 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003924syn_cmd_list(
3925 exarg_T *eap,
3926 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927{
3928 char_u *arg = eap->arg;
3929 int id;
3930 char_u *arg_end;
3931
3932 eap->nextcmd = find_nextcmd(arg);
3933 if (eap->skip)
3934 return;
3935
Bram Moolenaar860cae12010-06-05 23:22:07 +02003936 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003938 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003939 return;
3940 }
3941
3942 if (syncing)
3943 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003944 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 {
3946 MSG_PUTS(_("syncing on C-style comments"));
3947 syn_lines_msg();
3948 syn_match_msg();
3949 return;
3950 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003951 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003953 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 MSG_PUTS(_("no syncing"));
3955 else
3956 {
3957 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003958 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 MSG_PUTS(_(" lines before top line"));
3960 syn_match_msg();
3961 }
3962 return;
3963 }
3964 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003965 if (curwin->w_s->b_syn_sync_minlines > 0
3966 || curwin->w_s->b_syn_sync_maxlines > 0
3967 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 {
3969 MSG_PUTS(_("\nsyncing on items"));
3970 syn_lines_msg();
3971 syn_match_msg();
3972 }
3973 }
3974 else
3975 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3976 if (ends_excmd(*arg))
3977 {
3978 /*
3979 * No argument: List all group IDs and all syntax clusters.
3980 */
3981 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3982 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003983 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 syn_list_cluster(id);
3985 }
3986 else
3987 {
3988 /*
3989 * List the group IDs and syntax clusters that are in the argument.
3990 */
3991 while (!ends_excmd(*arg) && !got_int)
3992 {
3993 arg_end = skiptowhite(arg);
3994 if (*arg == '@')
3995 {
3996 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3997 if (id == 0)
3998 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3999 else
4000 syn_list_cluster(id - SYNID_CLUSTER);
4001 }
4002 else
4003 {
4004 id = syn_namen2id(arg, (int)(arg_end - arg));
4005 if (id == 0)
4006 EMSG2(_(e_nogroup), arg);
4007 else
4008 syn_list_one(id, syncing, TRUE);
4009 }
4010 arg = skipwhite(arg_end);
4011 }
4012 }
4013 eap->nextcmd = check_nextcmd(arg);
4014}
4015
4016 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004017syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004019 if (curwin->w_s->b_syn_sync_maxlines > 0
4020 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 {
4022 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004023 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004024 {
4025 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004026 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4027 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 MSG_PUTS(", ");
4029 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004030 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 {
4032 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004033 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 }
4035 MSG_PUTS(_(" lines before top line"));
4036 }
4037}
4038
4039 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004040syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004042 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 {
4044 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004045 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 MSG_PUTS(_(" line breaks"));
4047 }
4048}
4049
4050static int last_matchgroup;
4051
4052struct name_list
4053{
4054 int flag;
4055 char *name;
4056};
4057
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004058static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059
4060/*
4061 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4062 */
4063 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004064syn_list_one(
4065 int id,
4066 int syncing, /* when TRUE: list syncing items */
4067 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068{
4069 int attr;
4070 int idx;
4071 int did_header = FALSE;
4072 synpat_T *spp;
4073 static struct name_list namelist1[] =
4074 {
4075 {HL_DISPLAY, "display"},
4076 {HL_CONTAINED, "contained"},
4077 {HL_ONELINE, "oneline"},
4078 {HL_KEEPEND, "keepend"},
4079 {HL_EXTEND, "extend"},
4080 {HL_EXCLUDENL, "excludenl"},
4081 {HL_TRANSP, "transparent"},
4082 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004083#ifdef FEAT_CONCEAL
4084 {HL_CONCEAL, "conceal"},
4085 {HL_CONCEALENDS, "concealends"},
4086#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 {0, NULL}
4088 };
4089 static struct name_list namelist2[] =
4090 {
4091 {HL_SKIPWHITE, "skipwhite"},
4092 {HL_SKIPNL, "skipnl"},
4093 {HL_SKIPEMPTY, "skipempty"},
4094 {0, NULL}
4095 };
4096
Bram Moolenaar8820b482017-03-16 17:23:31 +01004097 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004098
4099 /* list the keywords for "id" */
4100 if (!syncing)
4101 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004102 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4103 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104 did_header, attr);
4105 }
4106
4107 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004108 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004110 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4112 continue;
4113
4114 (void)syn_list_header(did_header, 999, id);
4115 did_header = TRUE;
4116 last_matchgroup = 0;
4117 if (spp->sp_type == SPTYPE_MATCH)
4118 {
4119 put_pattern("match", ' ', spp, attr);
4120 msg_putchar(' ');
4121 }
4122 else if (spp->sp_type == SPTYPE_START)
4123 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004124 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4125 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4126 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4127 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4128 while (idx < curwin->w_s->b_syn_patterns.ga_len
4129 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4130 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131 --idx;
4132 msg_putchar(' ');
4133 }
4134 syn_list_flags(namelist1, spp->sp_flags, attr);
4135
4136 if (spp->sp_cont_list != NULL)
4137 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4138
4139 if (spp->sp_syn.cont_in_list != NULL)
4140 put_id_list((char_u *)"containedin",
4141 spp->sp_syn.cont_in_list, attr);
4142
4143 if (spp->sp_next_list != NULL)
4144 {
4145 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4146 syn_list_flags(namelist2, spp->sp_flags, attr);
4147 }
4148 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4149 {
4150 if (spp->sp_flags & HL_SYNC_HERE)
4151 msg_puts_attr((char_u *)"grouphere", attr);
4152 else
4153 msg_puts_attr((char_u *)"groupthere", attr);
4154 msg_putchar(' ');
4155 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004156 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4158 else
4159 MSG_PUTS("NONE");
4160 msg_putchar(' ');
4161 }
4162 }
4163
4164 /* list the link, if there is one */
4165 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4166 {
4167 (void)syn_list_header(did_header, 999, id);
4168 msg_puts_attr((char_u *)"links to", attr);
4169 msg_putchar(' ');
4170 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4171 }
4172}
4173
4174 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004175syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176{
4177 int i;
4178
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004179 for (i = 0; nlist[i].flag != 0; ++i)
4180 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004182 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 msg_putchar(' ');
4184 }
4185}
4186
4187/*
4188 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4189 */
4190 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004191syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192{
4193 int endcol = 15;
4194
4195 /* slight hack: roughly duplicate the guts of syn_list_header() */
4196 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004197 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198
4199 if (msg_col >= endcol) /* output at least one space */
4200 endcol = msg_col + 1;
4201 if (Columns <= endcol) /* avoid hang for tiny window */
4202 endcol = Columns - 1;
4203
4204 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004205 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004207 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004208 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 }
4210 else
4211 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004212 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 msg_puts((char_u *)"=NONE");
4214 }
4215}
4216
4217 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004218put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219{
4220 short *p;
4221
4222 msg_puts_attr(name, attr);
4223 msg_putchar('=');
4224 for (p = list; *p; ++p)
4225 {
4226 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4227 {
4228 if (p[1])
4229 MSG_PUTS("ALLBUT");
4230 else
4231 MSG_PUTS("ALL");
4232 }
4233 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4234 {
4235 MSG_PUTS("TOP");
4236 }
4237 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4238 {
4239 MSG_PUTS("CONTAINED");
4240 }
4241 else if (*p >= SYNID_CLUSTER)
4242 {
4243 short scl_id = *p - SYNID_CLUSTER;
4244
4245 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004246 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247 }
4248 else
4249 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4250 if (p[1])
4251 msg_putchar(',');
4252 }
4253 msg_putchar(' ');
4254}
4255
4256 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004257put_pattern(
4258 char *s,
4259 int c,
4260 synpat_T *spp,
4261 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262{
4263 long n;
4264 int mask;
4265 int first;
4266 static char *sepchars = "/+=-#@\"|'^&";
4267 int i;
4268
4269 /* May have to write "matchgroup=group" */
4270 if (last_matchgroup != spp->sp_syn_match_id)
4271 {
4272 last_matchgroup = spp->sp_syn_match_id;
4273 msg_puts_attr((char_u *)"matchgroup", attr);
4274 msg_putchar('=');
4275 if (last_matchgroup == 0)
4276 msg_outtrans((char_u *)"NONE");
4277 else
4278 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4279 msg_putchar(' ');
4280 }
4281
4282 /* output the name of the pattern and an '=' or ' ' */
4283 msg_puts_attr((char_u *)s, attr);
4284 msg_putchar(c);
4285
4286 /* output the pattern, in between a char that is not in the pattern */
4287 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4288 if (sepchars[++i] == NUL)
4289 {
4290 i = 0; /* no good char found, just use the first one */
4291 break;
4292 }
4293 msg_putchar(sepchars[i]);
4294 msg_outtrans(spp->sp_pattern);
4295 msg_putchar(sepchars[i]);
4296
4297 /* output any pattern options */
4298 first = TRUE;
4299 for (i = 0; i < SPO_COUNT; ++i)
4300 {
4301 mask = (1 << i);
4302 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4303 {
4304 if (!first)
4305 msg_putchar(','); /* separate with commas */
4306 msg_puts((char_u *)spo_name_tab[i]);
4307 n = spp->sp_offsets[i];
4308 if (i != SPO_LC_OFF)
4309 {
4310 if (spp->sp_off_flags & mask)
4311 msg_putchar('s');
4312 else
4313 msg_putchar('e');
4314 if (n > 0)
4315 msg_putchar('+');
4316 }
4317 if (n || i == SPO_LC_OFF)
4318 msg_outnum(n);
4319 first = FALSE;
4320 }
4321 }
4322 msg_putchar(' ');
4323}
4324
4325/*
4326 * List or clear the keywords for one syntax group.
4327 * Return TRUE if the header has been printed.
4328 */
4329 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004330syn_list_keywords(
4331 int id,
4332 hashtab_T *ht,
4333 int did_header, /* header has already been printed */
4334 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 hashitem_T *hi;
4338 keyentry_T *kp;
4339 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340 int prev_contained = 0;
4341 short *prev_next_list = NULL;
4342 short *prev_cont_in_list = NULL;
4343 int prev_skipnl = 0;
4344 int prev_skipwhite = 0;
4345 int prev_skipempty = 0;
4346
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 /*
4348 * Unfortunately, this list of keywords is not sorted on alphabet but on
4349 * hash value...
4350 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004351 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 --todo;
4357 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 if (prev_contained != (kp->flags & HL_CONTAINED)
4362 || prev_skipnl != (kp->flags & HL_SKIPNL)
4363 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4364 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4365 || prev_cont_in_list != kp->k_syn.cont_in_list
4366 || prev_next_list != kp->next_list)
4367 outlen = 9999;
4368 else
4369 outlen = (int)STRLEN(kp->keyword);
4370 /* output "contained" and "nextgroup" on each line */
4371 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 prev_contained = 0;
4374 prev_next_list = NULL;
4375 prev_cont_in_list = NULL;
4376 prev_skipnl = 0;
4377 prev_skipwhite = 0;
4378 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 did_header = TRUE;
4381 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 put_id_list((char_u *)"containedin",
4390 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 if (kp->next_list != prev_next_list)
4395 {
4396 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4397 msg_putchar(' ');
4398 prev_next_list = kp->next_list;
4399 if (kp->flags & HL_SKIPNL)
4400 {
4401 msg_puts_attr((char_u *)"skipnl", attr);
4402 msg_putchar(' ');
4403 prev_skipnl = (kp->flags & HL_SKIPNL);
4404 }
4405 if (kp->flags & HL_SKIPWHITE)
4406 {
4407 msg_puts_attr((char_u *)"skipwhite", attr);
4408 msg_putchar(' ');
4409 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4410 }
4411 if (kp->flags & HL_SKIPEMPTY)
4412 {
4413 msg_puts_attr((char_u *)"skipempty", attr);
4414 msg_putchar(' ');
4415 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4416 }
4417 }
4418 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 }
4421 }
4422 }
4423
4424 return did_header;
4425}
4426
4427 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004428syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004430 hashitem_T *hi;
4431 keyentry_T *kp;
4432 keyentry_T *kp_prev;
4433 keyentry_T *kp_next;
4434 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435
Bram Moolenaardad6b692005-01-25 22:14:34 +00004436 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004437 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004438 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 --todo;
4443 kp_prev = NULL;
4444 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 if (kp->k_syn.id == id)
4447 {
4448 kp_next = kp->ke_next;
4449 if (kp_prev == NULL)
4450 {
4451 if (kp_next == NULL)
4452 hash_remove(ht, hi);
4453 else
4454 hi->hi_key = KE2HIKEY(kp_next);
4455 }
4456 else
4457 kp_prev->ke_next = kp_next;
4458 vim_free(kp->next_list);
4459 vim_free(kp->k_syn.cont_in_list);
4460 vim_free(kp);
4461 kp = kp_next;
4462 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 {
4465 kp_prev = kp;
4466 kp = kp->ke_next;
4467 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 }
4469 }
4470 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004471 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472}
4473
4474/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004475 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476 */
4477 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004478clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004480 hashitem_T *hi;
4481 int todo;
4482 keyentry_T *kp;
4483 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004485 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004486 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004488 if (!HASHITEM_EMPTY(hi))
4489 {
4490 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004491 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 kp_next = kp->ke_next;
4494 vim_free(kp->next_list);
4495 vim_free(kp->k_syn.cont_in_list);
4496 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 hash_clear(ht);
4501 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502}
4503
4504/*
4505 * Add a keyword to the list of keywords.
4506 */
4507 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004508add_keyword(
4509 char_u *name, /* name of keyword */
4510 int id, /* group ID for this keyword */
4511 int flags, /* flags for this keyword */
4512 short *cont_in_list, /* containedin for this keyword */
4513 short *next_list, /* nextgroup for this keyword */
4514 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004516 keyentry_T *kp;
4517 hashtab_T *ht;
4518 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004520 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004521 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522
Bram Moolenaar860cae12010-06-05 23:22:07 +02004523 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 name_ic = str_foldcase(name, (int)STRLEN(name),
4525 name_folded, MAXKEYWLEN + 1);
4526 else
4527 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004528 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4529 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004531 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004532 kp->k_syn.id = id;
4533 kp->k_syn.inc_tag = current_syn_inc_tag;
4534 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004535 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004536 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004537 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004538 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004539 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540
Bram Moolenaar860cae12010-06-05 23:22:07 +02004541 if (curwin->w_s->b_syn_ic)
4542 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004544 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545
Bram Moolenaardad6b692005-01-25 22:14:34 +00004546 hash = hash_hash(kp->keyword);
4547 hi = hash_lookup(ht, kp->keyword, hash);
4548 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004550 /* new keyword, add to hashtable */
4551 kp->ke_next = NULL;
4552 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004554 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004556 /* keyword already exists, prepend to list */
4557 kp->ke_next = HI2KE(hi);
4558 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560}
4561
4562/*
4563 * Get the start and end of the group name argument.
4564 * Return a pointer to the first argument.
4565 * Return NULL if the end of the command was found instead of further args.
4566 */
4567 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004568get_group_name(
4569 char_u *arg, /* start of the argument */
4570 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004571{
4572 char_u *rest;
4573
4574 *name_end = skiptowhite(arg);
4575 rest = skipwhite(*name_end);
4576
4577 /*
4578 * Check if there are enough arguments. The first argument may be a
4579 * pattern, where '|' is allowed, so only check for NUL.
4580 */
4581 if (ends_excmd(*arg) || *rest == NUL)
4582 return NULL;
4583 return rest;
4584}
4585
4586/*
4587 * Check for syntax command option arguments.
4588 * This can be called at any place in the list of arguments, and just picks
4589 * out the arguments that are known. Can be called several times in a row to
4590 * collect all options in between other arguments.
4591 * Return a pointer to the next argument (which isn't an option).
4592 * Return NULL for any error;
4593 */
4594 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004595get_syn_options(
4596 char_u *arg, /* next argument to be checked */
4597 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004598 int *conceal_char UNUSED,
4599 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601 char_u *gname_start, *gname;
4602 int syn_id;
4603 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004604 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 int i;
4606 int fidx;
4607 static struct flag
4608 {
4609 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 int argtype;
4611 int flags;
4612 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4613 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4614 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4615 {"eExXtTeEnNdD", 0, HL_EXTEND},
4616 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4617 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4618 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4619 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4620 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4621 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4622 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4623 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4624 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004625 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4626 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4627 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 {"cCoOnNtTaAiInNsS", 1, 0},
4629 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4630 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004632 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633
4634 if (arg == NULL) /* already detected error */
4635 return NULL;
4636
Bram Moolenaar860cae12010-06-05 23:22:07 +02004637#ifdef FEAT_CONCEAL
4638 if (curwin->w_s->b_syn_conceal)
4639 opt->flags |= HL_CONCEAL;
4640#endif
4641
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642 for (;;)
4643 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004644 /*
4645 * This is used very often when a large number of keywords is defined.
4646 * Need to skip quickly when no option name is found.
4647 * Also avoid tolower(), it's slow.
4648 */
4649 if (strchr(first_letters, *arg) == NULL)
4650 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651
4652 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4653 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004654 p = flagtab[fidx].name;
4655 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4656 if (arg[len] != p[i] && arg[len] != p[i + 1])
4657 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004658 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004659 || (flagtab[fidx].argtype > 0
4660 ? arg[len] == '='
4661 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004663 if (opt->keyword
4664 && (flagtab[fidx].flags == HL_DISPLAY
4665 || flagtab[fidx].flags == HL_FOLD
4666 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 /* treat "display", "fold" and "extend" as a keyword */
4668 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 break;
4670 }
4671 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 if (fidx < 0) /* no match found */
4673 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004675 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004677 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 {
4679 EMSG(_("E395: contains argument not accepted here"));
4680 return NULL;
4681 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004682 if (get_id_list(&arg, 8, &opt->cont_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 == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004687 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 return NULL;
4689 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004690 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004692 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 return NULL;
4694 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004695 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4696 {
4697#ifdef FEAT_MBYTE
4698 /* cchar=? */
4699 if (has_mbyte)
4700 {
4701# ifdef FEAT_CONCEAL
4702 *conceal_char = mb_ptr2char(arg + 6);
4703# endif
4704 arg += mb_ptr2len(arg + 6) - 1;
4705 }
4706 else
4707#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004708 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004709#ifdef FEAT_CONCEAL
4710 *conceal_char = arg[6];
4711#else
4712 ;
4713#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004714 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004715#ifdef FEAT_CONCEAL
4716 if (!vim_isprintc_strict(*conceal_char))
4717 {
4718 EMSG(_("E844: invalid cchar value"));
4719 return NULL;
4720 }
4721#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004722 arg = skipwhite(arg + 7);
4723 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004725 {
4726 opt->flags |= flagtab[fidx].flags;
4727 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004729 if (flagtab[fidx].flags == HL_SYNC_HERE
4730 || flagtab[fidx].flags == HL_SYNC_THERE)
4731 {
4732 if (opt->sync_idx == NULL)
4733 {
4734 EMSG(_("E393: group[t]here not accepted here"));
4735 return NULL;
4736 }
4737 gname_start = arg;
4738 arg = skiptowhite(arg);
4739 if (gname_start == arg)
4740 return NULL;
4741 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4742 if (gname == NULL)
4743 return NULL;
4744 if (STRCMP(gname, "NONE") == 0)
4745 *opt->sync_idx = NONE_IDX;
4746 else
4747 {
4748 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004749 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4750 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4751 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004752 {
4753 *opt->sync_idx = i;
4754 break;
4755 }
4756 if (i < 0)
4757 {
4758 EMSG2(_("E394: Didn't find region item for %s"), gname);
4759 vim_free(gname);
4760 return NULL;
4761 }
4762 }
4763
4764 vim_free(gname);
4765 arg = skipwhite(arg);
4766 }
4767#ifdef FEAT_FOLDING
4768 else if (flagtab[fidx].flags == HL_FOLD
4769 && foldmethodIsSyntax(curwin))
4770 /* Need to update folds later. */
4771 foldUpdateAll(curwin);
4772#endif
4773 }
4774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775
4776 return arg;
4777}
4778
4779/*
4780 * Adjustments to syntax item when declared in a ":syn include"'d file.
4781 * Set the contained flag, and if the item is not already contained, add it
4782 * to the specified top-level group, if any.
4783 */
4784 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004785syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004787 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 return;
4789 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004790 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791 {
4792 /* We have to alloc this, because syn_combine_list() will free it. */
4793 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004794 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795
4796 if (grp_list != NULL)
4797 {
4798 grp_list[0] = id;
4799 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004800 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004801 CLUSTER_ADD);
4802 }
4803 }
4804}
4805
4806/*
4807 * Handle ":syntax include [@{group-name}] filename" command.
4808 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004810syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811{
4812 char_u *arg = eap->arg;
4813 int sgl_id = 1;
4814 char_u *group_name_end;
4815 char_u *rest;
4816 char_u *errormsg = NULL;
4817 int prev_toplvl_grp;
4818 int prev_syn_inc_tag;
4819 int source = FALSE;
4820
4821 eap->nextcmd = find_nextcmd(arg);
4822 if (eap->skip)
4823 return;
4824
4825 if (arg[0] == '@')
4826 {
4827 ++arg;
4828 rest = get_group_name(arg, &group_name_end);
4829 if (rest == NULL)
4830 {
4831 EMSG((char_u *)_("E397: Filename required"));
4832 return;
4833 }
4834 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004835 if (sgl_id == 0)
4836 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837 /* separate_nextcmd() and expand_filename() depend on this */
4838 eap->arg = rest;
4839 }
4840
4841 /*
4842 * Everything that's left, up to the next command, should be the
4843 * filename to include.
4844 */
4845 eap->argt |= (XFILE | NOSPC);
4846 separate_nextcmd(eap);
4847 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4848 {
4849 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4850 * file. Need to expand the file name first. In other cases
4851 * ":runtime!" is used. */
4852 source = TRUE;
4853 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4854 {
4855 if (errormsg != NULL)
4856 EMSG(errormsg);
4857 return;
4858 }
4859 }
4860
4861 /*
4862 * Save and restore the existing top-level grouplist id and ":syn
4863 * include" tag around the actual inclusion.
4864 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004865 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4866 {
4867 EMSG((char_u *)_("E847: Too many syntax includes"));
4868 return;
4869 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 prev_syn_inc_tag = current_syn_inc_tag;
4871 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004872 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4873 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004874 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004875 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004877 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 current_syn_inc_tag = prev_syn_inc_tag;
4879}
4880
4881/*
4882 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4883 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004885syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886{
4887 char_u *arg = eap->arg;
4888 char_u *group_name_end;
4889 int syn_id;
4890 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004891 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004893 char_u *kw;
4894 syn_opt_arg_T syn_opt_arg;
4895 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004896 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897
4898 rest = get_group_name(arg, &group_name_end);
4899
4900 if (rest != NULL)
4901 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004902 if (eap->skip)
4903 syn_id = -1;
4904 else
4905 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004906 if (syn_id != 0)
4907 /* allocate a buffer, for removing backslashes in the keyword */
4908 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 if (keyword_copy != NULL)
4910 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004911 syn_opt_arg.flags = 0;
4912 syn_opt_arg.keyword = TRUE;
4913 syn_opt_arg.sync_idx = NULL;
4914 syn_opt_arg.has_cont_list = FALSE;
4915 syn_opt_arg.cont_in_list = NULL;
4916 syn_opt_arg.next_list = NULL;
4917
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 /*
4919 * The options given apply to ALL keywords, so all options must be
4920 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004921 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004922 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004923 cnt = 0;
4924 p = keyword_copy;
4925 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004926 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004927 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4928 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004929 if (rest == NULL || ends_excmd(*rest))
4930 break;
4931 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004932 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004934 if (*rest == '\\' && rest[1] != NUL)
4935 ++rest;
4936 *p++ = *rest++;
4937 }
4938 *p++ = NUL;
4939 ++cnt;
4940 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004942 if (!eap->skip)
4943 {
4944 /* Adjust flags for use of ":syn include". */
4945 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4946
4947 /*
4948 * 2: Add an entry for each keyword.
4949 */
4950 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4951 {
4952 for (p = vim_strchr(kw, '['); ; )
4953 {
4954 if (p != NULL)
4955 *p = NUL;
4956 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004957 syn_opt_arg.cont_in_list,
4958 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004959 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004960 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004961 if (p[1] == NUL)
4962 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004963 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004964 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004965 }
4966 if (p[1] == ']')
4967 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004968 if (p[2] != NUL)
4969 {
4970 EMSG3(_("E890: trailing char after ']': %s]%s"),
4971 kw, &p[2]);
4972 goto error;
4973 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004974 kw = p + 1; /* skip over the "]" */
4975 break;
4976 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004977#ifdef FEAT_MBYTE
4978 if (has_mbyte)
4979 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004980 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004981
4982 mch_memmove(p, p + 1, l);
4983 p += l;
4984 }
4985 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004987 {
4988 p[0] = p[1];
4989 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004990 }
4991 }
4992 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004994error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004996 vim_free(syn_opt_arg.cont_in_list);
4997 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
4999 }
5000
5001 if (rest != NULL)
5002 eap->nextcmd = check_nextcmd(rest);
5003 else
5004 EMSG2(_(e_invarg2), arg);
5005
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005006 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005007 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005008}
5009
5010/*
5011 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5012 *
5013 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5014 */
5015 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005016syn_cmd_match(
5017 exarg_T *eap,
5018 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019{
5020 char_u *arg = eap->arg;
5021 char_u *group_name_end;
5022 char_u *rest;
5023 synpat_T item; /* the item found in the line */
5024 int syn_id;
5025 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005026 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005028 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005029
5030 /* Isolate the group name, check for validity */
5031 rest = get_group_name(arg, &group_name_end);
5032
5033 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005034 syn_opt_arg.flags = 0;
5035 syn_opt_arg.keyword = FALSE;
5036 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5037 syn_opt_arg.has_cont_list = TRUE;
5038 syn_opt_arg.cont_list = NULL;
5039 syn_opt_arg.cont_in_list = NULL;
5040 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005041 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005042
5043 /* get the pattern. */
5044 init_syn_patterns();
5045 vim_memset(&item, 0, sizeof(item));
5046 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005047 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5048 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049
5050 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005051 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005052
5053 if (rest != NULL) /* all arguments are valid */
5054 {
5055 /*
5056 * Check for trailing command and illegal trailing arguments.
5057 */
5058 eap->nextcmd = check_nextcmd(rest);
5059 if (!ends_excmd(*rest) || eap->skip)
5060 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005061 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005062 && (syn_id = syn_check_group(arg,
5063 (int)(group_name_end - arg))) != 0)
5064 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005065 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066 /*
5067 * Store the pattern in the syn_items list
5068 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005069 idx = curwin->w_s->b_syn_patterns.ga_len;
5070 SYN_ITEMS(curwin->w_s)[idx] = item;
5071 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5072 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5073 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5074 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5075 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5076 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5077 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5078 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005079 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005080#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005081 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005082#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005083 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005084 curwin->w_s->b_syn_containedin = TRUE;
5085 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5086 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087
5088 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005089 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005090 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005091#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005092 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005093 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094#endif
5095
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005096 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005097 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 return; /* don't free the progs and patterns now */
5099 }
5100 }
5101
5102 /*
5103 * Something failed, free the allocated memory.
5104 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005105 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005107 vim_free(syn_opt_arg.cont_list);
5108 vim_free(syn_opt_arg.cont_in_list);
5109 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110
5111 if (rest == NULL)
5112 EMSG2(_(e_invarg2), arg);
5113}
5114
5115/*
5116 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5117 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5118 */
5119 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005120syn_cmd_region(
5121 exarg_T *eap,
5122 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005123{
5124 char_u *arg = eap->arg;
5125 char_u *group_name_end;
5126 char_u *rest; /* next arg, NULL on error */
5127 char_u *key_end;
5128 char_u *key = NULL;
5129 char_u *p;
5130 int item;
5131#define ITEM_START 0
5132#define ITEM_SKIP 1
5133#define ITEM_END 2
5134#define ITEM_MATCHGROUP 3
5135 struct pat_ptr
5136 {
5137 synpat_T *pp_synp; /* pointer to syn_pattern */
5138 int pp_matchgroup_id; /* matchgroup ID */
5139 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5140 } *(pat_ptrs[3]);
5141 /* patterns found in the line */
5142 struct pat_ptr *ppp;
5143 struct pat_ptr *ppp_next;
5144 int pat_count = 0; /* nr of syn_patterns found */
5145 int syn_id;
5146 int matchgroup_id = 0;
5147 int not_enough = FALSE; /* not enough arguments */
5148 int illegal = FALSE; /* illegal arguments */
5149 int success = FALSE;
5150 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005151 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005152 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153
5154 /* Isolate the group name, check for validity */
5155 rest = get_group_name(arg, &group_name_end);
5156
5157 pat_ptrs[0] = NULL;
5158 pat_ptrs[1] = NULL;
5159 pat_ptrs[2] = NULL;
5160
5161 init_syn_patterns();
5162
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005163 syn_opt_arg.flags = 0;
5164 syn_opt_arg.keyword = FALSE;
5165 syn_opt_arg.sync_idx = NULL;
5166 syn_opt_arg.has_cont_list = TRUE;
5167 syn_opt_arg.cont_list = NULL;
5168 syn_opt_arg.cont_in_list = NULL;
5169 syn_opt_arg.next_list = NULL;
5170
Bram Moolenaar071d4272004-06-13 20:20:40 +00005171 /*
5172 * get the options, patterns and matchgroup.
5173 */
5174 while (rest != NULL && !ends_excmd(*rest))
5175 {
5176 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005177 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178 if (rest == NULL || ends_excmd(*rest))
5179 break;
5180
5181 /* must be a pattern or matchgroup then */
5182 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005183 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184 ++key_end;
5185 vim_free(key);
5186 key = vim_strnsave_up(rest, (int)(key_end - rest));
5187 if (key == NULL) /* out of memory */
5188 {
5189 rest = NULL;
5190 break;
5191 }
5192 if (STRCMP(key, "MATCHGROUP") == 0)
5193 item = ITEM_MATCHGROUP;
5194 else if (STRCMP(key, "START") == 0)
5195 item = ITEM_START;
5196 else if (STRCMP(key, "END") == 0)
5197 item = ITEM_END;
5198 else if (STRCMP(key, "SKIP") == 0)
5199 {
5200 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5201 {
5202 illegal = TRUE;
5203 break;
5204 }
5205 item = ITEM_SKIP;
5206 }
5207 else
5208 break;
5209 rest = skipwhite(key_end);
5210 if (*rest != '=')
5211 {
5212 rest = NULL;
5213 EMSG2(_("E398: Missing '=': %s"), arg);
5214 break;
5215 }
5216 rest = skipwhite(rest + 1);
5217 if (*rest == NUL)
5218 {
5219 not_enough = TRUE;
5220 break;
5221 }
5222
5223 if (item == ITEM_MATCHGROUP)
5224 {
5225 p = skiptowhite(rest);
5226 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5227 matchgroup_id = 0;
5228 else
5229 {
5230 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5231 if (matchgroup_id == 0)
5232 {
5233 illegal = TRUE;
5234 break;
5235 }
5236 }
5237 rest = skipwhite(p);
5238 }
5239 else
5240 {
5241 /*
5242 * Allocate room for a syn_pattern, and link it in the list of
5243 * syn_patterns for this item, at the start (because the list is
5244 * used from end to start).
5245 */
5246 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5247 if (ppp == NULL)
5248 {
5249 rest = NULL;
5250 break;
5251 }
5252 ppp->pp_next = pat_ptrs[item];
5253 pat_ptrs[item] = ppp;
5254 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5255 if (ppp->pp_synp == NULL)
5256 {
5257 rest = NULL;
5258 break;
5259 }
5260
5261 /*
5262 * Get the syntax pattern and the following offset(s).
5263 */
5264 /* Enable the appropriate \z specials. */
5265 if (item == ITEM_START)
5266 reg_do_extmatch = REX_SET;
5267 else if (item == ITEM_SKIP || item == ITEM_END)
5268 reg_do_extmatch = REX_USE;
5269 rest = get_syn_pattern(rest, ppp->pp_synp);
5270 reg_do_extmatch = 0;
5271 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005272 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005273 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5274 ppp->pp_matchgroup_id = matchgroup_id;
5275 ++pat_count;
5276 }
5277 }
5278 vim_free(key);
5279 if (illegal || not_enough)
5280 rest = NULL;
5281
5282 /*
5283 * Must have a "start" and "end" pattern.
5284 */
5285 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5286 pat_ptrs[ITEM_END] == NULL))
5287 {
5288 not_enough = TRUE;
5289 rest = NULL;
5290 }
5291
5292 if (rest != NULL)
5293 {
5294 /*
5295 * Check for trailing garbage or command.
5296 * If OK, add the item.
5297 */
5298 eap->nextcmd = check_nextcmd(rest);
5299 if (!ends_excmd(*rest) || eap->skip)
5300 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005301 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 && (syn_id = syn_check_group(arg,
5303 (int)(group_name_end - arg))) != 0)
5304 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005305 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306 /*
5307 * Store the start/skip/end in the syn_items list
5308 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310 for (item = ITEM_START; item <= ITEM_END; ++item)
5311 {
5312 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5313 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005314 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5315 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5316 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005317 (item == ITEM_START) ? SPTYPE_START :
5318 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005319 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5320 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005321 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5322 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005323 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005324 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005325#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005326 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005327#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005328 if (item == ITEM_START)
5329 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005330 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005331 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005332 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005333 syn_opt_arg.cont_in_list;
5334 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005335 curwin->w_s->b_syn_containedin = TRUE;
5336 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005337 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005338 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005339 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340 ++idx;
5341#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005342 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005343 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005344#endif
5345 }
5346 }
5347
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005348 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005349 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005350 success = TRUE; /* don't free the progs and patterns now */
5351 }
5352 }
5353
5354 /*
5355 * Free the allocated memory.
5356 */
5357 for (item = ITEM_START; item <= ITEM_END; ++item)
5358 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5359 {
5360 if (!success)
5361 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005362 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005363 vim_free(ppp->pp_synp->sp_pattern);
5364 }
5365 vim_free(ppp->pp_synp);
5366 ppp_next = ppp->pp_next;
5367 vim_free(ppp);
5368 }
5369
5370 if (!success)
5371 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005372 vim_free(syn_opt_arg.cont_list);
5373 vim_free(syn_opt_arg.cont_in_list);
5374 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005375 if (not_enough)
5376 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5377 else if (illegal || rest == NULL)
5378 EMSG2(_(e_invarg2), arg);
5379 }
5380}
5381
5382/*
5383 * A simple syntax group ID comparison function suitable for use in qsort()
5384 */
5385 static int
5386#ifdef __BORLANDC__
5387_RTLENTRYF
5388#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005389syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005390{
5391 const short *s1 = v1;
5392 const short *s2 = v2;
5393
5394 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5395}
5396
5397/*
5398 * Combines lists of syntax clusters.
5399 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5400 */
5401 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005402syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403{
5404 int count1 = 0;
5405 int count2 = 0;
5406 short *g1;
5407 short *g2;
5408 short *clstr = NULL;
5409 int count;
5410 int round;
5411
5412 /*
5413 * Handle degenerate cases.
5414 */
5415 if (*clstr2 == NULL)
5416 return;
5417 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5418 {
5419 if (list_op == CLUSTER_REPLACE)
5420 vim_free(*clstr1);
5421 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5422 *clstr1 = *clstr2;
5423 else
5424 vim_free(*clstr2);
5425 return;
5426 }
5427
5428 for (g1 = *clstr1; *g1; g1++)
5429 ++count1;
5430 for (g2 = *clstr2; *g2; g2++)
5431 ++count2;
5432
5433 /*
5434 * For speed purposes, sort both lists.
5435 */
5436 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5437 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5438
5439 /*
5440 * We proceed in two passes; in round 1, we count the elements to place
5441 * in the new list, and in round 2, we allocate and populate the new
5442 * list. For speed, we use a mergesort-like method, adding the smaller
5443 * of the current elements in each list to the new list.
5444 */
5445 for (round = 1; round <= 2; round++)
5446 {
5447 g1 = *clstr1;
5448 g2 = *clstr2;
5449 count = 0;
5450
5451 /*
5452 * First, loop through the lists until one of them is empty.
5453 */
5454 while (*g1 && *g2)
5455 {
5456 /*
5457 * We always want to add from the first list.
5458 */
5459 if (*g1 < *g2)
5460 {
5461 if (round == 2)
5462 clstr[count] = *g1;
5463 count++;
5464 g1++;
5465 continue;
5466 }
5467 /*
5468 * We only want to add from the second list if we're adding the
5469 * lists.
5470 */
5471 if (list_op == CLUSTER_ADD)
5472 {
5473 if (round == 2)
5474 clstr[count] = *g2;
5475 count++;
5476 }
5477 if (*g1 == *g2)
5478 g1++;
5479 g2++;
5480 }
5481
5482 /*
5483 * Now add the leftovers from whichever list didn't get finished
5484 * first. As before, we only want to add from the second list if
5485 * we're adding the lists.
5486 */
5487 for (; *g1; g1++, count++)
5488 if (round == 2)
5489 clstr[count] = *g1;
5490 if (list_op == CLUSTER_ADD)
5491 for (; *g2; g2++, count++)
5492 if (round == 2)
5493 clstr[count] = *g2;
5494
5495 if (round == 1)
5496 {
5497 /*
5498 * If the group ended up empty, we don't need to allocate any
5499 * space for it.
5500 */
5501 if (count == 0)
5502 {
5503 clstr = NULL;
5504 break;
5505 }
5506 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5507 if (clstr == NULL)
5508 break;
5509 clstr[count] = 0;
5510 }
5511 }
5512
5513 /*
5514 * Finally, put the new list in place.
5515 */
5516 vim_free(*clstr1);
5517 vim_free(*clstr2);
5518 *clstr1 = clstr;
5519}
5520
5521/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005522 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005523 * If it is not found, 0 is returned.
5524 */
5525 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005526syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527{
5528 int i;
5529 char_u *name_u;
5530
5531 /* Avoid using stricmp() too much, it's slow on some systems */
5532 name_u = vim_strsave_up(name);
5533 if (name_u == NULL)
5534 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005535 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5536 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5537 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538 break;
5539 vim_free(name_u);
5540 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5541}
5542
5543/*
5544 * Like syn_scl_name2id(), but take a pointer + length argument.
5545 */
5546 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005547syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548{
5549 char_u *name;
5550 int id = 0;
5551
5552 name = vim_strnsave(linep, len);
5553 if (name != NULL)
5554 {
5555 id = syn_scl_name2id(name);
5556 vim_free(name);
5557 }
5558 return id;
5559}
5560
5561/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005562 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 * The argument is a pointer to the name and the length of the name.
5564 * If it doesn't exist yet, a new entry is created.
5565 * Return 0 for failure.
5566 */
5567 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005568syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569{
5570 int id;
5571 char_u *name;
5572
5573 name = vim_strnsave(pp, len);
5574 if (name == NULL)
5575 return 0;
5576
5577 id = syn_scl_name2id(name);
5578 if (id == 0) /* doesn't exist yet */
5579 id = syn_add_cluster(name);
5580 else
5581 vim_free(name);
5582 return id;
5583}
5584
5585/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005586 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587 * "name" must be an allocated string, it will be consumed.
5588 * Return 0 for failure.
5589 */
5590 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005591syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005593 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594
5595 /*
5596 * First call for this growarray: init growing array.
5597 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005598 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005600 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5601 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602 }
5603
Bram Moolenaar42431a72011-04-01 14:44:59 +02005604 len = curwin->w_s->b_syn_clusters.ga_len;
5605 if (len >= MAX_CLUSTER_ID)
5606 {
5607 EMSG((char_u *)_("E848: Too many syntax clusters"));
5608 vim_free(name);
5609 return 0;
5610 }
5611
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612 /*
5613 * Make room for at least one other cluster entry.
5614 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005615 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616 {
5617 vim_free(name);
5618 return 0;
5619 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620
Bram Moolenaar860cae12010-06-05 23:22:07 +02005621 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5622 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5623 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5624 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5625 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005626
Bram Moolenaar217ad922005-03-20 22:37:15 +00005627 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005628 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005629 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005630 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005631
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632 return len + SYNID_CLUSTER;
5633}
5634
5635/*
5636 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5637 * [add={groupname},..] [remove={groupname},..]".
5638 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005640syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641{
5642 char_u *arg = eap->arg;
5643 char_u *group_name_end;
5644 char_u *rest;
5645 int scl_id;
5646 short *clstr_list;
5647 int got_clstr = FALSE;
5648 int opt_len;
5649 int list_op;
5650
5651 eap->nextcmd = find_nextcmd(arg);
5652 if (eap->skip)
5653 return;
5654
5655 rest = get_group_name(arg, &group_name_end);
5656
5657 if (rest != NULL)
5658 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005659 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5660 if (scl_id == 0)
5661 return;
5662 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663
5664 for (;;)
5665 {
5666 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005667 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 {
5669 opt_len = 3;
5670 list_op = CLUSTER_ADD;
5671 }
5672 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005673 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005674 {
5675 opt_len = 6;
5676 list_op = CLUSTER_SUBTRACT;
5677 }
5678 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005679 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 {
5681 opt_len = 8;
5682 list_op = CLUSTER_REPLACE;
5683 }
5684 else
5685 break;
5686
5687 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005688 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689 {
5690 EMSG2(_(e_invarg2), rest);
5691 break;
5692 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005693 if (scl_id >= 0)
5694 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005696 else
5697 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698 got_clstr = TRUE;
5699 }
5700
5701 if (got_clstr)
5702 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005703 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005704 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705 }
5706 }
5707
5708 if (!got_clstr)
5709 EMSG(_("E400: No cluster specified"));
5710 if (rest == NULL || !ends_excmd(*rest))
5711 EMSG2(_(e_invarg2), arg);
5712}
5713
5714/*
5715 * On first call for current buffer: Init growing array.
5716 */
5717 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005718init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005719{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005720 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5721 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722}
5723
5724/*
5725 * Get one pattern for a ":syntax match" or ":syntax region" command.
5726 * Stores the pattern and program in a synpat_T.
5727 * Returns a pointer to the next argument, or NULL in case of an error.
5728 */
5729 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005730get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731{
5732 char_u *end;
5733 int *p;
5734 int idx;
5735 char_u *cpo_save;
5736
5737 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005738 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 return NULL;
5740
5741 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5742 if (*end != *arg) /* end delimiter not found */
5743 {
5744 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5745 return NULL;
5746 }
5747 /* store the pattern and compiled regexp program */
5748 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5749 return NULL;
5750
5751 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5752 cpo_save = p_cpo;
5753 p_cpo = (char_u *)"";
5754 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5755 p_cpo = cpo_save;
5756
5757 if (ci->sp_prog == NULL)
5758 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005759 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005760#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005761 syn_clear_time(&ci->sp_time);
5762#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005763
5764 /*
5765 * Check for a match, highlight or region offset.
5766 */
5767 ++end;
5768 do
5769 {
5770 for (idx = SPO_COUNT; --idx >= 0; )
5771 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5772 break;
5773 if (idx >= 0)
5774 {
5775 p = &(ci->sp_offsets[idx]);
5776 if (idx != SPO_LC_OFF)
5777 switch (end[3])
5778 {
5779 case 's': break;
5780 case 'b': break;
5781 case 'e': idx += SPO_COUNT; break;
5782 default: idx = -1; break;
5783 }
5784 if (idx >= 0)
5785 {
5786 ci->sp_off_flags |= (1 << idx);
5787 if (idx == SPO_LC_OFF) /* lc=99 */
5788 {
5789 end += 3;
5790 *p = getdigits(&end);
5791
5792 /* "lc=" offset automatically sets "ms=" offset */
5793 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5794 {
5795 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5796 ci->sp_offsets[SPO_MS_OFF] = *p;
5797 }
5798 }
5799 else /* yy=x+99 */
5800 {
5801 end += 4;
5802 if (*end == '+')
5803 {
5804 ++end;
5805 *p = getdigits(&end); /* positive offset */
5806 }
5807 else if (*end == '-')
5808 {
5809 ++end;
5810 *p = -getdigits(&end); /* negative offset */
5811 }
5812 }
5813 if (*end != ',')
5814 break;
5815 ++end;
5816 }
5817 }
5818 } while (idx >= 0);
5819
Bram Moolenaar1c465442017-03-12 20:10:05 +01005820 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 {
5822 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5823 return NULL;
5824 }
5825 return skipwhite(end);
5826}
5827
5828/*
5829 * Handle ":syntax sync .." command.
5830 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005832syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833{
5834 char_u *arg_start = eap->arg;
5835 char_u *arg_end;
5836 char_u *key = NULL;
5837 char_u *next_arg;
5838 int illegal = FALSE;
5839 int finished = FALSE;
5840 long n;
5841 char_u *cpo_save;
5842
5843 if (ends_excmd(*arg_start))
5844 {
5845 syn_cmd_list(eap, TRUE);
5846 return;
5847 }
5848
5849 while (!ends_excmd(*arg_start))
5850 {
5851 arg_end = skiptowhite(arg_start);
5852 next_arg = skipwhite(arg_end);
5853 vim_free(key);
5854 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5855 if (STRCMP(key, "CCOMMENT") == 0)
5856 {
5857 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005858 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005859 if (!ends_excmd(*next_arg))
5860 {
5861 arg_end = skiptowhite(next_arg);
5862 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864 (int)(arg_end - next_arg));
5865 next_arg = skipwhite(arg_end);
5866 }
5867 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 }
5870 else if ( STRNCMP(key, "LINES", 5) == 0
5871 || STRNCMP(key, "MINLINES", 8) == 0
5872 || STRNCMP(key, "MAXLINES", 8) == 0
5873 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5874 {
5875 if (key[4] == 'S')
5876 arg_end = key + 6;
5877 else if (key[0] == 'L')
5878 arg_end = key + 11;
5879 else
5880 arg_end = key + 9;
5881 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5882 {
5883 illegal = TRUE;
5884 break;
5885 }
5886 n = getdigits(&arg_end);
5887 if (!eap->skip)
5888 {
5889 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005890 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005891 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005892 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005893 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005894 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005895 }
5896 }
5897 else if (STRCMP(key, "FROMSTART") == 0)
5898 {
5899 if (!eap->skip)
5900 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005901 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5902 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005903 }
5904 }
5905 else if (STRCMP(key, "LINECONT") == 0)
5906 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005907 if (*next_arg == NUL) /* missing pattern */
5908 {
5909 illegal = TRUE;
5910 break;
5911 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005912 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 {
5914 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5915 finished = TRUE;
5916 break;
5917 }
5918 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5919 if (*arg_end != *next_arg) /* end delimiter not found */
5920 {
5921 illegal = TRUE;
5922 break;
5923 }
5924
5925 if (!eap->skip)
5926 {
5927 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005928 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005929 (int)(arg_end - next_arg - 1))) == NULL)
5930 {
5931 finished = TRUE;
5932 break;
5933 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005934 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005935
5936 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5937 cpo_save = p_cpo;
5938 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005939 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005940 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005941 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005942#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005943 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5944#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005945
Bram Moolenaar860cae12010-06-05 23:22:07 +02005946 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005947 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005948 vim_free(curwin->w_s->b_syn_linecont_pat);
5949 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950 finished = TRUE;
5951 break;
5952 }
5953 }
5954 next_arg = skipwhite(arg_end + 1);
5955 }
5956 else
5957 {
5958 eap->arg = next_arg;
5959 if (STRCMP(key, "MATCH") == 0)
5960 syn_cmd_match(eap, TRUE);
5961 else if (STRCMP(key, "REGION") == 0)
5962 syn_cmd_region(eap, TRUE);
5963 else if (STRCMP(key, "CLEAR") == 0)
5964 syn_cmd_clear(eap, TRUE);
5965 else
5966 illegal = TRUE;
5967 finished = TRUE;
5968 break;
5969 }
5970 arg_start = next_arg;
5971 }
5972 vim_free(key);
5973 if (illegal)
5974 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5975 else if (!finished)
5976 {
5977 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005978 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005979 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005980 }
5981}
5982
5983/*
5984 * Convert a line of highlight group names into a list of group ID numbers.
5985 * "arg" should point to the "contains" or "nextgroup" keyword.
5986 * "arg" is advanced to after the last group name.
5987 * Careful: the argument is modified (NULs added).
5988 * returns FAIL for some error, OK for success.
5989 */
5990 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005991get_id_list(
5992 char_u **arg,
5993 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005994 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005995 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005996 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005997{
5998 char_u *p = NULL;
5999 char_u *end;
6000 int round;
6001 int count;
6002 int total_count = 0;
6003 short *retval = NULL;
6004 char_u *name;
6005 regmatch_T regmatch;
6006 int id;
6007 int i;
6008 int failed = FALSE;
6009
6010 /*
6011 * We parse the list twice:
6012 * round == 1: count the number of items, allocate the array.
6013 * round == 2: fill the array with the items.
6014 * In round 1 new groups may be added, causing the number of items to
6015 * grow when a regexp is used. In that case round 1 is done once again.
6016 */
6017 for (round = 1; round <= 2; ++round)
6018 {
6019 /*
6020 * skip "contains"
6021 */
6022 p = skipwhite(*arg + keylen);
6023 if (*p != '=')
6024 {
6025 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6026 break;
6027 }
6028 p = skipwhite(p + 1);
6029 if (ends_excmd(*p))
6030 {
6031 EMSG2(_("E406: Empty argument: %s"), *arg);
6032 break;
6033 }
6034
6035 /*
6036 * parse the arguments after "contains"
6037 */
6038 count = 0;
6039 while (!ends_excmd(*p))
6040 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006041 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 ;
6043 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6044 if (name == NULL)
6045 {
6046 failed = TRUE;
6047 break;
6048 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006049 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006050 if ( STRCMP(name + 1, "ALLBUT") == 0
6051 || STRCMP(name + 1, "ALL") == 0
6052 || STRCMP(name + 1, "TOP") == 0
6053 || STRCMP(name + 1, "CONTAINED") == 0)
6054 {
6055 if (TOUPPER_ASC(**arg) != 'C')
6056 {
6057 EMSG2(_("E407: %s not allowed here"), name + 1);
6058 failed = TRUE;
6059 vim_free(name);
6060 break;
6061 }
6062 if (count != 0)
6063 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006064 EMSG2(_("E408: %s must be first in contains list"),
6065 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006066 failed = TRUE;
6067 vim_free(name);
6068 break;
6069 }
6070 if (name[1] == 'A')
6071 id = SYNID_ALLBUT;
6072 else if (name[1] == 'T')
6073 id = SYNID_TOP;
6074 else
6075 id = SYNID_CONTAINED;
6076 id += current_syn_inc_tag;
6077 }
6078 else if (name[1] == '@')
6079 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006080 if (skip)
6081 id = -1;
6082 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006083 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006084 }
6085 else
6086 {
6087 /*
6088 * Handle full group name.
6089 */
6090 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6091 id = syn_check_group(name + 1, (int)(end - p));
6092 else
6093 {
6094 /*
6095 * Handle match of regexp with group names.
6096 */
6097 *name = '^';
6098 STRCAT(name, "$");
6099 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6100 if (regmatch.regprog == NULL)
6101 {
6102 failed = TRUE;
6103 vim_free(name);
6104 break;
6105 }
6106
6107 regmatch.rm_ic = TRUE;
6108 id = 0;
6109 for (i = highlight_ga.ga_len; --i >= 0; )
6110 {
6111 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6112 (colnr_T)0))
6113 {
6114 if (round == 2)
6115 {
6116 /* Got more items than expected; can happen
6117 * when adding items that match:
6118 * "contains=a.*b,axb".
6119 * Go back to first round */
6120 if (count >= total_count)
6121 {
6122 vim_free(retval);
6123 round = 1;
6124 }
6125 else
6126 retval[count] = i + 1;
6127 }
6128 ++count;
6129 id = -1; /* remember that we found one */
6130 }
6131 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006132 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006133 }
6134 }
6135 vim_free(name);
6136 if (id == 0)
6137 {
6138 EMSG2(_("E409: Unknown group name: %s"), p);
6139 failed = TRUE;
6140 break;
6141 }
6142 if (id > 0)
6143 {
6144 if (round == 2)
6145 {
6146 /* Got more items than expected, go back to first round */
6147 if (count >= total_count)
6148 {
6149 vim_free(retval);
6150 round = 1;
6151 }
6152 else
6153 retval[count] = id;
6154 }
6155 ++count;
6156 }
6157 p = skipwhite(end);
6158 if (*p != ',')
6159 break;
6160 p = skipwhite(p + 1); /* skip comma in between arguments */
6161 }
6162 if (failed)
6163 break;
6164 if (round == 1)
6165 {
6166 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6167 if (retval == NULL)
6168 break;
6169 retval[count] = 0; /* zero means end of the list */
6170 total_count = count;
6171 }
6172 }
6173
6174 *arg = p;
6175 if (failed || retval == NULL)
6176 {
6177 vim_free(retval);
6178 return FAIL;
6179 }
6180
6181 if (*list == NULL)
6182 *list = retval;
6183 else
6184 vim_free(retval); /* list already found, don't overwrite it */
6185
6186 return OK;
6187}
6188
6189/*
6190 * Make a copy of an ID list.
6191 */
6192 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006193copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006194{
6195 int len;
6196 int count;
6197 short *retval;
6198
6199 if (list == NULL)
6200 return NULL;
6201
6202 for (count = 0; list[count]; ++count)
6203 ;
6204 len = (count + 1) * sizeof(short);
6205 retval = (short *)alloc((unsigned)len);
6206 if (retval != NULL)
6207 mch_memmove(retval, list, (size_t)len);
6208
6209 return retval;
6210}
6211
6212/*
6213 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6214 * "cur_si" can be NULL if not checking the "containedin" list.
6215 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6216 * the current item.
6217 * This function is called very often, keep it fast!!
6218 */
6219 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006220in_id_list(
6221 stateitem_T *cur_si, /* current item or NULL */
6222 short *list, /* id list */
6223 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6224 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225{
6226 int retval;
6227 short *scl_list;
6228 short item;
6229 short id = ssp->id;
6230 static int depth = 0;
6231 int r;
6232
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006233 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006234 if (cur_si != NULL && ssp->cont_in_list != NULL
6235 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006236 {
6237 /* Ignore transparent items without a contains argument. Double check
6238 * that we don't go back past the first one. */
6239 while ((cur_si->si_flags & HL_TRANS_CONT)
6240 && cur_si > (stateitem_T *)(current_state.ga_data))
6241 --cur_si;
6242 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6243 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006244 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6245 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006246 return TRUE;
6247 }
6248
6249 if (list == NULL)
6250 return FALSE;
6251
6252 /*
6253 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6254 * inside anything. Only allow not-contained groups.
6255 */
6256 if (list == ID_LIST_ALL)
6257 return !contained;
6258
6259 /*
6260 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6261 * contains list. We also require that "id" is at the same ":syn include"
6262 * level as the list.
6263 */
6264 item = *list;
6265 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6266 {
6267 if (item < SYNID_TOP)
6268 {
6269 /* ALL or ALLBUT: accept all groups in the same file */
6270 if (item - SYNID_ALLBUT != ssp->inc_tag)
6271 return FALSE;
6272 }
6273 else if (item < SYNID_CONTAINED)
6274 {
6275 /* TOP: accept all not-contained groups in the same file */
6276 if (item - SYNID_TOP != ssp->inc_tag || contained)
6277 return FALSE;
6278 }
6279 else
6280 {
6281 /* CONTAINED: accept all contained groups in the same file */
6282 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6283 return FALSE;
6284 }
6285 item = *++list;
6286 retval = FALSE;
6287 }
6288 else
6289 retval = TRUE;
6290
6291 /*
6292 * Return "retval" if id is in the contains list.
6293 */
6294 while (item != 0)
6295 {
6296 if (item == id)
6297 return retval;
6298 if (item >= SYNID_CLUSTER)
6299 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006300 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006301 /* restrict recursiveness to 30 to avoid an endless loop for a
6302 * cluster that includes itself (indirectly) */
6303 if (scl_list != NULL && depth < 30)
6304 {
6305 ++depth;
6306 r = in_id_list(NULL, scl_list, ssp, contained);
6307 --depth;
6308 if (r)
6309 return retval;
6310 }
6311 }
6312 item = *++list;
6313 }
6314 return !retval;
6315}
6316
6317struct subcommand
6318{
Bram Moolenaard99df422016-01-29 23:20:40 +01006319 char *name; /* subcommand name */
6320 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006321};
6322
6323static struct subcommand subcommands[] =
6324{
6325 {"case", syn_cmd_case},
6326 {"clear", syn_cmd_clear},
6327 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006328 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006329 {"enable", syn_cmd_enable},
6330 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006331 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006332 {"keyword", syn_cmd_keyword},
6333 {"list", syn_cmd_list},
6334 {"manual", syn_cmd_manual},
6335 {"match", syn_cmd_match},
6336 {"on", syn_cmd_on},
6337 {"off", syn_cmd_off},
6338 {"region", syn_cmd_region},
6339 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006340 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341 {"sync", syn_cmd_sync},
6342 {"", syn_cmd_list},
6343 {NULL, NULL}
6344};
6345
6346/*
6347 * ":syntax".
6348 * This searches the subcommands[] table for the subcommand name, and calls a
6349 * syntax_subcommand() function to do the rest.
6350 */
6351 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006352ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353{
6354 char_u *arg = eap->arg;
6355 char_u *subcmd_end;
6356 char_u *subcmd_name;
6357 int i;
6358
6359 syn_cmdlinep = eap->cmdlinep;
6360
6361 /* isolate subcommand name */
6362 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6363 ;
6364 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6365 if (subcmd_name != NULL)
6366 {
6367 if (eap->skip) /* skip error messages for all subcommands */
6368 ++emsg_skip;
6369 for (i = 0; ; ++i)
6370 {
6371 if (subcommands[i].name == NULL)
6372 {
6373 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6374 break;
6375 }
6376 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6377 {
6378 eap->arg = skipwhite(subcmd_end);
6379 (subcommands[i].func)(eap, FALSE);
6380 break;
6381 }
6382 }
6383 vim_free(subcmd_name);
6384 if (eap->skip)
6385 --emsg_skip;
6386 }
6387}
6388
Bram Moolenaar860cae12010-06-05 23:22:07 +02006389 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006390ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006391{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006392 char_u *old_value;
6393 char_u *new_value;
6394
Bram Moolenaar860cae12010-06-05 23:22:07 +02006395 if (curwin->w_s == &curwin->w_buffer->b_s)
6396 {
6397 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6398 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006399 hash_init(&curwin->w_s->b_keywtab);
6400 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006401#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006402 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006403 curwin->w_p_spell = FALSE; /* No spell checking */
6404 clear_string_option(&curwin->w_s->b_p_spc);
6405 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006406 clear_string_option(&curwin->w_s->b_p_spl);
6407#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006408 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006409 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006410
6411 /* save value of b:current_syntax */
6412 old_value = get_var_value((char_u *)"b:current_syntax");
6413 if (old_value != NULL)
6414 old_value = vim_strsave(old_value);
6415
Bram Moolenaard1413d92016-03-02 21:51:56 +01006416#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006417 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6418 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006419 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006420#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006421
6422 /* move value of b:current_syntax to w:current_syntax */
6423 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006424 if (new_value != NULL)
6425 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006426
6427 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006428 if (old_value == NULL)
6429 do_unlet((char_u *)"b:current_syntax", TRUE);
6430 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006431 {
6432 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6433 vim_free(old_value);
6434 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006435}
6436
6437 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006438syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006439{
6440 return (win->w_s->b_syn_patterns.ga_len != 0
6441 || win->w_s->b_syn_clusters.ga_len != 0
6442 || win->w_s->b_keywtab.ht_used > 0
6443 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006444}
6445
6446#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6447
6448static enum
6449{
6450 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006451 EXP_CASE, /* expand ":syn case" arguments */
6452 EXP_SPELL, /* expand ":syn spell" arguments */
6453 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454} expand_what;
6455
Bram Moolenaar4f688582007-07-24 12:34:30 +00006456/*
6457 * Reset include_link, include_default, include_none to 0.
6458 * Called when we are done expanding.
6459 */
6460 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006461reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006462{
6463 include_link = include_default = include_none = 0;
6464}
6465
6466/*
6467 * Handle command line completion for :match and :echohl command: Add "None"
6468 * as highlight group.
6469 */
6470 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006471set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006472{
6473 xp->xp_context = EXPAND_HIGHLIGHT;
6474 xp->xp_pattern = arg;
6475 include_none = 1;
6476}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006477
6478/*
6479 * Handle command line completion for :syntax command.
6480 */
6481 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006482set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483{
6484 char_u *p;
6485
6486 /* Default: expand subcommands */
6487 xp->xp_context = EXPAND_SYNTAX;
6488 expand_what = EXP_SUBCMD;
6489 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006490 include_link = 0;
6491 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492
6493 /* (part of) subcommand already typed */
6494 if (*arg != NUL)
6495 {
6496 p = skiptowhite(arg);
6497 if (*p != NUL) /* past first word */
6498 {
6499 xp->xp_pattern = skipwhite(p);
6500 if (*skiptowhite(xp->xp_pattern) != NUL)
6501 xp->xp_context = EXPAND_NOTHING;
6502 else if (STRNICMP(arg, "case", p - arg) == 0)
6503 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006504 else if (STRNICMP(arg, "spell", p - arg) == 0)
6505 expand_what = EXP_SPELL;
6506 else if (STRNICMP(arg, "sync", p - arg) == 0)
6507 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006508 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6509 || STRNICMP(arg, "region", p - arg) == 0
6510 || STRNICMP(arg, "match", p - arg) == 0
6511 || STRNICMP(arg, "list", p - arg) == 0)
6512 xp->xp_context = EXPAND_HIGHLIGHT;
6513 else
6514 xp->xp_context = EXPAND_NOTHING;
6515 }
6516 }
6517}
6518
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519/*
6520 * Function given to ExpandGeneric() to obtain the list syntax names for
6521 * expansion.
6522 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006524get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006525{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006526 switch (expand_what)
6527 {
6528 case EXP_SUBCMD:
6529 return (char_u *)subcommands[idx].name;
6530 case EXP_CASE:
6531 {
6532 static char *case_args[] = {"match", "ignore", NULL};
6533 return (char_u *)case_args[idx];
6534 }
6535 case EXP_SPELL:
6536 {
6537 static char *spell_args[] =
6538 {"toplevel", "notoplevel", "default", NULL};
6539 return (char_u *)spell_args[idx];
6540 }
6541 case EXP_SYNC:
6542 {
6543 static char *sync_args[] =
6544 {"ccomment", "clear", "fromstart",
6545 "linebreaks=", "linecont", "lines=", "match",
6546 "maxlines=", "minlines=", "region", NULL};
6547 return (char_u *)sync_args[idx];
6548 }
6549 }
6550 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551}
6552
6553#endif /* FEAT_CMDL_COMPL */
6554
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555/*
6556 * Function called for expression evaluation: get syntax ID at file position.
6557 */
6558 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006559syn_get_id(
6560 win_T *wp,
6561 long lnum,
6562 colnr_T col,
6563 int trans, /* remove transparency */
6564 int *spellp, /* return: can do spell checking */
6565 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006566{
6567 /* When the position is not after the current position and in the same
6568 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006569 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006570 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006571 || col < current_col)
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006572 syntax_start(wp, lnum, NULL);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006573 else if (wp->w_buffer == syn_buf
6574 && lnum == current_lnum
6575 && col > current_col)
6576 /* next_match may not be correct when moving around, e.g. with the
6577 * "skip" expression in searchpair() */
6578 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006580 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006581
6582 return (trans ? current_trans_id : current_id);
6583}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584
Bram Moolenaar860cae12010-06-05 23:22:07 +02006585#if defined(FEAT_CONCEAL) || defined(PROTO)
6586/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006587 * Get extra information about the syntax item. Must be called right after
6588 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006589 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006590 * Returns the current flags.
6591 */
6592 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006593get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006594{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006595 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006596 return current_flags;
6597}
6598
6599/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006600 * Return conceal substitution character
6601 */
6602 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006603syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006604{
6605 return current_sub_char;
6606}
6607#endif
6608
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006609#if defined(FEAT_EVAL) || defined(PROTO)
6610/*
6611 * Return the syntax ID at position "i" in the current stack.
6612 * The caller must have called syn_get_id() before to fill the stack.
6613 * Returns -1 when "i" is out of range.
6614 */
6615 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006616syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006617{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006618 if (i >= current_state.ga_len)
6619 {
6620 /* Need to invalidate the state, because we didn't properly finish it
6621 * for the last character, "keep_state" was TRUE. */
6622 invalidate_current_state();
6623 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006624 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006625 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006626 return CUR_STATE(i).si_id;
6627}
6628#endif
6629
Bram Moolenaar071d4272004-06-13 20:20:40 +00006630#if defined(FEAT_FOLDING) || defined(PROTO)
6631/*
6632 * Function called to get folding level for line "lnum" in window "wp".
6633 */
6634 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006635syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006636{
6637 int level = 0;
6638 int i;
6639
6640 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006641 if (wp->w_s->b_syn_folditems != 0
6642 && !wp->w_s->b_syn_error
6643# ifdef SYN_TIME_LIMIT
6644 && !wp->w_s->b_syn_slow
6645# endif
6646 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006647 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006648 syntax_start(wp, lnum, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006649
6650 for (i = 0; i < current_state.ga_len; ++i)
6651 if (CUR_STATE(i).si_flags & HL_FOLD)
6652 ++level;
6653 }
6654 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006655 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006656 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006657 if (level < 0)
6658 level = 0;
6659 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006660 return level;
6661}
6662#endif
6663
Bram Moolenaar01615492015-02-03 13:00:38 +01006664#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006665/*
6666 * ":syntime".
6667 */
6668 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006669ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006670{
6671 if (STRCMP(eap->arg, "on") == 0)
6672 syn_time_on = TRUE;
6673 else if (STRCMP(eap->arg, "off") == 0)
6674 syn_time_on = FALSE;
6675 else if (STRCMP(eap->arg, "clear") == 0)
6676 syntime_clear();
6677 else if (STRCMP(eap->arg, "report") == 0)
6678 syntime_report();
6679 else
6680 EMSG2(_(e_invarg2), eap->arg);
6681}
6682
6683 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006684syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006685{
6686 profile_zero(&st->total);
6687 profile_zero(&st->slowest);
6688 st->count = 0;
6689 st->match = 0;
6690}
6691
6692/*
6693 * Clear the syntax timing for the current buffer.
6694 */
6695 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006696syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006697{
6698 int idx;
6699 synpat_T *spp;
6700
6701 if (!syntax_present(curwin))
6702 {
6703 MSG(_(msg_no_items));
6704 return;
6705 }
6706 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6707 {
6708 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6709 syn_clear_time(&spp->sp_time);
6710 }
6711}
6712
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006713#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6714/*
6715 * Function given to ExpandGeneric() to obtain the possible arguments of the
6716 * ":syntime {on,off,clear,report}" command.
6717 */
6718 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006719get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006720{
6721 switch (idx)
6722 {
6723 case 0: return (char_u *)"on";
6724 case 1: return (char_u *)"off";
6725 case 2: return (char_u *)"clear";
6726 case 3: return (char_u *)"report";
6727 }
6728 return NULL;
6729}
6730#endif
6731
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006732typedef struct
6733{
6734 proftime_T total;
6735 int count;
6736 int match;
6737 proftime_T slowest;
6738 proftime_T average;
6739 int id;
6740 char_u *pattern;
6741} time_entry_T;
6742
6743 static int
6744#ifdef __BORLANDC__
6745_RTLENTRYF
6746#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006747syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006748{
6749 const time_entry_T *s1 = v1;
6750 const time_entry_T *s2 = v2;
6751
6752 return profile_cmp(&s1->total, &s2->total);
6753}
6754
6755/*
6756 * Clear the syntax timing for the current buffer.
6757 */
6758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006759syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006760{
6761 int idx;
6762 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006763# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006764 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006765# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006766 int len;
6767 proftime_T total_total;
6768 int total_count = 0;
6769 garray_T ga;
6770 time_entry_T *p;
6771
6772 if (!syntax_present(curwin))
6773 {
6774 MSG(_(msg_no_items));
6775 return;
6776 }
6777
6778 ga_init2(&ga, sizeof(time_entry_T), 50);
6779 profile_zero(&total_total);
6780 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6781 {
6782 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6783 if (spp->sp_time.count > 0)
6784 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006785 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006786 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6787 p->total = spp->sp_time.total;
6788 profile_add(&total_total, &spp->sp_time.total);
6789 p->count = spp->sp_time.count;
6790 p->match = spp->sp_time.match;
6791 total_count += spp->sp_time.count;
6792 p->slowest = spp->sp_time.slowest;
6793# ifdef FEAT_FLOAT
6794 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6795 p->average = tm;
6796# endif
6797 p->id = spp->sp_syn.id;
6798 p->pattern = spp->sp_pattern;
6799 ++ga.ga_len;
6800 }
6801 }
6802
Bram Moolenaara2162552017-01-08 17:46:20 +01006803 /* Sort on total time. Skip if there are no items to avoid passing NULL
6804 * pointer to qsort(). */
6805 if (ga.ga_len > 1)
6806 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006807 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006808
6809 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6810 MSG_PUTS("\n");
6811 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6812 {
6813 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6814 p = ((time_entry_T *)ga.ga_data) + idx;
6815
6816 MSG_PUTS(profile_msg(&p->total));
6817 MSG_PUTS(" "); /* make sure there is always a separating space */
6818 msg_advance(13);
6819 msg_outnum(p->count);
6820 MSG_PUTS(" ");
6821 msg_advance(20);
6822 msg_outnum(p->match);
6823 MSG_PUTS(" ");
6824 msg_advance(26);
6825 MSG_PUTS(profile_msg(&p->slowest));
6826 MSG_PUTS(" ");
6827 msg_advance(38);
6828# ifdef FEAT_FLOAT
6829 MSG_PUTS(profile_msg(&p->average));
6830 MSG_PUTS(" ");
6831# endif
6832 msg_advance(50);
6833 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6834 MSG_PUTS(" ");
6835
6836 msg_advance(69);
6837 if (Columns < 80)
6838 len = 20; /* will wrap anyway */
6839 else
6840 len = Columns - 70;
6841 if (len > (int)STRLEN(p->pattern))
6842 len = (int)STRLEN(p->pattern);
6843 msg_outtrans_len(p->pattern, len);
6844 MSG_PUTS("\n");
6845 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006846 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006847 if (!got_int)
6848 {
6849 MSG_PUTS("\n");
6850 MSG_PUTS(profile_msg(&total_total));
6851 msg_advance(13);
6852 msg_outnum(total_count);
6853 MSG_PUTS("\n");
6854 }
6855}
6856#endif
6857
Bram Moolenaar071d4272004-06-13 20:20:40 +00006858#endif /* FEAT_SYN_HL */
6859
Bram Moolenaar071d4272004-06-13 20:20:40 +00006860/**************************************
6861 * Highlighting stuff *
6862 **************************************/
6863
6864/*
6865 * The default highlight groups. These are compiled-in for fast startup and
6866 * they still work when the runtime files can't be found.
6867 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006868 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6869 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006870 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006871#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006872# define CENT(a, b) b
6873#else
6874# define CENT(a, b) a
6875#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006876static char *(highlight_init_both[]) =
6877 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006878 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6879 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6880 CENT("IncSearch term=reverse cterm=reverse",
6881 "IncSearch term=reverse cterm=reverse gui=reverse"),
6882 CENT("ModeMsg term=bold cterm=bold",
6883 "ModeMsg term=bold cterm=bold gui=bold"),
6884 CENT("NonText term=bold ctermfg=Blue",
6885 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6886 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6887 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6888 CENT("StatusLineNC term=reverse cterm=reverse",
6889 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar3633cf52017-07-31 22:29:35 +02006890#ifdef FEAT_TERMINAL
6891 CENT("StatusLineTerm term=reverse cterm=reverse ctermFg=DarkGreen",
6892 "StatusLineTerm term=reverse cterm=reverse ctermFg=DarkGreen gui=reverse guifg=DarkGreen"),
6893#endif
Bram Moolenaar58b85342016-08-14 19:54:54 +02006894 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006895#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006896 CENT("VertSplit term=reverse cterm=reverse",
6897 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006898#endif
6899#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006900 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6901 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006902#endif
6903#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006904 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6905 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006906#endif
6907#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006908 CENT("PmenuSbar ctermbg=Grey",
6909 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006910#endif
6911#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006912 CENT("TabLineSel term=bold cterm=bold",
6913 "TabLineSel term=bold cterm=bold gui=bold"),
6914 CENT("TabLineFill term=reverse cterm=reverse",
6915 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006916#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006917#ifdef FEAT_GUI
6918 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006919 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006920#endif
Bram Moolenaarc768a202017-06-22 16:04:27 +02006921 "default link QuickFixLine Search",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006922 NULL
6923 };
6924
6925static char *(highlight_init_light[]) =
6926 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006927 CENT("Directory term=bold ctermfg=DarkBlue",
6928 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6929 CENT("LineNr term=underline ctermfg=Brown",
6930 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006931 CENT("CursorLineNr term=bold ctermfg=Brown",
6932 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006933 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6934 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6935 CENT("Question term=standout ctermfg=DarkGreen",
6936 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6937 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6938 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006939#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006940 CENT("SpellBad term=reverse ctermbg=LightRed",
6941 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6942 CENT("SpellCap term=reverse ctermbg=LightBlue",
6943 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6944 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6945 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6946 CENT("SpellLocal term=underline ctermbg=Cyan",
6947 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006948#endif
6949#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006950 CENT("PmenuThumb ctermbg=Black",
6951 "PmenuThumb ctermbg=Black guibg=Black"),
6952 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6953 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6954 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6955 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006956#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006957 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6958 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6959 CENT("Title term=bold ctermfg=DarkMagenta",
6960 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6961 CENT("WarningMsg term=standout ctermfg=DarkRed",
6962 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006963#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006964 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6965 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
6967#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006968 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6969 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6970 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6971 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006972#endif
6973#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006974 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6975 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006976#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006977 CENT("Visual term=reverse",
6978 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006979#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006980 CENT("DiffAdd term=bold ctermbg=LightBlue",
6981 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6982 CENT("DiffChange term=bold ctermbg=LightMagenta",
6983 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6984 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6985 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006986#endif
6987#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006988 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6989 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006990#endif
6991#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006992 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006993 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006994 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006995 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006996 CENT("ColorColumn term=reverse ctermbg=LightRed",
6997 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006998#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006999#ifdef FEAT_CONCEAL
7000 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7001 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7002#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007003#ifdef FEAT_AUTOCMD
7004 CENT("MatchParen term=reverse ctermbg=Cyan",
7005 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
7006#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007007#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007008 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007009#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007010 NULL
7011 };
7012
7013static char *(highlight_init_dark[]) =
7014 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007015 CENT("Directory term=bold ctermfg=LightCyan",
7016 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7017 CENT("LineNr term=underline ctermfg=Yellow",
7018 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01007019 CENT("CursorLineNr term=bold ctermfg=Yellow",
7020 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007021 CENT("MoreMsg term=bold ctermfg=LightGreen",
7022 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7023 CENT("Question term=standout ctermfg=LightGreen",
7024 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7025 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7026 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7027 CENT("SpecialKey term=bold ctermfg=LightBlue",
7028 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007029#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007030 CENT("SpellBad term=reverse ctermbg=Red",
7031 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7032 CENT("SpellCap term=reverse ctermbg=Blue",
7033 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7034 CENT("SpellRare term=reverse ctermbg=Magenta",
7035 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7036 CENT("SpellLocal term=underline ctermbg=Cyan",
7037 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007038#endif
7039#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007040 CENT("PmenuThumb ctermbg=White",
7041 "PmenuThumb ctermbg=White guibg=White"),
7042 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7043 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007044 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7045 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007046#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007047 CENT("Title term=bold ctermfg=LightMagenta",
7048 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7049 CENT("WarningMsg term=standout ctermfg=LightRed",
7050 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007051#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007052 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7053 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007054#endif
7055#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007056 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7057 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7058 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7059 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007060#endif
7061#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007062 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7063 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007064#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007065 CENT("Visual term=reverse",
7066 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007067#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007068 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7069 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7070 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7071 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7072 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7073 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007074#endif
7075#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007076 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7077 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007078#endif
7079#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007080 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007081 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007082 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007083 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007084 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7085 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007086#endif
7087#ifdef FEAT_AUTOCMD
7088 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7089 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007090#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007091#ifdef FEAT_CONCEAL
7092 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7093 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7094#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007095#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007096 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007097#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007098 NULL
7099 };
7100
7101 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007102init_highlight(
7103 int both, /* include groups where 'bg' doesn't matter */
7104 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007105{
7106 int i;
7107 char **pp;
7108 static int had_both = FALSE;
7109#ifdef FEAT_EVAL
7110 char_u *p;
7111
7112 /*
7113 * Try finding the color scheme file. Used when a color file was loaded
7114 * and 'background' or 't_Co' is changed.
7115 */
7116 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007117 if (p != NULL)
7118 {
7119 /* The value of g:colors_name could be freed when sourcing the script,
7120 * making "p" invalid, so copy it. */
7121 char_u *copy_p = vim_strsave(p);
7122 int r;
7123
7124 if (copy_p != NULL)
7125 {
7126 r = load_colors(copy_p);
7127 vim_free(copy_p);
7128 if (r == OK)
7129 return;
7130 }
7131 }
7132
Bram Moolenaar071d4272004-06-13 20:20:40 +00007133#endif
7134
7135 /*
7136 * Didn't use a color file, use the compiled-in colors.
7137 */
7138 if (both)
7139 {
7140 had_both = TRUE;
7141 pp = highlight_init_both;
7142 for (i = 0; pp[i] != NULL; ++i)
7143 do_highlight((char_u *)pp[i], reset, TRUE);
7144 }
7145 else if (!had_both)
7146 /* Don't do anything before the call with both == TRUE from main().
7147 * Not everything has been setup then, and that call will overrule
7148 * everything anyway. */
7149 return;
7150
7151 if (*p_bg == 'l')
7152 pp = highlight_init_light;
7153 else
7154 pp = highlight_init_dark;
7155 for (i = 0; pp[i] != NULL; ++i)
7156 do_highlight((char_u *)pp[i], reset, TRUE);
7157
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007158 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007159 * depend on the number of colors available.
7160 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007161 * to avoid Statement highlighted text disappears.
7162 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007163 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007164 do_highlight((char_u *)(*p_bg == 'l'
7165 ? "Visual cterm=NONE ctermbg=LightGrey"
7166 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007167 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007168 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007169 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7170 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007171 if (*p_bg == 'l')
7172 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7173 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007174
Bram Moolenaar071d4272004-06-13 20:20:40 +00007175#ifdef FEAT_SYN_HL
7176 /*
7177 * If syntax highlighting is enabled load the highlighting for it.
7178 */
7179 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007180 {
7181 static int recursive = 0;
7182
7183 if (recursive >= 5)
7184 EMSG(_("E679: recursive loop loading syncolor.vim"));
7185 else
7186 {
7187 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007188 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007189 --recursive;
7190 }
7191 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007192#endif
7193}
7194
7195/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007196 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197 * Return OK for success, FAIL for failure.
7198 */
7199 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007200load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007201{
7202 char_u *buf;
7203 int retval = FAIL;
7204 static int recursive = FALSE;
7205
7206 /* When being called recursively, this is probably because setting
7207 * 'background' caused the highlighting to be reloaded. This means it is
7208 * working, thus we should return OK. */
7209 if (recursive)
7210 return OK;
7211
7212 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007213 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007214 if (buf != NULL)
7215 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007216 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007217 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007218 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007219#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007220 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007221#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007222 }
7223 recursive = FALSE;
7224
7225 return retval;
7226}
7227
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007228static char *(color_names[28]) = {
7229 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7230 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7231 "Gray", "Grey", "LightGray", "LightGrey",
7232 "DarkGray", "DarkGrey",
7233 "Blue", "LightBlue", "Green", "LightGreen",
7234 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7235 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7236 /* indices:
7237 * 0, 1, 2, 3,
7238 * 4, 5, 6, 7,
7239 * 8, 9, 10, 11,
7240 * 12, 13,
7241 * 14, 15, 16, 17,
7242 * 18, 19, 20, 21, 22,
7243 * 23, 24, 25, 26, 27 */
7244static int color_numbers_16[28] = {0, 1, 2, 3,
7245 4, 5, 6, 6,
7246 7, 7, 7, 7,
7247 8, 8,
7248 9, 9, 10, 10,
7249 11, 11, 12, 12, 13,
7250 13, 14, 14, 15, -1};
7251/* for xterm with 88 colors... */
7252static int color_numbers_88[28] = {0, 4, 2, 6,
7253 1, 5, 32, 72,
7254 84, 84, 7, 7,
7255 82, 82,
7256 12, 43, 10, 61,
7257 14, 63, 9, 74, 13,
7258 75, 11, 78, 15, -1};
7259/* for xterm with 256 colors... */
7260static int color_numbers_256[28] = {0, 4, 2, 6,
7261 1, 5, 130, 130,
7262 248, 248, 7, 7,
7263 242, 242,
7264 12, 81, 10, 121,
7265 14, 159, 9, 224, 13,
7266 225, 11, 229, 15, -1};
7267/* for terminals with less than 16 colors... */
7268static int color_numbers_8[28] = {0, 4, 2, 6,
7269 1, 5, 3, 3,
7270 7, 7, 7, 7,
7271 0+8, 0+8,
7272 4+8, 4+8, 2+8, 2+8,
7273 6+8, 6+8, 1+8, 1+8, 5+8,
7274 5+8, 3+8, 3+8, 7+8, -1};
7275
7276/*
7277 * Lookup the "cterm" value to be used for color with index "idx" in
7278 * color_names[].
7279 */
7280 int
7281lookup_color(int idx, int foreground)
7282{
7283 int color = color_numbers_16[idx];
7284 char_u *p;
7285
7286 /* Use the _16 table to check if it's a valid color name. */
7287 if (color < 0)
7288 return -1;
7289
7290 if (t_colors == 8)
7291 {
7292 /* t_Co is 8: use the 8 colors table */
7293#if defined(__QNXNTO__)
7294 color = color_numbers_8_qansi[idx];
7295#else
7296 color = color_numbers_8[idx];
7297#endif
7298 if (foreground)
7299 {
7300 /* set/reset bold attribute to get light foreground
7301 * colors (on some terminals, e.g. "linux") */
7302 if (color & 8)
7303 {
7304 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7305 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7306 }
7307 else
7308 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7309 }
7310 color &= 7; /* truncate to 8 colors */
7311 }
7312 else if (t_colors == 16 || t_colors == 88
7313 || t_colors >= 256)
7314 {
7315 /*
7316 * Guess: if the termcap entry ends in 'm', it is
7317 * probably an xterm-like terminal. Use the changed
7318 * order for colors.
7319 */
7320 if (*T_CAF != NUL)
7321 p = T_CAF;
7322 else
7323 p = T_CSF;
7324 if (*p != NUL && (t_colors > 256
7325 || *(p + STRLEN(p) - 1) == 'm'))
7326 {
7327 if (t_colors == 88)
7328 color = color_numbers_88[idx];
7329 else if (t_colors >= 256)
7330 color = color_numbers_256[idx];
7331 else
7332 color = color_numbers_8[idx];
7333 }
7334 }
7335 return color;
7336}
7337
Bram Moolenaar071d4272004-06-13 20:20:40 +00007338/*
7339 * Handle the ":highlight .." command.
7340 * When using ":hi clear" this is called recursively for each group with
7341 * "forceit" and "init" both TRUE.
7342 */
7343 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007344do_highlight(
7345 char_u *line,
7346 int forceit,
7347 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007348{
7349 char_u *name_end;
7350 char_u *p;
7351 char_u *linep;
7352 char_u *key_start;
7353 char_u *arg_start;
7354 char_u *key = NULL, *arg = NULL;
7355 long i;
7356 int off;
7357 int len;
7358 int attr;
7359 int id;
7360 int idx;
7361 int dodefault = FALSE;
7362 int doclear = FALSE;
7363 int dolink = FALSE;
7364 int error = FALSE;
7365 int color;
7366 int is_normal_group = FALSE; /* "Normal" group */
7367#ifdef FEAT_GUI_X11
7368 int is_menu_group = FALSE; /* "Menu" group */
7369 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7370 int is_tooltip_group = FALSE; /* "Tooltip" group */
7371 int do_colors = FALSE; /* need to update colors? */
7372#else
7373# define is_menu_group 0
7374# define is_tooltip_group 0
7375#endif
7376
7377 /*
7378 * If no argument, list current highlighting.
7379 */
7380 if (ends_excmd(*line))
7381 {
7382 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7383 /* TODO: only call when the group has attributes set */
7384 highlight_list_one((int)i);
7385 return;
7386 }
7387
7388 /*
7389 * Isolate the name.
7390 */
7391 name_end = skiptowhite(line);
7392 linep = skipwhite(name_end);
7393
7394 /*
7395 * Check for "default" argument.
7396 */
7397 if (STRNCMP(line, "default", name_end - line) == 0)
7398 {
7399 dodefault = TRUE;
7400 line = linep;
7401 name_end = skiptowhite(line);
7402 linep = skipwhite(name_end);
7403 }
7404
7405 /*
7406 * Check for "clear" or "link" argument.
7407 */
7408 if (STRNCMP(line, "clear", name_end - line) == 0)
7409 doclear = TRUE;
7410 if (STRNCMP(line, "link", name_end - line) == 0)
7411 dolink = TRUE;
7412
7413 /*
7414 * ":highlight {group-name}": list highlighting for one group.
7415 */
7416 if (!doclear && !dolink && ends_excmd(*linep))
7417 {
7418 id = syn_namen2id(line, (int)(name_end - line));
7419 if (id == 0)
7420 EMSG2(_("E411: highlight group not found: %s"), line);
7421 else
7422 highlight_list_one(id);
7423 return;
7424 }
7425
7426 /*
7427 * Handle ":highlight link {from} {to}" command.
7428 */
7429 if (dolink)
7430 {
7431 char_u *from_start = linep;
7432 char_u *from_end;
7433 char_u *to_start;
7434 char_u *to_end;
7435 int from_id;
7436 int to_id;
7437
7438 from_end = skiptowhite(from_start);
7439 to_start = skipwhite(from_end);
7440 to_end = skiptowhite(to_start);
7441
7442 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7443 {
7444 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7445 from_start);
7446 return;
7447 }
7448
7449 if (!ends_excmd(*skipwhite(to_end)))
7450 {
7451 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7452 return;
7453 }
7454
7455 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7456 if (STRNCMP(to_start, "NONE", 4) == 0)
7457 to_id = 0;
7458 else
7459 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7460
7461 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7462 {
7463 /*
7464 * Don't allow a link when there already is some highlighting
7465 * for the group, unless '!' is used
7466 */
7467 if (to_id > 0 && !forceit && !init
7468 && hl_has_settings(from_id - 1, dodefault))
7469 {
7470 if (sourcing_name == NULL && !dodefault)
7471 EMSG(_("E414: group has settings, highlight link ignored"));
7472 }
7473 else
7474 {
7475 if (!init)
7476 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7477 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007478#ifdef FEAT_EVAL
7479 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7480#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01007481 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007482 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007483 }
7484 }
7485
7486 /* Only call highlight_changed() once, after sourcing a syntax file */
7487 need_highlight_changed = TRUE;
7488
7489 return;
7490 }
7491
7492 if (doclear)
7493 {
7494 /*
7495 * ":highlight clear [group]" command.
7496 */
7497 line = linep;
7498 if (ends_excmd(*line))
7499 {
7500#ifdef FEAT_GUI
7501 /* First, we do not destroy the old values, but allocate the new
7502 * ones and update the display. THEN we destroy the old values.
7503 * If we destroy the old values first, then the old values
7504 * (such as GuiFont's or GuiFontset's) will still be displayed but
7505 * invalid because they were free'd.
7506 */
7507 if (gui.in_use)
7508 {
7509# ifdef FEAT_BEVAL_TIP
7510 gui_init_tooltip_font();
7511# endif
7512# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7513 gui_init_menu_font();
7514# endif
7515 }
7516# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7517 gui_mch_def_colors();
7518# endif
7519# ifdef FEAT_GUI_X11
7520# ifdef FEAT_MENU
7521
7522 /* This only needs to be done when there is no Menu highlight
7523 * group defined by default, which IS currently the case.
7524 */
7525 gui_mch_new_menu_colors();
7526# endif
7527 if (gui.in_use)
7528 {
7529 gui_new_scrollbar_colors();
7530# ifdef FEAT_BEVAL
7531 gui_mch_new_tooltip_colors();
7532# endif
7533# ifdef FEAT_MENU
7534 gui_mch_new_menu_font();
7535# endif
7536 }
7537# endif
7538
7539 /* Ok, we're done allocating the new default graphics items.
7540 * The screen should already be refreshed at this point.
7541 * It is now Ok to clear out the old data.
7542 */
7543#endif
7544#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007545 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007546#endif
7547 restore_cterm_colors();
7548
7549 /*
7550 * Clear all default highlight groups and load the defaults.
7551 */
7552 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7553 highlight_clear(idx);
7554 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007555#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007556 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007557 highlight_gui_started();
7558#endif
7559 highlight_changed();
7560 redraw_later_clear();
7561 return;
7562 }
7563 name_end = skiptowhite(line);
7564 linep = skipwhite(name_end);
7565 }
7566
7567 /*
7568 * Find the group name in the table. If it does not exist yet, add it.
7569 */
7570 id = syn_check_group(line, (int)(name_end - line));
7571 if (id == 0) /* failed (out of memory) */
7572 return;
7573 idx = id - 1; /* index is ID minus one */
7574
7575 /* Return if "default" was used and the group already has settings. */
7576 if (dodefault && hl_has_settings(idx, TRUE))
7577 return;
7578
7579 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7580 is_normal_group = TRUE;
7581#ifdef FEAT_GUI_X11
7582 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7583 is_menu_group = TRUE;
7584 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7585 is_scrollbar_group = TRUE;
7586 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7587 is_tooltip_group = TRUE;
7588#endif
7589
7590 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7591 if (doclear || (forceit && init))
7592 {
7593 highlight_clear(idx);
7594 if (!doclear)
7595 HL_TABLE()[idx].sg_set = 0;
7596 }
7597
7598 if (!doclear)
7599 while (!ends_excmd(*linep))
7600 {
7601 key_start = linep;
7602 if (*linep == '=')
7603 {
7604 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7605 error = TRUE;
7606 break;
7607 }
7608
7609 /*
7610 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7611 * "guibg").
7612 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007613 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007614 ++linep;
7615 vim_free(key);
7616 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7617 if (key == NULL)
7618 {
7619 error = TRUE;
7620 break;
7621 }
7622 linep = skipwhite(linep);
7623
7624 if (STRCMP(key, "NONE") == 0)
7625 {
7626 if (!init || HL_TABLE()[idx].sg_set == 0)
7627 {
7628 if (!init)
7629 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7630 highlight_clear(idx);
7631 }
7632 continue;
7633 }
7634
7635 /*
7636 * Check for the equal sign.
7637 */
7638 if (*linep != '=')
7639 {
7640 EMSG2(_("E416: missing equal sign: %s"), key_start);
7641 error = TRUE;
7642 break;
7643 }
7644 ++linep;
7645
7646 /*
7647 * Isolate the argument.
7648 */
7649 linep = skipwhite(linep);
7650 if (*linep == '\'') /* guifg='color name' */
7651 {
7652 arg_start = ++linep;
7653 linep = vim_strchr(linep, '\'');
7654 if (linep == NULL)
7655 {
7656 EMSG2(_(e_invarg2), key_start);
7657 error = TRUE;
7658 break;
7659 }
7660 }
7661 else
7662 {
7663 arg_start = linep;
7664 linep = skiptowhite(linep);
7665 }
7666 if (linep == arg_start)
7667 {
7668 EMSG2(_("E417: missing argument: %s"), key_start);
7669 error = TRUE;
7670 break;
7671 }
7672 vim_free(arg);
7673 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7674 if (arg == NULL)
7675 {
7676 error = TRUE;
7677 break;
7678 }
7679 if (*linep == '\'')
7680 ++linep;
7681
7682 /*
7683 * Store the argument.
7684 */
7685 if ( STRCMP(key, "TERM") == 0
7686 || STRCMP(key, "CTERM") == 0
7687 || STRCMP(key, "GUI") == 0)
7688 {
7689 attr = 0;
7690 off = 0;
7691 while (arg[off] != NUL)
7692 {
7693 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7694 {
7695 len = (int)STRLEN(hl_name_table[i]);
7696 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7697 {
7698 attr |= hl_attr_table[i];
7699 off += len;
7700 break;
7701 }
7702 }
7703 if (i < 0)
7704 {
7705 EMSG2(_("E418: Illegal value: %s"), arg);
7706 error = TRUE;
7707 break;
7708 }
7709 if (arg[off] == ',') /* another one follows */
7710 ++off;
7711 }
7712 if (error)
7713 break;
7714 if (*key == 'T')
7715 {
7716 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7717 {
7718 if (!init)
7719 HL_TABLE()[idx].sg_set |= SG_TERM;
7720 HL_TABLE()[idx].sg_term = attr;
7721 }
7722 }
7723 else if (*key == 'C')
7724 {
7725 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7726 {
7727 if (!init)
7728 HL_TABLE()[idx].sg_set |= SG_CTERM;
7729 HL_TABLE()[idx].sg_cterm = attr;
7730 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7731 }
7732 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007733#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007734 else
7735 {
7736 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7737 {
7738 if (!init)
7739 HL_TABLE()[idx].sg_set |= SG_GUI;
7740 HL_TABLE()[idx].sg_gui = attr;
7741 }
7742 }
7743#endif
7744 }
7745 else if (STRCMP(key, "FONT") == 0)
7746 {
7747 /* in non-GUI fonts are simply ignored */
7748#ifdef FEAT_GUI
7749 if (!gui.shell_created)
7750 {
7751 /* GUI not started yet, always accept the name. */
7752 vim_free(HL_TABLE()[idx].sg_font_name);
7753 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7754 }
7755 else
7756 {
7757 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7758# ifdef FEAT_XFONTSET
7759 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7760# endif
7761 /* First, save the current font/fontset.
7762 * Then try to allocate the font/fontset.
7763 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7764 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7765 */
7766
7767 HL_TABLE()[idx].sg_font = NOFONT;
7768# ifdef FEAT_XFONTSET
7769 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7770# endif
7771 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007772 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773
7774# ifdef FEAT_XFONTSET
7775 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7776 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007777 /* New fontset was accepted. Free the old one, if there
7778 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779 gui_mch_free_fontset(temp_sg_fontset);
7780 vim_free(HL_TABLE()[idx].sg_font_name);
7781 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7782 }
7783 else
7784 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7785# endif
7786 if (HL_TABLE()[idx].sg_font != NOFONT)
7787 {
7788 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007789 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007790 gui_mch_free_font(temp_sg_font);
7791 vim_free(HL_TABLE()[idx].sg_font_name);
7792 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7793 }
7794 else
7795 HL_TABLE()[idx].sg_font = temp_sg_font;
7796 }
7797#endif
7798 }
7799 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7800 {
7801 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7802 {
7803 if (!init)
7804 HL_TABLE()[idx].sg_set |= SG_CTERM;
7805
7806 /* When setting the foreground color, and previously the "bold"
7807 * flag was set for a light color, reset it now */
7808 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7809 {
7810 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7811 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7812 }
7813
7814 if (VIM_ISDIGIT(*arg))
7815 color = atoi((char *)arg);
7816 else if (STRICMP(arg, "fg") == 0)
7817 {
7818 if (cterm_normal_fg_color)
7819 color = cterm_normal_fg_color - 1;
7820 else
7821 {
7822 EMSG(_("E419: FG color unknown"));
7823 error = TRUE;
7824 break;
7825 }
7826 }
7827 else if (STRICMP(arg, "bg") == 0)
7828 {
7829 if (cterm_normal_bg_color > 0)
7830 color = cterm_normal_bg_color - 1;
7831 else
7832 {
7833 EMSG(_("E420: BG color unknown"));
7834 error = TRUE;
7835 break;
7836 }
7837 }
7838 else
7839 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00007840#if defined(__QNXNTO__)
7841 static int *color_numbers_8_qansi = color_numbers_8;
7842 /* On qnx, the 8 & 16 color arrays are the same */
7843 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7844 color_numbers_8_qansi = color_numbers_16;
7845#endif
7846
7847 /* reduce calls to STRICMP a bit, it can be slow */
7848 off = TOUPPER_ASC(*arg);
7849 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7850 if (off == color_names[i][0]
7851 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7852 break;
7853 if (i < 0)
7854 {
7855 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7856 error = TRUE;
7857 break;
7858 }
7859
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007860 color = lookup_color(i, key[5] == 'F');
Bram Moolenaar071d4272004-06-13 20:20:40 +00007861 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007862
Bram Moolenaarccbab932010-05-13 15:40:30 +02007863 /* Add one to the argument, to avoid zero. Zero is used for
7864 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007865 if (key[5] == 'F')
7866 {
7867 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7868 if (is_normal_group)
7869 {
7870 cterm_normal_fg_color = color + 1;
7871 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7872#ifdef FEAT_GUI
7873 /* Don't do this if the GUI is used. */
7874 if (!gui.in_use && !gui.starting)
7875#endif
7876 {
7877 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007878 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007879 term_fg_color(color);
7880 }
7881 }
7882 }
7883 else
7884 {
7885 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7886 if (is_normal_group)
7887 {
7888 cterm_normal_bg_color = color + 1;
7889#ifdef FEAT_GUI
7890 /* Don't mess with 'background' if the GUI is used. */
7891 if (!gui.in_use && !gui.starting)
7892#endif
7893 {
7894 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007895 if (color >= 0)
7896 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007897 int dark = -1;
7898
Bram Moolenaarccbab932010-05-13 15:40:30 +02007899 if (termcap_active)
7900 term_bg_color(color);
7901 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007902 dark = (color == 0 || color == 4);
7903 /* Limit the heuristic to the standard 16 colors */
7904 else if (color < 16)
7905 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007906 /* Set the 'background' option if the value is
7907 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007908 if (dark != -1
7909 && dark != (*p_bg == 'd')
7910 && !option_was_set((char_u *)"bg"))
7911 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007912 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007913 (char_u *)(dark ? "dark" : "light"), 0);
7914 reset_option_was_set((char_u *)"bg");
7915 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007916 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007917 }
7918 }
7919 }
7920 }
7921 }
7922 else if (STRCMP(key, "GUIFG") == 0)
7923 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007924#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007925 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007926 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007927 if (!init)
7928 HL_TABLE()[idx].sg_set |= SG_GUI;
7929
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007930# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007931 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007932 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007933 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007934 {
7935 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007936# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007937 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
Bram Moolenaar26af85d2017-07-23 16:45:10 +02007938 if (STRCMP(arg, "NONE") != 0)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007939 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7940 else
7941 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007942# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007943# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007944 if (is_menu_group)
7945 gui.menu_fg_pixel = i;
7946 if (is_scrollbar_group)
7947 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007948# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007949 if (is_tooltip_group)
7950 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007951# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007952 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007953# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007954 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007955# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007956 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007957#endif
7958 }
7959 else if (STRCMP(key, "GUIBG") == 0)
7960 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007961#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007962 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007963 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007964 if (!init)
7965 HL_TABLE()[idx].sg_set |= SG_GUI;
7966
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007967# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007968 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007969 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007970 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007971 {
7972 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007973# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007974 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7975 if (STRCMP(arg, "NONE") != 0)
7976 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7977 else
7978 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007979# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007980# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007981 if (is_menu_group)
7982 gui.menu_bg_pixel = i;
7983 if (is_scrollbar_group)
7984 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007985# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007986 if (is_tooltip_group)
7987 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007988# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007989 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007990# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007991 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007992# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007993 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007994#endif
7995 }
7996 else if (STRCMP(key, "GUISP") == 0)
7997 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007998#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007999 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
8000 {
8001 if (!init)
8002 HL_TABLE()[idx].sg_set |= SG_GUI;
8003
Bram Moolenaar61623362010-07-14 22:04:22 +02008004# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008005 i = color_name2handle(arg);
8006 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8007 {
8008 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008009# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008010 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8011 if (STRCMP(arg, "NONE") != 0)
8012 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
8013 else
8014 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008015# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008016 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008017# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008018 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008019#endif
8020 }
8021 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8022 {
8023 char_u buf[100];
8024 char_u *tname;
8025
8026 if (!init)
8027 HL_TABLE()[idx].sg_set |= SG_TERM;
8028
8029 /*
8030 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008031 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008032 */
8033 if (STRNCMP(arg, "t_", 2) == 0)
8034 {
8035 off = 0;
8036 buf[0] = 0;
8037 while (arg[off] != NUL)
8038 {
8039 /* Isolate one termcap name */
8040 for (len = 0; arg[off + len] &&
8041 arg[off + len] != ','; ++len)
8042 ;
8043 tname = vim_strnsave(arg + off, len);
8044 if (tname == NULL) /* out of memory */
8045 {
8046 error = TRUE;
8047 break;
8048 }
8049 /* lookup the escape sequence for the item */
8050 p = get_term_code(tname);
8051 vim_free(tname);
8052 if (p == NULL) /* ignore non-existing things */
8053 p = (char_u *)"";
8054
8055 /* Append it to the already found stuff */
8056 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8057 {
8058 EMSG2(_("E422: terminal code too long: %s"), arg);
8059 error = TRUE;
8060 break;
8061 }
8062 STRCAT(buf, p);
8063
8064 /* Advance to the next item */
8065 off += len;
8066 if (arg[off] == ',') /* another one follows */
8067 ++off;
8068 }
8069 }
8070 else
8071 {
8072 /*
8073 * Copy characters from arg[] to buf[], translating <> codes.
8074 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008075 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008076 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008077 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008078 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008079 off += len;
8080 else /* copy as normal char */
8081 buf[off++] = *p++;
8082 }
8083 buf[off] = NUL;
8084 }
8085 if (error)
8086 break;
8087
8088 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8089 p = NULL;
8090 else
8091 p = vim_strsave(buf);
8092 if (key[2] == 'A')
8093 {
8094 vim_free(HL_TABLE()[idx].sg_start);
8095 HL_TABLE()[idx].sg_start = p;
8096 }
8097 else
8098 {
8099 vim_free(HL_TABLE()[idx].sg_stop);
8100 HL_TABLE()[idx].sg_stop = p;
8101 }
8102 }
8103 else
8104 {
8105 EMSG2(_("E423: Illegal argument: %s"), key_start);
8106 error = TRUE;
8107 break;
8108 }
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008109 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008110
8111 /*
8112 * When highlighting has been given for a group, don't link it.
8113 */
8114 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8115 HL_TABLE()[idx].sg_link = 0;
8116
8117 /*
8118 * Continue with next argument.
8119 */
8120 linep = skipwhite(linep);
8121 }
8122
8123 /*
8124 * If there is an error, and it's a new entry, remove it from the table.
8125 */
8126 if (error && idx == highlight_ga.ga_len)
8127 syn_unadd_group();
8128 else
8129 {
8130 if (is_normal_group)
8131 {
8132 HL_TABLE()[idx].sg_term_attr = 0;
8133 HL_TABLE()[idx].sg_cterm_attr = 0;
8134#ifdef FEAT_GUI
8135 HL_TABLE()[idx].sg_gui_attr = 0;
8136 /*
8137 * Need to update all groups, because they might be using "bg"
8138 * and/or "fg", which have been changed now.
8139 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008140#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008141#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008142 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008143 highlight_gui_started();
8144#endif
8145 }
8146#ifdef FEAT_GUI_X11
8147# ifdef FEAT_MENU
8148 else if (is_menu_group)
8149 {
8150 if (gui.in_use && do_colors)
8151 gui_mch_new_menu_colors();
8152 }
8153# endif
8154 else if (is_scrollbar_group)
8155 {
8156 if (gui.in_use && do_colors)
8157 gui_new_scrollbar_colors();
8158 }
8159# ifdef FEAT_BEVAL
8160 else if (is_tooltip_group)
8161 {
8162 if (gui.in_use && do_colors)
8163 gui_mch_new_tooltip_colors();
8164 }
8165# endif
8166#endif
8167 else
8168 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008169#ifdef FEAT_EVAL
8170 HL_TABLE()[idx].sg_scriptID = current_SID;
8171#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008172 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008173 }
8174 vim_free(key);
8175 vim_free(arg);
8176
8177 /* Only call highlight_changed() once, after sourcing a syntax file */
8178 need_highlight_changed = TRUE;
8179}
8180
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008181#if defined(EXITFREE) || defined(PROTO)
8182 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008183free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008184{
8185 int i;
8186
8187 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008188 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008189 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008190 vim_free(HL_TABLE()[i].sg_name);
8191 vim_free(HL_TABLE()[i].sg_name_u);
8192 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008193 ga_clear(&highlight_ga);
8194}
8195#endif
8196
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197/*
8198 * Reset the cterm colors to what they were before Vim was started, if
8199 * possible. Otherwise reset them to zero.
8200 */
8201 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008202restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008203{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008204#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008205 /* Since t_me has been set, this probably means that the user
8206 * wants to use this as default colors. Need to reset default
8207 * background/foreground colors. */
8208 mch_set_normal_colors();
8209#else
8210 cterm_normal_fg_color = 0;
8211 cterm_normal_fg_bold = 0;
8212 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008213# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008214 cterm_normal_fg_gui_color = INVALCOLOR;
8215 cterm_normal_bg_gui_color = INVALCOLOR;
8216# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217#endif
8218}
8219
8220/*
8221 * Return TRUE if highlight group "idx" has any settings.
8222 * When "check_link" is TRUE also check for an existing link.
8223 */
8224 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008225hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226{
8227 return ( HL_TABLE()[idx].sg_term_attr != 0
8228 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008229 || HL_TABLE()[idx].sg_cterm_fg != 0
8230 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008231#ifdef FEAT_GUI
8232 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008233 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8234 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8235 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008236 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237#endif
8238 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8239}
8240
8241/*
8242 * Clear highlighting for one group.
8243 */
8244 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008245highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008246{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008247 HL_TABLE()[idx].sg_cleared = TRUE;
8248
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249 HL_TABLE()[idx].sg_term = 0;
8250 vim_free(HL_TABLE()[idx].sg_start);
8251 HL_TABLE()[idx].sg_start = NULL;
8252 vim_free(HL_TABLE()[idx].sg_stop);
8253 HL_TABLE()[idx].sg_stop = NULL;
8254 HL_TABLE()[idx].sg_term_attr = 0;
8255 HL_TABLE()[idx].sg_cterm = 0;
8256 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8257 HL_TABLE()[idx].sg_cterm_fg = 0;
8258 HL_TABLE()[idx].sg_cterm_bg = 0;
8259 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008260#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008261 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008262 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8263 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008264 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8265 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008266 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8267 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008268#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008269#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008270 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8271 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008272#endif
8273#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008274 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008275 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8276 HL_TABLE()[idx].sg_font = NOFONT;
8277# ifdef FEAT_XFONTSET
8278 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8279 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8280# endif
8281 vim_free(HL_TABLE()[idx].sg_font_name);
8282 HL_TABLE()[idx].sg_font_name = NULL;
8283 HL_TABLE()[idx].sg_gui_attr = 0;
8284#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008285#ifdef FEAT_EVAL
8286 /* Clear the script ID only when there is no link, since that is not
8287 * cleared. */
8288 if (HL_TABLE()[idx].sg_link == 0)
8289 HL_TABLE()[idx].sg_scriptID = 0;
8290#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008291}
8292
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008293#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008294/*
8295 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008296 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297 * "Tooltip" colors.
8298 */
8299 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008300set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008301{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008302#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008303# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008304 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008305# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008306 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008307 if (set_group_colors((char_u *)"Normal",
8308 &gui.norm_pixel, &gui.back_pixel,
8309 FALSE, TRUE, FALSE))
8310 {
8311 gui_mch_new_colors();
8312 must_redraw = CLEAR;
8313 }
8314# ifdef FEAT_GUI_X11
8315 if (set_group_colors((char_u *)"Menu",
8316 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8317 TRUE, FALSE, FALSE))
8318 {
8319# ifdef FEAT_MENU
8320 gui_mch_new_menu_colors();
8321# endif
8322 must_redraw = CLEAR;
8323 }
8324# ifdef FEAT_BEVAL
8325 if (set_group_colors((char_u *)"Tooltip",
8326 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8327 FALSE, FALSE, TRUE))
8328 {
8329# ifdef FEAT_TOOLBAR
8330 gui_mch_new_tooltip_colors();
8331# endif
8332 must_redraw = CLEAR;
8333 }
8334# endif
8335 if (set_group_colors((char_u *)"Scrollbar",
8336 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8337 FALSE, FALSE, FALSE))
8338 {
8339 gui_new_scrollbar_colors();
8340 must_redraw = CLEAR;
8341 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008342# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008343 }
8344#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008345#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008346# ifdef FEAT_GUI
8347 else
8348# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008349 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008350 int idx;
8351
8352 idx = syn_name2id((char_u *)"Normal") - 1;
8353 if (idx >= 0)
8354 {
8355 gui_do_one_color(idx, FALSE, FALSE);
8356
8357 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8358 {
8359 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8360 must_redraw = CLEAR;
8361 }
8362 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8363 {
8364 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8365 must_redraw = CLEAR;
8366 }
8367 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008368 }
8369#endif
8370}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008371#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008372
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008373#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008374/*
8375 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8376 */
8377 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008378set_group_colors(
8379 char_u *name,
8380 guicolor_T *fgp,
8381 guicolor_T *bgp,
8382 int do_menu,
8383 int use_norm,
8384 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008385{
8386 int idx;
8387
8388 idx = syn_name2id(name) - 1;
8389 if (idx >= 0)
8390 {
8391 gui_do_one_color(idx, do_menu, do_tooltip);
8392
8393 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8394 *fgp = HL_TABLE()[idx].sg_gui_fg;
8395 else if (use_norm)
8396 *fgp = gui.def_norm_pixel;
8397 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8398 *bgp = HL_TABLE()[idx].sg_gui_bg;
8399 else if (use_norm)
8400 *bgp = gui.def_back_pixel;
8401 return TRUE;
8402 }
8403 return FALSE;
8404}
8405
8406/*
8407 * Get the font of the "Normal" group.
8408 * Returns "" when it's not found or not set.
8409 */
8410 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008411hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008412{
8413 int id;
8414 char_u *s;
8415
8416 id = syn_name2id((char_u *)"Normal");
8417 if (id > 0)
8418 {
8419 s = HL_TABLE()[id - 1].sg_font_name;
8420 if (s != NULL)
8421 return s;
8422 }
8423 return (char_u *)"";
8424}
8425
8426/*
8427 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8428 * actually chosen to be used.
8429 */
8430 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008431hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008432{
8433 int id;
8434
8435 id = syn_name2id((char_u *)"Normal");
8436 if (id > 0)
8437 {
8438 vim_free(HL_TABLE()[id - 1].sg_font_name);
8439 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8440 }
8441}
8442
8443/*
8444 * Set background color for "Normal" group. Called by gui_set_bg_color()
8445 * when the color is known.
8446 */
8447 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008448hl_set_bg_color_name(
8449 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450{
8451 int id;
8452
8453 if (name != NULL)
8454 {
8455 id = syn_name2id((char_u *)"Normal");
8456 if (id > 0)
8457 {
8458 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8459 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8460 }
8461 }
8462}
8463
8464/*
8465 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8466 * when the color is known.
8467 */
8468 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008469hl_set_fg_color_name(
8470 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008471{
8472 int id;
8473
8474 if (name != NULL)
8475 {
8476 id = syn_name2id((char_u *)"Normal");
8477 if (id > 0)
8478 {
8479 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8480 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8481 }
8482 }
8483}
8484
8485/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008486 * Return the handle for a font name.
8487 * Returns NOFONT when failed.
8488 */
8489 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008490font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008491{
8492 if (STRCMP(name, "NONE") == 0)
8493 return NOFONT;
8494
8495 return gui_mch_get_font(name, TRUE);
8496}
8497
8498# ifdef FEAT_XFONTSET
8499/*
8500 * Return the handle for a fontset name.
8501 * Returns NOFONTSET when failed.
8502 */
8503 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008504fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505{
8506 if (STRCMP(name, "NONE") == 0)
8507 return NOFONTSET;
8508
8509 return gui_mch_get_fontset(name, TRUE, fixed_width);
8510}
8511# endif
8512
8513/*
8514 * Get the font or fontset for one highlight group.
8515 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008516 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008517hl_do_font(
8518 int idx,
8519 char_u *arg,
8520 int do_normal, /* set normal font */
8521 int do_menu UNUSED, /* set menu font */
8522 int do_tooltip UNUSED, /* set tooltip font */
8523 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524{
8525# ifdef FEAT_XFONTSET
8526 /* If 'guifontset' is not empty, first try using the name as a
8527 * fontset. If that doesn't work, use it as a font name. */
8528 if (*p_guifontset != NUL
8529# ifdef FONTSET_ALWAYS
8530 || do_menu
8531# endif
8532# ifdef FEAT_BEVAL_TIP
8533 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8534 || do_tooltip
8535# endif
8536 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008537 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008538 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008539 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008540 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8541# ifdef FONTSET_ALWAYS
8542 || do_menu
8543# endif
8544# ifdef FEAT_BEVAL_TIP
8545 || do_tooltip
8546# endif
8547 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008548 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008549 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8550 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008551 /* If it worked and it's the Normal group, use it as the normal
8552 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008553 if (do_normal)
8554 gui_init_font(arg, TRUE);
8555# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8556 if (do_menu)
8557 {
8558# ifdef FONTSET_ALWAYS
8559 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8560# else
8561 /* YIKES! This is a bug waiting to crash the program */
8562 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8563# endif
8564 gui_mch_new_menu_font();
8565 }
8566# ifdef FEAT_BEVAL
8567 if (do_tooltip)
8568 {
8569 /* The Athena widget set cannot currently handle switching between
8570 * displaying a single font and a fontset.
8571 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008572 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008573 * XFontStruct is used.
8574 */
8575 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8576 gui_mch_new_tooltip_font();
8577 }
8578# endif
8579# endif
8580 }
8581 else
8582# endif
8583 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008584 if (free_font)
8585 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008586 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8587 /* If it worked and it's the Normal group, use it as the
8588 * normal font. Same for the Menu group. */
8589 if (HL_TABLE()[idx].sg_font != NOFONT)
8590 {
8591 if (do_normal)
8592 gui_init_font(arg, FALSE);
8593#ifndef FONTSET_ALWAYS
8594# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8595 if (do_menu)
8596 {
8597 gui.menu_font = HL_TABLE()[idx].sg_font;
8598 gui_mch_new_menu_font();
8599 }
8600# endif
8601#endif
8602 }
8603 }
8604}
8605
8606#endif /* FEAT_GUI */
8607
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008608#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008609/*
8610 * Return the handle for a color name.
8611 * Returns INVALCOLOR when failed.
8612 */
8613 static guicolor_T
8614color_name2handle(char_u *name)
8615{
8616 if (STRCMP(name, "NONE") == 0)
8617 return INVALCOLOR;
8618
8619 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8620 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008621#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008622 if (gui.in_use)
8623#endif
8624#ifdef FEAT_GUI
8625 return gui.norm_pixel;
8626#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008627#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008628 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008629 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008630 /* Guess that the foreground is black or white. */
8631 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008632#endif
8633 }
8634 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8635 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008636#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008637 if (gui.in_use)
8638#endif
8639#ifdef FEAT_GUI
8640 return gui.back_pixel;
8641#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008642#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008643 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008644 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008645 /* Guess that the background is white or black. */
8646 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008647#endif
8648 }
8649
8650 return GUI_GET_COLOR(name);
8651}
8652#endif
8653
Bram Moolenaar071d4272004-06-13 20:20:40 +00008654/*
8655 * Table with the specifications for an attribute number.
8656 * Note that this table is used by ALL buffers. This is required because the
8657 * GUI can redraw at any time for any buffer.
8658 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008659static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008660
8661#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8662
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008663static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008664
8665#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8666
8667#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008668static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669
8670#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8671#endif
8672
8673/*
8674 * Return the attr number for a set of colors and font.
8675 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8676 * if the combination is new.
8677 * Return 0 for error (no more room).
8678 */
8679 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008680get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681{
8682 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008683 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008684 static int recursive = FALSE;
8685
8686 /*
8687 * Init the table, in case it wasn't done yet.
8688 */
8689 table->ga_itemsize = sizeof(attrentry_T);
8690 table->ga_growsize = 7;
8691
8692 /*
8693 * Try to find an entry with the same specifications.
8694 */
8695 for (i = 0; i < table->ga_len; ++i)
8696 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008697 taep = &(((attrentry_T *)table->ga_data)[i]);
8698 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008699 && (
8700#ifdef FEAT_GUI
8701 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008702 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8703 && aep->ae_u.gui.bg_color
8704 == taep->ae_u.gui.bg_color
8705 && aep->ae_u.gui.sp_color
8706 == taep->ae_u.gui.sp_color
8707 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008708# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008709 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008710# endif
8711 ))
8712 ||
8713#endif
8714 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008715 && (aep->ae_u.term.start == NULL)
8716 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008717 && (aep->ae_u.term.start == NULL
8718 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008719 taep->ae_u.term.start) == 0)
8720 && (aep->ae_u.term.stop == NULL)
8721 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008722 && (aep->ae_u.term.stop == NULL
8723 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008724 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008725 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008726 && aep->ae_u.cterm.fg_color
8727 == taep->ae_u.cterm.fg_color
8728 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008729 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008730#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008731 && aep->ae_u.cterm.fg_rgb
8732 == taep->ae_u.cterm.fg_rgb
8733 && aep->ae_u.cterm.bg_rgb
8734 == taep->ae_u.cterm.bg_rgb
8735#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008736 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008737
8738 return i + ATTR_OFF;
8739 }
8740
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008741 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008742 {
8743 /*
8744 * Running out of attribute entries! remove all attributes, and
8745 * compute new ones for all groups.
8746 * When called recursively, we are really out of numbers.
8747 */
8748 if (recursive)
8749 {
8750 EMSG(_("E424: Too many different highlighting attributes in use"));
8751 return 0;
8752 }
8753 recursive = TRUE;
8754
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008755 clear_hl_tables();
8756
Bram Moolenaar071d4272004-06-13 20:20:40 +00008757 must_redraw = CLEAR;
8758
8759 for (i = 0; i < highlight_ga.ga_len; ++i)
8760 set_hl_attr(i);
8761
8762 recursive = FALSE;
8763 }
8764
8765 /*
8766 * This is a new combination of colors and font, add an entry.
8767 */
8768 if (ga_grow(table, 1) == FAIL)
8769 return 0;
8770
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008771 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8772 vim_memset(taep, 0, sizeof(attrentry_T));
8773 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008774#ifdef FEAT_GUI
8775 if (table == &gui_attr_table)
8776 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008777 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8778 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8779 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8780 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008781# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008782 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008783# endif
8784 }
8785#endif
8786 if (table == &term_attr_table)
8787 {
8788 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008789 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008790 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008791 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008792 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008793 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008794 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008795 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008796 }
8797 else if (table == &cterm_attr_table)
8798 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008799 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8800 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008801#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008802 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8803 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8804#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008805 }
8806 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008807 return (table->ga_len - 1 + ATTR_OFF);
8808}
8809
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008810/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008811 * Get an attribute index for a cterm entry.
8812 * Uses an existing entry when possible or adds one when needed.
8813 */
8814 int
8815get_cterm_attr_idx(int attr, int fg, int bg)
8816{
8817 attrentry_T at_en;
8818
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008819 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaareeac6772017-07-23 15:48:37 +02008820 at_en.ae_attr = attr;
8821 at_en.ae_u.cterm.fg_color = fg;
8822 at_en.ae_u.cterm.bg_color = bg;
8823 return get_attr_entry(&cterm_attr_table, &at_en);
8824}
8825
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008826#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8827/*
8828 * Get an attribute index for a 'termguicolors' entry.
8829 * Uses an existing entry when possible or adds one when needed.
8830 */
8831 int
8832get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8833{
8834 attrentry_T at_en;
8835
8836 vim_memset(&at_en, 0, sizeof(attrentry_T));
8837 at_en.ae_attr = attr;
8838 at_en.ae_u.cterm.fg_rgb = fg;
8839 at_en.ae_u.cterm.bg_rgb = bg;
8840 return get_attr_entry(&cterm_attr_table, &at_en);
8841}
8842#endif
8843
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008844#if defined(FEAT_GUI) || defined(PROTO)
8845/*
8846 * Get an attribute index for a cterm entry.
8847 * Uses an existing entry when possible or adds one when needed.
8848 */
8849 int
8850get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8851{
8852 attrentry_T at_en;
8853
8854 vim_memset(&at_en, 0, sizeof(attrentry_T));
8855 at_en.ae_attr = attr;
8856 at_en.ae_u.gui.fg_color = fg;
8857 at_en.ae_u.gui.bg_color = bg;
8858 return get_attr_entry(&gui_attr_table, &at_en);
8859}
8860#endif
8861
Bram Moolenaareeac6772017-07-23 15:48:37 +02008862/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008863 * Clear all highlight tables.
8864 */
8865 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008866clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008867{
8868 int i;
8869 attrentry_T *taep;
8870
8871#ifdef FEAT_GUI
8872 ga_clear(&gui_attr_table);
8873#endif
8874 for (i = 0; i < term_attr_table.ga_len; ++i)
8875 {
8876 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8877 vim_free(taep->ae_u.term.start);
8878 vim_free(taep->ae_u.term.stop);
8879 }
8880 ga_clear(&term_attr_table);
8881 ga_clear(&cterm_attr_table);
8882}
8883
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008884#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008885/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008886 * Combine special attributes (e.g., for spelling) with other attributes
8887 * (e.g., for syntax highlighting).
8888 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008889 * This creates a new group when required.
8890 * Since we expect there to be few spelling mistakes we don't cache the
8891 * result.
8892 * Return the resulting attributes.
8893 */
8894 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008895hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008896{
8897 attrentry_T *char_aep = NULL;
8898 attrentry_T *spell_aep;
8899 attrentry_T new_en;
8900
8901 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008902 return prim_attr;
8903 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8904 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008905#ifdef FEAT_GUI
8906 if (gui.in_use)
8907 {
8908 if (char_attr > HL_ALL)
8909 char_aep = syn_gui_attr2entry(char_attr);
8910 if (char_aep != NULL)
8911 new_en = *char_aep;
8912 else
8913 {
8914 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008915 new_en.ae_u.gui.fg_color = INVALCOLOR;
8916 new_en.ae_u.gui.bg_color = INVALCOLOR;
8917 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008918 if (char_attr <= HL_ALL)
8919 new_en.ae_attr = char_attr;
8920 }
8921
Bram Moolenaar30abd282005-06-22 22:35:10 +00008922 if (prim_attr <= HL_ALL)
8923 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008924 else
8925 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008926 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008927 if (spell_aep != NULL)
8928 {
8929 new_en.ae_attr |= spell_aep->ae_attr;
8930 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8931 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8932 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8933 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8934 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8935 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8936 if (spell_aep->ae_u.gui.font != NOFONT)
8937 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8938# ifdef FEAT_XFONTSET
8939 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8940 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8941# endif
8942 }
8943 }
8944 return get_attr_entry(&gui_attr_table, &new_en);
8945 }
8946#endif
8947
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008948 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008949 {
8950 if (char_attr > HL_ALL)
8951 char_aep = syn_cterm_attr2entry(char_attr);
8952 if (char_aep != NULL)
8953 new_en = *char_aep;
8954 else
8955 {
8956 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008957#ifdef FEAT_TERMGUICOLORS
8958 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8959 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8960#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008961 if (char_attr <= HL_ALL)
8962 new_en.ae_attr = char_attr;
8963 }
8964
Bram Moolenaar30abd282005-06-22 22:35:10 +00008965 if (prim_attr <= HL_ALL)
8966 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008967 else
8968 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008969 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008970 if (spell_aep != NULL)
8971 {
8972 new_en.ae_attr |= spell_aep->ae_attr;
8973 if (spell_aep->ae_u.cterm.fg_color > 0)
8974 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8975 if (spell_aep->ae_u.cterm.bg_color > 0)
8976 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008977#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008978 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008979 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008980 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008981 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8982#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008983 }
8984 }
8985 return get_attr_entry(&cterm_attr_table, &new_en);
8986 }
8987
8988 if (char_attr > HL_ALL)
8989 char_aep = syn_term_attr2entry(char_attr);
8990 if (char_aep != NULL)
8991 new_en = *char_aep;
8992 else
8993 {
8994 vim_memset(&new_en, 0, sizeof(new_en));
8995 if (char_attr <= HL_ALL)
8996 new_en.ae_attr = char_attr;
8997 }
8998
Bram Moolenaar30abd282005-06-22 22:35:10 +00008999 if (prim_attr <= HL_ALL)
9000 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009001 else
9002 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009003 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009004 if (spell_aep != NULL)
9005 {
9006 new_en.ae_attr |= spell_aep->ae_attr;
9007 if (spell_aep->ae_u.term.start != NULL)
9008 {
9009 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9010 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9011 }
9012 }
9013 }
9014 return get_attr_entry(&term_attr_table, &new_en);
9015}
9016#endif
9017
Bram Moolenaar071d4272004-06-13 20:20:40 +00009018#ifdef FEAT_GUI
9019
9020 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009021syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009022{
9023 attr -= ATTR_OFF;
9024 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9025 return NULL;
9026 return &(GUI_ATTR_ENTRY(attr));
9027}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009028#endif /* FEAT_GUI */
9029
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009030/*
9031 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9032 * Only to be used when "attr" > HL_ALL.
9033 */
9034 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009035syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009036{
9037 attrentry_T *aep;
9038
9039#ifdef FEAT_GUI
9040 if (gui.in_use)
9041 aep = syn_gui_attr2entry(attr);
9042 else
9043#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009044 if (IS_CTERM)
9045 aep = syn_cterm_attr2entry(attr);
9046 else
9047 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009048
9049 if (aep == NULL) /* highlighting not set */
9050 return 0;
9051 return aep->ae_attr;
9052}
9053
9054
Bram Moolenaar071d4272004-06-13 20:20:40 +00009055 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009056syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009057{
9058 attr -= ATTR_OFF;
9059 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9060 return NULL;
9061 return &(TERM_ATTR_ENTRY(attr));
9062}
9063
9064 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009065syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009066{
9067 attr -= ATTR_OFF;
9068 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9069 return NULL;
9070 return &(CTERM_ATTR_ENTRY(attr));
9071}
9072
9073#define LIST_ATTR 1
9074#define LIST_STRING 2
9075#define LIST_INT 3
9076
9077 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009078highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009079{
9080 struct hl_group *sgp;
9081 int didh = FALSE;
9082
9083 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9084
9085 didh = highlight_list_arg(id, didh, LIST_ATTR,
9086 sgp->sg_term, NULL, "term");
9087 didh = highlight_list_arg(id, didh, LIST_STRING,
9088 0, sgp->sg_start, "start");
9089 didh = highlight_list_arg(id, didh, LIST_STRING,
9090 0, sgp->sg_stop, "stop");
9091
9092 didh = highlight_list_arg(id, didh, LIST_ATTR,
9093 sgp->sg_cterm, NULL, "cterm");
9094 didh = highlight_list_arg(id, didh, LIST_INT,
9095 sgp->sg_cterm_fg, NULL, "ctermfg");
9096 didh = highlight_list_arg(id, didh, LIST_INT,
9097 sgp->sg_cterm_bg, NULL, "ctermbg");
9098
Bram Moolenaar61623362010-07-14 22:04:22 +02009099#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009100 didh = highlight_list_arg(id, didh, LIST_ATTR,
9101 sgp->sg_gui, NULL, "gui");
9102 didh = highlight_list_arg(id, didh, LIST_STRING,
9103 0, sgp->sg_gui_fg_name, "guifg");
9104 didh = highlight_list_arg(id, didh, LIST_STRING,
9105 0, sgp->sg_gui_bg_name, "guibg");
9106 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009107 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009108#endif
9109#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009110 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009111 0, sgp->sg_font_name, "font");
9112#endif
9113
Bram Moolenaar661b1822005-07-28 22:36:45 +00009114 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009115 {
9116 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009117 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009118 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009119 msg_putchar(' ');
9120 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9121 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009122
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009123 if (!didh)
9124 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009125#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009126 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009127 last_set_msg(sgp->sg_scriptID);
9128#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009129}
9130
9131 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009132highlight_list_arg(
9133 int id,
9134 int didh,
9135 int type,
9136 int iarg,
9137 char_u *sarg,
9138 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009139{
9140 char_u buf[100];
9141 char_u *ts;
9142 int i;
9143
Bram Moolenaar661b1822005-07-28 22:36:45 +00009144 if (got_int)
9145 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009146 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9147 {
9148 ts = buf;
9149 if (type == LIST_INT)
9150 sprintf((char *)buf, "%d", iarg - 1);
9151 else if (type == LIST_STRING)
9152 ts = sarg;
9153 else /* type == LIST_ATTR */
9154 {
9155 buf[0] = NUL;
9156 for (i = 0; hl_attr_table[i] != 0; ++i)
9157 {
9158 if (iarg & hl_attr_table[i])
9159 {
9160 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009161 vim_strcat(buf, (char_u *)",", 100);
9162 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009163 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9164 }
9165 }
9166 }
9167
9168 (void)syn_list_header(didh,
9169 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9170 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009171 if (!got_int)
9172 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009173 if (*name != NUL)
9174 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009175 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9176 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009177 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009178 msg_outtrans(ts);
9179 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009180 }
9181 return didh;
9182}
9183
9184#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9185/*
9186 * Return "1" if highlight group "id" has attribute "flag".
9187 * Return NULL otherwise.
9188 */
9189 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009190highlight_has_attr(
9191 int id,
9192 int flag,
9193 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009194{
9195 int attr;
9196
9197 if (id <= 0 || id > highlight_ga.ga_len)
9198 return NULL;
9199
Bram Moolenaar61623362010-07-14 22:04:22 +02009200#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009201 if (modec == 'g')
9202 attr = HL_TABLE()[id - 1].sg_gui;
9203 else
9204#endif
9205 if (modec == 'c')
9206 attr = HL_TABLE()[id - 1].sg_cterm;
9207 else
9208 attr = HL_TABLE()[id - 1].sg_term;
9209
9210 if (attr & flag)
9211 return (char_u *)"1";
9212 return NULL;
9213}
9214#endif
9215
9216#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9217/*
9218 * Return color name of highlight group "id".
9219 */
9220 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009221highlight_color(
9222 int id,
9223 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9224 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009225{
9226 static char_u name[20];
9227 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009228 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009229 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009230 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009231
9232 if (id <= 0 || id > highlight_ga.ga_len)
9233 return NULL;
9234
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009235 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009236 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009237 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009238 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009239 font = TRUE;
9240 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009241 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009242 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9243 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009244 if (modec == 'g')
9245 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009246# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009247# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009248 /* return font name */
9249 if (font)
9250 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009251# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009252
Bram Moolenaar071d4272004-06-13 20:20:40 +00009253 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009254 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009255 {
9256 guicolor_T color;
9257 long_u rgb;
9258 static char_u buf[10];
9259
9260 if (fg)
9261 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009262 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009263# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009264 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009265# else
9266 color = INVALCOLOR;
9267# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009268 else
9269 color = HL_TABLE()[id - 1].sg_gui_bg;
9270 if (color == INVALCOLOR)
9271 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009272 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009273 sprintf((char *)buf, "#%02x%02x%02x",
9274 (unsigned)(rgb >> 16),
9275 (unsigned)(rgb >> 8) & 255,
9276 (unsigned)rgb & 255);
9277 return buf;
9278 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009279# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009280 if (fg)
9281 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009282 if (sp)
9283 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009284 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9285 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009286 if (font || sp)
9287 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009288 if (modec == 'c')
9289 {
9290 if (fg)
9291 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9292 else
9293 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009294 if (n < 0)
9295 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009296 sprintf((char *)name, "%d", n);
9297 return name;
9298 }
9299 /* term doesn't have color */
9300 return NULL;
9301}
9302#endif
9303
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009304#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009305 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009306 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009307/*
9308 * Return color name of highlight group "id" as RGB value.
9309 */
9310 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009311highlight_gui_color_rgb(
9312 int id,
9313 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009314{
9315 guicolor_T color;
9316
9317 if (id <= 0 || id > highlight_ga.ga_len)
9318 return 0L;
9319
9320 if (fg)
9321 color = HL_TABLE()[id - 1].sg_gui_fg;
9322 else
9323 color = HL_TABLE()[id - 1].sg_gui_bg;
9324
9325 if (color == INVALCOLOR)
9326 return 0L;
9327
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009328 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009329}
9330#endif
9331
9332/*
9333 * Output the syntax list header.
9334 * Return TRUE when started a new line.
9335 */
9336 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009337syn_list_header(
9338 int did_header, /* did header already */
9339 int outlen, /* length of string that comes */
9340 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009341{
9342 int endcol = 19;
9343 int newline = TRUE;
9344
9345 if (!did_header)
9346 {
9347 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009348 if (got_int)
9349 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009350 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9351 endcol = 15;
9352 }
9353 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009354 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009356 if (got_int)
9357 return TRUE;
9358 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009359 else
9360 {
9361 if (msg_col >= endcol) /* wrap around is like starting a new line */
9362 newline = FALSE;
9363 }
9364
9365 if (msg_col >= endcol) /* output at least one space */
9366 endcol = msg_col + 1;
9367 if (Columns <= endcol) /* avoid hang for tiny window */
9368 endcol = Columns - 1;
9369
9370 msg_advance(endcol);
9371
9372 /* Show "xxx" with the attributes. */
9373 if (!did_header)
9374 {
9375 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9376 msg_putchar(' ');
9377 }
9378
9379 return newline;
9380}
9381
9382/*
9383 * Set the attribute numbers for a highlight group.
9384 * Called after one of the attributes has changed.
9385 */
9386 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009387set_hl_attr(
9388 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009389{
9390 attrentry_T at_en;
9391 struct hl_group *sgp = HL_TABLE() + idx;
9392
9393 /* The "Normal" group doesn't need an attribute number */
9394 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9395 return;
9396
9397#ifdef FEAT_GUI
9398 /*
9399 * For the GUI mode: If there are other than "normal" highlighting
9400 * attributes, need to allocate an attr number.
9401 */
9402 if (sgp->sg_gui_fg == INVALCOLOR
9403 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009404 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009405 && sgp->sg_font == NOFONT
9406# ifdef FEAT_XFONTSET
9407 && sgp->sg_fontset == NOFONTSET
9408# endif
9409 )
9410 {
9411 sgp->sg_gui_attr = sgp->sg_gui;
9412 }
9413 else
9414 {
9415 at_en.ae_attr = sgp->sg_gui;
9416 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9417 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009418 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009419 at_en.ae_u.gui.font = sgp->sg_font;
9420# ifdef FEAT_XFONTSET
9421 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9422# endif
9423 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9424 }
9425#endif
9426 /*
9427 * For the term mode: If there are other than "normal" highlighting
9428 * attributes, need to allocate an attr number.
9429 */
9430 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9431 sgp->sg_term_attr = sgp->sg_term;
9432 else
9433 {
9434 at_en.ae_attr = sgp->sg_term;
9435 at_en.ae_u.term.start = sgp->sg_start;
9436 at_en.ae_u.term.stop = sgp->sg_stop;
9437 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9438 }
9439
9440 /*
9441 * For the color term mode: If there are other than "normal"
9442 * highlighting attributes, need to allocate an attr number.
9443 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009444 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009445# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009446 && sgp->sg_gui_fg == INVALCOLOR
9447 && sgp->sg_gui_bg == INVALCOLOR
9448# endif
9449 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009450 sgp->sg_cterm_attr = sgp->sg_cterm;
9451 else
9452 {
9453 at_en.ae_attr = sgp->sg_cterm;
9454 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9455 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009456# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009457 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9458 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009459# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009460 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9461 }
9462}
9463
9464/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009465 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009466 * If it is not found, 0 is returned.
9467 */
9468 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009469syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009470{
9471 int i;
9472 char_u name_u[200];
9473
9474 /* Avoid using stricmp() too much, it's slow on some systems */
9475 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9476 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009477 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009478 vim_strup(name_u);
9479 for (i = highlight_ga.ga_len; --i >= 0; )
9480 if (HL_TABLE()[i].sg_name_u != NULL
9481 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9482 break;
9483 return i + 1;
9484}
9485
9486#if defined(FEAT_EVAL) || defined(PROTO)
9487/*
9488 * Return TRUE if highlight group "name" exists.
9489 */
9490 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009491highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009492{
9493 return (syn_name2id(name) > 0);
9494}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009495
9496# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9497/*
9498 * Return the name of highlight group "id".
9499 * When not a valid ID return an empty string.
9500 */
9501 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009502syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009503{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009504 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009505 return (char_u *)"";
9506 return HL_TABLE()[id - 1].sg_name;
9507}
9508# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009509#endif
9510
9511/*
9512 * Like syn_name2id(), but take a pointer + length argument.
9513 */
9514 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009515syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009516{
9517 char_u *name;
9518 int id = 0;
9519
9520 name = vim_strnsave(linep, len);
9521 if (name != NULL)
9522 {
9523 id = syn_name2id(name);
9524 vim_free(name);
9525 }
9526 return id;
9527}
9528
9529/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009530 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009531 * The argument is a pointer to the name and the length of the name.
9532 * If it doesn't exist yet, a new entry is created.
9533 * Return 0 for failure.
9534 */
9535 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009536syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009537{
9538 int id;
9539 char_u *name;
9540
9541 name = vim_strnsave(pp, len);
9542 if (name == NULL)
9543 return 0;
9544
9545 id = syn_name2id(name);
9546 if (id == 0) /* doesn't exist yet */
9547 id = syn_add_group(name);
9548 else
9549 vim_free(name);
9550 return id;
9551}
9552
9553/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009554 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009555 * "name" must be an allocated string, it will be consumed.
9556 * Return 0 for failure.
9557 */
9558 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009559syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009560{
9561 char_u *p;
9562
9563 /* Check that the name is ASCII letters, digits and underscore. */
9564 for (p = name; *p != NUL; ++p)
9565 {
9566 if (!vim_isprintc(*p))
9567 {
9568 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009569 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009570 return 0;
9571 }
9572 else if (!ASCII_ISALNUM(*p) && *p != '_')
9573 {
9574 /* This is an error, but since there previously was no check only
9575 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009576 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009577 MSG(_("W18: Invalid character in group name"));
9578 break;
9579 }
9580 }
9581
9582 /*
9583 * First call for this growarray: init growing array.
9584 */
9585 if (highlight_ga.ga_data == NULL)
9586 {
9587 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9588 highlight_ga.ga_growsize = 10;
9589 }
9590
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009591 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009592 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009593 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009594 vim_free(name);
9595 return 0;
9596 }
9597
Bram Moolenaar071d4272004-06-13 20:20:40 +00009598 /*
9599 * Make room for at least one other syntax_highlight entry.
9600 */
9601 if (ga_grow(&highlight_ga, 1) == FAIL)
9602 {
9603 vim_free(name);
9604 return 0;
9605 }
9606
9607 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9608 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9609 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009610#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009611 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9612 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009613# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009614 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009615# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009616#endif
9617 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009618
9619 return highlight_ga.ga_len; /* ID is index plus one */
9620}
9621
9622/*
9623 * When, just after calling syn_add_group(), an error is discovered, this
9624 * function deletes the new name.
9625 */
9626 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009627syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009628{
9629 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009630 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9631 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9632}
9633
9634/*
9635 * Translate a group ID to highlight attributes.
9636 */
9637 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009638syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009639{
9640 int attr;
9641 struct hl_group *sgp;
9642
9643 hl_id = syn_get_final_id(hl_id);
9644 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9645
9646#ifdef FEAT_GUI
9647 /*
9648 * Only use GUI attr when the GUI is being used.
9649 */
9650 if (gui.in_use)
9651 attr = sgp->sg_gui_attr;
9652 else
9653#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009654 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009655 attr = sgp->sg_cterm_attr;
9656 else
9657 attr = sgp->sg_term_attr;
9658
9659 return attr;
9660}
9661
9662#ifdef FEAT_GUI
9663/*
9664 * Get the GUI colors and attributes for a group ID.
9665 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9666 */
9667 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009668syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009669{
9670 struct hl_group *sgp;
9671
9672 hl_id = syn_get_final_id(hl_id);
9673 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9674
9675 *fgp = sgp->sg_gui_fg;
9676 *bgp = sgp->sg_gui_bg;
9677 return sgp->sg_gui;
9678}
9679#endif
9680
9681/*
9682 * Translate a group ID to the final group ID (following links).
9683 */
9684 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009685syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009686{
9687 int count;
9688 struct hl_group *sgp;
9689
9690 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9691 return 0; /* Can be called from eval!! */
9692
9693 /*
9694 * Follow links until there is no more.
9695 * Look out for loops! Break after 100 links.
9696 */
9697 for (count = 100; --count >= 0; )
9698 {
9699 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9700 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9701 break;
9702 hl_id = sgp->sg_link;
9703 }
9704
9705 return hl_id;
9706}
9707
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009708#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009709/*
9710 * Call this function just after the GUI has started.
9711 * It finds the font and color handles for the highlighting groups.
9712 */
9713 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009714highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009715{
9716 int idx;
9717
9718 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009719# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9720# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009721 if (USE_24BIT)
9722# endif
9723 set_normal_colors();
9724# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009725
9726 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9727 gui_do_one_color(idx, FALSE, FALSE);
9728
9729 highlight_changed();
9730}
9731
9732 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009733gui_do_one_color(
9734 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009735 int do_menu UNUSED, /* TRUE: might set the menu font */
9736 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009737{
9738 int didit = FALSE;
9739
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009740# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009741# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009742 if (gui.in_use)
9743# endif
9744 if (HL_TABLE()[idx].sg_font_name != NULL)
9745 {
9746 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009747 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009748 didit = TRUE;
9749 }
9750# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009751 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9752 {
9753 HL_TABLE()[idx].sg_gui_fg =
9754 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9755 didit = TRUE;
9756 }
9757 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9758 {
9759 HL_TABLE()[idx].sg_gui_bg =
9760 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9761 didit = TRUE;
9762 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009763# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009764 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9765 {
9766 HL_TABLE()[idx].sg_gui_sp =
9767 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9768 didit = TRUE;
9769 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009770# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009771 if (didit) /* need to get a new attr number */
9772 set_hl_attr(idx);
9773}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009774#endif
9775
9776/*
9777 * Translate the 'highlight' option into attributes in highlight_attr[] and
9778 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9779 * corresponding highlights to use on top of HLF_SNC is computed.
9780 * Called only when the 'highlight' option has been changed and upon first
9781 * screen redraw after any :highlight command.
9782 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9783 */
9784 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009785highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009786{
9787 int hlf;
9788 int i;
9789 char_u *p;
9790 int attr;
9791 char_u *end;
9792 int id;
9793#ifdef USER_HIGHLIGHT
9794 char_u userhl[10];
9795# ifdef FEAT_STL_OPT
9796 int id_SNC = -1;
9797 int id_S = -1;
9798 int hlcnt;
9799# endif
9800#endif
9801 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9802
9803 need_highlight_changed = FALSE;
9804
9805 /*
9806 * Clear all attributes.
9807 */
9808 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9809 highlight_attr[hlf] = 0;
9810
9811 /*
9812 * First set all attributes to their default value.
9813 * Then use the attributes from the 'highlight' option.
9814 */
9815 for (i = 0; i < 2; ++i)
9816 {
9817 if (i)
9818 p = p_hl;
9819 else
9820 p = get_highlight_default();
9821 if (p == NULL) /* just in case */
9822 continue;
9823
9824 while (*p)
9825 {
9826 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9827 if (hl_flags[hlf] == *p)
9828 break;
9829 ++p;
9830 if (hlf == (int)HLF_COUNT || *p == NUL)
9831 return FAIL;
9832
9833 /*
9834 * Allow several hl_flags to be combined, like "bu" for
9835 * bold-underlined.
9836 */
9837 attr = 0;
9838 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9839 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01009840 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009841 continue;
9842
9843 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9844 return FAIL;
9845
9846 switch (*p)
9847 {
9848 case 'b': attr |= HL_BOLD;
9849 break;
9850 case 'i': attr |= HL_ITALIC;
9851 break;
9852 case '-':
9853 case 'n': /* no highlighting */
9854 break;
9855 case 'r': attr |= HL_INVERSE;
9856 break;
9857 case 's': attr |= HL_STANDOUT;
9858 break;
9859 case 'u': attr |= HL_UNDERLINE;
9860 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009861 case 'c': attr |= HL_UNDERCURL;
9862 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009863 case ':': ++p; /* highlight group name */
9864 if (attr || *p == NUL) /* no combinations */
9865 return FAIL;
9866 end = vim_strchr(p, ',');
9867 if (end == NULL)
9868 end = p + STRLEN(p);
9869 id = syn_check_group(p, (int)(end - p));
9870 if (id == 0)
9871 return FAIL;
9872 attr = syn_id2attr(id);
9873 p = end - 1;
9874#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9875 if (hlf == (int)HLF_SNC)
9876 id_SNC = syn_get_final_id(id);
9877 else if (hlf == (int)HLF_S)
9878 id_S = syn_get_final_id(id);
9879#endif
9880 break;
9881 default: return FAIL;
9882 }
9883 }
9884 highlight_attr[hlf] = attr;
9885
9886 p = skip_to_option_part(p); /* skip comma and spaces */
9887 }
9888 }
9889
9890#ifdef USER_HIGHLIGHT
9891 /* Setup the user highlights
9892 *
9893 * Temporarily utilize 10 more hl entries. Have to be in there
9894 * simultaneously in case of table overflows in get_attr_entry()
9895 */
9896# ifdef FEAT_STL_OPT
9897 if (ga_grow(&highlight_ga, 10) == FAIL)
9898 return FAIL;
9899 hlcnt = highlight_ga.ga_len;
9900 if (id_S == 0)
9901 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009902 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009903 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9904 id_S = hlcnt + 10;
9905 }
9906# endif
9907 for (i = 0; i < 9; i++)
9908 {
9909 sprintf((char *)userhl, "User%d", i + 1);
9910 id = syn_name2id(userhl);
9911 if (id == 0)
9912 {
9913 highlight_user[i] = 0;
9914# ifdef FEAT_STL_OPT
9915 highlight_stlnc[i] = 0;
9916# endif
9917 }
9918 else
9919 {
9920# ifdef FEAT_STL_OPT
9921 struct hl_group *hlt = HL_TABLE();
9922# endif
9923
9924 highlight_user[i] = syn_id2attr(id);
9925# ifdef FEAT_STL_OPT
9926 if (id_SNC == 0)
9927 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009928 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009929 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9930 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009931# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009932 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9933# endif
9934 }
9935 else
9936 mch_memmove(&hlt[hlcnt + i],
9937 &hlt[id_SNC - 1],
9938 sizeof(struct hl_group));
9939 hlt[hlcnt + i].sg_link = 0;
9940
9941 /* Apply difference between UserX and HLF_S to HLF_SNC */
9942 hlt[hlcnt + i].sg_term ^=
9943 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9944 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9945 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9946 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9947 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9948 hlt[hlcnt + i].sg_cterm ^=
9949 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9950 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9951 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9952 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9953 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009954# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009955 hlt[hlcnt + i].sg_gui ^=
9956 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009957# endif
9958# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009959 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9960 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9961 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9962 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009963 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9964 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009965 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9966 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9967# ifdef FEAT_XFONTSET
9968 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9969 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9970# endif
9971# endif
9972 highlight_ga.ga_len = hlcnt + i + 1;
9973 set_hl_attr(hlcnt + i); /* At long last we can apply */
9974 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9975# endif
9976 }
9977 }
9978# ifdef FEAT_STL_OPT
9979 highlight_ga.ga_len = hlcnt;
9980# endif
9981
9982#endif /* USER_HIGHLIGHT */
9983
9984 return OK;
9985}
9986
Bram Moolenaar4f688582007-07-24 12:34:30 +00009987#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009988
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009989static void highlight_list(void);
9990static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009991
9992/*
9993 * Handle command line completion for :highlight command.
9994 */
9995 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009996set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009997{
9998 char_u *p;
9999
10000 /* Default: expand group names */
10001 xp->xp_context = EXPAND_HIGHLIGHT;
10002 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010003 include_link = 2;
10004 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010005
10006 /* (part of) subcommand already typed */
10007 if (*arg != NUL)
10008 {
10009 p = skiptowhite(arg);
10010 if (*p != NUL) /* past "default" or group name */
10011 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010012 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010013 if (STRNCMP("default", arg, p - arg) == 0)
10014 {
10015 arg = skipwhite(p);
10016 xp->xp_pattern = arg;
10017 p = skiptowhite(arg);
10018 }
10019 if (*p != NUL) /* past group name */
10020 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010021 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010022 if (arg[1] == 'i' && arg[0] == 'N')
10023 highlight_list();
10024 if (STRNCMP("link", arg, p - arg) == 0
10025 || STRNCMP("clear", arg, p - arg) == 0)
10026 {
10027 xp->xp_pattern = skipwhite(p);
10028 p = skiptowhite(xp->xp_pattern);
10029 if (*p != NUL) /* past first group name */
10030 {
10031 xp->xp_pattern = skipwhite(p);
10032 p = skiptowhite(xp->xp_pattern);
10033 }
10034 }
10035 if (*p != NUL) /* past group name(s) */
10036 xp->xp_context = EXPAND_NOTHING;
10037 }
10038 }
10039 }
10040}
10041
10042/*
10043 * List highlighting matches in a nice way.
10044 */
10045 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010046highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010047{
10048 int i;
10049
10050 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010051 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010052 for (i = 40; --i >= 0; )
10053 highlight_list_two(99, 0);
10054}
10055
10056 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010057highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010058{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010059 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010060 msg_clr_eos();
10061 out_flush();
10062 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10063}
10064
10065#endif /* FEAT_CMDL_COMPL */
10066
10067#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10068 || defined(FEAT_SIGNS) || defined(PROTO)
10069/*
10070 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010071 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010072 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010073get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010074{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010075 return get_highlight_name_ext(xp, idx, TRUE);
10076}
10077
10078/*
10079 * Obtain a highlight group name.
10080 * When "skip_cleared" is TRUE don't return a cleared entry.
10081 */
10082 char_u *
10083get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10084{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010085 if (idx < 0)
10086 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010087
10088 /* Items are never removed from the table, skip the ones that were
10089 * cleared. */
10090 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10091 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010092
Bram Moolenaar071d4272004-06-13 20:20:40 +000010093#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010094 if (idx == highlight_ga.ga_len && include_none != 0)
10095 return (char_u *)"none";
10096 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010097 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010098 if (idx == highlight_ga.ga_len + include_none + include_default
10099 && include_link != 0)
10100 return (char_u *)"link";
10101 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10102 && include_link != 0)
10103 return (char_u *)"clear";
10104#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010105 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010106 return NULL;
10107 return HL_TABLE()[idx].sg_name;
10108}
10109#endif
10110
Bram Moolenaar4f688582007-07-24 12:34:30 +000010111#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010112/*
10113 * Free all the highlight group fonts.
10114 * Used when quitting for systems which need it.
10115 */
10116 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010117free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010118{
10119 int idx;
10120
10121 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10122 {
10123 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10124 HL_TABLE()[idx].sg_font = NOFONT;
10125# ifdef FEAT_XFONTSET
10126 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10127 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10128# endif
10129 }
10130
10131 gui_mch_free_font(gui.norm_font);
10132# ifdef FEAT_XFONTSET
10133 gui_mch_free_fontset(gui.fontset);
10134# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010135# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010136 gui_mch_free_font(gui.bold_font);
10137 gui_mch_free_font(gui.ital_font);
10138 gui_mch_free_font(gui.boldital_font);
10139# endif
10140}
10141#endif
10142
10143/**************************************
10144 * End of Highlighting stuff *
10145 **************************************/