blob: 56d69c6df548b72e726feb400ebcdc98c63460b8 [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 Moolenaar071d4272004-06-13 20:20:40 +0000370static linenr_T current_lnum = 0; /* lnum of current state */
371static colnr_T current_col = 0; /* column of current state */
372static int current_state_stored = 0; /* TRUE if stored current state
373 * after setting current_finished */
374static int current_finished = 0; /* current line has been finished */
375static garray_T current_state /* current stack of state_items */
376 = {0, 0, 0, 0, NULL};
377static short *current_next_list = NULL; /* when non-zero, nextgroup list */
378static int current_next_flags = 0; /* flags for current_next_list */
379static int current_line_id = 0; /* unique number for current line */
380
381#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
382
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100383static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100384static void save_chartab(char_u *chartab);
385static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100386static int syn_match_linecont(linenr_T lnum);
387static void syn_start_line(void);
388static void syn_update_ends(int startofline);
389static void syn_stack_alloc(void);
390static int syn_stack_cleanup(void);
391static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
392static synstate_T *syn_stack_find_entry(linenr_T lnum);
393static synstate_T *store_current_state(void);
394static void load_current_state(synstate_T *from);
395static void invalidate_current_state(void);
396static int syn_stack_equal(synstate_T *sp);
397static void validate_current_state(void);
398static int syn_finish_line(int syncing);
399static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
400static int did_match_already(int idx, garray_T *gap);
401static stateitem_T *push_next_match(stateitem_T *cur_si);
402static void check_state_ends(void);
403static void update_si_attr(int idx);
404static void check_keepend(void);
405static void update_si_end(stateitem_T *sip, int startcol, int force);
406static short *copy_id_list(short *list);
407static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
408static int push_current_state(int idx);
409static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200410#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100411static void syn_clear_time(syn_time_T *tt);
412static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200413#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100414static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200415#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100416static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200417#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100418static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200419static int syn_time_on = FALSE;
420# define IF_SYN_TIME(p) (p)
421#else
422# define IF_SYN_TIME(p) NULL
423typedef int syn_time_T;
424#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100426static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
427static 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);
428static void clear_syn_state(synstate_T *p);
429static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100431static void limit_pos(lpos_T *pos, lpos_T *limit);
432static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
433static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
434static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
435static char_u *syn_getcurline(void);
436static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
437static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
438static void syn_cmd_case(exarg_T *eap, int syncing);
439static void syn_cmd_spell(exarg_T *eap, int syncing);
440static void syntax_sync_clear(void);
441static void syn_remove_pattern(synblock_T *block, int idx);
442static void syn_clear_pattern(synblock_T *block, int i);
443static void syn_clear_cluster(synblock_T *block, int i);
444static void syn_cmd_clear(exarg_T *eap, int syncing);
445static void syn_cmd_conceal(exarg_T *eap, int syncing);
446static void syn_clear_one(int id, int syncing);
447static void syn_cmd_on(exarg_T *eap, int syncing);
448static void syn_cmd_enable(exarg_T *eap, int syncing);
449static void syn_cmd_reset(exarg_T *eap, int syncing);
450static void syn_cmd_manual(exarg_T *eap, int syncing);
451static void syn_cmd_off(exarg_T *eap, int syncing);
452static void syn_cmd_onoff(exarg_T *eap, char *name);
453static void syn_cmd_list(exarg_T *eap, int syncing);
454static void syn_lines_msg(void);
455static void syn_match_msg(void);
456static void syn_stack_free_block(synblock_T *block);
457static void syn_list_one(int id, int syncing, int link_only);
458static void syn_list_cluster(int id);
459static void put_id_list(char_u *name, short *list, int attr);
460static void put_pattern(char *s, int c, synpat_T *spp, int attr);
461static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
462static void syn_clear_keyword(int id, hashtab_T *ht);
463static void clear_keywtab(hashtab_T *ht);
464static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
465static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100466static 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 +0100467static void syn_cmd_include(exarg_T *eap, int syncing);
468static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
469static void syn_cmd_keyword(exarg_T *eap, int syncing);
470static void syn_cmd_match(exarg_T *eap, int syncing);
471static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100473static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100475static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100477static void syn_cmd_cluster(exarg_T *eap, int syncing);
478static int syn_scl_name2id(char_u *name);
479static int syn_scl_namen2id(char_u *linep, int len);
480static int syn_check_cluster(char_u *pp, int len);
481static int syn_add_cluster(char_u *name);
482static void init_syn_patterns(void);
483static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
484static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100485static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100486static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
487static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000488
489/*
490 * Start the syntax recognition for a line. This function is normally called
491 * from the screen updating, once for each displayed line.
492 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
493 * it. Careful: curbuf and curwin are likely to point to another buffer and
494 * window.
495 */
496 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100497syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498{
499 synstate_T *p;
500 synstate_T *last_valid = NULL;
501 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000502 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503 linenr_T parsed_lnum;
504 linenr_T first_stored;
505 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100506 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200508#ifdef FEAT_CONCEAL
509 current_sub_char = NUL;
510#endif
511
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 /*
513 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000514 * Also do this when a change was made, the current state may be invalid
515 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200517 if (syn_block != wp->w_s
518 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100519 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 {
521 invalidate_current_state();
522 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200523 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100525 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 syn_win = wp;
527
528 /*
529 * Allocate syntax stack when needed.
530 */
531 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000533 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200534 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535
536 /*
537 * If the state of the end of the previous line is useful, store it.
538 */
539 if (VALID_STATE(&current_state)
540 && current_lnum < lnum
541 && current_lnum < syn_buf->b_ml.ml_line_count)
542 {
543 (void)syn_finish_line(FALSE);
544 if (!current_state_stored)
545 {
546 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000547 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 }
549
550 /*
551 * If the current_lnum is now the same as "lnum", keep the current
552 * state (this happens very often!). Otherwise invalidate
553 * current_state and figure it out below.
554 */
555 if (current_lnum != lnum)
556 invalidate_current_state();
557 }
558 else
559 invalidate_current_state();
560
561 /*
562 * Try to synchronize from a saved state in b_sst_array[].
563 * Only do this if lnum is not before and not to far beyond a saved state.
564 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200565 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 {
567 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200568 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 {
570 if (p->sst_lnum > lnum)
571 break;
572 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
573 {
574 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200575 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576 last_min_valid = p;
577 }
578 }
579 if (last_min_valid != NULL)
580 load_current_state(last_min_valid);
581 }
582
583 /*
584 * If "lnum" is before or far beyond a line with a saved state, need to
585 * re-synchronize.
586 */
587 if (INVALID_STATE(&current_state))
588 {
589 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200590 if (current_lnum == 1)
591 /* First line is always valid, no matter "minlines". */
592 first_stored = 1;
593 else
594 /* Need to parse "minlines" lines before state can be considered
595 * valid to store. */
596 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597 }
598 else
599 first_stored = current_lnum;
600
601 /*
602 * Advance from the sync point or saved state until the current line.
603 * Save some entries for syncing with later on.
604 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200605 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000606 dist = 999999;
607 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200608 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000609 while (current_lnum < lnum)
610 {
611 syn_start_line();
612 (void)syn_finish_line(FALSE);
613 ++current_lnum;
614
615 /* If we parsed at least "minlines" lines or started at a valid
616 * state, the current state is considered valid. */
617 if (current_lnum >= first_stored)
618 {
619 /* Check if the saved state entry is for the current line and is
620 * equal to the current state. If so, then validate all saved
621 * states that depended on a change before the parsed line. */
622 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000623 prev = syn_stack_find_entry(current_lnum - 1);
624 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200625 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000627 sp = prev;
628 while (sp != NULL && sp->sst_lnum < current_lnum)
629 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 if (sp != NULL
631 && sp->sst_lnum == current_lnum
632 && syn_stack_equal(sp))
633 {
634 parsed_lnum = current_lnum;
635 prev = sp;
636 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
637 {
638 if (sp->sst_lnum <= lnum)
639 /* valid state before desired line, use this one */
640 prev = sp;
641 else if (sp->sst_change_lnum == 0)
642 /* past saved states depending on change, break here. */
643 break;
644 sp->sst_change_lnum = 0;
645 sp = sp->sst_next;
646 }
647 load_current_state(prev);
648 }
649 /* Store the state at this line when it's the first one, the line
650 * where we start parsing, or some distance from the previously
651 * saved state. But only when parsed at least 'minlines'. */
652 else if (prev == NULL
653 || current_lnum == lnum
654 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000655 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 }
657
658 /* This can take a long time: break when CTRL-C pressed. The current
659 * state will be wrong then. */
660 line_breakcheck();
661 if (got_int)
662 {
663 current_lnum = lnum;
664 break;
665 }
666 }
667
668 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669}
670
671/*
672 * We cannot simply discard growarrays full of state_items or buf_states; we
673 * have to manually release their extmatch pointers first.
674 */
675 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100676clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677{
678 int i;
679 garray_T *gap;
680
681 if (p->sst_stacksize > SST_FIX_STATES)
682 {
683 gap = &(p->sst_union.sst_ga);
684 for (i = 0; i < gap->ga_len; i++)
685 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
686 ga_clear(gap);
687 }
688 else
689 {
690 for (i = 0; i < p->sst_stacksize; i++)
691 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
692 }
693}
694
695/*
696 * Cleanup the current_state stack.
697 */
698 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100699clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700{
701 int i;
702 stateitem_T *sip;
703
704 sip = (stateitem_T *)(current_state.ga_data);
705 for (i = 0; i < current_state.ga_len; i++)
706 unref_extmatch(sip[i].si_extmatch);
707 ga_clear(&current_state);
708}
709
710/*
711 * Try to find a synchronisation point for line "lnum".
712 *
713 * This sets current_lnum and the current state. One of three methods is
714 * used:
715 * 1. Search backwards for the end of a C-comment.
716 * 2. Search backwards for given sync patterns.
717 * 3. Simply start on a given number of lines above "lnum".
718 */
719 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100720syn_sync(
721 win_T *wp,
722 linenr_T start_lnum,
723 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000724{
725 buf_T *curbuf_save;
726 win_T *curwin_save;
727 pos_T cursor_save;
728 int idx;
729 linenr_T lnum;
730 linenr_T end_lnum;
731 linenr_T break_lnum;
732 int had_sync_point;
733 stateitem_T *cur_si;
734 synpat_T *spp;
735 char_u *line;
736 int found_flags = 0;
737 int found_match_idx = 0;
738 linenr_T found_current_lnum = 0;
739 int found_current_col= 0;
740 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000741 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742
743 /*
744 * Clear any current state that might be hanging around.
745 */
746 invalidate_current_state();
747
748 /*
749 * Start at least "minlines" back. Default starting point for parsing is
750 * there.
751 * Start further back, to avoid that scrolling backwards will result in
752 * resyncing for every line. Now it resyncs only one out of N lines,
753 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
754 * Watch out for overflow when minlines is MAXLNUM.
755 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200756 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 start_lnum = 1;
758 else
759 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200760 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200762 else if (syn_block->b_syn_sync_minlines < 10)
763 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200765 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
766 if (syn_block->b_syn_sync_maxlines != 0
767 && lnum > syn_block->b_syn_sync_maxlines)
768 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000769 if (lnum >= start_lnum)
770 start_lnum = 1;
771 else
772 start_lnum -= lnum;
773 }
774 current_lnum = start_lnum;
775
776 /*
777 * 1. Search backwards for the end of a C-style comment.
778 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200779 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000780 {
781 /* Need to make syn_buf the current buffer for a moment, to be able to
782 * use find_start_comment(). */
783 curwin_save = curwin;
784 curwin = wp;
785 curbuf_save = curbuf;
786 curbuf = syn_buf;
787
788 /*
789 * Skip lines that end in a backslash.
790 */
791 for ( ; start_lnum > 1; --start_lnum)
792 {
793 line = ml_get(start_lnum - 1);
794 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
795 break;
796 }
797 current_lnum = start_lnum;
798
799 /* set cursor to start of search */
800 cursor_save = wp->w_cursor;
801 wp->w_cursor.lnum = start_lnum;
802 wp->w_cursor.col = 0;
803
804 /*
805 * If the line is inside a comment, need to find the syntax item that
806 * defines the comment.
807 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
808 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200809 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000810 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200811 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
812 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
813 == syn_block->b_syn_sync_id
814 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000815 {
816 validate_current_state();
817 if (push_current_state(idx) == OK)
818 update_si_attr(current_state.ga_len - 1);
819 break;
820 }
821 }
822
823 /* restore cursor and buffer */
824 wp->w_cursor = cursor_save;
825 curwin = curwin_save;
826 curbuf = curbuf_save;
827 }
828
829 /*
830 * 2. Search backwards for given sync patterns.
831 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200832 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000833 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200834 if (syn_block->b_syn_sync_maxlines != 0
835 && start_lnum > syn_block->b_syn_sync_maxlines)
836 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000837 else
838 break_lnum = 0;
839
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000840 found_m_endpos.lnum = 0;
841 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000842 end_lnum = start_lnum;
843 lnum = start_lnum;
844 while (--lnum > break_lnum)
845 {
846 /* This can take a long time: break when CTRL-C pressed. */
847 line_breakcheck();
848 if (got_int)
849 {
850 invalidate_current_state();
851 current_lnum = start_lnum;
852 break;
853 }
854
855 /* Check if we have run into a valid saved state stack now. */
856 if (last_valid != NULL && lnum == last_valid->sst_lnum)
857 {
858 load_current_state(last_valid);
859 break;
860 }
861
862 /*
863 * Check if the previous line has the line-continuation pattern.
864 */
865 if (lnum > 1 && syn_match_linecont(lnum - 1))
866 continue;
867
868 /*
869 * Start with nothing on the state stack
870 */
871 validate_current_state();
872
873 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
874 {
875 syn_start_line();
876 for (;;)
877 {
878 had_sync_point = syn_finish_line(TRUE);
879 /*
880 * When a sync point has been found, remember where, and
881 * continue to look for another one, further on in the line.
882 */
883 if (had_sync_point && current_state.ga_len)
884 {
885 cur_si = &CUR_STATE(current_state.ga_len - 1);
886 if (cur_si->si_m_endpos.lnum > start_lnum)
887 {
888 /* ignore match that goes to after where started */
889 current_lnum = end_lnum;
890 break;
891 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000892 if (cur_si->si_idx < 0)
893 {
894 /* Cannot happen? */
895 found_flags = 0;
896 found_match_idx = KEYWORD_IDX;
897 }
898 else
899 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200900 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000901 found_flags = spp->sp_flags;
902 found_match_idx = spp->sp_sync_idx;
903 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 found_current_lnum = current_lnum;
905 found_current_col = current_col;
906 found_m_endpos = cur_si->si_m_endpos;
907 /*
908 * Continue after the match (be aware of a zero-length
909 * match).
910 */
911 if (found_m_endpos.lnum > current_lnum)
912 {
913 current_lnum = found_m_endpos.lnum;
914 current_col = found_m_endpos.col;
915 if (current_lnum >= end_lnum)
916 break;
917 }
918 else if (found_m_endpos.col > current_col)
919 current_col = found_m_endpos.col;
920 else
921 ++current_col;
922
923 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000924 * an item that ends here, need to do that now. Be
925 * careful not to go past the NUL. */
926 prev_current_col = current_col;
927 if (syn_getcurline()[current_col] != NUL)
928 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000929 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000930 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931 }
932 else
933 break;
934 }
935 }
936
937 /*
938 * If a sync point was encountered, break here.
939 */
940 if (found_flags)
941 {
942 /*
943 * Put the item that was specified by the sync point on the
944 * state stack. If there was no item specified, make the
945 * state stack empty.
946 */
947 clear_current_state();
948 if (found_match_idx >= 0
949 && push_current_state(found_match_idx) == OK)
950 update_si_attr(current_state.ga_len - 1);
951
952 /*
953 * When using "grouphere", continue from the sync point
954 * match, until the end of the line. Parsing starts at
955 * the next line.
956 * For "groupthere" the parsing starts at start_lnum.
957 */
958 if (found_flags & HL_SYNC_HERE)
959 {
960 if (current_state.ga_len)
961 {
962 cur_si = &CUR_STATE(current_state.ga_len - 1);
963 cur_si->si_h_startpos.lnum = found_current_lnum;
964 cur_si->si_h_startpos.col = found_current_col;
965 update_si_end(cur_si, (int)current_col, TRUE);
966 check_keepend();
967 }
968 current_col = found_m_endpos.col;
969 current_lnum = found_m_endpos.lnum;
970 (void)syn_finish_line(FALSE);
971 ++current_lnum;
972 }
973 else
974 current_lnum = start_lnum;
975
976 break;
977 }
978
979 end_lnum = lnum;
980 invalidate_current_state();
981 }
982
983 /* Ran into start of the file or exceeded maximum number of lines */
984 if (lnum <= break_lnum)
985 {
986 invalidate_current_state();
987 current_lnum = break_lnum + 1;
988 }
989 }
990
991 validate_current_state();
992}
993
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100994 static void
995save_chartab(char_u *chartab)
996{
997 if (syn_block->b_syn_isk != empty_option)
998 {
999 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1000 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1001 (size_t)32);
1002 }
1003}
1004
1005 static void
1006restore_chartab(char_u *chartab)
1007{
1008 if (syn_win->w_s->b_syn_isk != empty_option)
1009 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1010}
1011
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012/*
1013 * Return TRUE if the line-continuation pattern matches in line "lnum".
1014 */
1015 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001016syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001017{
1018 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001019 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001020 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021
Bram Moolenaar860cae12010-06-05 23:22:07 +02001022 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001024 /* use syntax iskeyword option */
1025 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001026 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1027 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001028 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001029 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001030 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001031 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001032 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001033 }
1034 return FALSE;
1035}
1036
1037/*
1038 * Prepare the current state for the start of a line.
1039 */
1040 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001041syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042{
1043 current_finished = FALSE;
1044 current_col = 0;
1045
1046 /*
1047 * Need to update the end of a start/skip/end that continues from the
1048 * previous line and regions that have "keepend".
1049 */
1050 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001051 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001053 check_state_ends();
1054 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055
1056 next_match_idx = -1;
1057 ++current_line_id;
1058}
1059
1060/*
1061 * Check for items in the stack that need their end updated.
1062 * When "startofline" is TRUE the last item is always updated.
1063 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1064 */
1065 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001066syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067{
1068 stateitem_T *cur_si;
1069 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001070 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001071
1072 if (startofline)
1073 {
1074 /* Check for a match carried over from a previous line with a
1075 * contained region. The match ends as soon as the region ends. */
1076 for (i = 0; i < current_state.ga_len; ++i)
1077 {
1078 cur_si = &CUR_STATE(i);
1079 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001080 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081 == SPTYPE_MATCH
1082 && cur_si->si_m_endpos.lnum < current_lnum)
1083 {
1084 cur_si->si_flags |= HL_MATCHCONT;
1085 cur_si->si_m_endpos.lnum = 0;
1086 cur_si->si_m_endpos.col = 0;
1087 cur_si->si_h_endpos = cur_si->si_m_endpos;
1088 cur_si->si_ends = TRUE;
1089 }
1090 }
1091 }
1092
1093 /*
1094 * Need to update the end of a start/skip/end that continues from the
1095 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001096 * influence contained items. If we've just removed "extend"
1097 * (startofline == 0) then we should update ends of normal regions
1098 * contained inside "keepend" because "extend" could have extended
1099 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 * Then check for items ending in column 0.
1101 */
1102 i = current_state.ga_len - 1;
1103 if (keepend_level >= 0)
1104 for ( ; i > keepend_level; --i)
1105 if (CUR_STATE(i).si_flags & HL_EXTEND)
1106 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001107
1108 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001109 for ( ; i < current_state.ga_len; ++i)
1110 {
1111 cur_si = &CUR_STATE(i);
1112 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001113 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 || (i == current_state.ga_len - 1 && startofline))
1115 {
1116 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1117 cur_si->si_h_startpos.lnum = current_lnum;
1118
1119 if (!(cur_si->si_flags & HL_MATCHCONT))
1120 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001121
1122 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1123 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 }
1125 }
1126 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127}
1128
1129/****************************************
1130 * Handling of the state stack cache.
1131 */
1132
1133/*
1134 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1135 *
1136 * To speed up syntax highlighting, the state stack for the start of some
1137 * lines is cached. These entries can be used to start parsing at that point.
1138 *
1139 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1140 * valid entries. b_sst_first points to the first one, then follow sst_next.
1141 * The entries are sorted on line number. The first entry is often for line 2
1142 * (line 1 always starts with an empty stack).
1143 * There is also a list for free entries. This construction is used to avoid
1144 * having to allocate and free memory blocks too often.
1145 *
1146 * When making changes to the buffer, this is logged in b_mod_*. When calling
1147 * update_screen() to update the display, it will call
1148 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1149 * entries. The entries which are inside the changed area are removed,
1150 * because they must be recomputed. Entries below the changed have their line
1151 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1152 * set to indicate that a check must be made if the changed lines would change
1153 * the cached entry.
1154 *
1155 * When later displaying lines, an entry is stored for each line. Displayed
1156 * lines are likely to be displayed again, in which case the state at the
1157 * start of the line is needed.
1158 * For not displayed lines, an entry is stored for every so many lines. These
1159 * entries will be used e.g., when scrolling backwards. The distance between
1160 * entries depends on the number of lines in the buffer. For small buffers
1161 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1162 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1163 */
1164
Bram Moolenaar860cae12010-06-05 23:22:07 +02001165 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001166syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167{
1168 synstate_T *p;
1169
1170 if (block->b_sst_array != NULL)
1171 {
1172 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1173 clear_syn_state(p);
1174 vim_free(block->b_sst_array);
1175 block->b_sst_array = NULL;
1176 block->b_sst_len = 0;
1177 }
1178}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179/*
1180 * Free b_sst_array[] for buffer "buf".
1181 * Used when syntax items changed to force resyncing everywhere.
1182 */
1183 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001184syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001186#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001188#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189
Bram Moolenaar860cae12010-06-05 23:22:07 +02001190 syn_stack_free_block(block);
1191
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192#ifdef FEAT_FOLDING
1193 /* When using "syntax" fold method, must update all folds. */
1194 FOR_ALL_WINDOWS(wp)
1195 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001196 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197 foldUpdateAll(wp);
1198 }
1199#endif
1200}
1201
1202/*
1203 * Allocate the syntax state stack for syn_buf when needed.
1204 * If the number of entries in b_sst_array[] is much too big or a bit too
1205 * small, reallocate it.
1206 * Also used to allocate b_sst_array[] for the first time.
1207 */
1208 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001209syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210{
1211 long len;
1212 synstate_T *to, *from;
1213 synstate_T *sstp;
1214
1215 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1216 if (len < SST_MIN_ENTRIES)
1217 len = SST_MIN_ENTRIES;
1218 else if (len > SST_MAX_ENTRIES)
1219 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001220 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 {
1222 /* Allocate 50% too much, to avoid reallocating too often. */
1223 len = syn_buf->b_ml.ml_line_count;
1224 len = (len + len / 2) / 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;
1229
Bram Moolenaar860cae12010-06-05 23:22:07 +02001230 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 {
1232 /* When shrinking the array, cleanup the existing stack.
1233 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001234 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 && syn_stack_cleanup())
1236 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1238 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 }
1240
1241 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1242 if (sstp == NULL) /* out of memory! */
1243 return;
1244
1245 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001246 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 {
1248 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 from = from->sst_next)
1251 {
1252 ++to;
1253 *to = *from;
1254 to->sst_next = to + 1;
1255 }
1256 }
1257 if (to != sstp - 1)
1258 {
1259 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001260 syn_block->b_sst_first = sstp;
1261 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 }
1263 else
1264 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 syn_block->b_sst_first = NULL;
1266 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 }
1268
1269 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001270 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 while (++to < sstp + len)
1272 to->sst_next = to + 1;
1273 (sstp + len - 1)->sst_next = NULL;
1274
Bram Moolenaar860cae12010-06-05 23:22:07 +02001275 vim_free(syn_block->b_sst_array);
1276 syn_block->b_sst_array = sstp;
1277 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 }
1279}
1280
1281/*
1282 * Check for changes in a buffer to affect stored syntax states. Uses the
1283 * b_mod_* fields.
1284 * Called from update_screen(), before screen is being updated, once for each
1285 * displayed buffer.
1286 */
1287 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001288syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001290 win_T *wp;
1291
1292 syn_stack_apply_changes_block(&buf->b_s, buf);
1293
1294 FOR_ALL_WINDOWS(wp)
1295 {
1296 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1297 syn_stack_apply_changes_block(wp->w_s, buf);
1298 }
1299}
1300
1301 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001302syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001303{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 synstate_T *p, *prev, *np;
1305 linenr_T n;
1306
Bram Moolenaar860cae12010-06-05 23:22:07 +02001307 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 return;
1309
1310 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
1315 n = p->sst_lnum + buf->b_mod_xlines;
1316 if (n <= buf->b_mod_bot)
1317 {
1318 /* this state is inside the changed area, remove it */
1319 np = p->sst_next;
1320 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001321 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 else
1323 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001324 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001325 p = np;
1326 continue;
1327 }
1328 /* This state is below the changed area. Remember the line
1329 * that needs to be parsed before this entry can be made valid
1330 * again. */
1331 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1332 {
1333 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1334 p->sst_change_lnum += buf->b_mod_xlines;
1335 else
1336 p->sst_change_lnum = buf->b_mod_top;
1337 }
1338 if (p->sst_change_lnum == 0
1339 || p->sst_change_lnum < buf->b_mod_bot)
1340 p->sst_change_lnum = buf->b_mod_bot;
1341
1342 p->sst_lnum = n;
1343 }
1344 prev = p;
1345 p = p->sst_next;
1346 }
1347}
1348
1349/*
1350 * Reduce the number of entries in the state stack for syn_buf.
1351 * Returns TRUE if at least one entry was freed.
1352 */
1353 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001354syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355{
1356 synstate_T *p, *prev;
1357 disptick_T tick;
1358 int above;
1359 int dist;
1360 int retval = FALSE;
1361
Bram Moolenaar860cae12010-06-05 23:22:07 +02001362 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 return retval;
1364
1365 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001366 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001367 dist = 999999;
1368 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001369 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370
1371 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001372 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 * be removed. Set "above" when the "tick" for the oldest entry is above
1374 * "b_sst_lasttick" (the display tick wraps around).
1375 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001378 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1380 {
1381 if (prev->sst_lnum + dist > p->sst_lnum)
1382 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001383 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 {
1385 if (!above || p->sst_tick < tick)
1386 tick = p->sst_tick;
1387 above = TRUE;
1388 }
1389 else if (!above && p->sst_tick < tick)
1390 tick = p->sst_tick;
1391 }
1392 }
1393
1394 /*
1395 * Go through the list to make the entries for the oldest tick at an
1396 * interval of several lines.
1397 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001398 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1400 {
1401 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1402 {
1403 /* Move this entry from used list to free list */
1404 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001405 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 p = prev;
1407 retval = TRUE;
1408 }
1409 }
1410 return retval;
1411}
1412
1413/*
1414 * Free the allocated memory for a syn_state item.
1415 * Move the entry into the free list.
1416 */
1417 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001418syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419{
1420 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001421 p->sst_next = block->b_sst_firstfree;
1422 block->b_sst_firstfree = p;
1423 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424}
1425
1426/*
1427 * Find an entry in the list of state stacks at or before "lnum".
1428 * Returns NULL when there is no entry or the first entry is after "lnum".
1429 */
1430 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001431syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432{
1433 synstate_T *p, *prev;
1434
1435 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001436 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 {
1438 if (p->sst_lnum == lnum)
1439 return p;
1440 if (p->sst_lnum > lnum)
1441 break;
1442 }
1443 return prev;
1444}
1445
1446/*
1447 * Try saving the current state in b_sst_array[].
1448 * The current state must be valid for the start of the current_lnum line!
1449 */
1450 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001451store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001452{
1453 int i;
1454 synstate_T *p;
1455 bufstate_T *bp;
1456 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001457 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458
1459 /*
1460 * If the current state contains a start or end pattern that continues
1461 * from the previous line, we can't use it. Don't store it then.
1462 */
1463 for (i = current_state.ga_len - 1; i >= 0; --i)
1464 {
1465 cur_si = &CUR_STATE(i);
1466 if (cur_si->si_h_startpos.lnum >= current_lnum
1467 || cur_si->si_m_endpos.lnum >= current_lnum
1468 || cur_si->si_h_endpos.lnum >= current_lnum
1469 || (cur_si->si_end_idx
1470 && cur_si->si_eoe_pos.lnum >= current_lnum))
1471 break;
1472 }
1473 if (i >= 0)
1474 {
1475 if (sp != NULL)
1476 {
1477 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001478 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001480 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 else
1482 {
1483 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001484 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 if (p->sst_next == sp)
1486 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001487 if (p != NULL) /* just in case */
1488 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001490 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 sp = NULL;
1492 }
1493 }
1494 else if (sp == NULL || sp->sst_lnum != current_lnum)
1495 {
1496 /*
1497 * Add a new entry
1498 */
1499 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001500 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 {
1502 (void)syn_stack_cleanup();
1503 /* "sp" may have been moved to the freelist now */
1504 sp = syn_stack_find_entry(current_lnum);
1505 }
1506 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001507 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 sp = NULL;
1509 else
1510 {
1511 /* Take the first item from the free list and put it in the used
1512 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001513 p = syn_block->b_sst_firstfree;
1514 syn_block->b_sst_firstfree = p->sst_next;
1515 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 if (sp == NULL)
1517 {
1518 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001519 p->sst_next = syn_block->b_sst_first;
1520 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 }
1522 else
1523 {
1524 /* insert in list after *sp */
1525 p->sst_next = sp->sst_next;
1526 sp->sst_next = p;
1527 }
1528 sp = p;
1529 sp->sst_stacksize = 0;
1530 sp->sst_lnum = current_lnum;
1531 }
1532 }
1533 if (sp != NULL)
1534 {
1535 /* When overwriting an existing state stack, clear it first */
1536 clear_syn_state(sp);
1537 sp->sst_stacksize = current_state.ga_len;
1538 if (current_state.ga_len > SST_FIX_STATES)
1539 {
1540 /* Need to clear it, might be something remaining from when the
1541 * length was less than SST_FIX_STATES. */
1542 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1543 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1544 sp->sst_stacksize = 0;
1545 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001547 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1548 }
1549 else
1550 bp = sp->sst_union.sst_stack;
1551 for (i = 0; i < sp->sst_stacksize; ++i)
1552 {
1553 bp[i].bs_idx = CUR_STATE(i).si_idx;
1554 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001555#ifdef FEAT_CONCEAL
1556 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1557 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1558#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001559 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1560 }
1561 sp->sst_next_flags = current_next_flags;
1562 sp->sst_next_list = current_next_list;
1563 sp->sst_tick = display_tick;
1564 sp->sst_change_lnum = 0;
1565 }
1566 current_state_stored = TRUE;
1567 return sp;
1568}
1569
1570/*
1571 * Copy a state stack from "from" in b_sst_array[] to current_state;
1572 */
1573 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001574load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575{
1576 int i;
1577 bufstate_T *bp;
1578
1579 clear_current_state();
1580 validate_current_state();
1581 keepend_level = -1;
1582 if (from->sst_stacksize
1583 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1584 {
1585 if (from->sst_stacksize > SST_FIX_STATES)
1586 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1587 else
1588 bp = from->sst_union.sst_stack;
1589 for (i = 0; i < from->sst_stacksize; ++i)
1590 {
1591 CUR_STATE(i).si_idx = bp[i].bs_idx;
1592 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001593#ifdef FEAT_CONCEAL
1594 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1595 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1596#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1598 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1599 keepend_level = i;
1600 CUR_STATE(i).si_ends = FALSE;
1601 CUR_STATE(i).si_m_lnum = 0;
1602 if (CUR_STATE(i).si_idx >= 0)
1603 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001604 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001605 else
1606 CUR_STATE(i).si_next_list = NULL;
1607 update_si_attr(i);
1608 }
1609 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610 }
1611 current_next_list = from->sst_next_list;
1612 current_next_flags = from->sst_next_flags;
1613 current_lnum = from->sst_lnum;
1614}
1615
1616/*
1617 * Compare saved state stack "*sp" with the current state.
1618 * Return TRUE when they are equal.
1619 */
1620 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001621syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622{
1623 int i, j;
1624 bufstate_T *bp;
1625 reg_extmatch_T *six, *bsx;
1626
1627 /* First a quick check if the stacks have the same size end nextlist. */
1628 if (sp->sst_stacksize == current_state.ga_len
1629 && sp->sst_next_list == current_next_list)
1630 {
1631 /* Need to compare all states on both stacks. */
1632 if (sp->sst_stacksize > SST_FIX_STATES)
1633 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1634 else
1635 bp = sp->sst_union.sst_stack;
1636
1637 for (i = current_state.ga_len; --i >= 0; )
1638 {
1639 /* If the item has another index the state is different. */
1640 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1641 break;
1642 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1643 {
1644 /* When the extmatch pointers are different, the strings in
1645 * them can still be the same. Check if the extmatch
1646 * references are equal. */
1647 bsx = bp[i].bs_extmatch;
1648 six = CUR_STATE(i).si_extmatch;
1649 /* If one of the extmatch pointers is NULL the states are
1650 * different. */
1651 if (bsx == NULL || six == NULL)
1652 break;
1653 for (j = 0; j < NSUBEXP; ++j)
1654 {
1655 /* Check each referenced match string. They must all be
1656 * equal. */
1657 if (bsx->matches[j] != six->matches[j])
1658 {
1659 /* If the pointer is different it can still be the
1660 * same text. Compare the strings, ignore case when
1661 * the start item has the sp_ic flag set. */
1662 if (bsx->matches[j] == NULL
1663 || six->matches[j] == NULL)
1664 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001665 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 ? MB_STRICMP(bsx->matches[j],
1667 six->matches[j]) != 0
1668 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1669 break;
1670 }
1671 }
1672 if (j != NSUBEXP)
1673 break;
1674 }
1675 }
1676 if (i < 0)
1677 return TRUE;
1678 }
1679 return FALSE;
1680}
1681
1682/*
1683 * We stop parsing syntax above line "lnum". If the stored state at or below
1684 * this line depended on a change before it, it now depends on the line below
1685 * the last parsed line.
1686 * The window looks like this:
1687 * line which changed
1688 * displayed line
1689 * displayed line
1690 * lnum -> line below window
1691 */
1692 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001693syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694{
1695 synstate_T *sp;
1696
1697 sp = syn_stack_find_entry(lnum);
1698 if (sp != NULL && sp->sst_lnum < lnum)
1699 sp = sp->sst_next;
1700
1701 if (sp != NULL && sp->sst_change_lnum != 0)
1702 sp->sst_change_lnum = lnum;
1703}
1704
1705/*
1706 * End of handling of the state stack.
1707 ****************************************/
1708
1709 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001710invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711{
1712 clear_current_state();
1713 current_state.ga_itemsize = 0; /* mark current_state invalid */
1714 current_next_list = NULL;
1715 keepend_level = -1;
1716}
1717
1718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001719validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720{
1721 current_state.ga_itemsize = sizeof(stateitem_T);
1722 current_state.ga_growsize = 3;
1723}
1724
1725/*
1726 * Return TRUE if the syntax at start of lnum changed since last time.
1727 * This will only be called just after get_syntax_attr() for the previous
1728 * line, to check if the next line needs to be redrawn too.
1729 */
1730 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001731syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732{
1733 int retval = TRUE;
1734 synstate_T *sp;
1735
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 /*
1737 * Check the state stack when:
1738 * - lnum is just below the previously syntaxed line.
1739 * - lnum is not before the lines with saved states.
1740 * - lnum is not past the lines with saved states.
1741 * - lnum is at or before the last changed line.
1742 */
1743 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1744 {
1745 sp = syn_stack_find_entry(lnum);
1746 if (sp != NULL && sp->sst_lnum == lnum)
1747 {
1748 /*
1749 * finish the previous line (needed when not all of the line was
1750 * drawn)
1751 */
1752 (void)syn_finish_line(FALSE);
1753
1754 /*
1755 * Compare the current state with the previously saved state of
1756 * the line.
1757 */
1758 if (syn_stack_equal(sp))
1759 retval = FALSE;
1760
1761 /*
1762 * Store the current state in b_sst_array[] for later use.
1763 */
1764 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001765 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 }
1767 }
1768
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 return retval;
1770}
1771
1772/*
1773 * Finish the current line.
1774 * This doesn't return any attributes, it only gets the state at the end of
1775 * the line. It can start anywhere in the line, as long as the current state
1776 * is valid.
1777 */
1778 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001779syn_finish_line(
1780 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781{
1782 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001783 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001785 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001787 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1788 /*
1789 * When syncing, and found some item, need to check the item.
1790 */
1791 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001792 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001794 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001796 cur_si = &CUR_STATE(current_state.ga_len - 1);
1797 if (cur_si->si_idx >= 0
1798 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1799 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1800 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001802 /* syn_current_attr() will have skipped the check for an item
1803 * that ends here, need to do that now. Be careful not to go
1804 * past the NUL. */
1805 prev_current_col = current_col;
1806 if (syn_getcurline()[current_col] != NUL)
1807 ++current_col;
1808 check_state_ends();
1809 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001811 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 }
1813 return FALSE;
1814}
1815
1816/*
1817 * Return highlight attributes for next character.
1818 * Must first call syntax_start() once for the line.
1819 * "col" is normally 0 for the first use in a line, and increments by one each
1820 * time. It's allowed to skip characters and to stop before the end of the
1821 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001822 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1823 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824 */
1825 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001826get_syntax_attr(
1827 colnr_T col,
1828 int *can_spell,
1829 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830{
1831 int attr = 0;
1832
Bram Moolenaar349955a2007-08-14 21:07:36 +00001833 if (can_spell != NULL)
1834 /* Default: Only do spelling when there is no @Spell cluster or when
1835 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001836 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1837 ? (syn_block->b_spell_cluster_id == 0)
1838 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001839
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001841 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 return 0;
1843
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001844 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001845 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001846 {
1847 clear_current_state();
1848#ifdef FEAT_EVAL
1849 current_id = 0;
1850 current_trans_id = 0;
1851#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001852#ifdef FEAT_CONCEAL
1853 current_flags = 0;
1854#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001855 return 0;
1856 }
1857
Bram Moolenaar071d4272004-06-13 20:20:40 +00001858 /* Make sure current_state is valid */
1859 if (INVALID_STATE(&current_state))
1860 validate_current_state();
1861
1862 /*
1863 * Skip from the current column to "col", get the attributes for "col".
1864 */
1865 while (current_col <= col)
1866 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001867 attr = syn_current_attr(FALSE, TRUE, can_spell,
1868 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 ++current_col;
1870 }
1871
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 return attr;
1873}
1874
1875/*
1876 * Get syntax attributes for current_lnum, current_col.
1877 */
1878 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001879syn_current_attr(
1880 int syncing, /* When 1: called for syncing */
1881 int displaying, /* result will be displayed */
1882 int *can_spell, /* return: do spell checking */
1883 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001884{
1885 int syn_id;
1886 lpos_T endpos; /* was: char_u *endp; */
1887 lpos_T hl_startpos; /* was: int hl_startcol; */
1888 lpos_T hl_endpos;
1889 lpos_T eos_pos; /* end-of-start match (start region) */
1890 lpos_T eoe_pos; /* end-of-end pattern */
1891 int end_idx; /* group ID for end pattern */
1892 int idx;
1893 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001894 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895 int startcol;
1896 int endcol;
1897 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001898 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001899 short *next_list;
1900 int found_match; /* found usable match */
1901 static int try_next_column = FALSE; /* must try in next col */
1902 int do_keywords;
1903 regmmatch_T regmatch;
1904 lpos_T pos;
1905 int lc_col;
1906 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001907 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001908 char_u *line; /* current line. NOTE: becomes invalid after
1909 looking for a pattern match! */
1910
1911 /* variables for zero-width matches that have a "nextgroup" argument */
1912 int keep_next_list;
1913 int zero_width_next_list = FALSE;
1914 garray_T zero_width_next_ga;
1915
1916 /*
1917 * No character, no attributes! Past end of line?
1918 * Do try matching with an empty line (could be the start of a region).
1919 */
1920 line = syn_getcurline();
1921 if (line[current_col] == NUL && current_col != 0)
1922 {
1923 /*
1924 * If we found a match after the last column, use it.
1925 */
1926 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1927 && next_match_col != MAXCOL)
1928 (void)push_next_match(NULL);
1929
1930 current_finished = TRUE;
1931 current_state_stored = FALSE;
1932 return 0;
1933 }
1934
1935 /* if the current or next character is NUL, we will finish the line now */
1936 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1937 {
1938 current_finished = TRUE;
1939 current_state_stored = FALSE;
1940 }
1941
1942 /*
1943 * When in the previous column there was a match but it could not be used
1944 * (empty match or already matched in this column) need to try again in
1945 * the next column.
1946 */
1947 if (try_next_column)
1948 {
1949 next_match_idx = -1;
1950 try_next_column = FALSE;
1951 }
1952
1953 /* Only check for keywords when not syncing and there are some. */
1954 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001955 && (syn_block->b_keywtab.ht_used > 0
1956 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001957
1958 /* Init the list of zero-width matches with a nextlist. This is used to
1959 * avoid matching the same item in the same position twice. */
1960 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1961
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001962 /* use syntax iskeyword option */
1963 save_chartab(buf_chartab);
1964
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965 /*
1966 * Repeat matching keywords and patterns, to find contained items at the
1967 * same column. This stops when there are no extra matches at the current
1968 * column.
1969 */
1970 do
1971 {
1972 found_match = FALSE;
1973 keep_next_list = FALSE;
1974 syn_id = 0;
1975
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001976
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977 /*
1978 * 1. Check for a current state.
1979 * Only when there is no current state, or if the current state may
1980 * contain other things, we need to check for keywords and patterns.
1981 * Always need to check for contained items if some item has the
1982 * "containedin" argument (takes extra time!).
1983 */
1984 if (current_state.ga_len)
1985 cur_si = &CUR_STATE(current_state.ga_len - 1);
1986 else
1987 cur_si = NULL;
1988
Bram Moolenaar860cae12010-06-05 23:22:07 +02001989 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001990 || cur_si->si_cont_list != NULL)
1991 {
1992 /*
1993 * 2. Check for keywords, if on a keyword char after a non-keyword
1994 * char. Don't do this when syncing.
1995 */
1996 if (do_keywords)
1997 {
1998 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001999 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002000 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002001 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002002#ifdef FEAT_MBYTE
2003 - (has_mbyte
2004 ? (*mb_head_off)(line, line + current_col - 1)
2005 : 0)
2006#endif
2007 , syn_buf)))
2008 {
2009 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002010 &endcol, &flags, &next_list, cur_si,
2011 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002012 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013 {
2014 if (push_current_state(KEYWORD_IDX) == OK)
2015 {
2016 cur_si = &CUR_STATE(current_state.ga_len - 1);
2017 cur_si->si_m_startcol = current_col;
2018 cur_si->si_h_startpos.lnum = current_lnum;
2019 cur_si->si_h_startpos.col = 0; /* starts right away */
2020 cur_si->si_m_endpos.lnum = current_lnum;
2021 cur_si->si_m_endpos.col = endcol;
2022 cur_si->si_h_endpos.lnum = current_lnum;
2023 cur_si->si_h_endpos.col = endcol;
2024 cur_si->si_ends = TRUE;
2025 cur_si->si_end_idx = 0;
2026 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002027#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002028 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002029 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002030 if (current_state.ga_len > 1)
2031 cur_si->si_flags |=
2032 CUR_STATE(current_state.ga_len - 2).si_flags
2033 & HL_CONCEAL;
2034#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 cur_si->si_id = syn_id;
2036 cur_si->si_trans_id = syn_id;
2037 if (flags & HL_TRANSP)
2038 {
2039 if (current_state.ga_len < 2)
2040 {
2041 cur_si->si_attr = 0;
2042 cur_si->si_trans_id = 0;
2043 }
2044 else
2045 {
2046 cur_si->si_attr = CUR_STATE(
2047 current_state.ga_len - 2).si_attr;
2048 cur_si->si_trans_id = CUR_STATE(
2049 current_state.ga_len - 2).si_trans_id;
2050 }
2051 }
2052 else
2053 cur_si->si_attr = syn_id2attr(syn_id);
2054 cur_si->si_cont_list = NULL;
2055 cur_si->si_next_list = next_list;
2056 check_keepend();
2057 }
2058 else
2059 vim_free(next_list);
2060 }
2061 }
2062 }
2063
2064 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002065 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002067 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 {
2069 /*
2070 * If we didn't check for a match yet, or we are past it, check
2071 * for any match with a pattern.
2072 */
2073 if (next_match_idx < 0 || next_match_col < (int)current_col)
2074 {
2075 /*
2076 * Check all relevant patterns for a match at this
2077 * position. This is complicated, because matching with a
2078 * pattern takes quite a bit of time, thus we want to
2079 * avoid doing it when it's not needed.
2080 */
2081 next_match_idx = 0; /* no match in this line yet */
2082 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002083 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002085 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 if ( spp->sp_syncing == syncing
2087 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2088 && (spp->sp_type == SPTYPE_MATCH
2089 || spp->sp_type == SPTYPE_START)
2090 && (current_next_list != NULL
2091 ? in_id_list(NULL, current_next_list,
2092 &spp->sp_syn, 0)
2093 : (cur_si == NULL
2094 ? !(spp->sp_flags & HL_CONTAINED)
2095 : in_id_list(cur_si,
2096 cur_si->si_cont_list, &spp->sp_syn,
2097 spp->sp_flags & HL_CONTAINED))))
2098 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002099 int r;
2100
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 /* If we already tried matching in this line, and
2102 * there isn't a match before next_match_col, skip
2103 * this item. */
2104 if (spp->sp_line_id == current_line_id
2105 && spp->sp_startcol >= next_match_col)
2106 continue;
2107 spp->sp_line_id = current_line_id;
2108
2109 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2110 if (lc_col < 0)
2111 lc_col = 0;
2112
2113 regmatch.rmm_ic = spp->sp_ic;
2114 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002115 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002116 current_lnum,
2117 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002118 IF_SYN_TIME(&spp->sp_time));
2119 spp->sp_prog = regmatch.regprog;
2120 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121 {
2122 /* no match in this line, try another one */
2123 spp->sp_startcol = MAXCOL;
2124 continue;
2125 }
2126
2127 /*
2128 * Compute the first column of the match.
2129 */
2130 syn_add_start_off(&pos, &regmatch,
2131 spp, SPO_MS_OFF, -1);
2132 if (pos.lnum > current_lnum)
2133 {
2134 /* must have used end of match in a next line,
2135 * we can't handle that */
2136 spp->sp_startcol = MAXCOL;
2137 continue;
2138 }
2139 startcol = pos.col;
2140
2141 /* remember the next column where this pattern
2142 * matches in the current line */
2143 spp->sp_startcol = startcol;
2144
2145 /*
2146 * If a previously found match starts at a lower
2147 * column number, don't use this one.
2148 */
2149 if (startcol >= next_match_col)
2150 continue;
2151
2152 /*
2153 * If we matched this pattern at this position
2154 * before, skip it. Must retry in the next
2155 * column, because it may match from there.
2156 */
2157 if (did_match_already(idx, &zero_width_next_ga))
2158 {
2159 try_next_column = TRUE;
2160 continue;
2161 }
2162
2163 endpos.lnum = regmatch.endpos[0].lnum;
2164 endpos.col = regmatch.endpos[0].col;
2165
2166 /* Compute the highlight start. */
2167 syn_add_start_off(&hl_startpos, &regmatch,
2168 spp, SPO_HS_OFF, -1);
2169
2170 /* Compute the region start. */
2171 /* Default is to use the end of the match. */
2172 syn_add_end_off(&eos_pos, &regmatch,
2173 spp, SPO_RS_OFF, 0);
2174
2175 /*
2176 * Grab the external submatches before they get
2177 * overwritten. Reference count doesn't change.
2178 */
2179 unref_extmatch(cur_extmatch);
2180 cur_extmatch = re_extmatch_out;
2181 re_extmatch_out = NULL;
2182
2183 flags = 0;
2184 eoe_pos.lnum = 0; /* avoid warning */
2185 eoe_pos.col = 0;
2186 end_idx = 0;
2187 hl_endpos.lnum = 0;
2188
2189 /*
2190 * For a "oneline" the end must be found in the
2191 * same line too. Search for it after the end of
2192 * the match with the start pattern. Set the
2193 * resulting end positions at the same time.
2194 */
2195 if (spp->sp_type == SPTYPE_START
2196 && (spp->sp_flags & HL_ONELINE))
2197 {
2198 lpos_T startpos;
2199
2200 startpos = endpos;
2201 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2202 &flags, &eoe_pos, &end_idx, cur_extmatch);
2203 if (endpos.lnum == 0)
2204 continue; /* not found */
2205 }
2206
2207 /*
2208 * For a "match" the size must be > 0 after the
2209 * end offset needs has been added. Except when
2210 * syncing.
2211 */
2212 else if (spp->sp_type == SPTYPE_MATCH)
2213 {
2214 syn_add_end_off(&hl_endpos, &regmatch, spp,
2215 SPO_HE_OFF, 0);
2216 syn_add_end_off(&endpos, &regmatch, spp,
2217 SPO_ME_OFF, 0);
2218 if (endpos.lnum == current_lnum
2219 && (int)endpos.col + syncing < startcol)
2220 {
2221 /*
2222 * If an empty string is matched, may need
2223 * to try matching again at next column.
2224 */
2225 if (regmatch.startpos[0].col
2226 == regmatch.endpos[0].col)
2227 try_next_column = TRUE;
2228 continue;
2229 }
2230 }
2231
2232 /*
2233 * keep the best match so far in next_match_*
2234 */
2235 /* Highlighting must start after startpos and end
2236 * before endpos. */
2237 if (hl_startpos.lnum == current_lnum
2238 && (int)hl_startpos.col < startcol)
2239 hl_startpos.col = startcol;
2240 limit_pos_zero(&hl_endpos, &endpos);
2241
2242 next_match_idx = idx;
2243 next_match_col = startcol;
2244 next_match_m_endpos = endpos;
2245 next_match_h_endpos = hl_endpos;
2246 next_match_h_startpos = hl_startpos;
2247 next_match_flags = flags;
2248 next_match_eos_pos = eos_pos;
2249 next_match_eoe_pos = eoe_pos;
2250 next_match_end_idx = end_idx;
2251 unref_extmatch(next_match_extmatch);
2252 next_match_extmatch = cur_extmatch;
2253 cur_extmatch = NULL;
2254 }
2255 }
2256 }
2257
2258 /*
2259 * If we found a match at the current column, use it.
2260 */
2261 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2262 {
2263 synpat_T *lspp;
2264
2265 /* When a zero-width item matched which has a nextgroup,
2266 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002267 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 if (next_match_m_endpos.lnum == current_lnum
2269 && next_match_m_endpos.col == current_col
2270 && lspp->sp_next_list != NULL)
2271 {
2272 current_next_list = lspp->sp_next_list;
2273 current_next_flags = lspp->sp_flags;
2274 keep_next_list = TRUE;
2275 zero_width_next_list = TRUE;
2276
2277 /* Add the index to a list, so that we can check
2278 * later that we don't match it again (and cause an
2279 * endless loop). */
2280 if (ga_grow(&zero_width_next_ga, 1) == OK)
2281 {
2282 ((int *)(zero_width_next_ga.ga_data))
2283 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284 }
2285 next_match_idx = -1;
2286 }
2287 else
2288 cur_si = push_next_match(cur_si);
2289 found_match = TRUE;
2290 }
2291 }
2292 }
2293
2294 /*
2295 * Handle searching for nextgroup match.
2296 */
2297 if (current_next_list != NULL && !keep_next_list)
2298 {
2299 /*
2300 * If a nextgroup was not found, continue looking for one if:
2301 * - this is an empty line and the "skipempty" option was given
2302 * - we are on white space and the "skipwhite" option was given
2303 */
2304 if (!found_match)
2305 {
2306 line = syn_getcurline();
2307 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002308 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002309 || ((current_next_flags & HL_SKIPEMPTY)
2310 && *line == NUL))
2311 break;
2312 }
2313
2314 /*
2315 * If a nextgroup was found: Use it, and continue looking for
2316 * contained matches.
2317 * If a nextgroup was not found: Continue looking for a normal
2318 * match.
2319 * When did set current_next_list for a zero-width item and no
2320 * match was found don't loop (would get stuck).
2321 */
2322 current_next_list = NULL;
2323 next_match_idx = -1;
2324 if (!zero_width_next_list)
2325 found_match = TRUE;
2326 }
2327
2328 } while (found_match);
2329
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002330 restore_chartab(buf_chartab);
2331
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 /*
2333 * Use attributes from the current state, if within its highlighting.
2334 * If not, use attributes from the current-but-one state, etc.
2335 */
2336 current_attr = 0;
2337#ifdef FEAT_EVAL
2338 current_id = 0;
2339 current_trans_id = 0;
2340#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002341#ifdef FEAT_CONCEAL
2342 current_flags = 0;
2343#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344 if (cur_si != NULL)
2345 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002346#ifndef FEAT_EVAL
2347 int current_trans_id = 0;
2348#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2350 {
2351 sip = &CUR_STATE(idx);
2352 if ((current_lnum > sip->si_h_startpos.lnum
2353 || (current_lnum == sip->si_h_startpos.lnum
2354 && current_col >= sip->si_h_startpos.col))
2355 && (sip->si_h_endpos.lnum == 0
2356 || current_lnum < sip->si_h_endpos.lnum
2357 || (current_lnum == sip->si_h_endpos.lnum
2358 && current_col < sip->si_h_endpos.col)))
2359 {
2360 current_attr = sip->si_attr;
2361#ifdef FEAT_EVAL
2362 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002364 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002365#ifdef FEAT_CONCEAL
2366 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002367 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002368 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002370 break;
2371 }
2372 }
2373
Bram Moolenaar217ad922005-03-20 22:37:15 +00002374 if (can_spell != NULL)
2375 {
2376 struct sp_syn sps;
2377
2378 /*
2379 * set "can_spell" to TRUE if spell checking is supposed to be
2380 * done in the current item.
2381 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002382 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002383 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002384 /* There is no @Spell cluster: Do spelling for items without
2385 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002386 if (syn_block->b_nospell_cluster_id == 0
2387 || current_trans_id == 0)
2388 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002389 else
2390 {
2391 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002392 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002393 sps.cont_in_list = NULL;
2394 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2395 }
2396 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002397 else
2398 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002399 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002400 * the @Spell cluster. But not when @NoSpell is also there.
2401 * At the toplevel only spell check when ":syn spell toplevel"
2402 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002403 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002404 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002405 else
2406 {
2407 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002408 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002409 sps.cont_in_list = NULL;
2410 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2411
Bram Moolenaar860cae12010-06-05 23:22:07 +02002412 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002413 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002414 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002415 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2416 *can_spell = FALSE;
2417 }
2418 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002419 }
2420 }
2421
2422
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 /*
2424 * Check for end of current state (and the states before it) at the
2425 * next column. Don't do this for syncing, because we would miss a
2426 * single character match.
2427 * First check if the current state ends at the current column. It
2428 * may be for an empty match and a containing item might end in the
2429 * current column.
2430 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002431 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002432 {
2433 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002434 if (current_state.ga_len > 0
2435 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002436 {
2437 ++current_col;
2438 check_state_ends();
2439 --current_col;
2440 }
2441 }
2442 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002443 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002444 /* Default: Only do spelling when there is no @Spell cluster or when
2445 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002446 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2447 ? (syn_block->b_spell_cluster_id == 0)
2448 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002449
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002450 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 if (current_next_list != NULL
2452 && syn_getcurline()[current_col + 1] == NUL
2453 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2454 current_next_list = NULL;
2455
2456 if (zero_width_next_ga.ga_len > 0)
2457 ga_clear(&zero_width_next_ga);
2458
2459 /* No longer need external matches. But keep next_match_extmatch. */
2460 unref_extmatch(re_extmatch_out);
2461 re_extmatch_out = NULL;
2462 unref_extmatch(cur_extmatch);
2463
2464 return current_attr;
2465}
2466
2467
2468/*
2469 * Check if we already matched pattern "idx" at the current column.
2470 */
2471 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002472did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473{
2474 int i;
2475
2476 for (i = current_state.ga_len; --i >= 0; )
2477 if (CUR_STATE(i).si_m_startcol == (int)current_col
2478 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2479 && CUR_STATE(i).si_idx == idx)
2480 return TRUE;
2481
2482 /* Zero-width matches with a nextgroup argument are not put on the syntax
2483 * stack, and can only be matched once anyway. */
2484 for (i = gap->ga_len; --i >= 0; )
2485 if (((int *)(gap->ga_data))[i] == idx)
2486 return TRUE;
2487
2488 return FALSE;
2489}
2490
2491/*
2492 * Push the next match onto the stack.
2493 */
2494 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002495push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002496{
2497 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002498#ifdef FEAT_CONCEAL
2499 int save_flags;
2500#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002501
Bram Moolenaar860cae12010-06-05 23:22:07 +02002502 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503
2504 /*
2505 * Push the item in current_state stack;
2506 */
2507 if (push_current_state(next_match_idx) == OK)
2508 {
2509 /*
2510 * If it's a start-skip-end type that crosses lines, figure out how
2511 * much it continues in this line. Otherwise just fill in the length.
2512 */
2513 cur_si = &CUR_STATE(current_state.ga_len - 1);
2514 cur_si->si_h_startpos = next_match_h_startpos;
2515 cur_si->si_m_startcol = current_col;
2516 cur_si->si_m_lnum = current_lnum;
2517 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002518#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002519 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002520 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002521 if (current_state.ga_len > 1)
2522 cur_si->si_flags |=
2523 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2524#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002525 cur_si->si_next_list = spp->sp_next_list;
2526 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2527 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2528 {
2529 /* Try to find the end pattern in the current line */
2530 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2531 check_keepend();
2532 }
2533 else
2534 {
2535 cur_si->si_m_endpos = next_match_m_endpos;
2536 cur_si->si_h_endpos = next_match_h_endpos;
2537 cur_si->si_ends = TRUE;
2538 cur_si->si_flags |= next_match_flags;
2539 cur_si->si_eoe_pos = next_match_eoe_pos;
2540 cur_si->si_end_idx = next_match_end_idx;
2541 }
2542 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2543 keepend_level = current_state.ga_len - 1;
2544 check_keepend();
2545 update_si_attr(current_state.ga_len - 1);
2546
Bram Moolenaar860cae12010-06-05 23:22:07 +02002547#ifdef FEAT_CONCEAL
2548 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2549#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550 /*
2551 * If the start pattern has another highlight group, push another item
2552 * on the stack for the start pattern.
2553 */
2554 if ( spp->sp_type == SPTYPE_START
2555 && spp->sp_syn_match_id != 0
2556 && push_current_state(next_match_idx) == OK)
2557 {
2558 cur_si = &CUR_STATE(current_state.ga_len - 1);
2559 cur_si->si_h_startpos = next_match_h_startpos;
2560 cur_si->si_m_startcol = current_col;
2561 cur_si->si_m_lnum = current_lnum;
2562 cur_si->si_m_endpos = next_match_eos_pos;
2563 cur_si->si_h_endpos = next_match_eos_pos;
2564 cur_si->si_ends = TRUE;
2565 cur_si->si_end_idx = 0;
2566 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002567#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002568 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002569 cur_si->si_flags |= save_flags;
2570 if (cur_si->si_flags & HL_CONCEALENDS)
2571 cur_si->si_flags |= HL_CONCEAL;
2572#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573 cur_si->si_next_list = NULL;
2574 check_keepend();
2575 update_si_attr(current_state.ga_len - 1);
2576 }
2577 }
2578
2579 next_match_idx = -1; /* try other match next time */
2580
2581 return cur_si;
2582}
2583
2584/*
2585 * Check for end of current state (and the states before it).
2586 */
2587 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002588check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589{
2590 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002591 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592
2593 cur_si = &CUR_STATE(current_state.ga_len - 1);
2594 for (;;)
2595 {
2596 if (cur_si->si_ends
2597 && (cur_si->si_m_endpos.lnum < current_lnum
2598 || (cur_si->si_m_endpos.lnum == current_lnum
2599 && cur_si->si_m_endpos.col <= current_col)))
2600 {
2601 /*
2602 * If there is an end pattern group ID, highlight the end pattern
2603 * now. No need to pop the current item from the stack.
2604 * Only do this if the end pattern continues beyond the current
2605 * position.
2606 */
2607 if (cur_si->si_end_idx
2608 && (cur_si->si_eoe_pos.lnum > current_lnum
2609 || (cur_si->si_eoe_pos.lnum == current_lnum
2610 && cur_si->si_eoe_pos.col > current_col)))
2611 {
2612 cur_si->si_idx = cur_si->si_end_idx;
2613 cur_si->si_end_idx = 0;
2614 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2615 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2616 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002617#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002618 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002619 if (cur_si->si_flags & HL_CONCEALENDS)
2620 cur_si->si_flags |= HL_CONCEAL;
2621#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002622 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002623
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002624 /* nextgroup= should not match in the end pattern */
2625 current_next_list = NULL;
2626
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002627 /* what matches next may be different now, clear it */
2628 next_match_idx = 0;
2629 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 break;
2631 }
2632 else
2633 {
2634 /* handle next_list, unless at end of line and no "skipnl" or
2635 * "skipempty" */
2636 current_next_list = cur_si->si_next_list;
2637 current_next_flags = cur_si->si_flags;
2638 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2639 && syn_getcurline()[current_col] == NUL)
2640 current_next_list = NULL;
2641
2642 /* When the ended item has "extend", another item with
2643 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002644 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645
2646 pop_current_state();
2647
2648 if (current_state.ga_len == 0)
2649 break;
2650
Bram Moolenaar81993f42008-01-11 20:27:45 +00002651 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002652 {
2653 syn_update_ends(FALSE);
2654 if (current_state.ga_len == 0)
2655 break;
2656 }
2657
2658 cur_si = &CUR_STATE(current_state.ga_len - 1);
2659
2660 /*
2661 * Only for a region the search for the end continues after
2662 * the end of the contained item. If the contained match
2663 * included the end-of-line, break here, the region continues.
2664 * Don't do this when:
2665 * - "keepend" is used for the contained item
2666 * - not at the end of the line (could be end="x$"me=e-1).
2667 * - "excludenl" is used (HL_HAS_EOL won't be set)
2668 */
2669 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002670 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671 == SPTYPE_START
2672 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2673 {
2674 update_si_end(cur_si, (int)current_col, TRUE);
2675 check_keepend();
2676 if ((current_next_flags & HL_HAS_EOL)
2677 && keepend_level < 0
2678 && syn_getcurline()[current_col] == NUL)
2679 break;
2680 }
2681 }
2682 }
2683 else
2684 break;
2685 }
2686}
2687
2688/*
2689 * Update an entry in the current_state stack for a match or region. This
2690 * fills in si_attr, si_next_list and si_cont_list.
2691 */
2692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002693update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002694{
2695 stateitem_T *sip = &CUR_STATE(idx);
2696 synpat_T *spp;
2697
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002698 /* This should not happen... */
2699 if (sip->si_idx < 0)
2700 return;
2701
Bram Moolenaar860cae12010-06-05 23:22:07 +02002702 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 if (sip->si_flags & HL_MATCH)
2704 sip->si_id = spp->sp_syn_match_id;
2705 else
2706 sip->si_id = spp->sp_syn.id;
2707 sip->si_attr = syn_id2attr(sip->si_id);
2708 sip->si_trans_id = sip->si_id;
2709 if (sip->si_flags & HL_MATCH)
2710 sip->si_cont_list = NULL;
2711 else
2712 sip->si_cont_list = spp->sp_cont_list;
2713
2714 /*
2715 * For transparent items, take attr from outer item.
2716 * Also take cont_list, if there is none.
2717 * Don't do this for the matchgroup of a start or end pattern.
2718 */
2719 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2720 {
2721 if (idx == 0)
2722 {
2723 sip->si_attr = 0;
2724 sip->si_trans_id = 0;
2725 if (sip->si_cont_list == NULL)
2726 sip->si_cont_list = ID_LIST_ALL;
2727 }
2728 else
2729 {
2730 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2731 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002732 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2733 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002734 if (sip->si_cont_list == NULL)
2735 {
2736 sip->si_flags |= HL_TRANS_CONT;
2737 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2738 }
2739 }
2740 }
2741}
2742
2743/*
2744 * Check the current stack for patterns with "keepend" flag.
2745 * Propagate the match-end to contained items, until a "skipend" item is found.
2746 */
2747 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002748check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749{
2750 int i;
2751 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002752 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002753 stateitem_T *sip;
2754
2755 /*
2756 * This check can consume a lot of time; only do it from the level where
2757 * there really is a keepend.
2758 */
2759 if (keepend_level < 0)
2760 return;
2761
2762 /*
2763 * Find the last index of an "extend" item. "keepend" items before that
2764 * won't do anything. If there is no "extend" item "i" will be
2765 * "keepend_level" and all "keepend" items will work normally.
2766 */
2767 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2768 if (CUR_STATE(i).si_flags & HL_EXTEND)
2769 break;
2770
2771 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002772 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002773 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002774 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 for ( ; i < current_state.ga_len; ++i)
2776 {
2777 sip = &CUR_STATE(i);
2778 if (maxpos.lnum != 0)
2779 {
2780 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002781 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002782 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2783 sip->si_ends = TRUE;
2784 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002785 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2786 {
2787 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788 || maxpos.lnum > sip->si_m_endpos.lnum
2789 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002790 && maxpos.col > sip->si_m_endpos.col))
2791 maxpos = sip->si_m_endpos;
2792 if (maxpos_h.lnum == 0
2793 || maxpos_h.lnum > sip->si_h_endpos.lnum
2794 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2795 && maxpos_h.col > sip->si_h_endpos.col))
2796 maxpos_h = sip->si_h_endpos;
2797 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002798 }
2799}
2800
2801/*
2802 * Update an entry in the current_state stack for a start-skip-end pattern.
2803 * This finds the end of the current item, if it's in the current line.
2804 *
2805 * Return the flags for the matched END.
2806 */
2807 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002808update_si_end(
2809 stateitem_T *sip,
2810 int startcol, /* where to start searching for the end */
2811 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002812{
2813 lpos_T startpos;
2814 lpos_T endpos;
2815 lpos_T hl_endpos;
2816 lpos_T end_endpos;
2817 int end_idx;
2818
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002819 /* return quickly for a keyword */
2820 if (sip->si_idx < 0)
2821 return;
2822
Bram Moolenaar071d4272004-06-13 20:20:40 +00002823 /* Don't update when it's already done. Can be a match of an end pattern
2824 * that started in a previous line. Watch out: can also be a "keepend"
2825 * from a containing item. */
2826 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2827 return;
2828
2829 /*
2830 * We need to find the end of the region. It may continue in the next
2831 * line.
2832 */
2833 end_idx = 0;
2834 startpos.lnum = current_lnum;
2835 startpos.col = startcol;
2836 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2837 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2838
2839 if (endpos.lnum == 0)
2840 {
2841 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002842 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 {
2844 /* a "oneline" never continues in the next line */
2845 sip->si_ends = TRUE;
2846 sip->si_m_endpos.lnum = current_lnum;
2847 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2848 }
2849 else
2850 {
2851 /* continues in the next line */
2852 sip->si_ends = FALSE;
2853 sip->si_m_endpos.lnum = 0;
2854 }
2855 sip->si_h_endpos = sip->si_m_endpos;
2856 }
2857 else
2858 {
2859 /* match within this line */
2860 sip->si_m_endpos = endpos;
2861 sip->si_h_endpos = hl_endpos;
2862 sip->si_eoe_pos = end_endpos;
2863 sip->si_ends = TRUE;
2864 sip->si_end_idx = end_idx;
2865 }
2866}
2867
2868/*
2869 * Add a new state to the current state stack.
2870 * It is cleared and the index set to "idx".
2871 * Return FAIL if it's not possible (out of memory).
2872 */
2873 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002874push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875{
2876 if (ga_grow(&current_state, 1) == FAIL)
2877 return FAIL;
2878 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2879 CUR_STATE(current_state.ga_len).si_idx = idx;
2880 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002881 return OK;
2882}
2883
2884/*
2885 * Remove a state from the current_state stack.
2886 */
2887 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002888pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889{
2890 if (current_state.ga_len)
2891 {
2892 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2893 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002894 }
2895 /* after the end of a pattern, try matching a keyword or pattern */
2896 next_match_idx = -1;
2897
2898 /* if first state with "keepend" is popped, reset keepend_level */
2899 if (keepend_level >= current_state.ga_len)
2900 keepend_level = -1;
2901}
2902
2903/*
2904 * Find the end of a start/skip/end syntax region after "startpos".
2905 * Only checks one line.
2906 * Also handles a match item that continued from a previous line.
2907 * If not found, the syntax item continues in the next line. m_endpos->lnum
2908 * will be 0.
2909 * If found, the end of the region and the end of the highlighting is
2910 * computed.
2911 */
2912 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002913find_endpos(
2914 int idx, /* index of the pattern */
2915 lpos_T *startpos, /* where to start looking for an END match */
2916 lpos_T *m_endpos, /* return: end of match */
2917 lpos_T *hl_endpos, /* return: end of highlighting */
2918 long *flagsp, /* return: flags of matching END */
2919 lpos_T *end_endpos, /* return: end of end pattern match */
2920 int *end_idx, /* return: group ID for end pat. match, or 0 */
2921 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922{
2923 colnr_T matchcol;
2924 synpat_T *spp, *spp_skip;
2925 int start_idx;
2926 int best_idx;
2927 regmmatch_T regmatch;
2928 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2929 lpos_T pos;
2930 char_u *line;
2931 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002932 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002934 /* just in case we are invoked for a keyword */
2935 if (idx < 0)
2936 return;
2937
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 /*
2939 * Check for being called with a START pattern.
2940 * Can happen with a match that continues to the next line, because it
2941 * contained a region.
2942 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002943 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944 if (spp->sp_type != SPTYPE_START)
2945 {
2946 *hl_endpos = *startpos;
2947 return;
2948 }
2949
2950 /*
2951 * Find the SKIP or first END pattern after the last START pattern.
2952 */
2953 for (;;)
2954 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002955 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002956 if (spp->sp_type != SPTYPE_START)
2957 break;
2958 ++idx;
2959 }
2960
2961 /*
2962 * Lookup the SKIP pattern (if present)
2963 */
2964 if (spp->sp_type == SPTYPE_SKIP)
2965 {
2966 spp_skip = spp;
2967 ++idx;
2968 }
2969 else
2970 spp_skip = NULL;
2971
2972 /* Setup external matches for syn_regexec(). */
2973 unref_extmatch(re_extmatch_in);
2974 re_extmatch_in = ref_extmatch(start_ext);
2975
2976 matchcol = startpos->col; /* start looking for a match at sstart */
2977 start_idx = idx; /* remember the first END pattern. */
2978 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002979
2980 /* use syntax iskeyword option */
2981 save_chartab(buf_chartab);
2982
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983 for (;;)
2984 {
2985 /*
2986 * Find end pattern that matches first after "matchcol".
2987 */
2988 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002989 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990 {
2991 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002992 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002993
Bram Moolenaar860cae12010-06-05 23:22:07 +02002994 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2996 break;
2997 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2998 if (lc_col < 0)
2999 lc_col = 0;
3000
3001 regmatch.rmm_ic = spp->sp_ic;
3002 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003003 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3004 IF_SYN_TIME(&spp->sp_time));
3005 spp->sp_prog = regmatch.regprog;
3006 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007 {
3008 if (best_idx == -1 || regmatch.startpos[0].col
3009 < best_regmatch.startpos[0].col)
3010 {
3011 best_idx = idx;
3012 best_regmatch.startpos[0] = regmatch.startpos[0];
3013 best_regmatch.endpos[0] = regmatch.endpos[0];
3014 }
3015 }
3016 }
3017
3018 /*
3019 * If all end patterns have been tried, and there is no match, the
3020 * item continues until end-of-line.
3021 */
3022 if (best_idx == -1)
3023 break;
3024
3025 /*
3026 * If the skip pattern matches before the end pattern,
3027 * continue searching after the skip pattern.
3028 */
3029 if (spp_skip != NULL)
3030 {
3031 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003032 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033
3034 if (lc_col < 0)
3035 lc_col = 0;
3036 regmatch.rmm_ic = spp_skip->sp_ic;
3037 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003038 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3039 IF_SYN_TIME(&spp_skip->sp_time));
3040 spp_skip->sp_prog = regmatch.regprog;
3041 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042 <= best_regmatch.startpos[0].col)
3043 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003044 int line_len;
3045
Bram Moolenaar071d4272004-06-13 20:20:40 +00003046 /* Add offset to skip pattern match */
3047 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3048
3049 /* If the skip pattern goes on to the next line, there is no
3050 * match with an end pattern in this line. */
3051 if (pos.lnum > startpos->lnum)
3052 break;
3053
3054 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003055 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056
3057 /* take care of an empty match or negative offset */
3058 if (pos.col <= matchcol)
3059 ++matchcol;
3060 else if (pos.col <= regmatch.endpos[0].col)
3061 matchcol = pos.col;
3062 else
3063 /* Be careful not to jump over the NUL at the end-of-line */
3064 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003065 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 ++matchcol)
3067 ;
3068
3069 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003070 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071 break;
3072
3073 continue; /* start with first end pattern again */
3074 }
3075 }
3076
3077 /*
3078 * Match from start pattern to end pattern.
3079 * Correct for match and highlight offset of end pattern.
3080 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003081 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3083 /* can't end before the start */
3084 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3085 m_endpos->col = startpos->col;
3086
3087 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3088 /* can't end before the start */
3089 if (end_endpos->lnum == startpos->lnum
3090 && end_endpos->col < startpos->col)
3091 end_endpos->col = startpos->col;
3092 /* can't end after the match */
3093 limit_pos(end_endpos, m_endpos);
3094
3095 /*
3096 * If the end group is highlighted differently, adjust the pointers.
3097 */
3098 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3099 {
3100 *end_idx = best_idx;
3101 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3102 {
3103 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3104 hl_endpos->col = best_regmatch.endpos[0].col;
3105 }
3106 else
3107 {
3108 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3109 hl_endpos->col = best_regmatch.startpos[0].col;
3110 }
3111 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3112
3113 /* can't end before the start */
3114 if (hl_endpos->lnum == startpos->lnum
3115 && hl_endpos->col < startpos->col)
3116 hl_endpos->col = startpos->col;
3117 limit_pos(hl_endpos, m_endpos);
3118
3119 /* now the match ends where the highlighting ends, it is turned
3120 * into the matchgroup for the end */
3121 *m_endpos = *hl_endpos;
3122 }
3123 else
3124 {
3125 *end_idx = 0;
3126 *hl_endpos = *end_endpos;
3127 }
3128
3129 *flagsp = spp->sp_flags;
3130
3131 had_match = TRUE;
3132 break;
3133 }
3134
3135 /* no match for an END pattern in this line */
3136 if (!had_match)
3137 m_endpos->lnum = 0;
3138
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003139 restore_chartab(buf_chartab);
3140
Bram Moolenaar071d4272004-06-13 20:20:40 +00003141 /* Remove external matches. */
3142 unref_extmatch(re_extmatch_in);
3143 re_extmatch_in = NULL;
3144}
3145
3146/*
3147 * Limit "pos" not to be after "limit".
3148 */
3149 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003150limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151{
3152 if (pos->lnum > limit->lnum)
3153 *pos = *limit;
3154 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3155 pos->col = limit->col;
3156}
3157
3158/*
3159 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3160 */
3161 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003162limit_pos_zero(
3163 lpos_T *pos,
3164 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165{
3166 if (pos->lnum == 0)
3167 *pos = *limit;
3168 else
3169 limit_pos(pos, limit);
3170}
3171
3172/*
3173 * Add offset to matched text for end of match or highlight.
3174 */
3175 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003176syn_add_end_off(
3177 lpos_T *result, /* returned position */
3178 regmmatch_T *regmatch, /* start/end of match */
3179 synpat_T *spp, /* matched pattern */
3180 int idx, /* index of offset */
3181 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003182{
3183 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003184 int off;
3185 char_u *base;
3186 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187
3188 if (spp->sp_off_flags & (1 << idx))
3189 {
3190 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003191 col = regmatch->startpos[0].col;
3192 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193 }
3194 else
3195 {
3196 result->lnum = regmatch->endpos[0].lnum;
3197 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003198 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003200 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3201 * is a matchgroup. Watch out for match with last NL in the buffer. */
3202 if (result->lnum > syn_buf->b_ml.ml_line_count)
3203 col = 0;
3204 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003205 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003206 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3207 p = base + col;
3208 if (off > 0)
3209 {
3210 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003211 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003212 }
3213 else if (off < 0)
3214 {
3215 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003216 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 }
3218 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003219 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003221}
3222
3223/*
3224 * Add offset to matched text for start of match or highlight.
3225 * Avoid resulting column to become negative.
3226 */
3227 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003228syn_add_start_off(
3229 lpos_T *result, /* returned position */
3230 regmmatch_T *regmatch, /* start/end of match */
3231 synpat_T *spp,
3232 int idx,
3233 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003234{
3235 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003236 int off;
3237 char_u *base;
3238 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239
3240 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3241 {
3242 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003243 col = regmatch->endpos[0].col;
3244 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245 }
3246 else
3247 {
3248 result->lnum = regmatch->startpos[0].lnum;
3249 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003250 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003252 if (result->lnum > syn_buf->b_ml.ml_line_count)
3253 {
3254 /* a "\n" at the end of the pattern may take us below the last line */
3255 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003256 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003257 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003258 if (off != 0)
3259 {
3260 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3261 p = base + col;
3262 if (off > 0)
3263 {
3264 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003265 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003266 }
3267 else if (off < 0)
3268 {
3269 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003270 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003271 }
3272 col = (int)(p - base);
3273 }
3274 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275}
3276
3277/*
3278 * Get current line in syntax buffer.
3279 */
3280 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003281syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282{
3283 return ml_get_buf(syn_buf, current_lnum, FALSE);
3284}
3285
3286/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003287 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288 * Returns TRUE when there is a match.
3289 */
3290 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003291syn_regexec(
3292 regmmatch_T *rmp,
3293 linenr_T lnum,
3294 colnr_T col,
3295 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003297 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003298#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003299 proftime_T pt;
3300
3301 if (syn_time_on)
3302 profile_start(&pt);
3303#endif
3304
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003305 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003306 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3307
Bram Moolenaarf7512552013-06-06 14:55:19 +02003308#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003309 if (syn_time_on)
3310 {
3311 profile_end(&pt);
3312 profile_add(&st->total, &pt);
3313 if (profile_cmp(&pt, &st->slowest) < 0)
3314 st->slowest = pt;
3315 ++st->count;
3316 if (r > 0)
3317 ++st->match;
3318 }
3319#endif
3320
3321 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322 {
3323 rmp->startpos[0].lnum += lnum;
3324 rmp->endpos[0].lnum += lnum;
3325 return TRUE;
3326 }
3327 return FALSE;
3328}
3329
3330/*
3331 * Check one position in a line for a matching keyword.
3332 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003333 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 */
3335 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003336check_keyword_id(
3337 char_u *line,
3338 int startcol, /* position in line to check for keyword */
3339 int *endcolp, /* return: character after found keyword */
3340 long *flagsp, /* return: flags of matching keyword */
3341 short **next_listp, /* return: next_list of matching keyword */
3342 stateitem_T *cur_si, /* item at the top of the stack */
3343 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003345 keyentry_T *kp;
3346 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003348 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003350 hashtab_T *ht;
3351 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352
3353 /* Find first character after the keyword. First character was already
3354 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003355 kwp = line + startcol;
3356 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 do
3358 {
3359#ifdef FEAT_MBYTE
3360 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003361 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003362 else
3363#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003364 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003366 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367
Bram Moolenaardad6b692005-01-25 22:14:34 +00003368 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369 return 0;
3370
3371 /*
3372 * Must make a copy of the keyword, so we can add a NUL and make it
3373 * lowercase.
3374 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003375 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376
3377 /*
3378 * Try twice:
3379 * 1. matching case
3380 * 2. ignoring case
3381 */
3382 for (round = 1; round <= 2; ++round)
3383 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003384 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003385 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003387 if (round == 2) /* ignore case */
3388 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389
3390 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003391 * Find keywords that match. There can be several with different
3392 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003393 * When current_next_list is non-zero accept only that group, otherwise:
3394 * Accept a not-contained keyword at toplevel.
3395 * Accept a keyword at other levels only if it is in the contains list.
3396 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003397 hi = hash_find(ht, keyword);
3398 if (!HASHITEM_EMPTY(hi))
3399 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003401 if (current_next_list != 0
3402 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3403 : (cur_si == NULL
3404 ? !(kp->flags & HL_CONTAINED)
3405 : in_id_list(cur_si, cur_si->si_cont_list,
3406 &kp->k_syn, kp->flags & HL_CONTAINED)))
3407 {
3408 *endcolp = startcol + kwlen;
3409 *flagsp = kp->flags;
3410 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003411#ifdef FEAT_CONCEAL
3412 *ccharp = kp->k_char;
3413#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003414 return kp->k_syn.id;
3415 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416 }
3417 }
3418 return 0;
3419}
3420
3421/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003422 * Handle ":syntax conceal" command.
3423 */
3424 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003425syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003426{
3427#ifdef FEAT_CONCEAL
3428 char_u *arg = eap->arg;
3429 char_u *next;
3430
3431 eap->nextcmd = find_nextcmd(arg);
3432 if (eap->skip)
3433 return;
3434
3435 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003436 if (*arg == NUL)
3437 {
3438 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003439 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003440 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003441 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003442 }
3443 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003444 curwin->w_s->b_syn_conceal = TRUE;
3445 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3446 curwin->w_s->b_syn_conceal = FALSE;
3447 else
3448 EMSG2(_("E390: Illegal argument: %s"), arg);
3449#endif
3450}
3451
3452/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453 * Handle ":syntax case" command.
3454 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003456syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003457{
3458 char_u *arg = eap->arg;
3459 char_u *next;
3460
3461 eap->nextcmd = find_nextcmd(arg);
3462 if (eap->skip)
3463 return;
3464
3465 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003466 if (*arg == NUL)
3467 {
3468 if (curwin->w_s->b_syn_ic)
3469 MSG(_("syntax case ignore"));
3470 else
3471 MSG(_("syntax case match"));
3472 }
3473 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003474 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003475 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003476 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 else
3478 EMSG2(_("E390: Illegal argument: %s"), arg);
3479}
3480
3481/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003482 * Handle ":syntax spell" command.
3483 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003484 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003485syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003486{
3487 char_u *arg = eap->arg;
3488 char_u *next;
3489
3490 eap->nextcmd = find_nextcmd(arg);
3491 if (eap->skip)
3492 return;
3493
3494 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003495 if (*arg == NUL)
3496 {
3497 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3498 MSG(_("syntax spell toplevel"));
3499 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3500 MSG(_("syntax spell notoplevel"));
3501 else
3502 MSG(_("syntax spell default"));
3503 }
3504 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003505 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003506 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003507 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003508 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003510 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003511 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003512 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003513 return;
3514 }
3515
3516 /* assume spell checking changed, force a redraw */
3517 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003518}
3519
3520/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003521 * Handle ":syntax iskeyword" command.
3522 */
3523 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003524syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003525{
3526 char_u *arg = eap->arg;
3527 char_u save_chartab[32];
3528 char_u *save_isk;
3529
3530 if (eap->skip)
3531 return;
3532
3533 arg = skipwhite(arg);
3534 if (*arg == NUL)
3535 {
3536 MSG_PUTS("\n");
3537 MSG_PUTS(_("syntax iskeyword "));
3538 if (curwin->w_s->b_syn_isk != empty_option)
3539 msg_outtrans(curwin->w_s->b_syn_isk);
3540 else
3541 msg_outtrans((char_u *)"not set");
3542 }
3543 else
3544 {
3545 if (STRNICMP(arg, "clear", 5) == 0)
3546 {
3547 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3548 (size_t)32);
3549 clear_string_option(&curwin->w_s->b_syn_isk);
3550 }
3551 else
3552 {
3553 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3554 save_isk = curbuf->b_p_isk;
3555 curbuf->b_p_isk = vim_strsave(arg);
3556
3557 buf_init_chartab(curbuf, FALSE);
3558 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3559 (size_t)32);
3560 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3561 clear_string_option(&curwin->w_s->b_syn_isk);
3562 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3563 curbuf->b_p_isk = save_isk;
3564 }
3565 }
3566 redraw_win_later(curwin, NOT_VALID);
3567}
3568
3569/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570 * Clear all syntax info for one buffer.
3571 */
3572 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003573syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574{
3575 int i;
3576
Bram Moolenaar860cae12010-06-05 23:22:07 +02003577 block->b_syn_error = FALSE; /* clear previous error */
3578 block->b_syn_ic = FALSE; /* Use case, by default */
3579 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3580 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003581#ifdef FEAT_CONCEAL
3582 block->b_syn_conceal = FALSE;
3583#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584
3585 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003586 clear_keywtab(&block->b_keywtab);
3587 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588
3589 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003590 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3591 syn_clear_pattern(block, i);
3592 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003593
3594 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003595 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3596 syn_clear_cluster(block, i);
3597 ga_clear(&block->b_syn_clusters);
3598 block->b_spell_cluster_id = 0;
3599 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003600
Bram Moolenaar860cae12010-06-05 23:22:07 +02003601 block->b_syn_sync_flags = 0;
3602 block->b_syn_sync_minlines = 0;
3603 block->b_syn_sync_maxlines = 0;
3604 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003605
Bram Moolenaar473de612013-06-08 18:19:48 +02003606 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003607 block->b_syn_linecont_prog = NULL;
3608 vim_free(block->b_syn_linecont_pat);
3609 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003613 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614
3615 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003616 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003618
3619 /* Reset the counter for ":syn include" */
3620 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621}
3622
3623/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003624 * Get rid of ownsyntax for window "wp".
3625 */
3626 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003627reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003628{
3629 if (wp->w_s != &wp->w_buffer->b_s)
3630 {
3631 syntax_clear(wp->w_s);
3632 vim_free(wp->w_s);
3633 wp->w_s = &wp->w_buffer->b_s;
3634 }
3635}
3636
3637/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638 * Clear syncing info for one buffer.
3639 */
3640 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003641syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642{
3643 int i;
3644
3645 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003646 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3647 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3648 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 curwin->w_s->b_syn_sync_flags = 0;
3651 curwin->w_s->b_syn_sync_minlines = 0;
3652 curwin->w_s->b_syn_sync_maxlines = 0;
3653 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654
Bram Moolenaar473de612013-06-08 18:19:48 +02003655 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003656 curwin->w_s->b_syn_linecont_prog = NULL;
3657 vim_free(curwin->w_s->b_syn_linecont_pat);
3658 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003659 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003661 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662}
3663
3664/*
3665 * Remove one pattern from the buffer's pattern list.
3666 */
3667 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003668syn_remove_pattern(
3669 synblock_T *block,
3670 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671{
3672 synpat_T *spp;
3673
Bram Moolenaar860cae12010-06-05 23:22:07 +02003674 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675#ifdef FEAT_FOLDING
3676 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003677 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003679 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003681 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3682 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683}
3684
3685/*
3686 * Clear and free one syntax pattern. When clearing all, must be called from
3687 * last to first!
3688 */
3689 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003690syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003693 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003695 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003697 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3698 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3699 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700 }
3701}
3702
3703/*
3704 * Clear and free one syntax cluster.
3705 */
3706 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003707syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003709 vim_free(SYN_CLSTR(block)[i].scl_name);
3710 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3711 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712}
3713
3714/*
3715 * Handle ":syntax clear" command.
3716 */
3717 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003718syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719{
3720 char_u *arg = eap->arg;
3721 char_u *arg_end;
3722 int id;
3723
3724 eap->nextcmd = find_nextcmd(arg);
3725 if (eap->skip)
3726 return;
3727
3728 /*
3729 * We have to disable this within ":syn include @group filename",
3730 * because otherwise @group would get deleted.
3731 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3732 * clear".
3733 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003734 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735 return;
3736
3737 if (ends_excmd(*arg))
3738 {
3739 /*
3740 * No argument: Clear all syntax items.
3741 */
3742 if (syncing)
3743 syntax_sync_clear();
3744 else
3745 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003746 syntax_clear(curwin->w_s);
3747 if (curwin->w_s == &curwin->w_buffer->b_s)
3748 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003749 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003750 }
3751 }
3752 else
3753 {
3754 /*
3755 * Clear the group IDs that are in the argument.
3756 */
3757 while (!ends_excmd(*arg))
3758 {
3759 arg_end = skiptowhite(arg);
3760 if (*arg == '@')
3761 {
3762 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3763 if (id == 0)
3764 {
3765 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3766 break;
3767 }
3768 else
3769 {
3770 /*
3771 * We can't physically delete a cluster without changing
3772 * the IDs of other clusters, so we do the next best thing
3773 * and make it empty.
3774 */
3775 short scl_id = id - SYNID_CLUSTER;
3776
Bram Moolenaar860cae12010-06-05 23:22:07 +02003777 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3778 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779 }
3780 }
3781 else
3782 {
3783 id = syn_namen2id(arg, (int)(arg_end - arg));
3784 if (id == 0)
3785 {
3786 EMSG2(_(e_nogroup), arg);
3787 break;
3788 }
3789 else
3790 syn_clear_one(id, syncing);
3791 }
3792 arg = skipwhite(arg_end);
3793 }
3794 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003795 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003796 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797}
3798
3799/*
3800 * Clear one syntax group for the current buffer.
3801 */
3802 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003803syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003804{
3805 synpat_T *spp;
3806 int idx;
3807
3808 /* Clear keywords only when not ":syn sync clear group-name" */
3809 if (!syncing)
3810 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003811 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3812 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 }
3814
3815 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003816 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003818 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3820 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003821 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 }
3823}
3824
3825/*
3826 * Handle ":syntax on" command.
3827 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003828 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003829syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830{
3831 syn_cmd_onoff(eap, "syntax");
3832}
3833
3834/*
3835 * Handle ":syntax enable" command.
3836 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003838syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839{
3840 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3841 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003842 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843}
3844
3845/*
3846 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003847 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003850syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851{
3852 eap->nextcmd = check_nextcmd(eap->arg);
3853 if (!eap->skip)
3854 {
3855 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3856 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003857 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 }
3859}
3860
3861/*
3862 * Handle ":syntax manual" command.
3863 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003865syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866{
3867 syn_cmd_onoff(eap, "manual");
3868}
3869
3870/*
3871 * Handle ":syntax off" command.
3872 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003874syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875{
3876 syn_cmd_onoff(eap, "nosyntax");
3877}
3878
3879 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003880syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881{
3882 char_u buf[100];
3883
3884 eap->nextcmd = check_nextcmd(eap->arg);
3885 if (!eap->skip)
3886 {
3887 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003888 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889 do_cmdline_cmd(buf);
3890 }
3891}
3892
3893/*
3894 * Handle ":syntax [list]" command: list current syntax words.
3895 */
3896 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003897syn_cmd_list(
3898 exarg_T *eap,
3899 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900{
3901 char_u *arg = eap->arg;
3902 int id;
3903 char_u *arg_end;
3904
3905 eap->nextcmd = find_nextcmd(arg);
3906 if (eap->skip)
3907 return;
3908
Bram Moolenaar860cae12010-06-05 23:22:07 +02003909 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003911 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912 return;
3913 }
3914
3915 if (syncing)
3916 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003917 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003918 {
3919 MSG_PUTS(_("syncing on C-style comments"));
3920 syn_lines_msg();
3921 syn_match_msg();
3922 return;
3923 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003924 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003926 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 MSG_PUTS(_("no syncing"));
3928 else
3929 {
3930 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003931 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 MSG_PUTS(_(" lines before top line"));
3933 syn_match_msg();
3934 }
3935 return;
3936 }
3937 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003938 if (curwin->w_s->b_syn_sync_minlines > 0
3939 || curwin->w_s->b_syn_sync_maxlines > 0
3940 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941 {
3942 MSG_PUTS(_("\nsyncing on items"));
3943 syn_lines_msg();
3944 syn_match_msg();
3945 }
3946 }
3947 else
3948 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3949 if (ends_excmd(*arg))
3950 {
3951 /*
3952 * No argument: List all group IDs and all syntax clusters.
3953 */
3954 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3955 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003956 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957 syn_list_cluster(id);
3958 }
3959 else
3960 {
3961 /*
3962 * List the group IDs and syntax clusters that are in the argument.
3963 */
3964 while (!ends_excmd(*arg) && !got_int)
3965 {
3966 arg_end = skiptowhite(arg);
3967 if (*arg == '@')
3968 {
3969 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3970 if (id == 0)
3971 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3972 else
3973 syn_list_cluster(id - SYNID_CLUSTER);
3974 }
3975 else
3976 {
3977 id = syn_namen2id(arg, (int)(arg_end - arg));
3978 if (id == 0)
3979 EMSG2(_(e_nogroup), arg);
3980 else
3981 syn_list_one(id, syncing, TRUE);
3982 }
3983 arg = skipwhite(arg_end);
3984 }
3985 }
3986 eap->nextcmd = check_nextcmd(arg);
3987}
3988
3989 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003990syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003992 if (curwin->w_s->b_syn_sync_maxlines > 0
3993 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994 {
3995 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003996 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 {
3998 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003999 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4000 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 MSG_PUTS(", ");
4002 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004003 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004 {
4005 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004006 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007 }
4008 MSG_PUTS(_(" lines before top line"));
4009 }
4010}
4011
4012 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004013syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004015 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 {
4017 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004018 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 MSG_PUTS(_(" line breaks"));
4020 }
4021}
4022
4023static int last_matchgroup;
4024
4025struct name_list
4026{
4027 int flag;
4028 char *name;
4029};
4030
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004031static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032
4033/*
4034 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4035 */
4036 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004037syn_list_one(
4038 int id,
4039 int syncing, /* when TRUE: list syncing items */
4040 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041{
4042 int attr;
4043 int idx;
4044 int did_header = FALSE;
4045 synpat_T *spp;
4046 static struct name_list namelist1[] =
4047 {
4048 {HL_DISPLAY, "display"},
4049 {HL_CONTAINED, "contained"},
4050 {HL_ONELINE, "oneline"},
4051 {HL_KEEPEND, "keepend"},
4052 {HL_EXTEND, "extend"},
4053 {HL_EXCLUDENL, "excludenl"},
4054 {HL_TRANSP, "transparent"},
4055 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004056#ifdef FEAT_CONCEAL
4057 {HL_CONCEAL, "conceal"},
4058 {HL_CONCEALENDS, "concealends"},
4059#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060 {0, NULL}
4061 };
4062 static struct name_list namelist2[] =
4063 {
4064 {HL_SKIPWHITE, "skipwhite"},
4065 {HL_SKIPNL, "skipnl"},
4066 {HL_SKIPEMPTY, "skipempty"},
4067 {0, NULL}
4068 };
4069
Bram Moolenaar8820b482017-03-16 17:23:31 +01004070 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004071
4072 /* list the keywords for "id" */
4073 if (!syncing)
4074 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004075 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4076 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077 did_header, attr);
4078 }
4079
4080 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004081 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004082 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004083 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4085 continue;
4086
4087 (void)syn_list_header(did_header, 999, id);
4088 did_header = TRUE;
4089 last_matchgroup = 0;
4090 if (spp->sp_type == SPTYPE_MATCH)
4091 {
4092 put_pattern("match", ' ', spp, attr);
4093 msg_putchar(' ');
4094 }
4095 else if (spp->sp_type == SPTYPE_START)
4096 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004097 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4098 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4099 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4100 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4101 while (idx < curwin->w_s->b_syn_patterns.ga_len
4102 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4103 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104 --idx;
4105 msg_putchar(' ');
4106 }
4107 syn_list_flags(namelist1, spp->sp_flags, attr);
4108
4109 if (spp->sp_cont_list != NULL)
4110 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4111
4112 if (spp->sp_syn.cont_in_list != NULL)
4113 put_id_list((char_u *)"containedin",
4114 spp->sp_syn.cont_in_list, attr);
4115
4116 if (spp->sp_next_list != NULL)
4117 {
4118 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4119 syn_list_flags(namelist2, spp->sp_flags, attr);
4120 }
4121 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4122 {
4123 if (spp->sp_flags & HL_SYNC_HERE)
4124 msg_puts_attr((char_u *)"grouphere", attr);
4125 else
4126 msg_puts_attr((char_u *)"groupthere", attr);
4127 msg_putchar(' ');
4128 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004129 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4131 else
4132 MSG_PUTS("NONE");
4133 msg_putchar(' ');
4134 }
4135 }
4136
4137 /* list the link, if there is one */
4138 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4139 {
4140 (void)syn_list_header(did_header, 999, id);
4141 msg_puts_attr((char_u *)"links to", attr);
4142 msg_putchar(' ');
4143 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4144 }
4145}
4146
4147 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004148syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149{
4150 int i;
4151
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004152 for (i = 0; nlist[i].flag != 0; ++i)
4153 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004155 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 msg_putchar(' ');
4157 }
4158}
4159
4160/*
4161 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4162 */
4163 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004164syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165{
4166 int endcol = 15;
4167
4168 /* slight hack: roughly duplicate the guts of syn_list_header() */
4169 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004170 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171
4172 if (msg_col >= endcol) /* output at least one space */
4173 endcol = msg_col + 1;
4174 if (Columns <= endcol) /* avoid hang for tiny window */
4175 endcol = Columns - 1;
4176
4177 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004178 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004180 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004181 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 }
4183 else
4184 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004185 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 msg_puts((char_u *)"=NONE");
4187 }
4188}
4189
4190 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004191put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192{
4193 short *p;
4194
4195 msg_puts_attr(name, attr);
4196 msg_putchar('=');
4197 for (p = list; *p; ++p)
4198 {
4199 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4200 {
4201 if (p[1])
4202 MSG_PUTS("ALLBUT");
4203 else
4204 MSG_PUTS("ALL");
4205 }
4206 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4207 {
4208 MSG_PUTS("TOP");
4209 }
4210 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4211 {
4212 MSG_PUTS("CONTAINED");
4213 }
4214 else if (*p >= SYNID_CLUSTER)
4215 {
4216 short scl_id = *p - SYNID_CLUSTER;
4217
4218 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004219 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220 }
4221 else
4222 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4223 if (p[1])
4224 msg_putchar(',');
4225 }
4226 msg_putchar(' ');
4227}
4228
4229 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004230put_pattern(
4231 char *s,
4232 int c,
4233 synpat_T *spp,
4234 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004235{
4236 long n;
4237 int mask;
4238 int first;
4239 static char *sepchars = "/+=-#@\"|'^&";
4240 int i;
4241
4242 /* May have to write "matchgroup=group" */
4243 if (last_matchgroup != spp->sp_syn_match_id)
4244 {
4245 last_matchgroup = spp->sp_syn_match_id;
4246 msg_puts_attr((char_u *)"matchgroup", attr);
4247 msg_putchar('=');
4248 if (last_matchgroup == 0)
4249 msg_outtrans((char_u *)"NONE");
4250 else
4251 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4252 msg_putchar(' ');
4253 }
4254
4255 /* output the name of the pattern and an '=' or ' ' */
4256 msg_puts_attr((char_u *)s, attr);
4257 msg_putchar(c);
4258
4259 /* output the pattern, in between a char that is not in the pattern */
4260 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4261 if (sepchars[++i] == NUL)
4262 {
4263 i = 0; /* no good char found, just use the first one */
4264 break;
4265 }
4266 msg_putchar(sepchars[i]);
4267 msg_outtrans(spp->sp_pattern);
4268 msg_putchar(sepchars[i]);
4269
4270 /* output any pattern options */
4271 first = TRUE;
4272 for (i = 0; i < SPO_COUNT; ++i)
4273 {
4274 mask = (1 << i);
4275 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4276 {
4277 if (!first)
4278 msg_putchar(','); /* separate with commas */
4279 msg_puts((char_u *)spo_name_tab[i]);
4280 n = spp->sp_offsets[i];
4281 if (i != SPO_LC_OFF)
4282 {
4283 if (spp->sp_off_flags & mask)
4284 msg_putchar('s');
4285 else
4286 msg_putchar('e');
4287 if (n > 0)
4288 msg_putchar('+');
4289 }
4290 if (n || i == SPO_LC_OFF)
4291 msg_outnum(n);
4292 first = FALSE;
4293 }
4294 }
4295 msg_putchar(' ');
4296}
4297
4298/*
4299 * List or clear the keywords for one syntax group.
4300 * Return TRUE if the header has been printed.
4301 */
4302 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004303syn_list_keywords(
4304 int id,
4305 hashtab_T *ht,
4306 int did_header, /* header has already been printed */
4307 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004310 hashitem_T *hi;
4311 keyentry_T *kp;
4312 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 int prev_contained = 0;
4314 short *prev_next_list = NULL;
4315 short *prev_cont_in_list = NULL;
4316 int prev_skipnl = 0;
4317 int prev_skipwhite = 0;
4318 int prev_skipempty = 0;
4319
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320 /*
4321 * Unfortunately, this list of keywords is not sorted on alphabet but on
4322 * hash value...
4323 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004324 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 --todo;
4330 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 if (prev_contained != (kp->flags & HL_CONTAINED)
4335 || prev_skipnl != (kp->flags & HL_SKIPNL)
4336 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4337 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4338 || prev_cont_in_list != kp->k_syn.cont_in_list
4339 || prev_next_list != kp->next_list)
4340 outlen = 9999;
4341 else
4342 outlen = (int)STRLEN(kp->keyword);
4343 /* output "contained" and "nextgroup" on each line */
4344 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004346 prev_contained = 0;
4347 prev_next_list = NULL;
4348 prev_cont_in_list = NULL;
4349 prev_skipnl = 0;
4350 prev_skipwhite = 0;
4351 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004353 did_header = TRUE;
4354 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 put_id_list((char_u *)"containedin",
4363 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004367 if (kp->next_list != prev_next_list)
4368 {
4369 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4370 msg_putchar(' ');
4371 prev_next_list = kp->next_list;
4372 if (kp->flags & HL_SKIPNL)
4373 {
4374 msg_puts_attr((char_u *)"skipnl", attr);
4375 msg_putchar(' ');
4376 prev_skipnl = (kp->flags & HL_SKIPNL);
4377 }
4378 if (kp->flags & HL_SKIPWHITE)
4379 {
4380 msg_puts_attr((char_u *)"skipwhite", attr);
4381 msg_putchar(' ');
4382 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4383 }
4384 if (kp->flags & HL_SKIPEMPTY)
4385 {
4386 msg_puts_attr((char_u *)"skipempty", attr);
4387 msg_putchar(' ');
4388 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4389 }
4390 }
4391 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
4394 }
4395 }
4396
4397 return did_header;
4398}
4399
4400 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004401syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004402{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004403 hashitem_T *hi;
4404 keyentry_T *kp;
4405 keyentry_T *kp_prev;
4406 keyentry_T *kp_next;
4407 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408
Bram Moolenaardad6b692005-01-25 22:14:34 +00004409 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004410 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004411 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004412 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004413 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004414 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004415 --todo;
4416 kp_prev = NULL;
4417 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 if (kp->k_syn.id == id)
4420 {
4421 kp_next = kp->ke_next;
4422 if (kp_prev == NULL)
4423 {
4424 if (kp_next == NULL)
4425 hash_remove(ht, hi);
4426 else
4427 hi->hi_key = KE2HIKEY(kp_next);
4428 }
4429 else
4430 kp_prev->ke_next = kp_next;
4431 vim_free(kp->next_list);
4432 vim_free(kp->k_syn.cont_in_list);
4433 vim_free(kp);
4434 kp = kp_next;
4435 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004437 {
4438 kp_prev = kp;
4439 kp = kp->ke_next;
4440 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 }
4442 }
4443 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445}
4446
4447/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004448 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449 */
4450 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004451clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004453 hashitem_T *hi;
4454 int todo;
4455 keyentry_T *kp;
4456 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004457
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004458 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004459 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004461 if (!HASHITEM_EMPTY(hi))
4462 {
4463 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004465 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004466 kp_next = kp->ke_next;
4467 vim_free(kp->next_list);
4468 vim_free(kp->k_syn.cont_in_list);
4469 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004471 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004473 hash_clear(ht);
4474 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475}
4476
4477/*
4478 * Add a keyword to the list of keywords.
4479 */
4480 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004481add_keyword(
4482 char_u *name, /* name of keyword */
4483 int id, /* group ID for this keyword */
4484 int flags, /* flags for this keyword */
4485 short *cont_in_list, /* containedin for this keyword */
4486 short *next_list, /* nextgroup for this keyword */
4487 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004489 keyentry_T *kp;
4490 hashtab_T *ht;
4491 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004492 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004494 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495
Bram Moolenaar860cae12010-06-05 23:22:07 +02004496 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 name_ic = str_foldcase(name, (int)STRLEN(name),
4498 name_folded, MAXKEYWLEN + 1);
4499 else
4500 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004501 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4502 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004504 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 kp->k_syn.id = id;
4506 kp->k_syn.inc_tag = current_syn_inc_tag;
4507 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004508 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004509 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004511 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004512 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513
Bram Moolenaar860cae12010-06-05 23:22:07 +02004514 if (curwin->w_s->b_syn_ic)
4515 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004517 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518
Bram Moolenaardad6b692005-01-25 22:14:34 +00004519 hash = hash_hash(kp->keyword);
4520 hi = hash_lookup(ht, kp->keyword, hash);
4521 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004523 /* new keyword, add to hashtable */
4524 kp->ke_next = NULL;
4525 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004527 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004529 /* keyword already exists, prepend to list */
4530 kp->ke_next = HI2KE(hi);
4531 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533}
4534
4535/*
4536 * Get the start and end of the group name argument.
4537 * Return a pointer to the first argument.
4538 * Return NULL if the end of the command was found instead of further args.
4539 */
4540 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004541get_group_name(
4542 char_u *arg, /* start of the argument */
4543 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544{
4545 char_u *rest;
4546
4547 *name_end = skiptowhite(arg);
4548 rest = skipwhite(*name_end);
4549
4550 /*
4551 * Check if there are enough arguments. The first argument may be a
4552 * pattern, where '|' is allowed, so only check for NUL.
4553 */
4554 if (ends_excmd(*arg) || *rest == NUL)
4555 return NULL;
4556 return rest;
4557}
4558
4559/*
4560 * Check for syntax command option arguments.
4561 * This can be called at any place in the list of arguments, and just picks
4562 * out the arguments that are known. Can be called several times in a row to
4563 * collect all options in between other arguments.
4564 * Return a pointer to the next argument (which isn't an option).
4565 * Return NULL for any error;
4566 */
4567 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004568get_syn_options(
4569 char_u *arg, /* next argument to be checked */
4570 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004571 int *conceal_char UNUSED,
4572 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004573{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 char_u *gname_start, *gname;
4575 int syn_id;
4576 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004577 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578 int i;
4579 int fidx;
4580 static struct flag
4581 {
4582 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004583 int argtype;
4584 int flags;
4585 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4586 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4587 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4588 {"eExXtTeEnNdD", 0, HL_EXTEND},
4589 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4590 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4591 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4592 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4593 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4594 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4595 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4596 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4597 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004598 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4599 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4600 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004601 {"cCoOnNtTaAiInNsS", 1, 0},
4602 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4603 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004604 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004605 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606
4607 if (arg == NULL) /* already detected error */
4608 return NULL;
4609
Bram Moolenaar860cae12010-06-05 23:22:07 +02004610#ifdef FEAT_CONCEAL
4611 if (curwin->w_s->b_syn_conceal)
4612 opt->flags |= HL_CONCEAL;
4613#endif
4614
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615 for (;;)
4616 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004617 /*
4618 * This is used very often when a large number of keywords is defined.
4619 * Need to skip quickly when no option name is found.
4620 * Also avoid tolower(), it's slow.
4621 */
4622 if (strchr(first_letters, *arg) == NULL)
4623 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004624
4625 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4626 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004627 p = flagtab[fidx].name;
4628 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4629 if (arg[len] != p[i] && arg[len] != p[i + 1])
4630 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004631 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004632 || (flagtab[fidx].argtype > 0
4633 ? arg[len] == '='
4634 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004635 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004636 if (opt->keyword
4637 && (flagtab[fidx].flags == HL_DISPLAY
4638 || flagtab[fidx].flags == HL_FOLD
4639 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640 /* treat "display", "fold" and "extend" as a keyword */
4641 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642 break;
4643 }
4644 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004645 if (fidx < 0) /* no match found */
4646 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004648 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004650 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651 {
4652 EMSG(_("E395: contains argument not accepted here"));
4653 return NULL;
4654 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004655 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004656 return NULL;
4657 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004658 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004660 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004661 return NULL;
4662 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004663 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004665 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004666 return NULL;
4667 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004668 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4669 {
4670#ifdef FEAT_MBYTE
4671 /* cchar=? */
4672 if (has_mbyte)
4673 {
4674# ifdef FEAT_CONCEAL
4675 *conceal_char = mb_ptr2char(arg + 6);
4676# endif
4677 arg += mb_ptr2len(arg + 6) - 1;
4678 }
4679 else
4680#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004681 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004682#ifdef FEAT_CONCEAL
4683 *conceal_char = arg[6];
4684#else
4685 ;
4686#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004687 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004688#ifdef FEAT_CONCEAL
4689 if (!vim_isprintc_strict(*conceal_char))
4690 {
4691 EMSG(_("E844: invalid cchar value"));
4692 return NULL;
4693 }
4694#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004695 arg = skipwhite(arg + 7);
4696 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004698 {
4699 opt->flags |= flagtab[fidx].flags;
4700 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004701
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004702 if (flagtab[fidx].flags == HL_SYNC_HERE
4703 || flagtab[fidx].flags == HL_SYNC_THERE)
4704 {
4705 if (opt->sync_idx == NULL)
4706 {
4707 EMSG(_("E393: group[t]here not accepted here"));
4708 return NULL;
4709 }
4710 gname_start = arg;
4711 arg = skiptowhite(arg);
4712 if (gname_start == arg)
4713 return NULL;
4714 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4715 if (gname == NULL)
4716 return NULL;
4717 if (STRCMP(gname, "NONE") == 0)
4718 *opt->sync_idx = NONE_IDX;
4719 else
4720 {
4721 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004722 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4723 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4724 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004725 {
4726 *opt->sync_idx = i;
4727 break;
4728 }
4729 if (i < 0)
4730 {
4731 EMSG2(_("E394: Didn't find region item for %s"), gname);
4732 vim_free(gname);
4733 return NULL;
4734 }
4735 }
4736
4737 vim_free(gname);
4738 arg = skipwhite(arg);
4739 }
4740#ifdef FEAT_FOLDING
4741 else if (flagtab[fidx].flags == HL_FOLD
4742 && foldmethodIsSyntax(curwin))
4743 /* Need to update folds later. */
4744 foldUpdateAll(curwin);
4745#endif
4746 }
4747 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748
4749 return arg;
4750}
4751
4752/*
4753 * Adjustments to syntax item when declared in a ":syn include"'d file.
4754 * Set the contained flag, and if the item is not already contained, add it
4755 * to the specified top-level group, if any.
4756 */
4757 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004758syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004759{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004760 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761 return;
4762 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004763 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764 {
4765 /* We have to alloc this, because syn_combine_list() will free it. */
4766 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004767 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768
4769 if (grp_list != NULL)
4770 {
4771 grp_list[0] = id;
4772 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004773 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 CLUSTER_ADD);
4775 }
4776 }
4777}
4778
4779/*
4780 * Handle ":syntax include [@{group-name}] filename" command.
4781 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004782 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004783syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784{
4785 char_u *arg = eap->arg;
4786 int sgl_id = 1;
4787 char_u *group_name_end;
4788 char_u *rest;
4789 char_u *errormsg = NULL;
4790 int prev_toplvl_grp;
4791 int prev_syn_inc_tag;
4792 int source = FALSE;
4793
4794 eap->nextcmd = find_nextcmd(arg);
4795 if (eap->skip)
4796 return;
4797
4798 if (arg[0] == '@')
4799 {
4800 ++arg;
4801 rest = get_group_name(arg, &group_name_end);
4802 if (rest == NULL)
4803 {
4804 EMSG((char_u *)_("E397: Filename required"));
4805 return;
4806 }
4807 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004808 if (sgl_id == 0)
4809 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 /* separate_nextcmd() and expand_filename() depend on this */
4811 eap->arg = rest;
4812 }
4813
4814 /*
4815 * Everything that's left, up to the next command, should be the
4816 * filename to include.
4817 */
4818 eap->argt |= (XFILE | NOSPC);
4819 separate_nextcmd(eap);
4820 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4821 {
4822 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4823 * file. Need to expand the file name first. In other cases
4824 * ":runtime!" is used. */
4825 source = TRUE;
4826 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4827 {
4828 if (errormsg != NULL)
4829 EMSG(errormsg);
4830 return;
4831 }
4832 }
4833
4834 /*
4835 * Save and restore the existing top-level grouplist id and ":syn
4836 * include" tag around the actual inclusion.
4837 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004838 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4839 {
4840 EMSG((char_u *)_("E847: Too many syntax includes"));
4841 return;
4842 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843 prev_syn_inc_tag = current_syn_inc_tag;
4844 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004845 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4846 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004847 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004848 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004849 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004850 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851 current_syn_inc_tag = prev_syn_inc_tag;
4852}
4853
4854/*
4855 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4856 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004858syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859{
4860 char_u *arg = eap->arg;
4861 char_u *group_name_end;
4862 int syn_id;
4863 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004864 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004865 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004866 char_u *kw;
4867 syn_opt_arg_T syn_opt_arg;
4868 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004869 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870
4871 rest = get_group_name(arg, &group_name_end);
4872
4873 if (rest != NULL)
4874 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004875 if (eap->skip)
4876 syn_id = -1;
4877 else
4878 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004879 if (syn_id != 0)
4880 /* allocate a buffer, for removing backslashes in the keyword */
4881 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 if (keyword_copy != NULL)
4883 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004884 syn_opt_arg.flags = 0;
4885 syn_opt_arg.keyword = TRUE;
4886 syn_opt_arg.sync_idx = NULL;
4887 syn_opt_arg.has_cont_list = FALSE;
4888 syn_opt_arg.cont_in_list = NULL;
4889 syn_opt_arg.next_list = NULL;
4890
Bram Moolenaar071d4272004-06-13 20:20:40 +00004891 /*
4892 * The options given apply to ALL keywords, so all options must be
4893 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004894 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896 cnt = 0;
4897 p = keyword_copy;
4898 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004900 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4901 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004902 if (rest == NULL || ends_excmd(*rest))
4903 break;
4904 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004905 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004907 if (*rest == '\\' && rest[1] != NUL)
4908 ++rest;
4909 *p++ = *rest++;
4910 }
4911 *p++ = NUL;
4912 ++cnt;
4913 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004914
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004915 if (!eap->skip)
4916 {
4917 /* Adjust flags for use of ":syn include". */
4918 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4919
4920 /*
4921 * 2: Add an entry for each keyword.
4922 */
4923 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4924 {
4925 for (p = vim_strchr(kw, '['); ; )
4926 {
4927 if (p != NULL)
4928 *p = NUL;
4929 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004930 syn_opt_arg.cont_in_list,
4931 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004932 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004933 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004934 if (p[1] == NUL)
4935 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004936 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004937 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004938 }
4939 if (p[1] == ']')
4940 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004941 if (p[2] != NUL)
4942 {
4943 EMSG3(_("E890: trailing char after ']': %s]%s"),
4944 kw, &p[2]);
4945 goto error;
4946 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004947 kw = p + 1; /* skip over the "]" */
4948 break;
4949 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004950#ifdef FEAT_MBYTE
4951 if (has_mbyte)
4952 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004953 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004954
4955 mch_memmove(p, p + 1, l);
4956 p += l;
4957 }
4958 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004959#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004960 {
4961 p[0] = p[1];
4962 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004963 }
4964 }
4965 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004967error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004969 vim_free(syn_opt_arg.cont_in_list);
4970 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 }
4972 }
4973
4974 if (rest != NULL)
4975 eap->nextcmd = check_nextcmd(rest);
4976 else
4977 EMSG2(_(e_invarg2), arg);
4978
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004979 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004980 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981}
4982
4983/*
4984 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4985 *
4986 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4987 */
4988 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004989syn_cmd_match(
4990 exarg_T *eap,
4991 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992{
4993 char_u *arg = eap->arg;
4994 char_u *group_name_end;
4995 char_u *rest;
4996 synpat_T item; /* the item found in the line */
4997 int syn_id;
4998 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004999 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005000 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005001 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002
5003 /* Isolate the group name, check for validity */
5004 rest = get_group_name(arg, &group_name_end);
5005
5006 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005007 syn_opt_arg.flags = 0;
5008 syn_opt_arg.keyword = FALSE;
5009 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5010 syn_opt_arg.has_cont_list = TRUE;
5011 syn_opt_arg.cont_list = NULL;
5012 syn_opt_arg.cont_in_list = NULL;
5013 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005014 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015
5016 /* get the pattern. */
5017 init_syn_patterns();
5018 vim_memset(&item, 0, sizeof(item));
5019 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005020 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5021 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022
5023 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005024 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025
5026 if (rest != NULL) /* all arguments are valid */
5027 {
5028 /*
5029 * Check for trailing command and illegal trailing arguments.
5030 */
5031 eap->nextcmd = check_nextcmd(rest);
5032 if (!ends_excmd(*rest) || eap->skip)
5033 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005034 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 && (syn_id = syn_check_group(arg,
5036 (int)(group_name_end - arg))) != 0)
5037 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005038 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039 /*
5040 * Store the pattern in the syn_items list
5041 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005042 idx = curwin->w_s->b_syn_patterns.ga_len;
5043 SYN_ITEMS(curwin->w_s)[idx] = item;
5044 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5045 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5046 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5047 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5048 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5049 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5050 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5051 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005052 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005053#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005054 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005055#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005056 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005057 curwin->w_s->b_syn_containedin = TRUE;
5058 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5059 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060
5061 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005062 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005063 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005064#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005065 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005066 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067#endif
5068
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005069 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005070 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 return; /* don't free the progs and patterns now */
5072 }
5073 }
5074
5075 /*
5076 * Something failed, free the allocated memory.
5077 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005078 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005079 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005080 vim_free(syn_opt_arg.cont_list);
5081 vim_free(syn_opt_arg.cont_in_list);
5082 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005083
5084 if (rest == NULL)
5085 EMSG2(_(e_invarg2), arg);
5086}
5087
5088/*
5089 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5090 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5091 */
5092 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005093syn_cmd_region(
5094 exarg_T *eap,
5095 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096{
5097 char_u *arg = eap->arg;
5098 char_u *group_name_end;
5099 char_u *rest; /* next arg, NULL on error */
5100 char_u *key_end;
5101 char_u *key = NULL;
5102 char_u *p;
5103 int item;
5104#define ITEM_START 0
5105#define ITEM_SKIP 1
5106#define ITEM_END 2
5107#define ITEM_MATCHGROUP 3
5108 struct pat_ptr
5109 {
5110 synpat_T *pp_synp; /* pointer to syn_pattern */
5111 int pp_matchgroup_id; /* matchgroup ID */
5112 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5113 } *(pat_ptrs[3]);
5114 /* patterns found in the line */
5115 struct pat_ptr *ppp;
5116 struct pat_ptr *ppp_next;
5117 int pat_count = 0; /* nr of syn_patterns found */
5118 int syn_id;
5119 int matchgroup_id = 0;
5120 int not_enough = FALSE; /* not enough arguments */
5121 int illegal = FALSE; /* illegal arguments */
5122 int success = FALSE;
5123 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005124 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005125 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005126
5127 /* Isolate the group name, check for validity */
5128 rest = get_group_name(arg, &group_name_end);
5129
5130 pat_ptrs[0] = NULL;
5131 pat_ptrs[1] = NULL;
5132 pat_ptrs[2] = NULL;
5133
5134 init_syn_patterns();
5135
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005136 syn_opt_arg.flags = 0;
5137 syn_opt_arg.keyword = FALSE;
5138 syn_opt_arg.sync_idx = NULL;
5139 syn_opt_arg.has_cont_list = TRUE;
5140 syn_opt_arg.cont_list = NULL;
5141 syn_opt_arg.cont_in_list = NULL;
5142 syn_opt_arg.next_list = NULL;
5143
Bram Moolenaar071d4272004-06-13 20:20:40 +00005144 /*
5145 * get the options, patterns and matchgroup.
5146 */
5147 while (rest != NULL && !ends_excmd(*rest))
5148 {
5149 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005150 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005151 if (rest == NULL || ends_excmd(*rest))
5152 break;
5153
5154 /* must be a pattern or matchgroup then */
5155 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005156 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 ++key_end;
5158 vim_free(key);
5159 key = vim_strnsave_up(rest, (int)(key_end - rest));
5160 if (key == NULL) /* out of memory */
5161 {
5162 rest = NULL;
5163 break;
5164 }
5165 if (STRCMP(key, "MATCHGROUP") == 0)
5166 item = ITEM_MATCHGROUP;
5167 else if (STRCMP(key, "START") == 0)
5168 item = ITEM_START;
5169 else if (STRCMP(key, "END") == 0)
5170 item = ITEM_END;
5171 else if (STRCMP(key, "SKIP") == 0)
5172 {
5173 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5174 {
5175 illegal = TRUE;
5176 break;
5177 }
5178 item = ITEM_SKIP;
5179 }
5180 else
5181 break;
5182 rest = skipwhite(key_end);
5183 if (*rest != '=')
5184 {
5185 rest = NULL;
5186 EMSG2(_("E398: Missing '=': %s"), arg);
5187 break;
5188 }
5189 rest = skipwhite(rest + 1);
5190 if (*rest == NUL)
5191 {
5192 not_enough = TRUE;
5193 break;
5194 }
5195
5196 if (item == ITEM_MATCHGROUP)
5197 {
5198 p = skiptowhite(rest);
5199 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5200 matchgroup_id = 0;
5201 else
5202 {
5203 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5204 if (matchgroup_id == 0)
5205 {
5206 illegal = TRUE;
5207 break;
5208 }
5209 }
5210 rest = skipwhite(p);
5211 }
5212 else
5213 {
5214 /*
5215 * Allocate room for a syn_pattern, and link it in the list of
5216 * syn_patterns for this item, at the start (because the list is
5217 * used from end to start).
5218 */
5219 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5220 if (ppp == NULL)
5221 {
5222 rest = NULL;
5223 break;
5224 }
5225 ppp->pp_next = pat_ptrs[item];
5226 pat_ptrs[item] = ppp;
5227 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5228 if (ppp->pp_synp == NULL)
5229 {
5230 rest = NULL;
5231 break;
5232 }
5233
5234 /*
5235 * Get the syntax pattern and the following offset(s).
5236 */
5237 /* Enable the appropriate \z specials. */
5238 if (item == ITEM_START)
5239 reg_do_extmatch = REX_SET;
5240 else if (item == ITEM_SKIP || item == ITEM_END)
5241 reg_do_extmatch = REX_USE;
5242 rest = get_syn_pattern(rest, ppp->pp_synp);
5243 reg_do_extmatch = 0;
5244 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005245 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005246 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5247 ppp->pp_matchgroup_id = matchgroup_id;
5248 ++pat_count;
5249 }
5250 }
5251 vim_free(key);
5252 if (illegal || not_enough)
5253 rest = NULL;
5254
5255 /*
5256 * Must have a "start" and "end" pattern.
5257 */
5258 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5259 pat_ptrs[ITEM_END] == NULL))
5260 {
5261 not_enough = TRUE;
5262 rest = NULL;
5263 }
5264
5265 if (rest != NULL)
5266 {
5267 /*
5268 * Check for trailing garbage or command.
5269 * If OK, add the item.
5270 */
5271 eap->nextcmd = check_nextcmd(rest);
5272 if (!ends_excmd(*rest) || eap->skip)
5273 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005274 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275 && (syn_id = syn_check_group(arg,
5276 (int)(group_name_end - arg))) != 0)
5277 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005278 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005279 /*
5280 * Store the start/skip/end in the syn_items list
5281 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005282 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005283 for (item = ITEM_START; item <= ITEM_END; ++item)
5284 {
5285 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5286 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005287 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5288 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5289 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290 (item == ITEM_START) ? SPTYPE_START :
5291 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005292 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5293 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005294 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5295 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005296 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005297 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005298#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005299 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005300#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301 if (item == ITEM_START)
5302 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005303 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005304 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005305 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005306 syn_opt_arg.cont_in_list;
5307 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005308 curwin->w_s->b_syn_containedin = TRUE;
5309 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005310 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005312 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005313 ++idx;
5314#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005315 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005316 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005317#endif
5318 }
5319 }
5320
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005321 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005322 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005323 success = TRUE; /* don't free the progs and patterns now */
5324 }
5325 }
5326
5327 /*
5328 * Free the allocated memory.
5329 */
5330 for (item = ITEM_START; item <= ITEM_END; ++item)
5331 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5332 {
5333 if (!success)
5334 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005335 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 vim_free(ppp->pp_synp->sp_pattern);
5337 }
5338 vim_free(ppp->pp_synp);
5339 ppp_next = ppp->pp_next;
5340 vim_free(ppp);
5341 }
5342
5343 if (!success)
5344 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005345 vim_free(syn_opt_arg.cont_list);
5346 vim_free(syn_opt_arg.cont_in_list);
5347 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 if (not_enough)
5349 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5350 else if (illegal || rest == NULL)
5351 EMSG2(_(e_invarg2), arg);
5352 }
5353}
5354
5355/*
5356 * A simple syntax group ID comparison function suitable for use in qsort()
5357 */
5358 static int
5359#ifdef __BORLANDC__
5360_RTLENTRYF
5361#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005362syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005363{
5364 const short *s1 = v1;
5365 const short *s2 = v2;
5366
5367 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5368}
5369
5370/*
5371 * Combines lists of syntax clusters.
5372 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5373 */
5374 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005375syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005376{
5377 int count1 = 0;
5378 int count2 = 0;
5379 short *g1;
5380 short *g2;
5381 short *clstr = NULL;
5382 int count;
5383 int round;
5384
5385 /*
5386 * Handle degenerate cases.
5387 */
5388 if (*clstr2 == NULL)
5389 return;
5390 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5391 {
5392 if (list_op == CLUSTER_REPLACE)
5393 vim_free(*clstr1);
5394 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5395 *clstr1 = *clstr2;
5396 else
5397 vim_free(*clstr2);
5398 return;
5399 }
5400
5401 for (g1 = *clstr1; *g1; g1++)
5402 ++count1;
5403 for (g2 = *clstr2; *g2; g2++)
5404 ++count2;
5405
5406 /*
5407 * For speed purposes, sort both lists.
5408 */
5409 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5410 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5411
5412 /*
5413 * We proceed in two passes; in round 1, we count the elements to place
5414 * in the new list, and in round 2, we allocate and populate the new
5415 * list. For speed, we use a mergesort-like method, adding the smaller
5416 * of the current elements in each list to the new list.
5417 */
5418 for (round = 1; round <= 2; round++)
5419 {
5420 g1 = *clstr1;
5421 g2 = *clstr2;
5422 count = 0;
5423
5424 /*
5425 * First, loop through the lists until one of them is empty.
5426 */
5427 while (*g1 && *g2)
5428 {
5429 /*
5430 * We always want to add from the first list.
5431 */
5432 if (*g1 < *g2)
5433 {
5434 if (round == 2)
5435 clstr[count] = *g1;
5436 count++;
5437 g1++;
5438 continue;
5439 }
5440 /*
5441 * We only want to add from the second list if we're adding the
5442 * lists.
5443 */
5444 if (list_op == CLUSTER_ADD)
5445 {
5446 if (round == 2)
5447 clstr[count] = *g2;
5448 count++;
5449 }
5450 if (*g1 == *g2)
5451 g1++;
5452 g2++;
5453 }
5454
5455 /*
5456 * Now add the leftovers from whichever list didn't get finished
5457 * first. As before, we only want to add from the second list if
5458 * we're adding the lists.
5459 */
5460 for (; *g1; g1++, count++)
5461 if (round == 2)
5462 clstr[count] = *g1;
5463 if (list_op == CLUSTER_ADD)
5464 for (; *g2; g2++, count++)
5465 if (round == 2)
5466 clstr[count] = *g2;
5467
5468 if (round == 1)
5469 {
5470 /*
5471 * If the group ended up empty, we don't need to allocate any
5472 * space for it.
5473 */
5474 if (count == 0)
5475 {
5476 clstr = NULL;
5477 break;
5478 }
5479 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5480 if (clstr == NULL)
5481 break;
5482 clstr[count] = 0;
5483 }
5484 }
5485
5486 /*
5487 * Finally, put the new list in place.
5488 */
5489 vim_free(*clstr1);
5490 vim_free(*clstr2);
5491 *clstr1 = clstr;
5492}
5493
5494/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005495 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005496 * If it is not found, 0 is returned.
5497 */
5498 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005499syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005500{
5501 int i;
5502 char_u *name_u;
5503
5504 /* Avoid using stricmp() too much, it's slow on some systems */
5505 name_u = vim_strsave_up(name);
5506 if (name_u == NULL)
5507 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005508 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5509 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5510 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005511 break;
5512 vim_free(name_u);
5513 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5514}
5515
5516/*
5517 * Like syn_scl_name2id(), but take a pointer + length argument.
5518 */
5519 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005520syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005521{
5522 char_u *name;
5523 int id = 0;
5524
5525 name = vim_strnsave(linep, len);
5526 if (name != NULL)
5527 {
5528 id = syn_scl_name2id(name);
5529 vim_free(name);
5530 }
5531 return id;
5532}
5533
5534/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005535 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 * The argument is a pointer to the name and the length of the name.
5537 * If it doesn't exist yet, a new entry is created.
5538 * Return 0 for failure.
5539 */
5540 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005541syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542{
5543 int id;
5544 char_u *name;
5545
5546 name = vim_strnsave(pp, len);
5547 if (name == NULL)
5548 return 0;
5549
5550 id = syn_scl_name2id(name);
5551 if (id == 0) /* doesn't exist yet */
5552 id = syn_add_cluster(name);
5553 else
5554 vim_free(name);
5555 return id;
5556}
5557
5558/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005559 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560 * "name" must be an allocated string, it will be consumed.
5561 * Return 0 for failure.
5562 */
5563 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005564syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005566 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567
5568 /*
5569 * First call for this growarray: init growing array.
5570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005571 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005573 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5574 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005575 }
5576
Bram Moolenaar42431a72011-04-01 14:44:59 +02005577 len = curwin->w_s->b_syn_clusters.ga_len;
5578 if (len >= MAX_CLUSTER_ID)
5579 {
5580 EMSG((char_u *)_("E848: Too many syntax clusters"));
5581 vim_free(name);
5582 return 0;
5583 }
5584
Bram Moolenaar071d4272004-06-13 20:20:40 +00005585 /*
5586 * Make room for at least one other cluster entry.
5587 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005588 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005589 {
5590 vim_free(name);
5591 return 0;
5592 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005593
Bram Moolenaar860cae12010-06-05 23:22:07 +02005594 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5595 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5596 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5597 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5598 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599
Bram Moolenaar217ad922005-03-20 22:37:15 +00005600 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005601 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005602 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005603 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005604
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605 return len + SYNID_CLUSTER;
5606}
5607
5608/*
5609 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5610 * [add={groupname},..] [remove={groupname},..]".
5611 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005613syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614{
5615 char_u *arg = eap->arg;
5616 char_u *group_name_end;
5617 char_u *rest;
5618 int scl_id;
5619 short *clstr_list;
5620 int got_clstr = FALSE;
5621 int opt_len;
5622 int list_op;
5623
5624 eap->nextcmd = find_nextcmd(arg);
5625 if (eap->skip)
5626 return;
5627
5628 rest = get_group_name(arg, &group_name_end);
5629
5630 if (rest != NULL)
5631 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005632 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5633 if (scl_id == 0)
5634 return;
5635 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636
5637 for (;;)
5638 {
5639 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005640 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641 {
5642 opt_len = 3;
5643 list_op = CLUSTER_ADD;
5644 }
5645 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005646 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647 {
5648 opt_len = 6;
5649 list_op = CLUSTER_SUBTRACT;
5650 }
5651 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005652 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653 {
5654 opt_len = 8;
5655 list_op = CLUSTER_REPLACE;
5656 }
5657 else
5658 break;
5659
5660 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005661 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005662 {
5663 EMSG2(_(e_invarg2), rest);
5664 break;
5665 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005666 if (scl_id >= 0)
5667 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005669 else
5670 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671 got_clstr = TRUE;
5672 }
5673
5674 if (got_clstr)
5675 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005676 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005677 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678 }
5679 }
5680
5681 if (!got_clstr)
5682 EMSG(_("E400: No cluster specified"));
5683 if (rest == NULL || !ends_excmd(*rest))
5684 EMSG2(_(e_invarg2), arg);
5685}
5686
5687/*
5688 * On first call for current buffer: Init growing array.
5689 */
5690 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005691init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005692{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005693 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5694 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695}
5696
5697/*
5698 * Get one pattern for a ":syntax match" or ":syntax region" command.
5699 * Stores the pattern and program in a synpat_T.
5700 * Returns a pointer to the next argument, or NULL in case of an error.
5701 */
5702 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005703get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704{
5705 char_u *end;
5706 int *p;
5707 int idx;
5708 char_u *cpo_save;
5709
5710 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005711 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005712 return NULL;
5713
5714 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5715 if (*end != *arg) /* end delimiter not found */
5716 {
5717 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5718 return NULL;
5719 }
5720 /* store the pattern and compiled regexp program */
5721 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5722 return NULL;
5723
5724 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5725 cpo_save = p_cpo;
5726 p_cpo = (char_u *)"";
5727 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5728 p_cpo = cpo_save;
5729
5730 if (ci->sp_prog == NULL)
5731 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005732 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005733#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005734 syn_clear_time(&ci->sp_time);
5735#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736
5737 /*
5738 * Check for a match, highlight or region offset.
5739 */
5740 ++end;
5741 do
5742 {
5743 for (idx = SPO_COUNT; --idx >= 0; )
5744 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5745 break;
5746 if (idx >= 0)
5747 {
5748 p = &(ci->sp_offsets[idx]);
5749 if (idx != SPO_LC_OFF)
5750 switch (end[3])
5751 {
5752 case 's': break;
5753 case 'b': break;
5754 case 'e': idx += SPO_COUNT; break;
5755 default: idx = -1; break;
5756 }
5757 if (idx >= 0)
5758 {
5759 ci->sp_off_flags |= (1 << idx);
5760 if (idx == SPO_LC_OFF) /* lc=99 */
5761 {
5762 end += 3;
5763 *p = getdigits(&end);
5764
5765 /* "lc=" offset automatically sets "ms=" offset */
5766 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5767 {
5768 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5769 ci->sp_offsets[SPO_MS_OFF] = *p;
5770 }
5771 }
5772 else /* yy=x+99 */
5773 {
5774 end += 4;
5775 if (*end == '+')
5776 {
5777 ++end;
5778 *p = getdigits(&end); /* positive offset */
5779 }
5780 else if (*end == '-')
5781 {
5782 ++end;
5783 *p = -getdigits(&end); /* negative offset */
5784 }
5785 }
5786 if (*end != ',')
5787 break;
5788 ++end;
5789 }
5790 }
5791 } while (idx >= 0);
5792
Bram Moolenaar1c465442017-03-12 20:10:05 +01005793 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005794 {
5795 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5796 return NULL;
5797 }
5798 return skipwhite(end);
5799}
5800
5801/*
5802 * Handle ":syntax sync .." command.
5803 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005804 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005805syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005806{
5807 char_u *arg_start = eap->arg;
5808 char_u *arg_end;
5809 char_u *key = NULL;
5810 char_u *next_arg;
5811 int illegal = FALSE;
5812 int finished = FALSE;
5813 long n;
5814 char_u *cpo_save;
5815
5816 if (ends_excmd(*arg_start))
5817 {
5818 syn_cmd_list(eap, TRUE);
5819 return;
5820 }
5821
5822 while (!ends_excmd(*arg_start))
5823 {
5824 arg_end = skiptowhite(arg_start);
5825 next_arg = skipwhite(arg_end);
5826 vim_free(key);
5827 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5828 if (STRCMP(key, "CCOMMENT") == 0)
5829 {
5830 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005831 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005832 if (!ends_excmd(*next_arg))
5833 {
5834 arg_end = skiptowhite(next_arg);
5835 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005836 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005837 (int)(arg_end - next_arg));
5838 next_arg = skipwhite(arg_end);
5839 }
5840 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005841 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842 }
5843 else if ( STRNCMP(key, "LINES", 5) == 0
5844 || STRNCMP(key, "MINLINES", 8) == 0
5845 || STRNCMP(key, "MAXLINES", 8) == 0
5846 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5847 {
5848 if (key[4] == 'S')
5849 arg_end = key + 6;
5850 else if (key[0] == 'L')
5851 arg_end = key + 11;
5852 else
5853 arg_end = key + 9;
5854 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5855 {
5856 illegal = TRUE;
5857 break;
5858 }
5859 n = getdigits(&arg_end);
5860 if (!eap->skip)
5861 {
5862 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005865 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005867 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005868 }
5869 }
5870 else if (STRCMP(key, "FROMSTART") == 0)
5871 {
5872 if (!eap->skip)
5873 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005874 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5875 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005876 }
5877 }
5878 else if (STRCMP(key, "LINECONT") == 0)
5879 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005880 if (*next_arg == NUL) /* missing pattern */
5881 {
5882 illegal = TRUE;
5883 break;
5884 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005885 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 {
5887 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5888 finished = TRUE;
5889 break;
5890 }
5891 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5892 if (*arg_end != *next_arg) /* end delimiter not found */
5893 {
5894 illegal = TRUE;
5895 break;
5896 }
5897
5898 if (!eap->skip)
5899 {
5900 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005901 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005902 (int)(arg_end - next_arg - 1))) == NULL)
5903 {
5904 finished = TRUE;
5905 break;
5906 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005907 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005908
5909 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5910 cpo_save = p_cpo;
5911 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005912 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005913 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005915#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005916 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5917#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005918
Bram Moolenaar860cae12010-06-05 23:22:07 +02005919 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005920 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005921 vim_free(curwin->w_s->b_syn_linecont_pat);
5922 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005923 finished = TRUE;
5924 break;
5925 }
5926 }
5927 next_arg = skipwhite(arg_end + 1);
5928 }
5929 else
5930 {
5931 eap->arg = next_arg;
5932 if (STRCMP(key, "MATCH") == 0)
5933 syn_cmd_match(eap, TRUE);
5934 else if (STRCMP(key, "REGION") == 0)
5935 syn_cmd_region(eap, TRUE);
5936 else if (STRCMP(key, "CLEAR") == 0)
5937 syn_cmd_clear(eap, TRUE);
5938 else
5939 illegal = TRUE;
5940 finished = TRUE;
5941 break;
5942 }
5943 arg_start = next_arg;
5944 }
5945 vim_free(key);
5946 if (illegal)
5947 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5948 else if (!finished)
5949 {
5950 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005951 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005952 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005953 }
5954}
5955
5956/*
5957 * Convert a line of highlight group names into a list of group ID numbers.
5958 * "arg" should point to the "contains" or "nextgroup" keyword.
5959 * "arg" is advanced to after the last group name.
5960 * Careful: the argument is modified (NULs added).
5961 * returns FAIL for some error, OK for success.
5962 */
5963 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005964get_id_list(
5965 char_u **arg,
5966 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005967 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005968 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005969 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005970{
5971 char_u *p = NULL;
5972 char_u *end;
5973 int round;
5974 int count;
5975 int total_count = 0;
5976 short *retval = NULL;
5977 char_u *name;
5978 regmatch_T regmatch;
5979 int id;
5980 int i;
5981 int failed = FALSE;
5982
5983 /*
5984 * We parse the list twice:
5985 * round == 1: count the number of items, allocate the array.
5986 * round == 2: fill the array with the items.
5987 * In round 1 new groups may be added, causing the number of items to
5988 * grow when a regexp is used. In that case round 1 is done once again.
5989 */
5990 for (round = 1; round <= 2; ++round)
5991 {
5992 /*
5993 * skip "contains"
5994 */
5995 p = skipwhite(*arg + keylen);
5996 if (*p != '=')
5997 {
5998 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5999 break;
6000 }
6001 p = skipwhite(p + 1);
6002 if (ends_excmd(*p))
6003 {
6004 EMSG2(_("E406: Empty argument: %s"), *arg);
6005 break;
6006 }
6007
6008 /*
6009 * parse the arguments after "contains"
6010 */
6011 count = 0;
6012 while (!ends_excmd(*p))
6013 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006014 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006015 ;
6016 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6017 if (name == NULL)
6018 {
6019 failed = TRUE;
6020 break;
6021 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006022 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006023 if ( STRCMP(name + 1, "ALLBUT") == 0
6024 || STRCMP(name + 1, "ALL") == 0
6025 || STRCMP(name + 1, "TOP") == 0
6026 || STRCMP(name + 1, "CONTAINED") == 0)
6027 {
6028 if (TOUPPER_ASC(**arg) != 'C')
6029 {
6030 EMSG2(_("E407: %s not allowed here"), name + 1);
6031 failed = TRUE;
6032 vim_free(name);
6033 break;
6034 }
6035 if (count != 0)
6036 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006037 EMSG2(_("E408: %s must be first in contains list"),
6038 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006039 failed = TRUE;
6040 vim_free(name);
6041 break;
6042 }
6043 if (name[1] == 'A')
6044 id = SYNID_ALLBUT;
6045 else if (name[1] == 'T')
6046 id = SYNID_TOP;
6047 else
6048 id = SYNID_CONTAINED;
6049 id += current_syn_inc_tag;
6050 }
6051 else if (name[1] == '@')
6052 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006053 if (skip)
6054 id = -1;
6055 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006056 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057 }
6058 else
6059 {
6060 /*
6061 * Handle full group name.
6062 */
6063 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6064 id = syn_check_group(name + 1, (int)(end - p));
6065 else
6066 {
6067 /*
6068 * Handle match of regexp with group names.
6069 */
6070 *name = '^';
6071 STRCAT(name, "$");
6072 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6073 if (regmatch.regprog == NULL)
6074 {
6075 failed = TRUE;
6076 vim_free(name);
6077 break;
6078 }
6079
6080 regmatch.rm_ic = TRUE;
6081 id = 0;
6082 for (i = highlight_ga.ga_len; --i >= 0; )
6083 {
6084 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6085 (colnr_T)0))
6086 {
6087 if (round == 2)
6088 {
6089 /* Got more items than expected; can happen
6090 * when adding items that match:
6091 * "contains=a.*b,axb".
6092 * Go back to first round */
6093 if (count >= total_count)
6094 {
6095 vim_free(retval);
6096 round = 1;
6097 }
6098 else
6099 retval[count] = i + 1;
6100 }
6101 ++count;
6102 id = -1; /* remember that we found one */
6103 }
6104 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006105 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006106 }
6107 }
6108 vim_free(name);
6109 if (id == 0)
6110 {
6111 EMSG2(_("E409: Unknown group name: %s"), p);
6112 failed = TRUE;
6113 break;
6114 }
6115 if (id > 0)
6116 {
6117 if (round == 2)
6118 {
6119 /* Got more items than expected, go back to first round */
6120 if (count >= total_count)
6121 {
6122 vim_free(retval);
6123 round = 1;
6124 }
6125 else
6126 retval[count] = id;
6127 }
6128 ++count;
6129 }
6130 p = skipwhite(end);
6131 if (*p != ',')
6132 break;
6133 p = skipwhite(p + 1); /* skip comma in between arguments */
6134 }
6135 if (failed)
6136 break;
6137 if (round == 1)
6138 {
6139 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6140 if (retval == NULL)
6141 break;
6142 retval[count] = 0; /* zero means end of the list */
6143 total_count = count;
6144 }
6145 }
6146
6147 *arg = p;
6148 if (failed || retval == NULL)
6149 {
6150 vim_free(retval);
6151 return FAIL;
6152 }
6153
6154 if (*list == NULL)
6155 *list = retval;
6156 else
6157 vim_free(retval); /* list already found, don't overwrite it */
6158
6159 return OK;
6160}
6161
6162/*
6163 * Make a copy of an ID list.
6164 */
6165 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006166copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006167{
6168 int len;
6169 int count;
6170 short *retval;
6171
6172 if (list == NULL)
6173 return NULL;
6174
6175 for (count = 0; list[count]; ++count)
6176 ;
6177 len = (count + 1) * sizeof(short);
6178 retval = (short *)alloc((unsigned)len);
6179 if (retval != NULL)
6180 mch_memmove(retval, list, (size_t)len);
6181
6182 return retval;
6183}
6184
6185/*
6186 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6187 * "cur_si" can be NULL if not checking the "containedin" list.
6188 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6189 * the current item.
6190 * This function is called very often, keep it fast!!
6191 */
6192 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006193in_id_list(
6194 stateitem_T *cur_si, /* current item or NULL */
6195 short *list, /* id list */
6196 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6197 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006198{
6199 int retval;
6200 short *scl_list;
6201 short item;
6202 short id = ssp->id;
6203 static int depth = 0;
6204 int r;
6205
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006206 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006207 if (cur_si != NULL && ssp->cont_in_list != NULL
6208 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006209 {
6210 /* Ignore transparent items without a contains argument. Double check
6211 * that we don't go back past the first one. */
6212 while ((cur_si->si_flags & HL_TRANS_CONT)
6213 && cur_si > (stateitem_T *)(current_state.ga_data))
6214 --cur_si;
6215 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6216 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006217 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6218 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006219 return TRUE;
6220 }
6221
6222 if (list == NULL)
6223 return FALSE;
6224
6225 /*
6226 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6227 * inside anything. Only allow not-contained groups.
6228 */
6229 if (list == ID_LIST_ALL)
6230 return !contained;
6231
6232 /*
6233 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6234 * contains list. We also require that "id" is at the same ":syn include"
6235 * level as the list.
6236 */
6237 item = *list;
6238 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6239 {
6240 if (item < SYNID_TOP)
6241 {
6242 /* ALL or ALLBUT: accept all groups in the same file */
6243 if (item - SYNID_ALLBUT != ssp->inc_tag)
6244 return FALSE;
6245 }
6246 else if (item < SYNID_CONTAINED)
6247 {
6248 /* TOP: accept all not-contained groups in the same file */
6249 if (item - SYNID_TOP != ssp->inc_tag || contained)
6250 return FALSE;
6251 }
6252 else
6253 {
6254 /* CONTAINED: accept all contained groups in the same file */
6255 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6256 return FALSE;
6257 }
6258 item = *++list;
6259 retval = FALSE;
6260 }
6261 else
6262 retval = TRUE;
6263
6264 /*
6265 * Return "retval" if id is in the contains list.
6266 */
6267 while (item != 0)
6268 {
6269 if (item == id)
6270 return retval;
6271 if (item >= SYNID_CLUSTER)
6272 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006273 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006274 /* restrict recursiveness to 30 to avoid an endless loop for a
6275 * cluster that includes itself (indirectly) */
6276 if (scl_list != NULL && depth < 30)
6277 {
6278 ++depth;
6279 r = in_id_list(NULL, scl_list, ssp, contained);
6280 --depth;
6281 if (r)
6282 return retval;
6283 }
6284 }
6285 item = *++list;
6286 }
6287 return !retval;
6288}
6289
6290struct subcommand
6291{
Bram Moolenaard99df422016-01-29 23:20:40 +01006292 char *name; /* subcommand name */
6293 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006294};
6295
6296static struct subcommand subcommands[] =
6297{
6298 {"case", syn_cmd_case},
6299 {"clear", syn_cmd_clear},
6300 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006301 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 {"enable", syn_cmd_enable},
6303 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006304 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305 {"keyword", syn_cmd_keyword},
6306 {"list", syn_cmd_list},
6307 {"manual", syn_cmd_manual},
6308 {"match", syn_cmd_match},
6309 {"on", syn_cmd_on},
6310 {"off", syn_cmd_off},
6311 {"region", syn_cmd_region},
6312 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006313 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006314 {"sync", syn_cmd_sync},
6315 {"", syn_cmd_list},
6316 {NULL, NULL}
6317};
6318
6319/*
6320 * ":syntax".
6321 * This searches the subcommands[] table for the subcommand name, and calls a
6322 * syntax_subcommand() function to do the rest.
6323 */
6324 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006325ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326{
6327 char_u *arg = eap->arg;
6328 char_u *subcmd_end;
6329 char_u *subcmd_name;
6330 int i;
6331
6332 syn_cmdlinep = eap->cmdlinep;
6333
6334 /* isolate subcommand name */
6335 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6336 ;
6337 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6338 if (subcmd_name != NULL)
6339 {
6340 if (eap->skip) /* skip error messages for all subcommands */
6341 ++emsg_skip;
6342 for (i = 0; ; ++i)
6343 {
6344 if (subcommands[i].name == NULL)
6345 {
6346 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6347 break;
6348 }
6349 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6350 {
6351 eap->arg = skipwhite(subcmd_end);
6352 (subcommands[i].func)(eap, FALSE);
6353 break;
6354 }
6355 }
6356 vim_free(subcmd_name);
6357 if (eap->skip)
6358 --emsg_skip;
6359 }
6360}
6361
Bram Moolenaar860cae12010-06-05 23:22:07 +02006362 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006363ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006364{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006365 char_u *old_value;
6366 char_u *new_value;
6367
Bram Moolenaar860cae12010-06-05 23:22:07 +02006368 if (curwin->w_s == &curwin->w_buffer->b_s)
6369 {
6370 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6371 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006372 hash_init(&curwin->w_s->b_keywtab);
6373 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006374#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006375 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006376 curwin->w_p_spell = FALSE; /* No spell checking */
6377 clear_string_option(&curwin->w_s->b_p_spc);
6378 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006379 clear_string_option(&curwin->w_s->b_p_spl);
6380#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006381 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006382 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006383
6384 /* save value of b:current_syntax */
6385 old_value = get_var_value((char_u *)"b:current_syntax");
6386 if (old_value != NULL)
6387 old_value = vim_strsave(old_value);
6388
Bram Moolenaard1413d92016-03-02 21:51:56 +01006389#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006390 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6391 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006392 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006393#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006394
6395 /* move value of b:current_syntax to w:current_syntax */
6396 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006397 if (new_value != NULL)
6398 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006399
6400 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006401 if (old_value == NULL)
6402 do_unlet((char_u *)"b:current_syntax", TRUE);
6403 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006404 {
6405 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6406 vim_free(old_value);
6407 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006408}
6409
6410 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006411syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006412{
6413 return (win->w_s->b_syn_patterns.ga_len != 0
6414 || win->w_s->b_syn_clusters.ga_len != 0
6415 || win->w_s->b_keywtab.ht_used > 0
6416 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006417}
6418
6419#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6420
6421static enum
6422{
6423 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006424 EXP_CASE, /* expand ":syn case" arguments */
6425 EXP_SPELL, /* expand ":syn spell" arguments */
6426 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006427} expand_what;
6428
Bram Moolenaar4f688582007-07-24 12:34:30 +00006429/*
6430 * Reset include_link, include_default, include_none to 0.
6431 * Called when we are done expanding.
6432 */
6433 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006434reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006435{
6436 include_link = include_default = include_none = 0;
6437}
6438
6439/*
6440 * Handle command line completion for :match and :echohl command: Add "None"
6441 * as highlight group.
6442 */
6443 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006444set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006445{
6446 xp->xp_context = EXPAND_HIGHLIGHT;
6447 xp->xp_pattern = arg;
6448 include_none = 1;
6449}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450
6451/*
6452 * Handle command line completion for :syntax command.
6453 */
6454 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006455set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006456{
6457 char_u *p;
6458
6459 /* Default: expand subcommands */
6460 xp->xp_context = EXPAND_SYNTAX;
6461 expand_what = EXP_SUBCMD;
6462 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006463 include_link = 0;
6464 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006465
6466 /* (part of) subcommand already typed */
6467 if (*arg != NUL)
6468 {
6469 p = skiptowhite(arg);
6470 if (*p != NUL) /* past first word */
6471 {
6472 xp->xp_pattern = skipwhite(p);
6473 if (*skiptowhite(xp->xp_pattern) != NUL)
6474 xp->xp_context = EXPAND_NOTHING;
6475 else if (STRNICMP(arg, "case", p - arg) == 0)
6476 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006477 else if (STRNICMP(arg, "spell", p - arg) == 0)
6478 expand_what = EXP_SPELL;
6479 else if (STRNICMP(arg, "sync", p - arg) == 0)
6480 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006481 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6482 || STRNICMP(arg, "region", p - arg) == 0
6483 || STRNICMP(arg, "match", p - arg) == 0
6484 || STRNICMP(arg, "list", p - arg) == 0)
6485 xp->xp_context = EXPAND_HIGHLIGHT;
6486 else
6487 xp->xp_context = EXPAND_NOTHING;
6488 }
6489 }
6490}
6491
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492/*
6493 * Function given to ExpandGeneric() to obtain the list syntax names for
6494 * expansion.
6495 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006497get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006499 switch (expand_what)
6500 {
6501 case EXP_SUBCMD:
6502 return (char_u *)subcommands[idx].name;
6503 case EXP_CASE:
6504 {
6505 static char *case_args[] = {"match", "ignore", NULL};
6506 return (char_u *)case_args[idx];
6507 }
6508 case EXP_SPELL:
6509 {
6510 static char *spell_args[] =
6511 {"toplevel", "notoplevel", "default", NULL};
6512 return (char_u *)spell_args[idx];
6513 }
6514 case EXP_SYNC:
6515 {
6516 static char *sync_args[] =
6517 {"ccomment", "clear", "fromstart",
6518 "linebreaks=", "linecont", "lines=", "match",
6519 "maxlines=", "minlines=", "region", NULL};
6520 return (char_u *)sync_args[idx];
6521 }
6522 }
6523 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524}
6525
6526#endif /* FEAT_CMDL_COMPL */
6527
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528/*
6529 * Function called for expression evaluation: get syntax ID at file position.
6530 */
6531 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006532syn_get_id(
6533 win_T *wp,
6534 long lnum,
6535 colnr_T col,
6536 int trans, /* remove transparency */
6537 int *spellp, /* return: can do spell checking */
6538 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539{
6540 /* When the position is not after the current position and in the same
6541 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006542 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006543 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006544 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006545 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006546 else if (wp->w_buffer == syn_buf
6547 && lnum == current_lnum
6548 && col > current_col)
6549 /* next_match may not be correct when moving around, e.g. with the
6550 * "skip" expression in searchpair() */
6551 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006553 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006554
6555 return (trans ? current_trans_id : current_id);
6556}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557
Bram Moolenaar860cae12010-06-05 23:22:07 +02006558#if defined(FEAT_CONCEAL) || defined(PROTO)
6559/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006560 * Get extra information about the syntax item. Must be called right after
6561 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006562 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006563 * Returns the current flags.
6564 */
6565 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006566get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006567{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006568 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006569 return current_flags;
6570}
6571
6572/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006573 * Return conceal substitution character
6574 */
6575 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006576syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006577{
6578 return current_sub_char;
6579}
6580#endif
6581
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006582#if defined(FEAT_EVAL) || defined(PROTO)
6583/*
6584 * Return the syntax ID at position "i" in the current stack.
6585 * The caller must have called syn_get_id() before to fill the stack.
6586 * Returns -1 when "i" is out of range.
6587 */
6588 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006589syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006590{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006591 if (i >= current_state.ga_len)
6592 {
6593 /* Need to invalidate the state, because we didn't properly finish it
6594 * for the last character, "keep_state" was TRUE. */
6595 invalidate_current_state();
6596 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006597 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006598 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006599 return CUR_STATE(i).si_id;
6600}
6601#endif
6602
Bram Moolenaar071d4272004-06-13 20:20:40 +00006603#if defined(FEAT_FOLDING) || defined(PROTO)
6604/*
6605 * Function called to get folding level for line "lnum" in window "wp".
6606 */
6607 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006608syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006609{
6610 int level = 0;
6611 int i;
6612
6613 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006614 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006615 {
6616 syntax_start(wp, lnum);
6617
6618 for (i = 0; i < current_state.ga_len; ++i)
6619 if (CUR_STATE(i).si_flags & HL_FOLD)
6620 ++level;
6621 }
6622 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006623 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006624 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006625 if (level < 0)
6626 level = 0;
6627 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006628 return level;
6629}
6630#endif
6631
Bram Moolenaar01615492015-02-03 13:00:38 +01006632#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006633/*
6634 * ":syntime".
6635 */
6636 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006637ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006638{
6639 if (STRCMP(eap->arg, "on") == 0)
6640 syn_time_on = TRUE;
6641 else if (STRCMP(eap->arg, "off") == 0)
6642 syn_time_on = FALSE;
6643 else if (STRCMP(eap->arg, "clear") == 0)
6644 syntime_clear();
6645 else if (STRCMP(eap->arg, "report") == 0)
6646 syntime_report();
6647 else
6648 EMSG2(_(e_invarg2), eap->arg);
6649}
6650
6651 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006652syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006653{
6654 profile_zero(&st->total);
6655 profile_zero(&st->slowest);
6656 st->count = 0;
6657 st->match = 0;
6658}
6659
6660/*
6661 * Clear the syntax timing for the current buffer.
6662 */
6663 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006664syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006665{
6666 int idx;
6667 synpat_T *spp;
6668
6669 if (!syntax_present(curwin))
6670 {
6671 MSG(_(msg_no_items));
6672 return;
6673 }
6674 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6675 {
6676 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6677 syn_clear_time(&spp->sp_time);
6678 }
6679}
6680
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006681#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6682/*
6683 * Function given to ExpandGeneric() to obtain the possible arguments of the
6684 * ":syntime {on,off,clear,report}" command.
6685 */
6686 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006687get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006688{
6689 switch (idx)
6690 {
6691 case 0: return (char_u *)"on";
6692 case 1: return (char_u *)"off";
6693 case 2: return (char_u *)"clear";
6694 case 3: return (char_u *)"report";
6695 }
6696 return NULL;
6697}
6698#endif
6699
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006700typedef struct
6701{
6702 proftime_T total;
6703 int count;
6704 int match;
6705 proftime_T slowest;
6706 proftime_T average;
6707 int id;
6708 char_u *pattern;
6709} time_entry_T;
6710
6711 static int
6712#ifdef __BORLANDC__
6713_RTLENTRYF
6714#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006715syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006716{
6717 const time_entry_T *s1 = v1;
6718 const time_entry_T *s2 = v2;
6719
6720 return profile_cmp(&s1->total, &s2->total);
6721}
6722
6723/*
6724 * Clear the syntax timing for the current buffer.
6725 */
6726 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006727syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006728{
6729 int idx;
6730 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006731# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006732 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006733# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006734 int len;
6735 proftime_T total_total;
6736 int total_count = 0;
6737 garray_T ga;
6738 time_entry_T *p;
6739
6740 if (!syntax_present(curwin))
6741 {
6742 MSG(_(msg_no_items));
6743 return;
6744 }
6745
6746 ga_init2(&ga, sizeof(time_entry_T), 50);
6747 profile_zero(&total_total);
6748 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6749 {
6750 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6751 if (spp->sp_time.count > 0)
6752 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006753 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006754 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6755 p->total = spp->sp_time.total;
6756 profile_add(&total_total, &spp->sp_time.total);
6757 p->count = spp->sp_time.count;
6758 p->match = spp->sp_time.match;
6759 total_count += spp->sp_time.count;
6760 p->slowest = spp->sp_time.slowest;
6761# ifdef FEAT_FLOAT
6762 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6763 p->average = tm;
6764# endif
6765 p->id = spp->sp_syn.id;
6766 p->pattern = spp->sp_pattern;
6767 ++ga.ga_len;
6768 }
6769 }
6770
Bram Moolenaara2162552017-01-08 17:46:20 +01006771 /* Sort on total time. Skip if there are no items to avoid passing NULL
6772 * pointer to qsort(). */
6773 if (ga.ga_len > 1)
6774 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006775 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006776
6777 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6778 MSG_PUTS("\n");
6779 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6780 {
6781 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6782 p = ((time_entry_T *)ga.ga_data) + idx;
6783
6784 MSG_PUTS(profile_msg(&p->total));
6785 MSG_PUTS(" "); /* make sure there is always a separating space */
6786 msg_advance(13);
6787 msg_outnum(p->count);
6788 MSG_PUTS(" ");
6789 msg_advance(20);
6790 msg_outnum(p->match);
6791 MSG_PUTS(" ");
6792 msg_advance(26);
6793 MSG_PUTS(profile_msg(&p->slowest));
6794 MSG_PUTS(" ");
6795 msg_advance(38);
6796# ifdef FEAT_FLOAT
6797 MSG_PUTS(profile_msg(&p->average));
6798 MSG_PUTS(" ");
6799# endif
6800 msg_advance(50);
6801 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6802 MSG_PUTS(" ");
6803
6804 msg_advance(69);
6805 if (Columns < 80)
6806 len = 20; /* will wrap anyway */
6807 else
6808 len = Columns - 70;
6809 if (len > (int)STRLEN(p->pattern))
6810 len = (int)STRLEN(p->pattern);
6811 msg_outtrans_len(p->pattern, len);
6812 MSG_PUTS("\n");
6813 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006814 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006815 if (!got_int)
6816 {
6817 MSG_PUTS("\n");
6818 MSG_PUTS(profile_msg(&total_total));
6819 msg_advance(13);
6820 msg_outnum(total_count);
6821 MSG_PUTS("\n");
6822 }
6823}
6824#endif
6825
Bram Moolenaar071d4272004-06-13 20:20:40 +00006826#endif /* FEAT_SYN_HL */
6827
Bram Moolenaar071d4272004-06-13 20:20:40 +00006828/**************************************
6829 * Highlighting stuff *
6830 **************************************/
6831
6832/*
6833 * The default highlight groups. These are compiled-in for fast startup and
6834 * they still work when the runtime files can't be found.
6835 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006836 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6837 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006838 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006839#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006840# define CENT(a, b) b
6841#else
6842# define CENT(a, b) a
6843#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006844static char *(highlight_init_both[]) =
6845 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006846 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6847 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6848 CENT("IncSearch term=reverse cterm=reverse",
6849 "IncSearch term=reverse cterm=reverse gui=reverse"),
6850 CENT("ModeMsg term=bold cterm=bold",
6851 "ModeMsg term=bold cterm=bold gui=bold"),
6852 CENT("NonText term=bold ctermfg=Blue",
6853 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6854 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6855 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6856 CENT("StatusLineNC term=reverse cterm=reverse",
6857 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006858 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006859#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006860 CENT("VertSplit term=reverse cterm=reverse",
6861 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006862#endif
6863#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006864 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6865 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006866#endif
6867#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006868 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6869 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006870#endif
6871#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006872 CENT("PmenuSbar ctermbg=Grey",
6873 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006874#endif
6875#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006876 CENT("TabLineSel term=bold cterm=bold",
6877 "TabLineSel term=bold cterm=bold gui=bold"),
6878 CENT("TabLineFill term=reverse cterm=reverse",
6879 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006880#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006881#ifdef FEAT_GUI
6882 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006883 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006884#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006885 NULL
6886 };
6887
6888static char *(highlight_init_light[]) =
6889 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006890 CENT("Directory term=bold ctermfg=DarkBlue",
6891 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6892 CENT("LineNr term=underline ctermfg=Brown",
6893 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006894 CENT("CursorLineNr term=bold ctermfg=Brown",
6895 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006896 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6897 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6898 CENT("Question term=standout ctermfg=DarkGreen",
6899 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6900 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6901 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006902#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006903 CENT("SpellBad term=reverse ctermbg=LightRed",
6904 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6905 CENT("SpellCap term=reverse ctermbg=LightBlue",
6906 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6907 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6908 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6909 CENT("SpellLocal term=underline ctermbg=Cyan",
6910 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006911#endif
6912#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006913 CENT("PmenuThumb ctermbg=Black",
6914 "PmenuThumb ctermbg=Black guibg=Black"),
6915 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6916 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6917 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6918 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006919#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006920 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6921 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6922 CENT("Title term=bold ctermfg=DarkMagenta",
6923 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6924 CENT("WarningMsg term=standout ctermfg=DarkRed",
6925 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006926#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006927 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6928 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006929#endif
6930#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006931 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6932 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6933 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6934 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006935#endif
6936#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006937 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6938 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006939#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006940 CENT("Visual term=reverse",
6941 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006942#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006943 CENT("DiffAdd term=bold ctermbg=LightBlue",
6944 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6945 CENT("DiffChange term=bold ctermbg=LightMagenta",
6946 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6947 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6948 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006949#endif
6950#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006951 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6952 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006953#endif
6954#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006955 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006956 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006957 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006958 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006959 CENT("ColorColumn term=reverse ctermbg=LightRed",
6960 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006961#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006962#ifdef FEAT_CONCEAL
6963 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6964 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6965#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006966#ifdef FEAT_AUTOCMD
6967 CENT("MatchParen term=reverse ctermbg=Cyan",
6968 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6969#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006970#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006971 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006972#endif
Bram Moolenaar21020352017-06-13 17:21:04 +02006973 CENT("QuickFixLine term=reverse ctermbg=Cyan",
6974 "QuickFixLine term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006975 NULL
6976 };
6977
6978static char *(highlight_init_dark[]) =
6979 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006980 CENT("Directory term=bold ctermfg=LightCyan",
6981 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6982 CENT("LineNr term=underline ctermfg=Yellow",
6983 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006984 CENT("CursorLineNr term=bold ctermfg=Yellow",
6985 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006986 CENT("MoreMsg term=bold ctermfg=LightGreen",
6987 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6988 CENT("Question term=standout ctermfg=LightGreen",
6989 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6990 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6991 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6992 CENT("SpecialKey term=bold ctermfg=LightBlue",
6993 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006994#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006995 CENT("SpellBad term=reverse ctermbg=Red",
6996 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6997 CENT("SpellCap term=reverse ctermbg=Blue",
6998 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6999 CENT("SpellRare term=reverse ctermbg=Magenta",
7000 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7001 CENT("SpellLocal term=underline ctermbg=Cyan",
7002 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007003#endif
7004#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007005 CENT("PmenuThumb ctermbg=White",
7006 "PmenuThumb ctermbg=White guibg=White"),
7007 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7008 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007009 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7010 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007011#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007012 CENT("Title term=bold ctermfg=LightMagenta",
7013 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7014 CENT("WarningMsg term=standout ctermfg=LightRed",
7015 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007016#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007017 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7018 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007019#endif
7020#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007021 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7022 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7023 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7024 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007025#endif
7026#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007027 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7028 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007029#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007030 CENT("Visual term=reverse",
7031 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007032#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007033 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7034 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7035 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7036 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7037 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7038 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007039#endif
7040#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007041 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7042 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007043#endif
7044#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007045 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007046 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007047 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007048 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007049 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7050 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007051#endif
7052#ifdef FEAT_AUTOCMD
7053 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7054 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007055#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007056#ifdef FEAT_CONCEAL
7057 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7058 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7059#endif
Bram Moolenaar21020352017-06-13 17:21:04 +02007060 CENT("QuickFixLine term=reverse ctermbg=Cyan",
7061 "QuickFixLine term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007062#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007063 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007064#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007065 NULL
7066 };
7067
7068 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007069init_highlight(
7070 int both, /* include groups where 'bg' doesn't matter */
7071 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007072{
7073 int i;
7074 char **pp;
7075 static int had_both = FALSE;
7076#ifdef FEAT_EVAL
7077 char_u *p;
7078
7079 /*
7080 * Try finding the color scheme file. Used when a color file was loaded
7081 * and 'background' or 't_Co' is changed.
7082 */
7083 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007084 if (p != NULL)
7085 {
7086 /* The value of g:colors_name could be freed when sourcing the script,
7087 * making "p" invalid, so copy it. */
7088 char_u *copy_p = vim_strsave(p);
7089 int r;
7090
7091 if (copy_p != NULL)
7092 {
7093 r = load_colors(copy_p);
7094 vim_free(copy_p);
7095 if (r == OK)
7096 return;
7097 }
7098 }
7099
Bram Moolenaar071d4272004-06-13 20:20:40 +00007100#endif
7101
7102 /*
7103 * Didn't use a color file, use the compiled-in colors.
7104 */
7105 if (both)
7106 {
7107 had_both = TRUE;
7108 pp = highlight_init_both;
7109 for (i = 0; pp[i] != NULL; ++i)
7110 do_highlight((char_u *)pp[i], reset, TRUE);
7111 }
7112 else if (!had_both)
7113 /* Don't do anything before the call with both == TRUE from main().
7114 * Not everything has been setup then, and that call will overrule
7115 * everything anyway. */
7116 return;
7117
7118 if (*p_bg == 'l')
7119 pp = highlight_init_light;
7120 else
7121 pp = highlight_init_dark;
7122 for (i = 0; pp[i] != NULL; ++i)
7123 do_highlight((char_u *)pp[i], reset, TRUE);
7124
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007125 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007126 * depend on the number of colors available.
7127 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007128 * to avoid Statement highlighted text disappears.
7129 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007130 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007131 do_highlight((char_u *)(*p_bg == 'l'
7132 ? "Visual cterm=NONE ctermbg=LightGrey"
7133 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007134 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007135 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007136 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7137 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007138 if (*p_bg == 'l')
7139 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7140 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007141
Bram Moolenaar071d4272004-06-13 20:20:40 +00007142#ifdef FEAT_SYN_HL
7143 /*
7144 * If syntax highlighting is enabled load the highlighting for it.
7145 */
7146 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007147 {
7148 static int recursive = 0;
7149
7150 if (recursive >= 5)
7151 EMSG(_("E679: recursive loop loading syncolor.vim"));
7152 else
7153 {
7154 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007155 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007156 --recursive;
7157 }
7158 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007159#endif
7160}
7161
7162/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007163 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007164 * Return OK for success, FAIL for failure.
7165 */
7166 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007167load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007168{
7169 char_u *buf;
7170 int retval = FAIL;
7171 static int recursive = FALSE;
7172
7173 /* When being called recursively, this is probably because setting
7174 * 'background' caused the highlighting to be reloaded. This means it is
7175 * working, thus we should return OK. */
7176 if (recursive)
7177 return OK;
7178
7179 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007180 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007181 if (buf != NULL)
7182 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007183 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007184 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007185 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007186#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007187 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007188#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007189 }
7190 recursive = FALSE;
7191
7192 return retval;
7193}
7194
7195/*
7196 * Handle the ":highlight .." command.
7197 * When using ":hi clear" this is called recursively for each group with
7198 * "forceit" and "init" both TRUE.
7199 */
7200 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007201do_highlight(
7202 char_u *line,
7203 int forceit,
7204 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007205{
7206 char_u *name_end;
7207 char_u *p;
7208 char_u *linep;
7209 char_u *key_start;
7210 char_u *arg_start;
7211 char_u *key = NULL, *arg = NULL;
7212 long i;
7213 int off;
7214 int len;
7215 int attr;
7216 int id;
7217 int idx;
7218 int dodefault = FALSE;
7219 int doclear = FALSE;
7220 int dolink = FALSE;
7221 int error = FALSE;
7222 int color;
7223 int is_normal_group = FALSE; /* "Normal" group */
7224#ifdef FEAT_GUI_X11
7225 int is_menu_group = FALSE; /* "Menu" group */
7226 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7227 int is_tooltip_group = FALSE; /* "Tooltip" group */
7228 int do_colors = FALSE; /* need to update colors? */
7229#else
7230# define is_menu_group 0
7231# define is_tooltip_group 0
7232#endif
7233
7234 /*
7235 * If no argument, list current highlighting.
7236 */
7237 if (ends_excmd(*line))
7238 {
7239 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7240 /* TODO: only call when the group has attributes set */
7241 highlight_list_one((int)i);
7242 return;
7243 }
7244
7245 /*
7246 * Isolate the name.
7247 */
7248 name_end = skiptowhite(line);
7249 linep = skipwhite(name_end);
7250
7251 /*
7252 * Check for "default" argument.
7253 */
7254 if (STRNCMP(line, "default", name_end - line) == 0)
7255 {
7256 dodefault = TRUE;
7257 line = linep;
7258 name_end = skiptowhite(line);
7259 linep = skipwhite(name_end);
7260 }
7261
7262 /*
7263 * Check for "clear" or "link" argument.
7264 */
7265 if (STRNCMP(line, "clear", name_end - line) == 0)
7266 doclear = TRUE;
7267 if (STRNCMP(line, "link", name_end - line) == 0)
7268 dolink = TRUE;
7269
7270 /*
7271 * ":highlight {group-name}": list highlighting for one group.
7272 */
7273 if (!doclear && !dolink && ends_excmd(*linep))
7274 {
7275 id = syn_namen2id(line, (int)(name_end - line));
7276 if (id == 0)
7277 EMSG2(_("E411: highlight group not found: %s"), line);
7278 else
7279 highlight_list_one(id);
7280 return;
7281 }
7282
7283 /*
7284 * Handle ":highlight link {from} {to}" command.
7285 */
7286 if (dolink)
7287 {
7288 char_u *from_start = linep;
7289 char_u *from_end;
7290 char_u *to_start;
7291 char_u *to_end;
7292 int from_id;
7293 int to_id;
7294
7295 from_end = skiptowhite(from_start);
7296 to_start = skipwhite(from_end);
7297 to_end = skiptowhite(to_start);
7298
7299 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7300 {
7301 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7302 from_start);
7303 return;
7304 }
7305
7306 if (!ends_excmd(*skipwhite(to_end)))
7307 {
7308 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7309 return;
7310 }
7311
7312 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7313 if (STRNCMP(to_start, "NONE", 4) == 0)
7314 to_id = 0;
7315 else
7316 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7317
7318 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7319 {
7320 /*
7321 * Don't allow a link when there already is some highlighting
7322 * for the group, unless '!' is used
7323 */
7324 if (to_id > 0 && !forceit && !init
7325 && hl_has_settings(from_id - 1, dodefault))
7326 {
7327 if (sourcing_name == NULL && !dodefault)
7328 EMSG(_("E414: group has settings, highlight link ignored"));
7329 }
7330 else
7331 {
7332 if (!init)
7333 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7334 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007335#ifdef FEAT_EVAL
7336 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7337#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01007338 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007339 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007340 }
7341 }
7342
7343 /* Only call highlight_changed() once, after sourcing a syntax file */
7344 need_highlight_changed = TRUE;
7345
7346 return;
7347 }
7348
7349 if (doclear)
7350 {
7351 /*
7352 * ":highlight clear [group]" command.
7353 */
7354 line = linep;
7355 if (ends_excmd(*line))
7356 {
7357#ifdef FEAT_GUI
7358 /* First, we do not destroy the old values, but allocate the new
7359 * ones and update the display. THEN we destroy the old values.
7360 * If we destroy the old values first, then the old values
7361 * (such as GuiFont's or GuiFontset's) will still be displayed but
7362 * invalid because they were free'd.
7363 */
7364 if (gui.in_use)
7365 {
7366# ifdef FEAT_BEVAL_TIP
7367 gui_init_tooltip_font();
7368# endif
7369# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7370 gui_init_menu_font();
7371# endif
7372 }
7373# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7374 gui_mch_def_colors();
7375# endif
7376# ifdef FEAT_GUI_X11
7377# ifdef FEAT_MENU
7378
7379 /* This only needs to be done when there is no Menu highlight
7380 * group defined by default, which IS currently the case.
7381 */
7382 gui_mch_new_menu_colors();
7383# endif
7384 if (gui.in_use)
7385 {
7386 gui_new_scrollbar_colors();
7387# ifdef FEAT_BEVAL
7388 gui_mch_new_tooltip_colors();
7389# endif
7390# ifdef FEAT_MENU
7391 gui_mch_new_menu_font();
7392# endif
7393 }
7394# endif
7395
7396 /* Ok, we're done allocating the new default graphics items.
7397 * The screen should already be refreshed at this point.
7398 * It is now Ok to clear out the old data.
7399 */
7400#endif
7401#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007402 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007403#endif
7404 restore_cterm_colors();
7405
7406 /*
7407 * Clear all default highlight groups and load the defaults.
7408 */
7409 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7410 highlight_clear(idx);
7411 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007412#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007413 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007414 highlight_gui_started();
7415#endif
7416 highlight_changed();
7417 redraw_later_clear();
7418 return;
7419 }
7420 name_end = skiptowhite(line);
7421 linep = skipwhite(name_end);
7422 }
7423
7424 /*
7425 * Find the group name in the table. If it does not exist yet, add it.
7426 */
7427 id = syn_check_group(line, (int)(name_end - line));
7428 if (id == 0) /* failed (out of memory) */
7429 return;
7430 idx = id - 1; /* index is ID minus one */
7431
7432 /* Return if "default" was used and the group already has settings. */
7433 if (dodefault && hl_has_settings(idx, TRUE))
7434 return;
7435
7436 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7437 is_normal_group = TRUE;
7438#ifdef FEAT_GUI_X11
7439 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7440 is_menu_group = TRUE;
7441 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7442 is_scrollbar_group = TRUE;
7443 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7444 is_tooltip_group = TRUE;
7445#endif
7446
7447 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7448 if (doclear || (forceit && init))
7449 {
7450 highlight_clear(idx);
7451 if (!doclear)
7452 HL_TABLE()[idx].sg_set = 0;
7453 }
7454
7455 if (!doclear)
7456 while (!ends_excmd(*linep))
7457 {
7458 key_start = linep;
7459 if (*linep == '=')
7460 {
7461 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7462 error = TRUE;
7463 break;
7464 }
7465
7466 /*
7467 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7468 * "guibg").
7469 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007470 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007471 ++linep;
7472 vim_free(key);
7473 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7474 if (key == NULL)
7475 {
7476 error = TRUE;
7477 break;
7478 }
7479 linep = skipwhite(linep);
7480
7481 if (STRCMP(key, "NONE") == 0)
7482 {
7483 if (!init || HL_TABLE()[idx].sg_set == 0)
7484 {
7485 if (!init)
7486 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7487 highlight_clear(idx);
7488 }
7489 continue;
7490 }
7491
7492 /*
7493 * Check for the equal sign.
7494 */
7495 if (*linep != '=')
7496 {
7497 EMSG2(_("E416: missing equal sign: %s"), key_start);
7498 error = TRUE;
7499 break;
7500 }
7501 ++linep;
7502
7503 /*
7504 * Isolate the argument.
7505 */
7506 linep = skipwhite(linep);
7507 if (*linep == '\'') /* guifg='color name' */
7508 {
7509 arg_start = ++linep;
7510 linep = vim_strchr(linep, '\'');
7511 if (linep == NULL)
7512 {
7513 EMSG2(_(e_invarg2), key_start);
7514 error = TRUE;
7515 break;
7516 }
7517 }
7518 else
7519 {
7520 arg_start = linep;
7521 linep = skiptowhite(linep);
7522 }
7523 if (linep == arg_start)
7524 {
7525 EMSG2(_("E417: missing argument: %s"), key_start);
7526 error = TRUE;
7527 break;
7528 }
7529 vim_free(arg);
7530 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7531 if (arg == NULL)
7532 {
7533 error = TRUE;
7534 break;
7535 }
7536 if (*linep == '\'')
7537 ++linep;
7538
7539 /*
7540 * Store the argument.
7541 */
7542 if ( STRCMP(key, "TERM") == 0
7543 || STRCMP(key, "CTERM") == 0
7544 || STRCMP(key, "GUI") == 0)
7545 {
7546 attr = 0;
7547 off = 0;
7548 while (arg[off] != NUL)
7549 {
7550 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7551 {
7552 len = (int)STRLEN(hl_name_table[i]);
7553 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7554 {
7555 attr |= hl_attr_table[i];
7556 off += len;
7557 break;
7558 }
7559 }
7560 if (i < 0)
7561 {
7562 EMSG2(_("E418: Illegal value: %s"), arg);
7563 error = TRUE;
7564 break;
7565 }
7566 if (arg[off] == ',') /* another one follows */
7567 ++off;
7568 }
7569 if (error)
7570 break;
7571 if (*key == 'T')
7572 {
7573 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7574 {
7575 if (!init)
7576 HL_TABLE()[idx].sg_set |= SG_TERM;
7577 HL_TABLE()[idx].sg_term = attr;
7578 }
7579 }
7580 else if (*key == 'C')
7581 {
7582 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7583 {
7584 if (!init)
7585 HL_TABLE()[idx].sg_set |= SG_CTERM;
7586 HL_TABLE()[idx].sg_cterm = attr;
7587 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7588 }
7589 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007590#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591 else
7592 {
7593 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7594 {
7595 if (!init)
7596 HL_TABLE()[idx].sg_set |= SG_GUI;
7597 HL_TABLE()[idx].sg_gui = attr;
7598 }
7599 }
7600#endif
7601 }
7602 else if (STRCMP(key, "FONT") == 0)
7603 {
7604 /* in non-GUI fonts are simply ignored */
7605#ifdef FEAT_GUI
7606 if (!gui.shell_created)
7607 {
7608 /* GUI not started yet, always accept the name. */
7609 vim_free(HL_TABLE()[idx].sg_font_name);
7610 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7611 }
7612 else
7613 {
7614 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7615# ifdef FEAT_XFONTSET
7616 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7617# endif
7618 /* First, save the current font/fontset.
7619 * Then try to allocate the font/fontset.
7620 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7621 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7622 */
7623
7624 HL_TABLE()[idx].sg_font = NOFONT;
7625# ifdef FEAT_XFONTSET
7626 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7627# endif
7628 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007629 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007630
7631# ifdef FEAT_XFONTSET
7632 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7633 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007634 /* New fontset was accepted. Free the old one, if there
7635 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007636 gui_mch_free_fontset(temp_sg_fontset);
7637 vim_free(HL_TABLE()[idx].sg_font_name);
7638 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7639 }
7640 else
7641 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7642# endif
7643 if (HL_TABLE()[idx].sg_font != NOFONT)
7644 {
7645 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007646 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007647 gui_mch_free_font(temp_sg_font);
7648 vim_free(HL_TABLE()[idx].sg_font_name);
7649 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7650 }
7651 else
7652 HL_TABLE()[idx].sg_font = temp_sg_font;
7653 }
7654#endif
7655 }
7656 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7657 {
7658 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7659 {
7660 if (!init)
7661 HL_TABLE()[idx].sg_set |= SG_CTERM;
7662
7663 /* When setting the foreground color, and previously the "bold"
7664 * flag was set for a light color, reset it now */
7665 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7666 {
7667 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7668 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7669 }
7670
7671 if (VIM_ISDIGIT(*arg))
7672 color = atoi((char *)arg);
7673 else if (STRICMP(arg, "fg") == 0)
7674 {
7675 if (cterm_normal_fg_color)
7676 color = cterm_normal_fg_color - 1;
7677 else
7678 {
7679 EMSG(_("E419: FG color unknown"));
7680 error = TRUE;
7681 break;
7682 }
7683 }
7684 else if (STRICMP(arg, "bg") == 0)
7685 {
7686 if (cterm_normal_bg_color > 0)
7687 color = cterm_normal_bg_color - 1;
7688 else
7689 {
7690 EMSG(_("E420: BG color unknown"));
7691 error = TRUE;
7692 break;
7693 }
7694 }
7695 else
7696 {
7697 static char *(color_names[28]) = {
7698 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7699 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7700 "Gray", "Grey",
7701 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7702 "Blue", "LightBlue", "Green", "LightGreen",
7703 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7704 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7705 static int color_numbers_16[28] = {0, 1, 2, 3,
7706 4, 5, 6, 6,
7707 7, 7,
7708 7, 7, 8, 8,
7709 9, 9, 10, 10,
7710 11, 11, 12, 12, 13,
7711 13, 14, 14, 15, -1};
7712 /* for xterm with 88 colors... */
7713 static int color_numbers_88[28] = {0, 4, 2, 6,
7714 1, 5, 32, 72,
7715 84, 84,
7716 7, 7, 82, 82,
7717 12, 43, 10, 61,
7718 14, 63, 9, 74, 13,
7719 75, 11, 78, 15, -1};
7720 /* for xterm with 256 colors... */
7721 static int color_numbers_256[28] = {0, 4, 2, 6,
7722 1, 5, 130, 130,
7723 248, 248,
7724 7, 7, 242, 242,
7725 12, 81, 10, 121,
7726 14, 159, 9, 224, 13,
7727 225, 11, 229, 15, -1};
7728 /* for terminals with less than 16 colors... */
7729 static int color_numbers_8[28] = {0, 4, 2, 6,
7730 1, 5, 3, 3,
7731 7, 7,
7732 7, 7, 0+8, 0+8,
7733 4+8, 4+8, 2+8, 2+8,
7734 6+8, 6+8, 1+8, 1+8, 5+8,
7735 5+8, 3+8, 3+8, 7+8, -1};
7736#if defined(__QNXNTO__)
7737 static int *color_numbers_8_qansi = color_numbers_8;
7738 /* On qnx, the 8 & 16 color arrays are the same */
7739 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7740 color_numbers_8_qansi = color_numbers_16;
7741#endif
7742
7743 /* reduce calls to STRICMP a bit, it can be slow */
7744 off = TOUPPER_ASC(*arg);
7745 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7746 if (off == color_names[i][0]
7747 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7748 break;
7749 if (i < 0)
7750 {
7751 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7752 error = TRUE;
7753 break;
7754 }
7755
Bram Moolenaaraab93b12017-03-18 21:37:28 +01007756 /* Use the _16 table to check if it's a valid color name. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757 color = color_numbers_16[i];
7758 if (color >= 0)
7759 {
7760 if (t_colors == 8)
7761 {
7762 /* t_Co is 8: use the 8 colors table */
7763#if defined(__QNXNTO__)
7764 color = color_numbers_8_qansi[i];
7765#else
7766 color = color_numbers_8[i];
7767#endif
7768 if (key[5] == 'F')
7769 {
7770 /* set/reset bold attribute to get light foreground
7771 * colors (on some terminals, e.g. "linux") */
7772 if (color & 8)
7773 {
7774 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7775 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7776 }
7777 else
7778 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7779 }
7780 color &= 7; /* truncate to 8 colors */
7781 }
7782 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007783 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007784 {
7785 /*
7786 * Guess: if the termcap entry ends in 'm', it is
7787 * probably an xterm-like terminal. Use the changed
7788 * order for colors.
7789 */
7790 if (*T_CAF != NUL)
7791 p = T_CAF;
7792 else
7793 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007794 if (*p != NUL && (t_colors > 256
7795 || *(p + STRLEN(p) - 1) == 'm'))
7796 {
7797 if (t_colors == 88)
7798 color = color_numbers_88[i];
7799 else if (t_colors >= 256)
7800 color = color_numbers_256[i];
7801 else
7802 color = color_numbers_8[i];
7803 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804 }
7805 }
7806 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007807 /* Add one to the argument, to avoid zero. Zero is used for
7808 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809 if (key[5] == 'F')
7810 {
7811 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7812 if (is_normal_group)
7813 {
7814 cterm_normal_fg_color = color + 1;
7815 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7816#ifdef FEAT_GUI
7817 /* Don't do this if the GUI is used. */
7818 if (!gui.in_use && !gui.starting)
7819#endif
7820 {
7821 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007822 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823 term_fg_color(color);
7824 }
7825 }
7826 }
7827 else
7828 {
7829 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7830 if (is_normal_group)
7831 {
7832 cterm_normal_bg_color = color + 1;
7833#ifdef FEAT_GUI
7834 /* Don't mess with 'background' if the GUI is used. */
7835 if (!gui.in_use && !gui.starting)
7836#endif
7837 {
7838 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007839 if (color >= 0)
7840 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007841 int dark = -1;
7842
Bram Moolenaarccbab932010-05-13 15:40:30 +02007843 if (termcap_active)
7844 term_bg_color(color);
7845 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007846 dark = (color == 0 || color == 4);
7847 /* Limit the heuristic to the standard 16 colors */
7848 else if (color < 16)
7849 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007850 /* Set the 'background' option if the value is
7851 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007852 if (dark != -1
7853 && dark != (*p_bg == 'd')
7854 && !option_was_set((char_u *)"bg"))
7855 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007856 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007857 (char_u *)(dark ? "dark" : "light"), 0);
7858 reset_option_was_set((char_u *)"bg");
7859 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007860 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007861 }
7862 }
7863 }
7864 }
7865 }
7866 else if (STRCMP(key, "GUIFG") == 0)
7867 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007868#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007869 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007870 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007871 if (!init)
7872 HL_TABLE()[idx].sg_set |= SG_GUI;
7873
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007874# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007875 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007876 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007877 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007878 {
7879 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007880# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007881 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7882 if (STRCMP(arg, "NONE"))
7883 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7884 else
7885 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007886# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007887# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007888 if (is_menu_group)
7889 gui.menu_fg_pixel = i;
7890 if (is_scrollbar_group)
7891 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007892# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007893 if (is_tooltip_group)
7894 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007895# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007896 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007897# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007898 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007899# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007900 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007901#endif
7902 }
7903 else if (STRCMP(key, "GUIBG") == 0)
7904 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007905#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007906 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007907 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007908 if (!init)
7909 HL_TABLE()[idx].sg_set |= SG_GUI;
7910
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007911# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007912 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007913 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007914 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007915 {
7916 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007917# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007918 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7919 if (STRCMP(arg, "NONE") != 0)
7920 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7921 else
7922 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007923# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007924# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007925 if (is_menu_group)
7926 gui.menu_bg_pixel = i;
7927 if (is_scrollbar_group)
7928 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007929# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007930 if (is_tooltip_group)
7931 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007932# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007933 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007934# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007935 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007936# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007937 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007938#endif
7939 }
7940 else if (STRCMP(key, "GUISP") == 0)
7941 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007942#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007943 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7944 {
7945 if (!init)
7946 HL_TABLE()[idx].sg_set |= SG_GUI;
7947
Bram Moolenaar61623362010-07-14 22:04:22 +02007948# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007949 i = color_name2handle(arg);
7950 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7951 {
7952 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007953# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007954 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7955 if (STRCMP(arg, "NONE") != 0)
7956 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7957 else
7958 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007959# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007960 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007961# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007962 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007963#endif
7964 }
7965 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7966 {
7967 char_u buf[100];
7968 char_u *tname;
7969
7970 if (!init)
7971 HL_TABLE()[idx].sg_set |= SG_TERM;
7972
7973 /*
7974 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007975 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007976 */
7977 if (STRNCMP(arg, "t_", 2) == 0)
7978 {
7979 off = 0;
7980 buf[0] = 0;
7981 while (arg[off] != NUL)
7982 {
7983 /* Isolate one termcap name */
7984 for (len = 0; arg[off + len] &&
7985 arg[off + len] != ','; ++len)
7986 ;
7987 tname = vim_strnsave(arg + off, len);
7988 if (tname == NULL) /* out of memory */
7989 {
7990 error = TRUE;
7991 break;
7992 }
7993 /* lookup the escape sequence for the item */
7994 p = get_term_code(tname);
7995 vim_free(tname);
7996 if (p == NULL) /* ignore non-existing things */
7997 p = (char_u *)"";
7998
7999 /* Append it to the already found stuff */
8000 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8001 {
8002 EMSG2(_("E422: terminal code too long: %s"), arg);
8003 error = TRUE;
8004 break;
8005 }
8006 STRCAT(buf, p);
8007
8008 /* Advance to the next item */
8009 off += len;
8010 if (arg[off] == ',') /* another one follows */
8011 ++off;
8012 }
8013 }
8014 else
8015 {
8016 /*
8017 * Copy characters from arg[] to buf[], translating <> codes.
8018 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008019 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008020 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008021 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008022 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008023 off += len;
8024 else /* copy as normal char */
8025 buf[off++] = *p++;
8026 }
8027 buf[off] = NUL;
8028 }
8029 if (error)
8030 break;
8031
8032 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8033 p = NULL;
8034 else
8035 p = vim_strsave(buf);
8036 if (key[2] == 'A')
8037 {
8038 vim_free(HL_TABLE()[idx].sg_start);
8039 HL_TABLE()[idx].sg_start = p;
8040 }
8041 else
8042 {
8043 vim_free(HL_TABLE()[idx].sg_stop);
8044 HL_TABLE()[idx].sg_stop = p;
8045 }
8046 }
8047 else
8048 {
8049 EMSG2(_("E423: Illegal argument: %s"), key_start);
8050 error = TRUE;
8051 break;
8052 }
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008053 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008054
8055 /*
8056 * When highlighting has been given for a group, don't link it.
8057 */
8058 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8059 HL_TABLE()[idx].sg_link = 0;
8060
8061 /*
8062 * Continue with next argument.
8063 */
8064 linep = skipwhite(linep);
8065 }
8066
8067 /*
8068 * If there is an error, and it's a new entry, remove it from the table.
8069 */
8070 if (error && idx == highlight_ga.ga_len)
8071 syn_unadd_group();
8072 else
8073 {
8074 if (is_normal_group)
8075 {
8076 HL_TABLE()[idx].sg_term_attr = 0;
8077 HL_TABLE()[idx].sg_cterm_attr = 0;
8078#ifdef FEAT_GUI
8079 HL_TABLE()[idx].sg_gui_attr = 0;
8080 /*
8081 * Need to update all groups, because they might be using "bg"
8082 * and/or "fg", which have been changed now.
8083 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008084#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008085#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008086 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008087 highlight_gui_started();
8088#endif
8089 }
8090#ifdef FEAT_GUI_X11
8091# ifdef FEAT_MENU
8092 else if (is_menu_group)
8093 {
8094 if (gui.in_use && do_colors)
8095 gui_mch_new_menu_colors();
8096 }
8097# endif
8098 else if (is_scrollbar_group)
8099 {
8100 if (gui.in_use && do_colors)
8101 gui_new_scrollbar_colors();
8102 }
8103# ifdef FEAT_BEVAL
8104 else if (is_tooltip_group)
8105 {
8106 if (gui.in_use && do_colors)
8107 gui_mch_new_tooltip_colors();
8108 }
8109# endif
8110#endif
8111 else
8112 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008113#ifdef FEAT_EVAL
8114 HL_TABLE()[idx].sg_scriptID = current_SID;
8115#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008116 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008117 }
8118 vim_free(key);
8119 vim_free(arg);
8120
8121 /* Only call highlight_changed() once, after sourcing a syntax file */
8122 need_highlight_changed = TRUE;
8123}
8124
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008125#if defined(EXITFREE) || defined(PROTO)
8126 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008127free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008128{
8129 int i;
8130
8131 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008132 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008133 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008134 vim_free(HL_TABLE()[i].sg_name);
8135 vim_free(HL_TABLE()[i].sg_name_u);
8136 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008137 ga_clear(&highlight_ga);
8138}
8139#endif
8140
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141/*
8142 * Reset the cterm colors to what they were before Vim was started, if
8143 * possible. Otherwise reset them to zero.
8144 */
8145 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008146restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008147{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008148#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149 /* Since t_me has been set, this probably means that the user
8150 * wants to use this as default colors. Need to reset default
8151 * background/foreground colors. */
8152 mch_set_normal_colors();
8153#else
8154 cterm_normal_fg_color = 0;
8155 cterm_normal_fg_bold = 0;
8156 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008157# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008158 cterm_normal_fg_gui_color = INVALCOLOR;
8159 cterm_normal_bg_gui_color = INVALCOLOR;
8160# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008161#endif
8162}
8163
8164/*
8165 * Return TRUE if highlight group "idx" has any settings.
8166 * When "check_link" is TRUE also check for an existing link.
8167 */
8168 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008169hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008170{
8171 return ( HL_TABLE()[idx].sg_term_attr != 0
8172 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008173 || HL_TABLE()[idx].sg_cterm_fg != 0
8174 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008175#ifdef FEAT_GUI
8176 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008177 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8178 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8179 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008180 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008181#endif
8182 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8183}
8184
8185/*
8186 * Clear highlighting for one group.
8187 */
8188 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008189highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008190{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008191 HL_TABLE()[idx].sg_cleared = TRUE;
8192
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193 HL_TABLE()[idx].sg_term = 0;
8194 vim_free(HL_TABLE()[idx].sg_start);
8195 HL_TABLE()[idx].sg_start = NULL;
8196 vim_free(HL_TABLE()[idx].sg_stop);
8197 HL_TABLE()[idx].sg_stop = NULL;
8198 HL_TABLE()[idx].sg_term_attr = 0;
8199 HL_TABLE()[idx].sg_cterm = 0;
8200 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8201 HL_TABLE()[idx].sg_cterm_fg = 0;
8202 HL_TABLE()[idx].sg_cterm_bg = 0;
8203 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008204#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008205 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008206 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8207 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8209 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008210 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8211 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008212#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008213#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008214 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8215 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008216#endif
8217#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008218 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8220 HL_TABLE()[idx].sg_font = NOFONT;
8221# ifdef FEAT_XFONTSET
8222 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8223 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8224# endif
8225 vim_free(HL_TABLE()[idx].sg_font_name);
8226 HL_TABLE()[idx].sg_font_name = NULL;
8227 HL_TABLE()[idx].sg_gui_attr = 0;
8228#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008229#ifdef FEAT_EVAL
8230 /* Clear the script ID only when there is no link, since that is not
8231 * cleared. */
8232 if (HL_TABLE()[idx].sg_link == 0)
8233 HL_TABLE()[idx].sg_scriptID = 0;
8234#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008235}
8236
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008237#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238/*
8239 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008240 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008241 * "Tooltip" colors.
8242 */
8243 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008244set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008245{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008246#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008247# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008248 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008250 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008251 if (set_group_colors((char_u *)"Normal",
8252 &gui.norm_pixel, &gui.back_pixel,
8253 FALSE, TRUE, FALSE))
8254 {
8255 gui_mch_new_colors();
8256 must_redraw = CLEAR;
8257 }
8258# ifdef FEAT_GUI_X11
8259 if (set_group_colors((char_u *)"Menu",
8260 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8261 TRUE, FALSE, FALSE))
8262 {
8263# ifdef FEAT_MENU
8264 gui_mch_new_menu_colors();
8265# endif
8266 must_redraw = CLEAR;
8267 }
8268# ifdef FEAT_BEVAL
8269 if (set_group_colors((char_u *)"Tooltip",
8270 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8271 FALSE, FALSE, TRUE))
8272 {
8273# ifdef FEAT_TOOLBAR
8274 gui_mch_new_tooltip_colors();
8275# endif
8276 must_redraw = CLEAR;
8277 }
8278# endif
8279 if (set_group_colors((char_u *)"Scrollbar",
8280 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8281 FALSE, FALSE, FALSE))
8282 {
8283 gui_new_scrollbar_colors();
8284 must_redraw = CLEAR;
8285 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008286# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008287 }
8288#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008289#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008290# ifdef FEAT_GUI
8291 else
8292# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008293 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008294 int idx;
8295
8296 idx = syn_name2id((char_u *)"Normal") - 1;
8297 if (idx >= 0)
8298 {
8299 gui_do_one_color(idx, FALSE, FALSE);
8300
8301 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8302 {
8303 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8304 must_redraw = CLEAR;
8305 }
8306 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8307 {
8308 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8309 must_redraw = CLEAR;
8310 }
8311 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008312 }
8313#endif
8314}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008315#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008316
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008317#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008318/*
8319 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8320 */
8321 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008322set_group_colors(
8323 char_u *name,
8324 guicolor_T *fgp,
8325 guicolor_T *bgp,
8326 int do_menu,
8327 int use_norm,
8328 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008329{
8330 int idx;
8331
8332 idx = syn_name2id(name) - 1;
8333 if (idx >= 0)
8334 {
8335 gui_do_one_color(idx, do_menu, do_tooltip);
8336
8337 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8338 *fgp = HL_TABLE()[idx].sg_gui_fg;
8339 else if (use_norm)
8340 *fgp = gui.def_norm_pixel;
8341 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8342 *bgp = HL_TABLE()[idx].sg_gui_bg;
8343 else if (use_norm)
8344 *bgp = gui.def_back_pixel;
8345 return TRUE;
8346 }
8347 return FALSE;
8348}
8349
8350/*
8351 * Get the font of the "Normal" group.
8352 * Returns "" when it's not found or not set.
8353 */
8354 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008355hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008356{
8357 int id;
8358 char_u *s;
8359
8360 id = syn_name2id((char_u *)"Normal");
8361 if (id > 0)
8362 {
8363 s = HL_TABLE()[id - 1].sg_font_name;
8364 if (s != NULL)
8365 return s;
8366 }
8367 return (char_u *)"";
8368}
8369
8370/*
8371 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8372 * actually chosen to be used.
8373 */
8374 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008375hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008376{
8377 int id;
8378
8379 id = syn_name2id((char_u *)"Normal");
8380 if (id > 0)
8381 {
8382 vim_free(HL_TABLE()[id - 1].sg_font_name);
8383 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8384 }
8385}
8386
8387/*
8388 * Set background color for "Normal" group. Called by gui_set_bg_color()
8389 * when the color is known.
8390 */
8391 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008392hl_set_bg_color_name(
8393 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008394{
8395 int id;
8396
8397 if (name != NULL)
8398 {
8399 id = syn_name2id((char_u *)"Normal");
8400 if (id > 0)
8401 {
8402 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8403 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8404 }
8405 }
8406}
8407
8408/*
8409 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8410 * when the color is known.
8411 */
8412 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008413hl_set_fg_color_name(
8414 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415{
8416 int id;
8417
8418 if (name != NULL)
8419 {
8420 id = syn_name2id((char_u *)"Normal");
8421 if (id > 0)
8422 {
8423 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8424 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8425 }
8426 }
8427}
8428
8429/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008430 * Return the handle for a font name.
8431 * Returns NOFONT when failed.
8432 */
8433 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008434font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008435{
8436 if (STRCMP(name, "NONE") == 0)
8437 return NOFONT;
8438
8439 return gui_mch_get_font(name, TRUE);
8440}
8441
8442# ifdef FEAT_XFONTSET
8443/*
8444 * Return the handle for a fontset name.
8445 * Returns NOFONTSET when failed.
8446 */
8447 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008448fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008449{
8450 if (STRCMP(name, "NONE") == 0)
8451 return NOFONTSET;
8452
8453 return gui_mch_get_fontset(name, TRUE, fixed_width);
8454}
8455# endif
8456
8457/*
8458 * Get the font or fontset for one highlight group.
8459 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008460 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008461hl_do_font(
8462 int idx,
8463 char_u *arg,
8464 int do_normal, /* set normal font */
8465 int do_menu UNUSED, /* set menu font */
8466 int do_tooltip UNUSED, /* set tooltip font */
8467 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008468{
8469# ifdef FEAT_XFONTSET
8470 /* If 'guifontset' is not empty, first try using the name as a
8471 * fontset. If that doesn't work, use it as a font name. */
8472 if (*p_guifontset != NUL
8473# ifdef FONTSET_ALWAYS
8474 || do_menu
8475# endif
8476# ifdef FEAT_BEVAL_TIP
8477 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8478 || do_tooltip
8479# endif
8480 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008481 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008482 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008483 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008484 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8485# ifdef FONTSET_ALWAYS
8486 || do_menu
8487# endif
8488# ifdef FEAT_BEVAL_TIP
8489 || do_tooltip
8490# endif
8491 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008492 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008493 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8494 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008495 /* If it worked and it's the Normal group, use it as the normal
8496 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008497 if (do_normal)
8498 gui_init_font(arg, TRUE);
8499# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8500 if (do_menu)
8501 {
8502# ifdef FONTSET_ALWAYS
8503 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8504# else
8505 /* YIKES! This is a bug waiting to crash the program */
8506 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8507# endif
8508 gui_mch_new_menu_font();
8509 }
8510# ifdef FEAT_BEVAL
8511 if (do_tooltip)
8512 {
8513 /* The Athena widget set cannot currently handle switching between
8514 * displaying a single font and a fontset.
8515 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008516 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008517 * XFontStruct is used.
8518 */
8519 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8520 gui_mch_new_tooltip_font();
8521 }
8522# endif
8523# endif
8524 }
8525 else
8526# endif
8527 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008528 if (free_font)
8529 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8531 /* If it worked and it's the Normal group, use it as the
8532 * normal font. Same for the Menu group. */
8533 if (HL_TABLE()[idx].sg_font != NOFONT)
8534 {
8535 if (do_normal)
8536 gui_init_font(arg, FALSE);
8537#ifndef FONTSET_ALWAYS
8538# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8539 if (do_menu)
8540 {
8541 gui.menu_font = HL_TABLE()[idx].sg_font;
8542 gui_mch_new_menu_font();
8543 }
8544# endif
8545#endif
8546 }
8547 }
8548}
8549
8550#endif /* FEAT_GUI */
8551
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008552#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008553/*
8554 * Return the handle for a color name.
8555 * Returns INVALCOLOR when failed.
8556 */
8557 static guicolor_T
8558color_name2handle(char_u *name)
8559{
8560 if (STRCMP(name, "NONE") == 0)
8561 return INVALCOLOR;
8562
8563 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8564 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008565#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008566 if (gui.in_use)
8567#endif
8568#ifdef FEAT_GUI
8569 return gui.norm_pixel;
8570#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008571#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008572 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008573 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008574 /* Guess that the foreground is black or white. */
8575 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008576#endif
8577 }
8578 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8579 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008580#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008581 if (gui.in_use)
8582#endif
8583#ifdef FEAT_GUI
8584 return gui.back_pixel;
8585#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008586#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008587 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008588 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008589 /* Guess that the background is white or black. */
8590 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008591#endif
8592 }
8593
8594 return GUI_GET_COLOR(name);
8595}
8596#endif
8597
Bram Moolenaar071d4272004-06-13 20:20:40 +00008598/*
8599 * Table with the specifications for an attribute number.
8600 * Note that this table is used by ALL buffers. This is required because the
8601 * GUI can redraw at any time for any buffer.
8602 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008603static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008604
8605#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8606
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008607static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008608
8609#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8610
8611#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008612static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613
8614#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8615#endif
8616
8617/*
8618 * Return the attr number for a set of colors and font.
8619 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8620 * if the combination is new.
8621 * Return 0 for error (no more room).
8622 */
8623 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008624get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008625{
8626 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008627 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628 static int recursive = FALSE;
8629
8630 /*
8631 * Init the table, in case it wasn't done yet.
8632 */
8633 table->ga_itemsize = sizeof(attrentry_T);
8634 table->ga_growsize = 7;
8635
8636 /*
8637 * Try to find an entry with the same specifications.
8638 */
8639 for (i = 0; i < table->ga_len; ++i)
8640 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008641 taep = &(((attrentry_T *)table->ga_data)[i]);
8642 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008643 && (
8644#ifdef FEAT_GUI
8645 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008646 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8647 && aep->ae_u.gui.bg_color
8648 == taep->ae_u.gui.bg_color
8649 && aep->ae_u.gui.sp_color
8650 == taep->ae_u.gui.sp_color
8651 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008652# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008653 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008654# endif
8655 ))
8656 ||
8657#endif
8658 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008659 && (aep->ae_u.term.start == NULL)
8660 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008661 && (aep->ae_u.term.start == NULL
8662 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008663 taep->ae_u.term.start) == 0)
8664 && (aep->ae_u.term.stop == NULL)
8665 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008666 && (aep->ae_u.term.stop == NULL
8667 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008668 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008670 && aep->ae_u.cterm.fg_color
8671 == taep->ae_u.cterm.fg_color
8672 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008673 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008674#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008675 && aep->ae_u.cterm.fg_rgb
8676 == taep->ae_u.cterm.fg_rgb
8677 && aep->ae_u.cterm.bg_rgb
8678 == taep->ae_u.cterm.bg_rgb
8679#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008680 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681
8682 return i + ATTR_OFF;
8683 }
8684
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008685 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008686 {
8687 /*
8688 * Running out of attribute entries! remove all attributes, and
8689 * compute new ones for all groups.
8690 * When called recursively, we are really out of numbers.
8691 */
8692 if (recursive)
8693 {
8694 EMSG(_("E424: Too many different highlighting attributes in use"));
8695 return 0;
8696 }
8697 recursive = TRUE;
8698
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008699 clear_hl_tables();
8700
Bram Moolenaar071d4272004-06-13 20:20:40 +00008701 must_redraw = CLEAR;
8702
8703 for (i = 0; i < highlight_ga.ga_len; ++i)
8704 set_hl_attr(i);
8705
8706 recursive = FALSE;
8707 }
8708
8709 /*
8710 * This is a new combination of colors and font, add an entry.
8711 */
8712 if (ga_grow(table, 1) == FAIL)
8713 return 0;
8714
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008715 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8716 vim_memset(taep, 0, sizeof(attrentry_T));
8717 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008718#ifdef FEAT_GUI
8719 if (table == &gui_attr_table)
8720 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008721 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8722 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8723 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8724 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008725# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008726 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008727# endif
8728 }
8729#endif
8730 if (table == &term_attr_table)
8731 {
8732 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008733 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008734 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008735 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008736 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008737 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008738 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008739 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008740 }
8741 else if (table == &cterm_attr_table)
8742 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008743 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8744 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008745#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008746 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8747 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8748#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008749 }
8750 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008751 return (table->ga_len - 1 + ATTR_OFF);
8752}
8753
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008754/*
8755 * Clear all highlight tables.
8756 */
8757 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008758clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008759{
8760 int i;
8761 attrentry_T *taep;
8762
8763#ifdef FEAT_GUI
8764 ga_clear(&gui_attr_table);
8765#endif
8766 for (i = 0; i < term_attr_table.ga_len; ++i)
8767 {
8768 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8769 vim_free(taep->ae_u.term.start);
8770 vim_free(taep->ae_u.term.stop);
8771 }
8772 ga_clear(&term_attr_table);
8773 ga_clear(&cterm_attr_table);
8774}
8775
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008776#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008777/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008778 * Combine special attributes (e.g., for spelling) with other attributes
8779 * (e.g., for syntax highlighting).
8780 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008781 * This creates a new group when required.
8782 * Since we expect there to be few spelling mistakes we don't cache the
8783 * result.
8784 * Return the resulting attributes.
8785 */
8786 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008787hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008788{
8789 attrentry_T *char_aep = NULL;
8790 attrentry_T *spell_aep;
8791 attrentry_T new_en;
8792
8793 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008794 return prim_attr;
8795 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8796 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008797#ifdef FEAT_GUI
8798 if (gui.in_use)
8799 {
8800 if (char_attr > HL_ALL)
8801 char_aep = syn_gui_attr2entry(char_attr);
8802 if (char_aep != NULL)
8803 new_en = *char_aep;
8804 else
8805 {
8806 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008807 new_en.ae_u.gui.fg_color = INVALCOLOR;
8808 new_en.ae_u.gui.bg_color = INVALCOLOR;
8809 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008810 if (char_attr <= HL_ALL)
8811 new_en.ae_attr = char_attr;
8812 }
8813
Bram Moolenaar30abd282005-06-22 22:35:10 +00008814 if (prim_attr <= HL_ALL)
8815 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008816 else
8817 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008818 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008819 if (spell_aep != NULL)
8820 {
8821 new_en.ae_attr |= spell_aep->ae_attr;
8822 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8823 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8824 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8825 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8826 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8827 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8828 if (spell_aep->ae_u.gui.font != NOFONT)
8829 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8830# ifdef FEAT_XFONTSET
8831 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8832 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8833# endif
8834 }
8835 }
8836 return get_attr_entry(&gui_attr_table, &new_en);
8837 }
8838#endif
8839
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008840 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008841 {
8842 if (char_attr > HL_ALL)
8843 char_aep = syn_cterm_attr2entry(char_attr);
8844 if (char_aep != NULL)
8845 new_en = *char_aep;
8846 else
8847 {
8848 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008849#ifdef FEAT_TERMGUICOLORS
8850 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8851 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8852#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008853 if (char_attr <= HL_ALL)
8854 new_en.ae_attr = char_attr;
8855 }
8856
Bram Moolenaar30abd282005-06-22 22:35:10 +00008857 if (prim_attr <= HL_ALL)
8858 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008859 else
8860 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008861 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008862 if (spell_aep != NULL)
8863 {
8864 new_en.ae_attr |= spell_aep->ae_attr;
8865 if (spell_aep->ae_u.cterm.fg_color > 0)
8866 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8867 if (spell_aep->ae_u.cterm.bg_color > 0)
8868 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008869#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008870 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008871 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008872 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008873 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8874#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008875 }
8876 }
8877 return get_attr_entry(&cterm_attr_table, &new_en);
8878 }
8879
8880 if (char_attr > HL_ALL)
8881 char_aep = syn_term_attr2entry(char_attr);
8882 if (char_aep != NULL)
8883 new_en = *char_aep;
8884 else
8885 {
8886 vim_memset(&new_en, 0, sizeof(new_en));
8887 if (char_attr <= HL_ALL)
8888 new_en.ae_attr = char_attr;
8889 }
8890
Bram Moolenaar30abd282005-06-22 22:35:10 +00008891 if (prim_attr <= HL_ALL)
8892 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008893 else
8894 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008895 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008896 if (spell_aep != NULL)
8897 {
8898 new_en.ae_attr |= spell_aep->ae_attr;
8899 if (spell_aep->ae_u.term.start != NULL)
8900 {
8901 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8902 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8903 }
8904 }
8905 }
8906 return get_attr_entry(&term_attr_table, &new_en);
8907}
8908#endif
8909
Bram Moolenaar071d4272004-06-13 20:20:40 +00008910#ifdef FEAT_GUI
8911
8912 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008913syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008914{
8915 attr -= ATTR_OFF;
8916 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8917 return NULL;
8918 return &(GUI_ATTR_ENTRY(attr));
8919}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008920#endif /* FEAT_GUI */
8921
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008922/*
8923 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8924 * Only to be used when "attr" > HL_ALL.
8925 */
8926 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008927syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008928{
8929 attrentry_T *aep;
8930
8931#ifdef FEAT_GUI
8932 if (gui.in_use)
8933 aep = syn_gui_attr2entry(attr);
8934 else
8935#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008936 if (IS_CTERM)
8937 aep = syn_cterm_attr2entry(attr);
8938 else
8939 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008940
8941 if (aep == NULL) /* highlighting not set */
8942 return 0;
8943 return aep->ae_attr;
8944}
8945
8946
Bram Moolenaar071d4272004-06-13 20:20:40 +00008947 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008948syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008949{
8950 attr -= ATTR_OFF;
8951 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8952 return NULL;
8953 return &(TERM_ATTR_ENTRY(attr));
8954}
8955
8956 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008957syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008958{
8959 attr -= ATTR_OFF;
8960 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8961 return NULL;
8962 return &(CTERM_ATTR_ENTRY(attr));
8963}
8964
8965#define LIST_ATTR 1
8966#define LIST_STRING 2
8967#define LIST_INT 3
8968
8969 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008970highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008971{
8972 struct hl_group *sgp;
8973 int didh = FALSE;
8974
8975 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8976
8977 didh = highlight_list_arg(id, didh, LIST_ATTR,
8978 sgp->sg_term, NULL, "term");
8979 didh = highlight_list_arg(id, didh, LIST_STRING,
8980 0, sgp->sg_start, "start");
8981 didh = highlight_list_arg(id, didh, LIST_STRING,
8982 0, sgp->sg_stop, "stop");
8983
8984 didh = highlight_list_arg(id, didh, LIST_ATTR,
8985 sgp->sg_cterm, NULL, "cterm");
8986 didh = highlight_list_arg(id, didh, LIST_INT,
8987 sgp->sg_cterm_fg, NULL, "ctermfg");
8988 didh = highlight_list_arg(id, didh, LIST_INT,
8989 sgp->sg_cterm_bg, NULL, "ctermbg");
8990
Bram Moolenaar61623362010-07-14 22:04:22 +02008991#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008992 didh = highlight_list_arg(id, didh, LIST_ATTR,
8993 sgp->sg_gui, NULL, "gui");
8994 didh = highlight_list_arg(id, didh, LIST_STRING,
8995 0, sgp->sg_gui_fg_name, "guifg");
8996 didh = highlight_list_arg(id, didh, LIST_STRING,
8997 0, sgp->sg_gui_bg_name, "guibg");
8998 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008999 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009000#endif
9001#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009002 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009003 0, sgp->sg_font_name, "font");
9004#endif
9005
Bram Moolenaar661b1822005-07-28 22:36:45 +00009006 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009007 {
9008 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009009 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009010 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009011 msg_putchar(' ');
9012 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9013 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009014
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009015 if (!didh)
9016 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009017#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009018 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009019 last_set_msg(sgp->sg_scriptID);
9020#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009021}
9022
9023 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009024highlight_list_arg(
9025 int id,
9026 int didh,
9027 int type,
9028 int iarg,
9029 char_u *sarg,
9030 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009031{
9032 char_u buf[100];
9033 char_u *ts;
9034 int i;
9035
Bram Moolenaar661b1822005-07-28 22:36:45 +00009036 if (got_int)
9037 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009038 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9039 {
9040 ts = buf;
9041 if (type == LIST_INT)
9042 sprintf((char *)buf, "%d", iarg - 1);
9043 else if (type == LIST_STRING)
9044 ts = sarg;
9045 else /* type == LIST_ATTR */
9046 {
9047 buf[0] = NUL;
9048 for (i = 0; hl_attr_table[i] != 0; ++i)
9049 {
9050 if (iarg & hl_attr_table[i])
9051 {
9052 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009053 vim_strcat(buf, (char_u *)",", 100);
9054 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009055 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9056 }
9057 }
9058 }
9059
9060 (void)syn_list_header(didh,
9061 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9062 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009063 if (!got_int)
9064 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009065 if (*name != NUL)
9066 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009067 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9068 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009069 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009070 msg_outtrans(ts);
9071 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009072 }
9073 return didh;
9074}
9075
9076#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9077/*
9078 * Return "1" if highlight group "id" has attribute "flag".
9079 * Return NULL otherwise.
9080 */
9081 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009082highlight_has_attr(
9083 int id,
9084 int flag,
9085 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009086{
9087 int attr;
9088
9089 if (id <= 0 || id > highlight_ga.ga_len)
9090 return NULL;
9091
Bram Moolenaar61623362010-07-14 22:04:22 +02009092#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009093 if (modec == 'g')
9094 attr = HL_TABLE()[id - 1].sg_gui;
9095 else
9096#endif
9097 if (modec == 'c')
9098 attr = HL_TABLE()[id - 1].sg_cterm;
9099 else
9100 attr = HL_TABLE()[id - 1].sg_term;
9101
9102 if (attr & flag)
9103 return (char_u *)"1";
9104 return NULL;
9105}
9106#endif
9107
9108#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9109/*
9110 * Return color name of highlight group "id".
9111 */
9112 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009113highlight_color(
9114 int id,
9115 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9116 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009117{
9118 static char_u name[20];
9119 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009120 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009121 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009122 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009123
9124 if (id <= 0 || id > highlight_ga.ga_len)
9125 return NULL;
9126
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009127 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009128 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009129 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009130 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009131 font = TRUE;
9132 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009133 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009134 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9135 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009136 if (modec == 'g')
9137 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009138# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009139# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009140 /* return font name */
9141 if (font)
9142 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009143# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009144
Bram Moolenaar071d4272004-06-13 20:20:40 +00009145 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009146 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009147 {
9148 guicolor_T color;
9149 long_u rgb;
9150 static char_u buf[10];
9151
9152 if (fg)
9153 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009154 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009155# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009156 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009157# else
9158 color = INVALCOLOR;
9159# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009160 else
9161 color = HL_TABLE()[id - 1].sg_gui_bg;
9162 if (color == INVALCOLOR)
9163 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009164 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009165 sprintf((char *)buf, "#%02x%02x%02x",
9166 (unsigned)(rgb >> 16),
9167 (unsigned)(rgb >> 8) & 255,
9168 (unsigned)rgb & 255);
9169 return buf;
9170 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009171# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009172 if (fg)
9173 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009174 if (sp)
9175 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009176 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9177 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009178 if (font || sp)
9179 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009180 if (modec == 'c')
9181 {
9182 if (fg)
9183 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9184 else
9185 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009186 if (n < 0)
9187 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009188 sprintf((char *)name, "%d", n);
9189 return name;
9190 }
9191 /* term doesn't have color */
9192 return NULL;
9193}
9194#endif
9195
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009196#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009197 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009198 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009199/*
9200 * Return color name of highlight group "id" as RGB value.
9201 */
9202 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009203highlight_gui_color_rgb(
9204 int id,
9205 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009206{
9207 guicolor_T color;
9208
9209 if (id <= 0 || id > highlight_ga.ga_len)
9210 return 0L;
9211
9212 if (fg)
9213 color = HL_TABLE()[id - 1].sg_gui_fg;
9214 else
9215 color = HL_TABLE()[id - 1].sg_gui_bg;
9216
9217 if (color == INVALCOLOR)
9218 return 0L;
9219
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009220 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009221}
9222#endif
9223
9224/*
9225 * Output the syntax list header.
9226 * Return TRUE when started a new line.
9227 */
9228 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009229syn_list_header(
9230 int did_header, /* did header already */
9231 int outlen, /* length of string that comes */
9232 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009233{
9234 int endcol = 19;
9235 int newline = TRUE;
9236
9237 if (!did_header)
9238 {
9239 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009240 if (got_int)
9241 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009242 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9243 endcol = 15;
9244 }
9245 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009246 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009247 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009248 if (got_int)
9249 return TRUE;
9250 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009251 else
9252 {
9253 if (msg_col >= endcol) /* wrap around is like starting a new line */
9254 newline = FALSE;
9255 }
9256
9257 if (msg_col >= endcol) /* output at least one space */
9258 endcol = msg_col + 1;
9259 if (Columns <= endcol) /* avoid hang for tiny window */
9260 endcol = Columns - 1;
9261
9262 msg_advance(endcol);
9263
9264 /* Show "xxx" with the attributes. */
9265 if (!did_header)
9266 {
9267 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9268 msg_putchar(' ');
9269 }
9270
9271 return newline;
9272}
9273
9274/*
9275 * Set the attribute numbers for a highlight group.
9276 * Called after one of the attributes has changed.
9277 */
9278 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009279set_hl_attr(
9280 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009281{
9282 attrentry_T at_en;
9283 struct hl_group *sgp = HL_TABLE() + idx;
9284
9285 /* The "Normal" group doesn't need an attribute number */
9286 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9287 return;
9288
9289#ifdef FEAT_GUI
9290 /*
9291 * For the GUI mode: If there are other than "normal" highlighting
9292 * attributes, need to allocate an attr number.
9293 */
9294 if (sgp->sg_gui_fg == INVALCOLOR
9295 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009296 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009297 && sgp->sg_font == NOFONT
9298# ifdef FEAT_XFONTSET
9299 && sgp->sg_fontset == NOFONTSET
9300# endif
9301 )
9302 {
9303 sgp->sg_gui_attr = sgp->sg_gui;
9304 }
9305 else
9306 {
9307 at_en.ae_attr = sgp->sg_gui;
9308 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9309 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009310 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009311 at_en.ae_u.gui.font = sgp->sg_font;
9312# ifdef FEAT_XFONTSET
9313 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9314# endif
9315 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9316 }
9317#endif
9318 /*
9319 * For the term mode: If there are other than "normal" highlighting
9320 * attributes, need to allocate an attr number.
9321 */
9322 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9323 sgp->sg_term_attr = sgp->sg_term;
9324 else
9325 {
9326 at_en.ae_attr = sgp->sg_term;
9327 at_en.ae_u.term.start = sgp->sg_start;
9328 at_en.ae_u.term.stop = sgp->sg_stop;
9329 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9330 }
9331
9332 /*
9333 * For the color term mode: If there are other than "normal"
9334 * highlighting attributes, need to allocate an attr number.
9335 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009336 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009337# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009338 && sgp->sg_gui_fg == INVALCOLOR
9339 && sgp->sg_gui_bg == INVALCOLOR
9340# endif
9341 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009342 sgp->sg_cterm_attr = sgp->sg_cterm;
9343 else
9344 {
9345 at_en.ae_attr = sgp->sg_cterm;
9346 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9347 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009348# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009349 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9350 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009351# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009352 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9353 }
9354}
9355
9356/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009357 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009358 * If it is not found, 0 is returned.
9359 */
9360 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009361syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009362{
9363 int i;
9364 char_u name_u[200];
9365
9366 /* Avoid using stricmp() too much, it's slow on some systems */
9367 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9368 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009369 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009370 vim_strup(name_u);
9371 for (i = highlight_ga.ga_len; --i >= 0; )
9372 if (HL_TABLE()[i].sg_name_u != NULL
9373 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9374 break;
9375 return i + 1;
9376}
9377
9378#if defined(FEAT_EVAL) || defined(PROTO)
9379/*
9380 * Return TRUE if highlight group "name" exists.
9381 */
9382 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009383highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009384{
9385 return (syn_name2id(name) > 0);
9386}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009387
9388# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9389/*
9390 * Return the name of highlight group "id".
9391 * When not a valid ID return an empty string.
9392 */
9393 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009394syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009395{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009396 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009397 return (char_u *)"";
9398 return HL_TABLE()[id - 1].sg_name;
9399}
9400# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009401#endif
9402
9403/*
9404 * Like syn_name2id(), but take a pointer + length argument.
9405 */
9406 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009407syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009408{
9409 char_u *name;
9410 int id = 0;
9411
9412 name = vim_strnsave(linep, len);
9413 if (name != NULL)
9414 {
9415 id = syn_name2id(name);
9416 vim_free(name);
9417 }
9418 return id;
9419}
9420
9421/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009422 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009423 * The argument is a pointer to the name and the length of the name.
9424 * If it doesn't exist yet, a new entry is created.
9425 * Return 0 for failure.
9426 */
9427 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009428syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009429{
9430 int id;
9431 char_u *name;
9432
9433 name = vim_strnsave(pp, len);
9434 if (name == NULL)
9435 return 0;
9436
9437 id = syn_name2id(name);
9438 if (id == 0) /* doesn't exist yet */
9439 id = syn_add_group(name);
9440 else
9441 vim_free(name);
9442 return id;
9443}
9444
9445/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009446 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009447 * "name" must be an allocated string, it will be consumed.
9448 * Return 0 for failure.
9449 */
9450 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009451syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009452{
9453 char_u *p;
9454
9455 /* Check that the name is ASCII letters, digits and underscore. */
9456 for (p = name; *p != NUL; ++p)
9457 {
9458 if (!vim_isprintc(*p))
9459 {
9460 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009461 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009462 return 0;
9463 }
9464 else if (!ASCII_ISALNUM(*p) && *p != '_')
9465 {
9466 /* This is an error, but since there previously was no check only
9467 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009468 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009469 MSG(_("W18: Invalid character in group name"));
9470 break;
9471 }
9472 }
9473
9474 /*
9475 * First call for this growarray: init growing array.
9476 */
9477 if (highlight_ga.ga_data == NULL)
9478 {
9479 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9480 highlight_ga.ga_growsize = 10;
9481 }
9482
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009483 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009484 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009485 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009486 vim_free(name);
9487 return 0;
9488 }
9489
Bram Moolenaar071d4272004-06-13 20:20:40 +00009490 /*
9491 * Make room for at least one other syntax_highlight entry.
9492 */
9493 if (ga_grow(&highlight_ga, 1) == FAIL)
9494 {
9495 vim_free(name);
9496 return 0;
9497 }
9498
9499 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9500 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9501 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009502#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009503 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9504 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009505# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009506 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009507# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009508#endif
9509 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009510
9511 return highlight_ga.ga_len; /* ID is index plus one */
9512}
9513
9514/*
9515 * When, just after calling syn_add_group(), an error is discovered, this
9516 * function deletes the new name.
9517 */
9518 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009519syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009520{
9521 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009522 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9523 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9524}
9525
9526/*
9527 * Translate a group ID to highlight attributes.
9528 */
9529 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009530syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009531{
9532 int attr;
9533 struct hl_group *sgp;
9534
9535 hl_id = syn_get_final_id(hl_id);
9536 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9537
9538#ifdef FEAT_GUI
9539 /*
9540 * Only use GUI attr when the GUI is being used.
9541 */
9542 if (gui.in_use)
9543 attr = sgp->sg_gui_attr;
9544 else
9545#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009546 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009547 attr = sgp->sg_cterm_attr;
9548 else
9549 attr = sgp->sg_term_attr;
9550
9551 return attr;
9552}
9553
9554#ifdef FEAT_GUI
9555/*
9556 * Get the GUI colors and attributes for a group ID.
9557 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9558 */
9559 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009560syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009561{
9562 struct hl_group *sgp;
9563
9564 hl_id = syn_get_final_id(hl_id);
9565 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9566
9567 *fgp = sgp->sg_gui_fg;
9568 *bgp = sgp->sg_gui_bg;
9569 return sgp->sg_gui;
9570}
9571#endif
9572
9573/*
9574 * Translate a group ID to the final group ID (following links).
9575 */
9576 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009577syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009578{
9579 int count;
9580 struct hl_group *sgp;
9581
9582 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9583 return 0; /* Can be called from eval!! */
9584
9585 /*
9586 * Follow links until there is no more.
9587 * Look out for loops! Break after 100 links.
9588 */
9589 for (count = 100; --count >= 0; )
9590 {
9591 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9592 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9593 break;
9594 hl_id = sgp->sg_link;
9595 }
9596
9597 return hl_id;
9598}
9599
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009600#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009601/*
9602 * Call this function just after the GUI has started.
9603 * It finds the font and color handles for the highlighting groups.
9604 */
9605 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009606highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009607{
9608 int idx;
9609
9610 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009611# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9612# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009613 if (USE_24BIT)
9614# endif
9615 set_normal_colors();
9616# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009617
9618 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9619 gui_do_one_color(idx, FALSE, FALSE);
9620
9621 highlight_changed();
9622}
9623
9624 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009625gui_do_one_color(
9626 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009627 int do_menu UNUSED, /* TRUE: might set the menu font */
9628 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009629{
9630 int didit = FALSE;
9631
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009632# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009633# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009634 if (gui.in_use)
9635# endif
9636 if (HL_TABLE()[idx].sg_font_name != NULL)
9637 {
9638 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009639 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009640 didit = TRUE;
9641 }
9642# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009643 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9644 {
9645 HL_TABLE()[idx].sg_gui_fg =
9646 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9647 didit = TRUE;
9648 }
9649 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9650 {
9651 HL_TABLE()[idx].sg_gui_bg =
9652 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9653 didit = TRUE;
9654 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009655# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009656 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9657 {
9658 HL_TABLE()[idx].sg_gui_sp =
9659 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9660 didit = TRUE;
9661 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009662# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009663 if (didit) /* need to get a new attr number */
9664 set_hl_attr(idx);
9665}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009666#endif
9667
9668/*
9669 * Translate the 'highlight' option into attributes in highlight_attr[] and
9670 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9671 * corresponding highlights to use on top of HLF_SNC is computed.
9672 * Called only when the 'highlight' option has been changed and upon first
9673 * screen redraw after any :highlight command.
9674 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9675 */
9676 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009677highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009678{
9679 int hlf;
9680 int i;
9681 char_u *p;
9682 int attr;
9683 char_u *end;
9684 int id;
9685#ifdef USER_HIGHLIGHT
9686 char_u userhl[10];
9687# ifdef FEAT_STL_OPT
9688 int id_SNC = -1;
9689 int id_S = -1;
9690 int hlcnt;
9691# endif
9692#endif
9693 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9694
9695 need_highlight_changed = FALSE;
9696
9697 /*
9698 * Clear all attributes.
9699 */
9700 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9701 highlight_attr[hlf] = 0;
9702
9703 /*
9704 * First set all attributes to their default value.
9705 * Then use the attributes from the 'highlight' option.
9706 */
9707 for (i = 0; i < 2; ++i)
9708 {
9709 if (i)
9710 p = p_hl;
9711 else
9712 p = get_highlight_default();
9713 if (p == NULL) /* just in case */
9714 continue;
9715
9716 while (*p)
9717 {
9718 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9719 if (hl_flags[hlf] == *p)
9720 break;
9721 ++p;
9722 if (hlf == (int)HLF_COUNT || *p == NUL)
9723 return FAIL;
9724
9725 /*
9726 * Allow several hl_flags to be combined, like "bu" for
9727 * bold-underlined.
9728 */
9729 attr = 0;
9730 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9731 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01009732 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009733 continue;
9734
9735 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9736 return FAIL;
9737
9738 switch (*p)
9739 {
9740 case 'b': attr |= HL_BOLD;
9741 break;
9742 case 'i': attr |= HL_ITALIC;
9743 break;
9744 case '-':
9745 case 'n': /* no highlighting */
9746 break;
9747 case 'r': attr |= HL_INVERSE;
9748 break;
9749 case 's': attr |= HL_STANDOUT;
9750 break;
9751 case 'u': attr |= HL_UNDERLINE;
9752 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009753 case 'c': attr |= HL_UNDERCURL;
9754 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009755 case ':': ++p; /* highlight group name */
9756 if (attr || *p == NUL) /* no combinations */
9757 return FAIL;
9758 end = vim_strchr(p, ',');
9759 if (end == NULL)
9760 end = p + STRLEN(p);
9761 id = syn_check_group(p, (int)(end - p));
9762 if (id == 0)
9763 return FAIL;
9764 attr = syn_id2attr(id);
9765 p = end - 1;
9766#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9767 if (hlf == (int)HLF_SNC)
9768 id_SNC = syn_get_final_id(id);
9769 else if (hlf == (int)HLF_S)
9770 id_S = syn_get_final_id(id);
9771#endif
9772 break;
9773 default: return FAIL;
9774 }
9775 }
9776 highlight_attr[hlf] = attr;
9777
9778 p = skip_to_option_part(p); /* skip comma and spaces */
9779 }
9780 }
9781
9782#ifdef USER_HIGHLIGHT
9783 /* Setup the user highlights
9784 *
9785 * Temporarily utilize 10 more hl entries. Have to be in there
9786 * simultaneously in case of table overflows in get_attr_entry()
9787 */
9788# ifdef FEAT_STL_OPT
9789 if (ga_grow(&highlight_ga, 10) == FAIL)
9790 return FAIL;
9791 hlcnt = highlight_ga.ga_len;
9792 if (id_S == 0)
9793 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009794 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009795 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9796 id_S = hlcnt + 10;
9797 }
9798# endif
9799 for (i = 0; i < 9; i++)
9800 {
9801 sprintf((char *)userhl, "User%d", i + 1);
9802 id = syn_name2id(userhl);
9803 if (id == 0)
9804 {
9805 highlight_user[i] = 0;
9806# ifdef FEAT_STL_OPT
9807 highlight_stlnc[i] = 0;
9808# endif
9809 }
9810 else
9811 {
9812# ifdef FEAT_STL_OPT
9813 struct hl_group *hlt = HL_TABLE();
9814# endif
9815
9816 highlight_user[i] = syn_id2attr(id);
9817# ifdef FEAT_STL_OPT
9818 if (id_SNC == 0)
9819 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009820 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009821 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9822 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009823# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009824 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9825# endif
9826 }
9827 else
9828 mch_memmove(&hlt[hlcnt + i],
9829 &hlt[id_SNC - 1],
9830 sizeof(struct hl_group));
9831 hlt[hlcnt + i].sg_link = 0;
9832
9833 /* Apply difference between UserX and HLF_S to HLF_SNC */
9834 hlt[hlcnt + i].sg_term ^=
9835 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9836 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9837 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9838 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9839 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9840 hlt[hlcnt + i].sg_cterm ^=
9841 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9842 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9843 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9844 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9845 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009846# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009847 hlt[hlcnt + i].sg_gui ^=
9848 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009849# endif
9850# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009851 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9852 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9853 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9854 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009855 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9856 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009857 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9858 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9859# ifdef FEAT_XFONTSET
9860 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9861 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9862# endif
9863# endif
9864 highlight_ga.ga_len = hlcnt + i + 1;
9865 set_hl_attr(hlcnt + i); /* At long last we can apply */
9866 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9867# endif
9868 }
9869 }
9870# ifdef FEAT_STL_OPT
9871 highlight_ga.ga_len = hlcnt;
9872# endif
9873
9874#endif /* USER_HIGHLIGHT */
9875
9876 return OK;
9877}
9878
Bram Moolenaar4f688582007-07-24 12:34:30 +00009879#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009880
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009881static void highlight_list(void);
9882static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009883
9884/*
9885 * Handle command line completion for :highlight command.
9886 */
9887 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009888set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009889{
9890 char_u *p;
9891
9892 /* Default: expand group names */
9893 xp->xp_context = EXPAND_HIGHLIGHT;
9894 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009895 include_link = 2;
9896 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009897
9898 /* (part of) subcommand already typed */
9899 if (*arg != NUL)
9900 {
9901 p = skiptowhite(arg);
9902 if (*p != NUL) /* past "default" or group name */
9903 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009904 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009905 if (STRNCMP("default", arg, p - arg) == 0)
9906 {
9907 arg = skipwhite(p);
9908 xp->xp_pattern = arg;
9909 p = skiptowhite(arg);
9910 }
9911 if (*p != NUL) /* past group name */
9912 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009913 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009914 if (arg[1] == 'i' && arg[0] == 'N')
9915 highlight_list();
9916 if (STRNCMP("link", arg, p - arg) == 0
9917 || STRNCMP("clear", arg, p - arg) == 0)
9918 {
9919 xp->xp_pattern = skipwhite(p);
9920 p = skiptowhite(xp->xp_pattern);
9921 if (*p != NUL) /* past first group name */
9922 {
9923 xp->xp_pattern = skipwhite(p);
9924 p = skiptowhite(xp->xp_pattern);
9925 }
9926 }
9927 if (*p != NUL) /* past group name(s) */
9928 xp->xp_context = EXPAND_NOTHING;
9929 }
9930 }
9931 }
9932}
9933
9934/*
9935 * List highlighting matches in a nice way.
9936 */
9937 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009938highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009939{
9940 int i;
9941
9942 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +01009943 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009944 for (i = 40; --i >= 0; )
9945 highlight_list_two(99, 0);
9946}
9947
9948 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009949highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009950{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009951 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009952 msg_clr_eos();
9953 out_flush();
9954 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9955}
9956
9957#endif /* FEAT_CMDL_COMPL */
9958
9959#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9960 || defined(FEAT_SIGNS) || defined(PROTO)
9961/*
9962 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009963 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009964 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009965get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009966{
Bram Moolenaarc96272e2017-03-26 13:50:09 +02009967 return get_highlight_name_ext(xp, idx, TRUE);
9968}
9969
9970/*
9971 * Obtain a highlight group name.
9972 * When "skip_cleared" is TRUE don't return a cleared entry.
9973 */
9974 char_u *
9975get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
9976{
Bram Moolenaar15eedf12017-01-22 19:25:33 +01009977 if (idx < 0)
9978 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +02009979
9980 /* Items are never removed from the table, skip the ones that were
9981 * cleared. */
9982 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
9983 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +01009984
Bram Moolenaar071d4272004-06-13 20:20:40 +00009985#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009986 if (idx == highlight_ga.ga_len && include_none != 0)
9987 return (char_u *)"none";
9988 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009989 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009990 if (idx == highlight_ga.ga_len + include_none + include_default
9991 && include_link != 0)
9992 return (char_u *)"link";
9993 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9994 && include_link != 0)
9995 return (char_u *)"clear";
9996#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01009997 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009998 return NULL;
9999 return HL_TABLE()[idx].sg_name;
10000}
10001#endif
10002
Bram Moolenaar4f688582007-07-24 12:34:30 +000010003#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010004/*
10005 * Free all the highlight group fonts.
10006 * Used when quitting for systems which need it.
10007 */
10008 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010009free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010010{
10011 int idx;
10012
10013 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10014 {
10015 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10016 HL_TABLE()[idx].sg_font = NOFONT;
10017# ifdef FEAT_XFONTSET
10018 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10019 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10020# endif
10021 }
10022
10023 gui_mch_free_font(gui.norm_font);
10024# ifdef FEAT_XFONTSET
10025 gui_mch_free_fontset(gui.fontset);
10026# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010027# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010028 gui_mch_free_font(gui.bold_font);
10029 gui_mch_free_font(gui.ital_font);
10030 gui_mch_free_font(gui.boldital_font);
10031# endif
10032}
10033#endif
10034
10035/**************************************
10036 * End of Highlighting stuff *
10037 **************************************/