blob: e466de8985d43598f8296e50a36abc7bdc1910dc [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 Moolenaar3fdfa4a2004-10-07 21:02:47 +0000506 static int 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
519 || changedtick != syn_buf->b_changedtick)
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 Moolenaar3fdfa4a2004-10-07 21:02:47 +0000525 changedtick = syn_buf->b_changedtick;
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 Moolenaar071d4272004-06-13 20:20:40 +00001186 win_T *wp;
1187
Bram Moolenaar860cae12010-06-05 23:22:07 +02001188 syn_stack_free_block(block);
1189
1190
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191#ifdef FEAT_FOLDING
1192 /* When using "syntax" fold method, must update all folds. */
1193 FOR_ALL_WINDOWS(wp)
1194 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001195 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 foldUpdateAll(wp);
1197 }
1198#endif
1199}
1200
1201/*
1202 * Allocate the syntax state stack for syn_buf when needed.
1203 * If the number of entries in b_sst_array[] is much too big or a bit too
1204 * small, reallocate it.
1205 * Also used to allocate b_sst_array[] for the first time.
1206 */
1207 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001208syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209{
1210 long len;
1211 synstate_T *to, *from;
1212 synstate_T *sstp;
1213
1214 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1215 if (len < SST_MIN_ENTRIES)
1216 len = SST_MIN_ENTRIES;
1217 else if (len > SST_MAX_ENTRIES)
1218 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001219 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 {
1221 /* Allocate 50% too much, to avoid reallocating too often. */
1222 len = syn_buf->b_ml.ml_line_count;
1223 len = (len + len / 2) / SST_DIST + Rows * 2;
1224 if (len < SST_MIN_ENTRIES)
1225 len = SST_MIN_ENTRIES;
1226 else if (len > SST_MAX_ENTRIES)
1227 len = SST_MAX_ENTRIES;
1228
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 {
1231 /* When shrinking the array, cleanup the existing stack.
1232 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001233 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 && syn_stack_cleanup())
1235 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1237 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 }
1239
1240 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1241 if (sstp == NULL) /* out of memory! */
1242 return;
1243
1244 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001245 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001246 {
1247 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001248 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 from = from->sst_next)
1250 {
1251 ++to;
1252 *to = *from;
1253 to->sst_next = to + 1;
1254 }
1255 }
1256 if (to != sstp - 1)
1257 {
1258 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001259 syn_block->b_sst_first = sstp;
1260 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 }
1262 else
1263 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 syn_block->b_sst_first = NULL;
1265 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 }
1267
1268 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 while (++to < sstp + len)
1271 to->sst_next = to + 1;
1272 (sstp + len - 1)->sst_next = NULL;
1273
Bram Moolenaar860cae12010-06-05 23:22:07 +02001274 vim_free(syn_block->b_sst_array);
1275 syn_block->b_sst_array = sstp;
1276 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 }
1278}
1279
1280/*
1281 * Check for changes in a buffer to affect stored syntax states. Uses the
1282 * b_mod_* fields.
1283 * Called from update_screen(), before screen is being updated, once for each
1284 * displayed buffer.
1285 */
1286 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001287syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001289 win_T *wp;
1290
1291 syn_stack_apply_changes_block(&buf->b_s, buf);
1292
1293 FOR_ALL_WINDOWS(wp)
1294 {
1295 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1296 syn_stack_apply_changes_block(wp->w_s, buf);
1297 }
1298}
1299
1300 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001301syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001302{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 synstate_T *p, *prev, *np;
1304 linenr_T n;
1305
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 return;
1308
1309 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001310 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001312 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 {
1314 n = p->sst_lnum + buf->b_mod_xlines;
1315 if (n <= buf->b_mod_bot)
1316 {
1317 /* this state is inside the changed area, remove it */
1318 np = p->sst_next;
1319 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001320 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 else
1322 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 p = np;
1325 continue;
1326 }
1327 /* This state is below the changed area. Remember the line
1328 * that needs to be parsed before this entry can be made valid
1329 * again. */
1330 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1331 {
1332 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1333 p->sst_change_lnum += buf->b_mod_xlines;
1334 else
1335 p->sst_change_lnum = buf->b_mod_top;
1336 }
1337 if (p->sst_change_lnum == 0
1338 || p->sst_change_lnum < buf->b_mod_bot)
1339 p->sst_change_lnum = buf->b_mod_bot;
1340
1341 p->sst_lnum = n;
1342 }
1343 prev = p;
1344 p = p->sst_next;
1345 }
1346}
1347
1348/*
1349 * Reduce the number of entries in the state stack for syn_buf.
1350 * Returns TRUE if at least one entry was freed.
1351 */
1352 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001353syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001354{
1355 synstate_T *p, *prev;
1356 disptick_T tick;
1357 int above;
1358 int dist;
1359 int retval = FALSE;
1360
Bram Moolenaar860cae12010-06-05 23:22:07 +02001361 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 return retval;
1363
1364 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001365 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001366 dist = 999999;
1367 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001368 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369
1370 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001371 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372 * be removed. Set "above" when the "tick" for the oldest entry is above
1373 * "b_sst_lasttick" (the display tick wraps around).
1374 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001377 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001378 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1379 {
1380 if (prev->sst_lnum + dist > p->sst_lnum)
1381 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001382 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 {
1384 if (!above || p->sst_tick < tick)
1385 tick = p->sst_tick;
1386 above = TRUE;
1387 }
1388 else if (!above && p->sst_tick < tick)
1389 tick = p->sst_tick;
1390 }
1391 }
1392
1393 /*
1394 * Go through the list to make the entries for the oldest tick at an
1395 * interval of several lines.
1396 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001397 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1399 {
1400 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1401 {
1402 /* Move this entry from used list to free list */
1403 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001404 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001405 p = prev;
1406 retval = TRUE;
1407 }
1408 }
1409 return retval;
1410}
1411
1412/*
1413 * Free the allocated memory for a syn_state item.
1414 * Move the entry into the free list.
1415 */
1416 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001417syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418{
1419 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001420 p->sst_next = block->b_sst_firstfree;
1421 block->b_sst_firstfree = p;
1422 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001423}
1424
1425/*
1426 * Find an entry in the list of state stacks at or before "lnum".
1427 * Returns NULL when there is no entry or the first entry is after "lnum".
1428 */
1429 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001430syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431{
1432 synstate_T *p, *prev;
1433
1434 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001435 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001436 {
1437 if (p->sst_lnum == lnum)
1438 return p;
1439 if (p->sst_lnum > lnum)
1440 break;
1441 }
1442 return prev;
1443}
1444
1445/*
1446 * Try saving the current state in b_sst_array[].
1447 * The current state must be valid for the start of the current_lnum line!
1448 */
1449 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001450store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451{
1452 int i;
1453 synstate_T *p;
1454 bufstate_T *bp;
1455 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001456 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457
1458 /*
1459 * If the current state contains a start or end pattern that continues
1460 * from the previous line, we can't use it. Don't store it then.
1461 */
1462 for (i = current_state.ga_len - 1; i >= 0; --i)
1463 {
1464 cur_si = &CUR_STATE(i);
1465 if (cur_si->si_h_startpos.lnum >= current_lnum
1466 || cur_si->si_m_endpos.lnum >= current_lnum
1467 || cur_si->si_h_endpos.lnum >= current_lnum
1468 || (cur_si->si_end_idx
1469 && cur_si->si_eoe_pos.lnum >= current_lnum))
1470 break;
1471 }
1472 if (i >= 0)
1473 {
1474 if (sp != NULL)
1475 {
1476 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001477 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001479 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480 else
1481 {
1482 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001483 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 if (p->sst_next == sp)
1485 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001486 if (p != NULL) /* just in case */
1487 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001489 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 sp = NULL;
1491 }
1492 }
1493 else if (sp == NULL || sp->sst_lnum != current_lnum)
1494 {
1495 /*
1496 * Add a new entry
1497 */
1498 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 {
1501 (void)syn_stack_cleanup();
1502 /* "sp" may have been moved to the freelist now */
1503 sp = syn_stack_find_entry(current_lnum);
1504 }
1505 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001506 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 sp = NULL;
1508 else
1509 {
1510 /* Take the first item from the free list and put it in the used
1511 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001512 p = syn_block->b_sst_firstfree;
1513 syn_block->b_sst_firstfree = p->sst_next;
1514 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 if (sp == NULL)
1516 {
1517 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001518 p->sst_next = syn_block->b_sst_first;
1519 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001520 }
1521 else
1522 {
1523 /* insert in list after *sp */
1524 p->sst_next = sp->sst_next;
1525 sp->sst_next = p;
1526 }
1527 sp = p;
1528 sp->sst_stacksize = 0;
1529 sp->sst_lnum = current_lnum;
1530 }
1531 }
1532 if (sp != NULL)
1533 {
1534 /* When overwriting an existing state stack, clear it first */
1535 clear_syn_state(sp);
1536 sp->sst_stacksize = current_state.ga_len;
1537 if (current_state.ga_len > SST_FIX_STATES)
1538 {
1539 /* Need to clear it, might be something remaining from when the
1540 * length was less than SST_FIX_STATES. */
1541 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1542 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1543 sp->sst_stacksize = 0;
1544 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1547 }
1548 else
1549 bp = sp->sst_union.sst_stack;
1550 for (i = 0; i < sp->sst_stacksize; ++i)
1551 {
1552 bp[i].bs_idx = CUR_STATE(i).si_idx;
1553 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001554#ifdef FEAT_CONCEAL
1555 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1556 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1557#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001558 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1559 }
1560 sp->sst_next_flags = current_next_flags;
1561 sp->sst_next_list = current_next_list;
1562 sp->sst_tick = display_tick;
1563 sp->sst_change_lnum = 0;
1564 }
1565 current_state_stored = TRUE;
1566 return sp;
1567}
1568
1569/*
1570 * Copy a state stack from "from" in b_sst_array[] to current_state;
1571 */
1572 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001573load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574{
1575 int i;
1576 bufstate_T *bp;
1577
1578 clear_current_state();
1579 validate_current_state();
1580 keepend_level = -1;
1581 if (from->sst_stacksize
1582 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1583 {
1584 if (from->sst_stacksize > SST_FIX_STATES)
1585 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1586 else
1587 bp = from->sst_union.sst_stack;
1588 for (i = 0; i < from->sst_stacksize; ++i)
1589 {
1590 CUR_STATE(i).si_idx = bp[i].bs_idx;
1591 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001592#ifdef FEAT_CONCEAL
1593 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1594 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1595#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1597 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1598 keepend_level = i;
1599 CUR_STATE(i).si_ends = FALSE;
1600 CUR_STATE(i).si_m_lnum = 0;
1601 if (CUR_STATE(i).si_idx >= 0)
1602 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001603 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604 else
1605 CUR_STATE(i).si_next_list = NULL;
1606 update_si_attr(i);
1607 }
1608 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001609 }
1610 current_next_list = from->sst_next_list;
1611 current_next_flags = from->sst_next_flags;
1612 current_lnum = from->sst_lnum;
1613}
1614
1615/*
1616 * Compare saved state stack "*sp" with the current state.
1617 * Return TRUE when they are equal.
1618 */
1619 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001620syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001621{
1622 int i, j;
1623 bufstate_T *bp;
1624 reg_extmatch_T *six, *bsx;
1625
1626 /* First a quick check if the stacks have the same size end nextlist. */
1627 if (sp->sst_stacksize == current_state.ga_len
1628 && sp->sst_next_list == current_next_list)
1629 {
1630 /* Need to compare all states on both stacks. */
1631 if (sp->sst_stacksize > SST_FIX_STATES)
1632 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1633 else
1634 bp = sp->sst_union.sst_stack;
1635
1636 for (i = current_state.ga_len; --i >= 0; )
1637 {
1638 /* If the item has another index the state is different. */
1639 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1640 break;
1641 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1642 {
1643 /* When the extmatch pointers are different, the strings in
1644 * them can still be the same. Check if the extmatch
1645 * references are equal. */
1646 bsx = bp[i].bs_extmatch;
1647 six = CUR_STATE(i).si_extmatch;
1648 /* If one of the extmatch pointers is NULL the states are
1649 * different. */
1650 if (bsx == NULL || six == NULL)
1651 break;
1652 for (j = 0; j < NSUBEXP; ++j)
1653 {
1654 /* Check each referenced match string. They must all be
1655 * equal. */
1656 if (bsx->matches[j] != six->matches[j])
1657 {
1658 /* If the pointer is different it can still be the
1659 * same text. Compare the strings, ignore case when
1660 * the start item has the sp_ic flag set. */
1661 if (bsx->matches[j] == NULL
1662 || six->matches[j] == NULL)
1663 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001664 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665 ? MB_STRICMP(bsx->matches[j],
1666 six->matches[j]) != 0
1667 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1668 break;
1669 }
1670 }
1671 if (j != NSUBEXP)
1672 break;
1673 }
1674 }
1675 if (i < 0)
1676 return TRUE;
1677 }
1678 return FALSE;
1679}
1680
1681/*
1682 * We stop parsing syntax above line "lnum". If the stored state at or below
1683 * this line depended on a change before it, it now depends on the line below
1684 * the last parsed line.
1685 * The window looks like this:
1686 * line which changed
1687 * displayed line
1688 * displayed line
1689 * lnum -> line below window
1690 */
1691 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001692syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693{
1694 synstate_T *sp;
1695
1696 sp = syn_stack_find_entry(lnum);
1697 if (sp != NULL && sp->sst_lnum < lnum)
1698 sp = sp->sst_next;
1699
1700 if (sp != NULL && sp->sst_change_lnum != 0)
1701 sp->sst_change_lnum = lnum;
1702}
1703
1704/*
1705 * End of handling of the state stack.
1706 ****************************************/
1707
1708 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001709invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710{
1711 clear_current_state();
1712 current_state.ga_itemsize = 0; /* mark current_state invalid */
1713 current_next_list = NULL;
1714 keepend_level = -1;
1715}
1716
1717 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001718validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719{
1720 current_state.ga_itemsize = sizeof(stateitem_T);
1721 current_state.ga_growsize = 3;
1722}
1723
1724/*
1725 * Return TRUE if the syntax at start of lnum changed since last time.
1726 * This will only be called just after get_syntax_attr() for the previous
1727 * line, to check if the next line needs to be redrawn too.
1728 */
1729 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001730syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731{
1732 int retval = TRUE;
1733 synstate_T *sp;
1734
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735 /*
1736 * Check the state stack when:
1737 * - lnum is just below the previously syntaxed line.
1738 * - lnum is not before the lines with saved states.
1739 * - lnum is not past the lines with saved states.
1740 * - lnum is at or before the last changed line.
1741 */
1742 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1743 {
1744 sp = syn_stack_find_entry(lnum);
1745 if (sp != NULL && sp->sst_lnum == lnum)
1746 {
1747 /*
1748 * finish the previous line (needed when not all of the line was
1749 * drawn)
1750 */
1751 (void)syn_finish_line(FALSE);
1752
1753 /*
1754 * Compare the current state with the previously saved state of
1755 * the line.
1756 */
1757 if (syn_stack_equal(sp))
1758 retval = FALSE;
1759
1760 /*
1761 * Store the current state in b_sst_array[] for later use.
1762 */
1763 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001764 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 }
1766 }
1767
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768 return retval;
1769}
1770
1771/*
1772 * Finish the current line.
1773 * This doesn't return any attributes, it only gets the state at the end of
1774 * the line. It can start anywhere in the line, as long as the current state
1775 * is valid.
1776 */
1777 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001778syn_finish_line(
1779 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780{
1781 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001782 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001783
1784 if (!current_finished)
1785 {
1786 while (!current_finished)
1787 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001788 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001789 /*
1790 * When syncing, and found some item, need to check the item.
1791 */
1792 if (syncing && current_state.ga_len)
1793 {
1794 /*
1795 * Check for match with sync item.
1796 */
1797 cur_si = &CUR_STATE(current_state.ga_len - 1);
1798 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001799 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1801 return TRUE;
1802
1803 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001804 * that ends here, need to do that now. Be careful not to go
1805 * past the NUL. */
1806 prev_current_col = current_col;
1807 if (syn_getcurline()[current_col] != NUL)
1808 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001810 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 }
1812 ++current_col;
1813 }
1814 }
1815 return FALSE;
1816}
1817
1818/*
1819 * Return highlight attributes for next character.
1820 * Must first call syntax_start() once for the line.
1821 * "col" is normally 0 for the first use in a line, and increments by one each
1822 * time. It's allowed to skip characters and to stop before the end of the
1823 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001824 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1825 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826 */
1827 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001828get_syntax_attr(
1829 colnr_T col,
1830 int *can_spell,
1831 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832{
1833 int attr = 0;
1834
Bram Moolenaar349955a2007-08-14 21:07:36 +00001835 if (can_spell != NULL)
1836 /* Default: Only do spelling when there is no @Spell cluster or when
1837 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001838 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1839 ? (syn_block->b_spell_cluster_id == 0)
1840 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001841
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001843 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001844 return 0;
1845
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001846 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001847 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001848 {
1849 clear_current_state();
1850#ifdef FEAT_EVAL
1851 current_id = 0;
1852 current_trans_id = 0;
1853#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001854#ifdef FEAT_CONCEAL
1855 current_flags = 0;
1856#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001857 return 0;
1858 }
1859
Bram Moolenaar071d4272004-06-13 20:20:40 +00001860 /* Make sure current_state is valid */
1861 if (INVALID_STATE(&current_state))
1862 validate_current_state();
1863
1864 /*
1865 * Skip from the current column to "col", get the attributes for "col".
1866 */
1867 while (current_col <= col)
1868 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001869 attr = syn_current_attr(FALSE, TRUE, can_spell,
1870 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001871 ++current_col;
1872 }
1873
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874 return attr;
1875}
1876
1877/*
1878 * Get syntax attributes for current_lnum, current_col.
1879 */
1880 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001881syn_current_attr(
1882 int syncing, /* When 1: called for syncing */
1883 int displaying, /* result will be displayed */
1884 int *can_spell, /* return: do spell checking */
1885 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886{
1887 int syn_id;
1888 lpos_T endpos; /* was: char_u *endp; */
1889 lpos_T hl_startpos; /* was: int hl_startcol; */
1890 lpos_T hl_endpos;
1891 lpos_T eos_pos; /* end-of-start match (start region) */
1892 lpos_T eoe_pos; /* end-of-end pattern */
1893 int end_idx; /* group ID for end pattern */
1894 int idx;
1895 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001896 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001897 int startcol;
1898 int endcol;
1899 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001900 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 short *next_list;
1902 int found_match; /* found usable match */
1903 static int try_next_column = FALSE; /* must try in next col */
1904 int do_keywords;
1905 regmmatch_T regmatch;
1906 lpos_T pos;
1907 int lc_col;
1908 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001909 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910 char_u *line; /* current line. NOTE: becomes invalid after
1911 looking for a pattern match! */
1912
1913 /* variables for zero-width matches that have a "nextgroup" argument */
1914 int keep_next_list;
1915 int zero_width_next_list = FALSE;
1916 garray_T zero_width_next_ga;
1917
1918 /*
1919 * No character, no attributes! Past end of line?
1920 * Do try matching with an empty line (could be the start of a region).
1921 */
1922 line = syn_getcurline();
1923 if (line[current_col] == NUL && current_col != 0)
1924 {
1925 /*
1926 * If we found a match after the last column, use it.
1927 */
1928 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1929 && next_match_col != MAXCOL)
1930 (void)push_next_match(NULL);
1931
1932 current_finished = TRUE;
1933 current_state_stored = FALSE;
1934 return 0;
1935 }
1936
1937 /* if the current or next character is NUL, we will finish the line now */
1938 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1939 {
1940 current_finished = TRUE;
1941 current_state_stored = FALSE;
1942 }
1943
1944 /*
1945 * When in the previous column there was a match but it could not be used
1946 * (empty match or already matched in this column) need to try again in
1947 * the next column.
1948 */
1949 if (try_next_column)
1950 {
1951 next_match_idx = -1;
1952 try_next_column = FALSE;
1953 }
1954
1955 /* Only check for keywords when not syncing and there are some. */
1956 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001957 && (syn_block->b_keywtab.ht_used > 0
1958 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001959
1960 /* Init the list of zero-width matches with a nextlist. This is used to
1961 * avoid matching the same item in the same position twice. */
1962 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1963
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001964 /* use syntax iskeyword option */
1965 save_chartab(buf_chartab);
1966
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967 /*
1968 * Repeat matching keywords and patterns, to find contained items at the
1969 * same column. This stops when there are no extra matches at the current
1970 * column.
1971 */
1972 do
1973 {
1974 found_match = FALSE;
1975 keep_next_list = FALSE;
1976 syn_id = 0;
1977
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001978
Bram Moolenaar071d4272004-06-13 20:20:40 +00001979 /*
1980 * 1. Check for a current state.
1981 * Only when there is no current state, or if the current state may
1982 * contain other things, we need to check for keywords and patterns.
1983 * Always need to check for contained items if some item has the
1984 * "containedin" argument (takes extra time!).
1985 */
1986 if (current_state.ga_len)
1987 cur_si = &CUR_STATE(current_state.ga_len - 1);
1988 else
1989 cur_si = NULL;
1990
Bram Moolenaar860cae12010-06-05 23:22:07 +02001991 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001992 || cur_si->si_cont_list != NULL)
1993 {
1994 /*
1995 * 2. Check for keywords, if on a keyword char after a non-keyword
1996 * char. Don't do this when syncing.
1997 */
1998 if (do_keywords)
1999 {
2000 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002001 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002002 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002003 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002004#ifdef FEAT_MBYTE
2005 - (has_mbyte
2006 ? (*mb_head_off)(line, line + current_col - 1)
2007 : 0)
2008#endif
2009 , syn_buf)))
2010 {
2011 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002012 &endcol, &flags, &next_list, cur_si,
2013 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002014 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002015 {
2016 if (push_current_state(KEYWORD_IDX) == OK)
2017 {
2018 cur_si = &CUR_STATE(current_state.ga_len - 1);
2019 cur_si->si_m_startcol = current_col;
2020 cur_si->si_h_startpos.lnum = current_lnum;
2021 cur_si->si_h_startpos.col = 0; /* starts right away */
2022 cur_si->si_m_endpos.lnum = current_lnum;
2023 cur_si->si_m_endpos.col = endcol;
2024 cur_si->si_h_endpos.lnum = current_lnum;
2025 cur_si->si_h_endpos.col = endcol;
2026 cur_si->si_ends = TRUE;
2027 cur_si->si_end_idx = 0;
2028 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002029#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002030 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002031 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002032 if (current_state.ga_len > 1)
2033 cur_si->si_flags |=
2034 CUR_STATE(current_state.ga_len - 2).si_flags
2035 & HL_CONCEAL;
2036#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037 cur_si->si_id = syn_id;
2038 cur_si->si_trans_id = syn_id;
2039 if (flags & HL_TRANSP)
2040 {
2041 if (current_state.ga_len < 2)
2042 {
2043 cur_si->si_attr = 0;
2044 cur_si->si_trans_id = 0;
2045 }
2046 else
2047 {
2048 cur_si->si_attr = CUR_STATE(
2049 current_state.ga_len - 2).si_attr;
2050 cur_si->si_trans_id = CUR_STATE(
2051 current_state.ga_len - 2).si_trans_id;
2052 }
2053 }
2054 else
2055 cur_si->si_attr = syn_id2attr(syn_id);
2056 cur_si->si_cont_list = NULL;
2057 cur_si->si_next_list = next_list;
2058 check_keepend();
2059 }
2060 else
2061 vim_free(next_list);
2062 }
2063 }
2064 }
2065
2066 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002067 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002069 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070 {
2071 /*
2072 * If we didn't check for a match yet, or we are past it, check
2073 * for any match with a pattern.
2074 */
2075 if (next_match_idx < 0 || next_match_col < (int)current_col)
2076 {
2077 /*
2078 * Check all relevant patterns for a match at this
2079 * position. This is complicated, because matching with a
2080 * pattern takes quite a bit of time, thus we want to
2081 * avoid doing it when it's not needed.
2082 */
2083 next_match_idx = 0; /* no match in this line yet */
2084 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002085 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002087 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002088 if ( spp->sp_syncing == syncing
2089 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2090 && (spp->sp_type == SPTYPE_MATCH
2091 || spp->sp_type == SPTYPE_START)
2092 && (current_next_list != NULL
2093 ? in_id_list(NULL, current_next_list,
2094 &spp->sp_syn, 0)
2095 : (cur_si == NULL
2096 ? !(spp->sp_flags & HL_CONTAINED)
2097 : in_id_list(cur_si,
2098 cur_si->si_cont_list, &spp->sp_syn,
2099 spp->sp_flags & HL_CONTAINED))))
2100 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002101 int r;
2102
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 /* If we already tried matching in this line, and
2104 * there isn't a match before next_match_col, skip
2105 * this item. */
2106 if (spp->sp_line_id == current_line_id
2107 && spp->sp_startcol >= next_match_col)
2108 continue;
2109 spp->sp_line_id = current_line_id;
2110
2111 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2112 if (lc_col < 0)
2113 lc_col = 0;
2114
2115 regmatch.rmm_ic = spp->sp_ic;
2116 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002117 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002118 current_lnum,
2119 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002120 IF_SYN_TIME(&spp->sp_time));
2121 spp->sp_prog = regmatch.regprog;
2122 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002123 {
2124 /* no match in this line, try another one */
2125 spp->sp_startcol = MAXCOL;
2126 continue;
2127 }
2128
2129 /*
2130 * Compute the first column of the match.
2131 */
2132 syn_add_start_off(&pos, &regmatch,
2133 spp, SPO_MS_OFF, -1);
2134 if (pos.lnum > current_lnum)
2135 {
2136 /* must have used end of match in a next line,
2137 * we can't handle that */
2138 spp->sp_startcol = MAXCOL;
2139 continue;
2140 }
2141 startcol = pos.col;
2142
2143 /* remember the next column where this pattern
2144 * matches in the current line */
2145 spp->sp_startcol = startcol;
2146
2147 /*
2148 * If a previously found match starts at a lower
2149 * column number, don't use this one.
2150 */
2151 if (startcol >= next_match_col)
2152 continue;
2153
2154 /*
2155 * If we matched this pattern at this position
2156 * before, skip it. Must retry in the next
2157 * column, because it may match from there.
2158 */
2159 if (did_match_already(idx, &zero_width_next_ga))
2160 {
2161 try_next_column = TRUE;
2162 continue;
2163 }
2164
2165 endpos.lnum = regmatch.endpos[0].lnum;
2166 endpos.col = regmatch.endpos[0].col;
2167
2168 /* Compute the highlight start. */
2169 syn_add_start_off(&hl_startpos, &regmatch,
2170 spp, SPO_HS_OFF, -1);
2171
2172 /* Compute the region start. */
2173 /* Default is to use the end of the match. */
2174 syn_add_end_off(&eos_pos, &regmatch,
2175 spp, SPO_RS_OFF, 0);
2176
2177 /*
2178 * Grab the external submatches before they get
2179 * overwritten. Reference count doesn't change.
2180 */
2181 unref_extmatch(cur_extmatch);
2182 cur_extmatch = re_extmatch_out;
2183 re_extmatch_out = NULL;
2184
2185 flags = 0;
2186 eoe_pos.lnum = 0; /* avoid warning */
2187 eoe_pos.col = 0;
2188 end_idx = 0;
2189 hl_endpos.lnum = 0;
2190
2191 /*
2192 * For a "oneline" the end must be found in the
2193 * same line too. Search for it after the end of
2194 * the match with the start pattern. Set the
2195 * resulting end positions at the same time.
2196 */
2197 if (spp->sp_type == SPTYPE_START
2198 && (spp->sp_flags & HL_ONELINE))
2199 {
2200 lpos_T startpos;
2201
2202 startpos = endpos;
2203 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2204 &flags, &eoe_pos, &end_idx, cur_extmatch);
2205 if (endpos.lnum == 0)
2206 continue; /* not found */
2207 }
2208
2209 /*
2210 * For a "match" the size must be > 0 after the
2211 * end offset needs has been added. Except when
2212 * syncing.
2213 */
2214 else if (spp->sp_type == SPTYPE_MATCH)
2215 {
2216 syn_add_end_off(&hl_endpos, &regmatch, spp,
2217 SPO_HE_OFF, 0);
2218 syn_add_end_off(&endpos, &regmatch, spp,
2219 SPO_ME_OFF, 0);
2220 if (endpos.lnum == current_lnum
2221 && (int)endpos.col + syncing < startcol)
2222 {
2223 /*
2224 * If an empty string is matched, may need
2225 * to try matching again at next column.
2226 */
2227 if (regmatch.startpos[0].col
2228 == regmatch.endpos[0].col)
2229 try_next_column = TRUE;
2230 continue;
2231 }
2232 }
2233
2234 /*
2235 * keep the best match so far in next_match_*
2236 */
2237 /* Highlighting must start after startpos and end
2238 * before endpos. */
2239 if (hl_startpos.lnum == current_lnum
2240 && (int)hl_startpos.col < startcol)
2241 hl_startpos.col = startcol;
2242 limit_pos_zero(&hl_endpos, &endpos);
2243
2244 next_match_idx = idx;
2245 next_match_col = startcol;
2246 next_match_m_endpos = endpos;
2247 next_match_h_endpos = hl_endpos;
2248 next_match_h_startpos = hl_startpos;
2249 next_match_flags = flags;
2250 next_match_eos_pos = eos_pos;
2251 next_match_eoe_pos = eoe_pos;
2252 next_match_end_idx = end_idx;
2253 unref_extmatch(next_match_extmatch);
2254 next_match_extmatch = cur_extmatch;
2255 cur_extmatch = NULL;
2256 }
2257 }
2258 }
2259
2260 /*
2261 * If we found a match at the current column, use it.
2262 */
2263 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2264 {
2265 synpat_T *lspp;
2266
2267 /* When a zero-width item matched which has a nextgroup,
2268 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002269 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002270 if (next_match_m_endpos.lnum == current_lnum
2271 && next_match_m_endpos.col == current_col
2272 && lspp->sp_next_list != NULL)
2273 {
2274 current_next_list = lspp->sp_next_list;
2275 current_next_flags = lspp->sp_flags;
2276 keep_next_list = TRUE;
2277 zero_width_next_list = TRUE;
2278
2279 /* Add the index to a list, so that we can check
2280 * later that we don't match it again (and cause an
2281 * endless loop). */
2282 if (ga_grow(&zero_width_next_ga, 1) == OK)
2283 {
2284 ((int *)(zero_width_next_ga.ga_data))
2285 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 }
2287 next_match_idx = -1;
2288 }
2289 else
2290 cur_si = push_next_match(cur_si);
2291 found_match = TRUE;
2292 }
2293 }
2294 }
2295
2296 /*
2297 * Handle searching for nextgroup match.
2298 */
2299 if (current_next_list != NULL && !keep_next_list)
2300 {
2301 /*
2302 * If a nextgroup was not found, continue looking for one if:
2303 * - this is an empty line and the "skipempty" option was given
2304 * - we are on white space and the "skipwhite" option was given
2305 */
2306 if (!found_match)
2307 {
2308 line = syn_getcurline();
2309 if (((current_next_flags & HL_SKIPWHITE)
2310 && vim_iswhite(line[current_col]))
2311 || ((current_next_flags & HL_SKIPEMPTY)
2312 && *line == NUL))
2313 break;
2314 }
2315
2316 /*
2317 * If a nextgroup was found: Use it, and continue looking for
2318 * contained matches.
2319 * If a nextgroup was not found: Continue looking for a normal
2320 * match.
2321 * When did set current_next_list for a zero-width item and no
2322 * match was found don't loop (would get stuck).
2323 */
2324 current_next_list = NULL;
2325 next_match_idx = -1;
2326 if (!zero_width_next_list)
2327 found_match = TRUE;
2328 }
2329
2330 } while (found_match);
2331
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002332 restore_chartab(buf_chartab);
2333
Bram Moolenaar071d4272004-06-13 20:20:40 +00002334 /*
2335 * Use attributes from the current state, if within its highlighting.
2336 * If not, use attributes from the current-but-one state, etc.
2337 */
2338 current_attr = 0;
2339#ifdef FEAT_EVAL
2340 current_id = 0;
2341 current_trans_id = 0;
2342#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002343#ifdef FEAT_CONCEAL
2344 current_flags = 0;
2345#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 if (cur_si != NULL)
2347 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002348#ifndef FEAT_EVAL
2349 int current_trans_id = 0;
2350#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2352 {
2353 sip = &CUR_STATE(idx);
2354 if ((current_lnum > sip->si_h_startpos.lnum
2355 || (current_lnum == sip->si_h_startpos.lnum
2356 && current_col >= sip->si_h_startpos.col))
2357 && (sip->si_h_endpos.lnum == 0
2358 || current_lnum < sip->si_h_endpos.lnum
2359 || (current_lnum == sip->si_h_endpos.lnum
2360 && current_col < sip->si_h_endpos.col)))
2361 {
2362 current_attr = sip->si_attr;
2363#ifdef FEAT_EVAL
2364 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002366 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002367#ifdef FEAT_CONCEAL
2368 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002369 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002370 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002371#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002372 break;
2373 }
2374 }
2375
Bram Moolenaar217ad922005-03-20 22:37:15 +00002376 if (can_spell != NULL)
2377 {
2378 struct sp_syn sps;
2379
2380 /*
2381 * set "can_spell" to TRUE if spell checking is supposed to be
2382 * done in the current item.
2383 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002384 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002385 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002386 /* There is no @Spell cluster: Do spelling for items without
2387 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388 if (syn_block->b_nospell_cluster_id == 0
2389 || current_trans_id == 0)
2390 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002391 else
2392 {
2393 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002394 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002395 sps.cont_in_list = NULL;
2396 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2397 }
2398 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002399 else
2400 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002401 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002402 * the @Spell cluster. But not when @NoSpell is also there.
2403 * At the toplevel only spell check when ":syn spell toplevel"
2404 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002405 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002406 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002407 else
2408 {
2409 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002410 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002411 sps.cont_in_list = NULL;
2412 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2413
Bram Moolenaar860cae12010-06-05 23:22:07 +02002414 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002415 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002416 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002417 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2418 *can_spell = FALSE;
2419 }
2420 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002421 }
2422 }
2423
2424
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 /*
2426 * Check for end of current state (and the states before it) at the
2427 * next column. Don't do this for syncing, because we would miss a
2428 * single character match.
2429 * First check if the current state ends at the current column. It
2430 * may be for an empty match and a containing item might end in the
2431 * current column.
2432 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002433 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 {
2435 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002436 if (current_state.ga_len > 0
2437 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002438 {
2439 ++current_col;
2440 check_state_ends();
2441 --current_col;
2442 }
2443 }
2444 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002445 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002446 /* Default: Only do spelling when there is no @Spell cluster or when
2447 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002448 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2449 ? (syn_block->b_spell_cluster_id == 0)
2450 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002452 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002453 if (current_next_list != NULL
2454 && syn_getcurline()[current_col + 1] == NUL
2455 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2456 current_next_list = NULL;
2457
2458 if (zero_width_next_ga.ga_len > 0)
2459 ga_clear(&zero_width_next_ga);
2460
2461 /* No longer need external matches. But keep next_match_extmatch. */
2462 unref_extmatch(re_extmatch_out);
2463 re_extmatch_out = NULL;
2464 unref_extmatch(cur_extmatch);
2465
2466 return current_attr;
2467}
2468
2469
2470/*
2471 * Check if we already matched pattern "idx" at the current column.
2472 */
2473 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002474did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475{
2476 int i;
2477
2478 for (i = current_state.ga_len; --i >= 0; )
2479 if (CUR_STATE(i).si_m_startcol == (int)current_col
2480 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2481 && CUR_STATE(i).si_idx == idx)
2482 return TRUE;
2483
2484 /* Zero-width matches with a nextgroup argument are not put on the syntax
2485 * stack, and can only be matched once anyway. */
2486 for (i = gap->ga_len; --i >= 0; )
2487 if (((int *)(gap->ga_data))[i] == idx)
2488 return TRUE;
2489
2490 return FALSE;
2491}
2492
2493/*
2494 * Push the next match onto the stack.
2495 */
2496 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002497push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498{
2499 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002500#ifdef FEAT_CONCEAL
2501 int save_flags;
2502#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503
Bram Moolenaar860cae12010-06-05 23:22:07 +02002504 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505
2506 /*
2507 * Push the item in current_state stack;
2508 */
2509 if (push_current_state(next_match_idx) == OK)
2510 {
2511 /*
2512 * If it's a start-skip-end type that crosses lines, figure out how
2513 * much it continues in this line. Otherwise just fill in the length.
2514 */
2515 cur_si = &CUR_STATE(current_state.ga_len - 1);
2516 cur_si->si_h_startpos = next_match_h_startpos;
2517 cur_si->si_m_startcol = current_col;
2518 cur_si->si_m_lnum = current_lnum;
2519 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002520#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002521 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002522 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002523 if (current_state.ga_len > 1)
2524 cur_si->si_flags |=
2525 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2526#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527 cur_si->si_next_list = spp->sp_next_list;
2528 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2529 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2530 {
2531 /* Try to find the end pattern in the current line */
2532 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2533 check_keepend();
2534 }
2535 else
2536 {
2537 cur_si->si_m_endpos = next_match_m_endpos;
2538 cur_si->si_h_endpos = next_match_h_endpos;
2539 cur_si->si_ends = TRUE;
2540 cur_si->si_flags |= next_match_flags;
2541 cur_si->si_eoe_pos = next_match_eoe_pos;
2542 cur_si->si_end_idx = next_match_end_idx;
2543 }
2544 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2545 keepend_level = current_state.ga_len - 1;
2546 check_keepend();
2547 update_si_attr(current_state.ga_len - 1);
2548
Bram Moolenaar860cae12010-06-05 23:22:07 +02002549#ifdef FEAT_CONCEAL
2550 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2551#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552 /*
2553 * If the start pattern has another highlight group, push another item
2554 * on the stack for the start pattern.
2555 */
2556 if ( spp->sp_type == SPTYPE_START
2557 && spp->sp_syn_match_id != 0
2558 && push_current_state(next_match_idx) == OK)
2559 {
2560 cur_si = &CUR_STATE(current_state.ga_len - 1);
2561 cur_si->si_h_startpos = next_match_h_startpos;
2562 cur_si->si_m_startcol = current_col;
2563 cur_si->si_m_lnum = current_lnum;
2564 cur_si->si_m_endpos = next_match_eos_pos;
2565 cur_si->si_h_endpos = next_match_eos_pos;
2566 cur_si->si_ends = TRUE;
2567 cur_si->si_end_idx = 0;
2568 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002569#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002570 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002571 cur_si->si_flags |= save_flags;
2572 if (cur_si->si_flags & HL_CONCEALENDS)
2573 cur_si->si_flags |= HL_CONCEAL;
2574#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575 cur_si->si_next_list = NULL;
2576 check_keepend();
2577 update_si_attr(current_state.ga_len - 1);
2578 }
2579 }
2580
2581 next_match_idx = -1; /* try other match next time */
2582
2583 return cur_si;
2584}
2585
2586/*
2587 * Check for end of current state (and the states before it).
2588 */
2589 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002590check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002591{
2592 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002593 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594
2595 cur_si = &CUR_STATE(current_state.ga_len - 1);
2596 for (;;)
2597 {
2598 if (cur_si->si_ends
2599 && (cur_si->si_m_endpos.lnum < current_lnum
2600 || (cur_si->si_m_endpos.lnum == current_lnum
2601 && cur_si->si_m_endpos.col <= current_col)))
2602 {
2603 /*
2604 * If there is an end pattern group ID, highlight the end pattern
2605 * now. No need to pop the current item from the stack.
2606 * Only do this if the end pattern continues beyond the current
2607 * position.
2608 */
2609 if (cur_si->si_end_idx
2610 && (cur_si->si_eoe_pos.lnum > current_lnum
2611 || (cur_si->si_eoe_pos.lnum == current_lnum
2612 && cur_si->si_eoe_pos.col > current_col)))
2613 {
2614 cur_si->si_idx = cur_si->si_end_idx;
2615 cur_si->si_end_idx = 0;
2616 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2617 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2618 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002619#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002620 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002621 if (cur_si->si_flags & HL_CONCEALENDS)
2622 cur_si->si_flags |= HL_CONCEAL;
2623#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002625
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002626 /* nextgroup= should not match in the end pattern */
2627 current_next_list = NULL;
2628
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002629 /* what matches next may be different now, clear it */
2630 next_match_idx = 0;
2631 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 break;
2633 }
2634 else
2635 {
2636 /* handle next_list, unless at end of line and no "skipnl" or
2637 * "skipempty" */
2638 current_next_list = cur_si->si_next_list;
2639 current_next_flags = cur_si->si_flags;
2640 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2641 && syn_getcurline()[current_col] == NUL)
2642 current_next_list = NULL;
2643
2644 /* When the ended item has "extend", another item with
2645 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002646 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647
2648 pop_current_state();
2649
2650 if (current_state.ga_len == 0)
2651 break;
2652
Bram Moolenaar81993f42008-01-11 20:27:45 +00002653 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654 {
2655 syn_update_ends(FALSE);
2656 if (current_state.ga_len == 0)
2657 break;
2658 }
2659
2660 cur_si = &CUR_STATE(current_state.ga_len - 1);
2661
2662 /*
2663 * Only for a region the search for the end continues after
2664 * the end of the contained item. If the contained match
2665 * included the end-of-line, break here, the region continues.
2666 * Don't do this when:
2667 * - "keepend" is used for the contained item
2668 * - not at the end of the line (could be end="x$"me=e-1).
2669 * - "excludenl" is used (HL_HAS_EOL won't be set)
2670 */
2671 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002672 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 == SPTYPE_START
2674 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2675 {
2676 update_si_end(cur_si, (int)current_col, TRUE);
2677 check_keepend();
2678 if ((current_next_flags & HL_HAS_EOL)
2679 && keepend_level < 0
2680 && syn_getcurline()[current_col] == NUL)
2681 break;
2682 }
2683 }
2684 }
2685 else
2686 break;
2687 }
2688}
2689
2690/*
2691 * Update an entry in the current_state stack for a match or region. This
2692 * fills in si_attr, si_next_list and si_cont_list.
2693 */
2694 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002695update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002696{
2697 stateitem_T *sip = &CUR_STATE(idx);
2698 synpat_T *spp;
2699
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002700 /* This should not happen... */
2701 if (sip->si_idx < 0)
2702 return;
2703
Bram Moolenaar860cae12010-06-05 23:22:07 +02002704 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705 if (sip->si_flags & HL_MATCH)
2706 sip->si_id = spp->sp_syn_match_id;
2707 else
2708 sip->si_id = spp->sp_syn.id;
2709 sip->si_attr = syn_id2attr(sip->si_id);
2710 sip->si_trans_id = sip->si_id;
2711 if (sip->si_flags & HL_MATCH)
2712 sip->si_cont_list = NULL;
2713 else
2714 sip->si_cont_list = spp->sp_cont_list;
2715
2716 /*
2717 * For transparent items, take attr from outer item.
2718 * Also take cont_list, if there is none.
2719 * Don't do this for the matchgroup of a start or end pattern.
2720 */
2721 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2722 {
2723 if (idx == 0)
2724 {
2725 sip->si_attr = 0;
2726 sip->si_trans_id = 0;
2727 if (sip->si_cont_list == NULL)
2728 sip->si_cont_list = ID_LIST_ALL;
2729 }
2730 else
2731 {
2732 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2733 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002734 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2735 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 if (sip->si_cont_list == NULL)
2737 {
2738 sip->si_flags |= HL_TRANS_CONT;
2739 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2740 }
2741 }
2742 }
2743}
2744
2745/*
2746 * Check the current stack for patterns with "keepend" flag.
2747 * Propagate the match-end to contained items, until a "skipend" item is found.
2748 */
2749 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002750check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002751{
2752 int i;
2753 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002754 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 stateitem_T *sip;
2756
2757 /*
2758 * This check can consume a lot of time; only do it from the level where
2759 * there really is a keepend.
2760 */
2761 if (keepend_level < 0)
2762 return;
2763
2764 /*
2765 * Find the last index of an "extend" item. "keepend" items before that
2766 * won't do anything. If there is no "extend" item "i" will be
2767 * "keepend_level" and all "keepend" items will work normally.
2768 */
2769 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2770 if (CUR_STATE(i).si_flags & HL_EXTEND)
2771 break;
2772
2773 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002774 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002775 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002776 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 for ( ; i < current_state.ga_len; ++i)
2778 {
2779 sip = &CUR_STATE(i);
2780 if (maxpos.lnum != 0)
2781 {
2782 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002783 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002784 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2785 sip->si_ends = TRUE;
2786 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002787 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2788 {
2789 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790 || maxpos.lnum > sip->si_m_endpos.lnum
2791 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002792 && maxpos.col > sip->si_m_endpos.col))
2793 maxpos = sip->si_m_endpos;
2794 if (maxpos_h.lnum == 0
2795 || maxpos_h.lnum > sip->si_h_endpos.lnum
2796 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2797 && maxpos_h.col > sip->si_h_endpos.col))
2798 maxpos_h = sip->si_h_endpos;
2799 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 }
2801}
2802
2803/*
2804 * Update an entry in the current_state stack for a start-skip-end pattern.
2805 * This finds the end of the current item, if it's in the current line.
2806 *
2807 * Return the flags for the matched END.
2808 */
2809 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002810update_si_end(
2811 stateitem_T *sip,
2812 int startcol, /* where to start searching for the end */
2813 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002814{
2815 lpos_T startpos;
2816 lpos_T endpos;
2817 lpos_T hl_endpos;
2818 lpos_T end_endpos;
2819 int end_idx;
2820
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002821 /* return quickly for a keyword */
2822 if (sip->si_idx < 0)
2823 return;
2824
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 /* Don't update when it's already done. Can be a match of an end pattern
2826 * that started in a previous line. Watch out: can also be a "keepend"
2827 * from a containing item. */
2828 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2829 return;
2830
2831 /*
2832 * We need to find the end of the region. It may continue in the next
2833 * line.
2834 */
2835 end_idx = 0;
2836 startpos.lnum = current_lnum;
2837 startpos.col = startcol;
2838 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2839 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2840
2841 if (endpos.lnum == 0)
2842 {
2843 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002844 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002845 {
2846 /* a "oneline" never continues in the next line */
2847 sip->si_ends = TRUE;
2848 sip->si_m_endpos.lnum = current_lnum;
2849 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2850 }
2851 else
2852 {
2853 /* continues in the next line */
2854 sip->si_ends = FALSE;
2855 sip->si_m_endpos.lnum = 0;
2856 }
2857 sip->si_h_endpos = sip->si_m_endpos;
2858 }
2859 else
2860 {
2861 /* match within this line */
2862 sip->si_m_endpos = endpos;
2863 sip->si_h_endpos = hl_endpos;
2864 sip->si_eoe_pos = end_endpos;
2865 sip->si_ends = TRUE;
2866 sip->si_end_idx = end_idx;
2867 }
2868}
2869
2870/*
2871 * Add a new state to the current state stack.
2872 * It is cleared and the index set to "idx".
2873 * Return FAIL if it's not possible (out of memory).
2874 */
2875 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002876push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002877{
2878 if (ga_grow(&current_state, 1) == FAIL)
2879 return FAIL;
2880 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2881 CUR_STATE(current_state.ga_len).si_idx = idx;
2882 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 return OK;
2884}
2885
2886/*
2887 * Remove a state from the current_state stack.
2888 */
2889 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002890pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002891{
2892 if (current_state.ga_len)
2893 {
2894 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2895 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896 }
2897 /* after the end of a pattern, try matching a keyword or pattern */
2898 next_match_idx = -1;
2899
2900 /* if first state with "keepend" is popped, reset keepend_level */
2901 if (keepend_level >= current_state.ga_len)
2902 keepend_level = -1;
2903}
2904
2905/*
2906 * Find the end of a start/skip/end syntax region after "startpos".
2907 * Only checks one line.
2908 * Also handles a match item that continued from a previous line.
2909 * If not found, the syntax item continues in the next line. m_endpos->lnum
2910 * will be 0.
2911 * If found, the end of the region and the end of the highlighting is
2912 * computed.
2913 */
2914 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002915find_endpos(
2916 int idx, /* index of the pattern */
2917 lpos_T *startpos, /* where to start looking for an END match */
2918 lpos_T *m_endpos, /* return: end of match */
2919 lpos_T *hl_endpos, /* return: end of highlighting */
2920 long *flagsp, /* return: flags of matching END */
2921 lpos_T *end_endpos, /* return: end of end pattern match */
2922 int *end_idx, /* return: group ID for end pat. match, or 0 */
2923 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002924{
2925 colnr_T matchcol;
2926 synpat_T *spp, *spp_skip;
2927 int start_idx;
2928 int best_idx;
2929 regmmatch_T regmatch;
2930 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2931 lpos_T pos;
2932 char_u *line;
2933 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002934 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002936 /* just in case we are invoked for a keyword */
2937 if (idx < 0)
2938 return;
2939
Bram Moolenaar071d4272004-06-13 20:20:40 +00002940 /*
2941 * Check for being called with a START pattern.
2942 * Can happen with a match that continues to the next line, because it
2943 * contained a region.
2944 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002945 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002946 if (spp->sp_type != SPTYPE_START)
2947 {
2948 *hl_endpos = *startpos;
2949 return;
2950 }
2951
2952 /*
2953 * Find the SKIP or first END pattern after the last START pattern.
2954 */
2955 for (;;)
2956 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002957 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958 if (spp->sp_type != SPTYPE_START)
2959 break;
2960 ++idx;
2961 }
2962
2963 /*
2964 * Lookup the SKIP pattern (if present)
2965 */
2966 if (spp->sp_type == SPTYPE_SKIP)
2967 {
2968 spp_skip = spp;
2969 ++idx;
2970 }
2971 else
2972 spp_skip = NULL;
2973
2974 /* Setup external matches for syn_regexec(). */
2975 unref_extmatch(re_extmatch_in);
2976 re_extmatch_in = ref_extmatch(start_ext);
2977
2978 matchcol = startpos->col; /* start looking for a match at sstart */
2979 start_idx = idx; /* remember the first END pattern. */
2980 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002981
2982 /* use syntax iskeyword option */
2983 save_chartab(buf_chartab);
2984
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 for (;;)
2986 {
2987 /*
2988 * Find end pattern that matches first after "matchcol".
2989 */
2990 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002991 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 {
2993 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002994 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995
Bram Moolenaar860cae12010-06-05 23:22:07 +02002996 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002997 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2998 break;
2999 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3000 if (lc_col < 0)
3001 lc_col = 0;
3002
3003 regmatch.rmm_ic = spp->sp_ic;
3004 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003005 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3006 IF_SYN_TIME(&spp->sp_time));
3007 spp->sp_prog = regmatch.regprog;
3008 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003009 {
3010 if (best_idx == -1 || regmatch.startpos[0].col
3011 < best_regmatch.startpos[0].col)
3012 {
3013 best_idx = idx;
3014 best_regmatch.startpos[0] = regmatch.startpos[0];
3015 best_regmatch.endpos[0] = regmatch.endpos[0];
3016 }
3017 }
3018 }
3019
3020 /*
3021 * If all end patterns have been tried, and there is no match, the
3022 * item continues until end-of-line.
3023 */
3024 if (best_idx == -1)
3025 break;
3026
3027 /*
3028 * If the skip pattern matches before the end pattern,
3029 * continue searching after the skip pattern.
3030 */
3031 if (spp_skip != NULL)
3032 {
3033 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003034 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003035
3036 if (lc_col < 0)
3037 lc_col = 0;
3038 regmatch.rmm_ic = spp_skip->sp_ic;
3039 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003040 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3041 IF_SYN_TIME(&spp_skip->sp_time));
3042 spp_skip->sp_prog = regmatch.regprog;
3043 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044 <= best_regmatch.startpos[0].col)
3045 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003046 int line_len;
3047
Bram Moolenaar071d4272004-06-13 20:20:40 +00003048 /* Add offset to skip pattern match */
3049 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3050
3051 /* If the skip pattern goes on to the next line, there is no
3052 * match with an end pattern in this line. */
3053 if (pos.lnum > startpos->lnum)
3054 break;
3055
3056 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003057 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058
3059 /* take care of an empty match or negative offset */
3060 if (pos.col <= matchcol)
3061 ++matchcol;
3062 else if (pos.col <= regmatch.endpos[0].col)
3063 matchcol = pos.col;
3064 else
3065 /* Be careful not to jump over the NUL at the end-of-line */
3066 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003067 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068 ++matchcol)
3069 ;
3070
3071 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003072 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073 break;
3074
3075 continue; /* start with first end pattern again */
3076 }
3077 }
3078
3079 /*
3080 * Match from start pattern to end pattern.
3081 * Correct for match and highlight offset of end pattern.
3082 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003083 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3085 /* can't end before the start */
3086 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3087 m_endpos->col = startpos->col;
3088
3089 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3090 /* can't end before the start */
3091 if (end_endpos->lnum == startpos->lnum
3092 && end_endpos->col < startpos->col)
3093 end_endpos->col = startpos->col;
3094 /* can't end after the match */
3095 limit_pos(end_endpos, m_endpos);
3096
3097 /*
3098 * If the end group is highlighted differently, adjust the pointers.
3099 */
3100 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3101 {
3102 *end_idx = best_idx;
3103 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3104 {
3105 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3106 hl_endpos->col = best_regmatch.endpos[0].col;
3107 }
3108 else
3109 {
3110 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3111 hl_endpos->col = best_regmatch.startpos[0].col;
3112 }
3113 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3114
3115 /* can't end before the start */
3116 if (hl_endpos->lnum == startpos->lnum
3117 && hl_endpos->col < startpos->col)
3118 hl_endpos->col = startpos->col;
3119 limit_pos(hl_endpos, m_endpos);
3120
3121 /* now the match ends where the highlighting ends, it is turned
3122 * into the matchgroup for the end */
3123 *m_endpos = *hl_endpos;
3124 }
3125 else
3126 {
3127 *end_idx = 0;
3128 *hl_endpos = *end_endpos;
3129 }
3130
3131 *flagsp = spp->sp_flags;
3132
3133 had_match = TRUE;
3134 break;
3135 }
3136
3137 /* no match for an END pattern in this line */
3138 if (!had_match)
3139 m_endpos->lnum = 0;
3140
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003141 restore_chartab(buf_chartab);
3142
Bram Moolenaar071d4272004-06-13 20:20:40 +00003143 /* Remove external matches. */
3144 unref_extmatch(re_extmatch_in);
3145 re_extmatch_in = NULL;
3146}
3147
3148/*
3149 * Limit "pos" not to be after "limit".
3150 */
3151 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003152limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153{
3154 if (pos->lnum > limit->lnum)
3155 *pos = *limit;
3156 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3157 pos->col = limit->col;
3158}
3159
3160/*
3161 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3162 */
3163 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003164limit_pos_zero(
3165 lpos_T *pos,
3166 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167{
3168 if (pos->lnum == 0)
3169 *pos = *limit;
3170 else
3171 limit_pos(pos, limit);
3172}
3173
3174/*
3175 * Add offset to matched text for end of match or highlight.
3176 */
3177 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003178syn_add_end_off(
3179 lpos_T *result, /* returned position */
3180 regmmatch_T *regmatch, /* start/end of match */
3181 synpat_T *spp, /* matched pattern */
3182 int idx, /* index of offset */
3183 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184{
3185 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003186 int off;
3187 char_u *base;
3188 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003189
3190 if (spp->sp_off_flags & (1 << idx))
3191 {
3192 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003193 col = regmatch->startpos[0].col;
3194 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003195 }
3196 else
3197 {
3198 result->lnum = regmatch->endpos[0].lnum;
3199 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003200 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003201 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003202 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3203 * is a matchgroup. Watch out for match with last NL in the buffer. */
3204 if (result->lnum > syn_buf->b_ml.ml_line_count)
3205 col = 0;
3206 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003207 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003208 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3209 p = base + col;
3210 if (off > 0)
3211 {
3212 while (off-- > 0 && *p != NUL)
3213 mb_ptr_adv(p);
3214 }
3215 else if (off < 0)
3216 {
3217 while (off++ < 0 && base < p)
3218 mb_ptr_back(base, p);
3219 }
3220 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003221 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003222 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223}
3224
3225/*
3226 * Add offset to matched text for start of match or highlight.
3227 * Avoid resulting column to become negative.
3228 */
3229 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003230syn_add_start_off(
3231 lpos_T *result, /* returned position */
3232 regmmatch_T *regmatch, /* start/end of match */
3233 synpat_T *spp,
3234 int idx,
3235 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236{
3237 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003238 int off;
3239 char_u *base;
3240 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241
3242 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3243 {
3244 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003245 col = regmatch->endpos[0].col;
3246 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 }
3248 else
3249 {
3250 result->lnum = regmatch->startpos[0].lnum;
3251 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003252 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003254 if (result->lnum > syn_buf->b_ml.ml_line_count)
3255 {
3256 /* a "\n" at the end of the pattern may take us below the last line */
3257 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003258 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003259 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003260 if (off != 0)
3261 {
3262 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3263 p = base + col;
3264 if (off > 0)
3265 {
3266 while (off-- && *p != NUL)
3267 mb_ptr_adv(p);
3268 }
3269 else if (off < 0)
3270 {
3271 while (off++ && base < p)
3272 mb_ptr_back(base, p);
3273 }
3274 col = (int)(p - base);
3275 }
3276 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277}
3278
3279/*
3280 * Get current line in syntax buffer.
3281 */
3282 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003283syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003284{
3285 return ml_get_buf(syn_buf, current_lnum, FALSE);
3286}
3287
3288/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003289 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003290 * Returns TRUE when there is a match.
3291 */
3292 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003293syn_regexec(
3294 regmmatch_T *rmp,
3295 linenr_T lnum,
3296 colnr_T col,
3297 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003298{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003299 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003300#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003301 proftime_T pt;
3302
3303 if (syn_time_on)
3304 profile_start(&pt);
3305#endif
3306
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003307 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003308 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3309
Bram Moolenaarf7512552013-06-06 14:55:19 +02003310#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003311 if (syn_time_on)
3312 {
3313 profile_end(&pt);
3314 profile_add(&st->total, &pt);
3315 if (profile_cmp(&pt, &st->slowest) < 0)
3316 st->slowest = pt;
3317 ++st->count;
3318 if (r > 0)
3319 ++st->match;
3320 }
3321#endif
3322
3323 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003324 {
3325 rmp->startpos[0].lnum += lnum;
3326 rmp->endpos[0].lnum += lnum;
3327 return TRUE;
3328 }
3329 return FALSE;
3330}
3331
3332/*
3333 * Check one position in a line for a matching keyword.
3334 * The caller must check if a keyword can start at startcol.
3335 * Return it's ID if found, 0 otherwise.
3336 */
3337 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003338check_keyword_id(
3339 char_u *line,
3340 int startcol, /* position in line to check for keyword */
3341 int *endcolp, /* return: character after found keyword */
3342 long *flagsp, /* return: flags of matching keyword */
3343 short **next_listp, /* return: next_list of matching keyword */
3344 stateitem_T *cur_si, /* item at the top of the stack */
3345 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003347 keyentry_T *kp;
3348 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003350 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003351 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003352 hashtab_T *ht;
3353 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354
3355 /* Find first character after the keyword. First character was already
3356 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003357 kwp = line + startcol;
3358 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 do
3360 {
3361#ifdef FEAT_MBYTE
3362 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003363 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364 else
3365#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003366 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003368 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369
Bram Moolenaardad6b692005-01-25 22:14:34 +00003370 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 return 0;
3372
3373 /*
3374 * Must make a copy of the keyword, so we can add a NUL and make it
3375 * lowercase.
3376 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003377 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378
3379 /*
3380 * Try twice:
3381 * 1. matching case
3382 * 2. ignoring case
3383 */
3384 for (round = 1; round <= 2; ++round)
3385 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003386 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003387 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003389 if (round == 2) /* ignore case */
3390 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391
3392 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003393 * Find keywords that match. There can be several with different
3394 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395 * When current_next_list is non-zero accept only that group, otherwise:
3396 * Accept a not-contained keyword at toplevel.
3397 * Accept a keyword at other levels only if it is in the contains list.
3398 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003399 hi = hash_find(ht, keyword);
3400 if (!HASHITEM_EMPTY(hi))
3401 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003402 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003403 if (current_next_list != 0
3404 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3405 : (cur_si == NULL
3406 ? !(kp->flags & HL_CONTAINED)
3407 : in_id_list(cur_si, cur_si->si_cont_list,
3408 &kp->k_syn, kp->flags & HL_CONTAINED)))
3409 {
3410 *endcolp = startcol + kwlen;
3411 *flagsp = kp->flags;
3412 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413#ifdef FEAT_CONCEAL
3414 *ccharp = kp->k_char;
3415#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003416 return kp->k_syn.id;
3417 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003418 }
3419 }
3420 return 0;
3421}
3422
3423/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003424 * Handle ":syntax conceal" command.
3425 */
3426 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003427syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003428{
3429#ifdef FEAT_CONCEAL
3430 char_u *arg = eap->arg;
3431 char_u *next;
3432
3433 eap->nextcmd = find_nextcmd(arg);
3434 if (eap->skip)
3435 return;
3436
3437 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003438 if (*arg == NUL)
3439 {
3440 if (curwin->w_s->b_syn_conceal)
3441 MSG(_("syn conceal on"));
3442 else
3443 MSG(_("syn conceal off"));
3444 }
3445 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003446 curwin->w_s->b_syn_conceal = TRUE;
3447 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3448 curwin->w_s->b_syn_conceal = FALSE;
3449 else
3450 EMSG2(_("E390: Illegal argument: %s"), arg);
3451#endif
3452}
3453
3454/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455 * Handle ":syntax case" command.
3456 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003457 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003458syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003459{
3460 char_u *arg = eap->arg;
3461 char_u *next;
3462
3463 eap->nextcmd = find_nextcmd(arg);
3464 if (eap->skip)
3465 return;
3466
3467 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003468 if (*arg == NUL)
3469 {
3470 if (curwin->w_s->b_syn_ic)
3471 MSG(_("syntax case ignore"));
3472 else
3473 MSG(_("syntax case match"));
3474 }
3475 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003476 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003478 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479 else
3480 EMSG2(_("E390: Illegal argument: %s"), arg);
3481}
3482
3483/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003484 * Handle ":syntax spell" command.
3485 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003486 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003487syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003488{
3489 char_u *arg = eap->arg;
3490 char_u *next;
3491
3492 eap->nextcmd = find_nextcmd(arg);
3493 if (eap->skip)
3494 return;
3495
3496 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003497 if (*arg == NUL)
3498 {
3499 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3500 MSG(_("syntax spell toplevel"));
3501 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3502 MSG(_("syntax spell notoplevel"));
3503 else
3504 MSG(_("syntax spell default"));
3505 }
3506 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003507 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003508 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003510 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003512 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003513 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003514 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003515 return;
3516 }
3517
3518 /* assume spell checking changed, force a redraw */
3519 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003520}
3521
3522/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003523 * Handle ":syntax iskeyword" command.
3524 */
3525 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003526syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003527{
3528 char_u *arg = eap->arg;
3529 char_u save_chartab[32];
3530 char_u *save_isk;
3531
3532 if (eap->skip)
3533 return;
3534
3535 arg = skipwhite(arg);
3536 if (*arg == NUL)
3537 {
3538 MSG_PUTS("\n");
3539 MSG_PUTS(_("syntax iskeyword "));
3540 if (curwin->w_s->b_syn_isk != empty_option)
3541 msg_outtrans(curwin->w_s->b_syn_isk);
3542 else
3543 msg_outtrans((char_u *)"not set");
3544 }
3545 else
3546 {
3547 if (STRNICMP(arg, "clear", 5) == 0)
3548 {
3549 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3550 (size_t)32);
3551 clear_string_option(&curwin->w_s->b_syn_isk);
3552 }
3553 else
3554 {
3555 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3556 save_isk = curbuf->b_p_isk;
3557 curbuf->b_p_isk = vim_strsave(arg);
3558
3559 buf_init_chartab(curbuf, FALSE);
3560 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3561 (size_t)32);
3562 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3563 clear_string_option(&curwin->w_s->b_syn_isk);
3564 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3565 curbuf->b_p_isk = save_isk;
3566 }
3567 }
3568 redraw_win_later(curwin, NOT_VALID);
3569}
3570
3571/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572 * Clear all syntax info for one buffer.
3573 */
3574 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003575syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576{
3577 int i;
3578
Bram Moolenaar860cae12010-06-05 23:22:07 +02003579 block->b_syn_error = FALSE; /* clear previous error */
3580 block->b_syn_ic = FALSE; /* Use case, by default */
3581 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3582 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003583#ifdef FEAT_CONCEAL
3584 block->b_syn_conceal = FALSE;
3585#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586
3587 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003588 clear_keywtab(&block->b_keywtab);
3589 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590
3591 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003592 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3593 syn_clear_pattern(block, i);
3594 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595
3596 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003597 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3598 syn_clear_cluster(block, i);
3599 ga_clear(&block->b_syn_clusters);
3600 block->b_spell_cluster_id = 0;
3601 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003602
Bram Moolenaar860cae12010-06-05 23:22:07 +02003603 block->b_syn_sync_flags = 0;
3604 block->b_syn_sync_minlines = 0;
3605 block->b_syn_sync_maxlines = 0;
3606 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607
Bram Moolenaar473de612013-06-08 18:19:48 +02003608 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003609 block->b_syn_linecont_prog = NULL;
3610 vim_free(block->b_syn_linecont_pat);
3611 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003615 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616
3617 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003618 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003620
3621 /* Reset the counter for ":syn include" */
3622 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623}
3624
3625/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003626 * Get rid of ownsyntax for window "wp".
3627 */
3628 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003629reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003630{
3631 if (wp->w_s != &wp->w_buffer->b_s)
3632 {
3633 syntax_clear(wp->w_s);
3634 vim_free(wp->w_s);
3635 wp->w_s = &wp->w_buffer->b_s;
3636 }
3637}
3638
3639/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640 * Clear syncing info for one buffer.
3641 */
3642 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003643syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644{
3645 int i;
3646
3647 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3649 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3650 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 curwin->w_s->b_syn_sync_flags = 0;
3653 curwin->w_s->b_syn_sync_minlines = 0;
3654 curwin->w_s->b_syn_sync_maxlines = 0;
3655 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656
Bram Moolenaar473de612013-06-08 18:19:48 +02003657 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003658 curwin->w_s->b_syn_linecont_prog = NULL;
3659 vim_free(curwin->w_s->b_syn_linecont_pat);
3660 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003661 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003663 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664}
3665
3666/*
3667 * Remove one pattern from the buffer's pattern list.
3668 */
3669 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003670syn_remove_pattern(
3671 synblock_T *block,
3672 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003673{
3674 synpat_T *spp;
3675
Bram Moolenaar860cae12010-06-05 23:22:07 +02003676 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677#ifdef FEAT_FOLDING
3678 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003679 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003680#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003681 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3684 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685}
3686
3687/*
3688 * Clear and free one syntax pattern. When clearing all, must be called from
3689 * last to first!
3690 */
3691 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003692syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003694 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003695 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003697 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003698 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003699 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3700 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3701 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 }
3703}
3704
3705/*
3706 * Clear and free one syntax cluster.
3707 */
3708 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003709syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003711 vim_free(SYN_CLSTR(block)[i].scl_name);
3712 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3713 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714}
3715
3716/*
3717 * Handle ":syntax clear" command.
3718 */
3719 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003720syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721{
3722 char_u *arg = eap->arg;
3723 char_u *arg_end;
3724 int id;
3725
3726 eap->nextcmd = find_nextcmd(arg);
3727 if (eap->skip)
3728 return;
3729
3730 /*
3731 * We have to disable this within ":syn include @group filename",
3732 * because otherwise @group would get deleted.
3733 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3734 * clear".
3735 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003736 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737 return;
3738
3739 if (ends_excmd(*arg))
3740 {
3741 /*
3742 * No argument: Clear all syntax items.
3743 */
3744 if (syncing)
3745 syntax_sync_clear();
3746 else
3747 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003748 syntax_clear(curwin->w_s);
3749 if (curwin->w_s == &curwin->w_buffer->b_s)
3750 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003751 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752 }
3753 }
3754 else
3755 {
3756 /*
3757 * Clear the group IDs that are in the argument.
3758 */
3759 while (!ends_excmd(*arg))
3760 {
3761 arg_end = skiptowhite(arg);
3762 if (*arg == '@')
3763 {
3764 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3765 if (id == 0)
3766 {
3767 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3768 break;
3769 }
3770 else
3771 {
3772 /*
3773 * We can't physically delete a cluster without changing
3774 * the IDs of other clusters, so we do the next best thing
3775 * and make it empty.
3776 */
3777 short scl_id = id - SYNID_CLUSTER;
3778
Bram Moolenaar860cae12010-06-05 23:22:07 +02003779 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3780 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003781 }
3782 }
3783 else
3784 {
3785 id = syn_namen2id(arg, (int)(arg_end - arg));
3786 if (id == 0)
3787 {
3788 EMSG2(_(e_nogroup), arg);
3789 break;
3790 }
3791 else
3792 syn_clear_one(id, syncing);
3793 }
3794 arg = skipwhite(arg_end);
3795 }
3796 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003797 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003798 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003799}
3800
3801/*
3802 * Clear one syntax group for the current buffer.
3803 */
3804 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003805syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003806{
3807 synpat_T *spp;
3808 int idx;
3809
3810 /* Clear keywords only when not ":syn sync clear group-name" */
3811 if (!syncing)
3812 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003813 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3814 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003815 }
3816
3817 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003818 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003820 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3822 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003823 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 }
3825}
3826
3827/*
3828 * Handle ":syntax on" command.
3829 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003831syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832{
3833 syn_cmd_onoff(eap, "syntax");
3834}
3835
3836/*
3837 * Handle ":syntax enable" command.
3838 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003840syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841{
3842 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3843 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003844 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845}
3846
3847/*
3848 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003849 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003852syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853{
3854 eap->nextcmd = check_nextcmd(eap->arg);
3855 if (!eap->skip)
3856 {
3857 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3858 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003859 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 }
3861}
3862
3863/*
3864 * Handle ":syntax manual" command.
3865 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003867syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868{
3869 syn_cmd_onoff(eap, "manual");
3870}
3871
3872/*
3873 * Handle ":syntax off" command.
3874 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003876syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877{
3878 syn_cmd_onoff(eap, "nosyntax");
3879}
3880
3881 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003882syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883{
3884 char_u buf[100];
3885
3886 eap->nextcmd = check_nextcmd(eap->arg);
3887 if (!eap->skip)
3888 {
3889 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003890 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891 do_cmdline_cmd(buf);
3892 }
3893}
3894
3895/*
3896 * Handle ":syntax [list]" command: list current syntax words.
3897 */
3898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003899syn_cmd_list(
3900 exarg_T *eap,
3901 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902{
3903 char_u *arg = eap->arg;
3904 int id;
3905 char_u *arg_end;
3906
3907 eap->nextcmd = find_nextcmd(arg);
3908 if (eap->skip)
3909 return;
3910
Bram Moolenaar860cae12010-06-05 23:22:07 +02003911 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003913 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 return;
3915 }
3916
3917 if (syncing)
3918 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003919 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003920 {
3921 MSG_PUTS(_("syncing on C-style comments"));
3922 syn_lines_msg();
3923 syn_match_msg();
3924 return;
3925 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003926 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003928 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 MSG_PUTS(_("no syncing"));
3930 else
3931 {
3932 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003933 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934 MSG_PUTS(_(" lines before top line"));
3935 syn_match_msg();
3936 }
3937 return;
3938 }
3939 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003940 if (curwin->w_s->b_syn_sync_minlines > 0
3941 || curwin->w_s->b_syn_sync_maxlines > 0
3942 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943 {
3944 MSG_PUTS(_("\nsyncing on items"));
3945 syn_lines_msg();
3946 syn_match_msg();
3947 }
3948 }
3949 else
3950 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3951 if (ends_excmd(*arg))
3952 {
3953 /*
3954 * No argument: List all group IDs and all syntax clusters.
3955 */
3956 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3957 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003958 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 syn_list_cluster(id);
3960 }
3961 else
3962 {
3963 /*
3964 * List the group IDs and syntax clusters that are in the argument.
3965 */
3966 while (!ends_excmd(*arg) && !got_int)
3967 {
3968 arg_end = skiptowhite(arg);
3969 if (*arg == '@')
3970 {
3971 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3972 if (id == 0)
3973 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3974 else
3975 syn_list_cluster(id - SYNID_CLUSTER);
3976 }
3977 else
3978 {
3979 id = syn_namen2id(arg, (int)(arg_end - arg));
3980 if (id == 0)
3981 EMSG2(_(e_nogroup), arg);
3982 else
3983 syn_list_one(id, syncing, TRUE);
3984 }
3985 arg = skipwhite(arg_end);
3986 }
3987 }
3988 eap->nextcmd = check_nextcmd(arg);
3989}
3990
3991 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003992syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003994 if (curwin->w_s->b_syn_sync_maxlines > 0
3995 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003996 {
3997 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003998 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999 {
4000 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004001 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4002 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003 MSG_PUTS(", ");
4004 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004005 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004006 {
4007 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004008 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009 }
4010 MSG_PUTS(_(" lines before top line"));
4011 }
4012}
4013
4014 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004015syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004017 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 {
4019 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004020 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 MSG_PUTS(_(" line breaks"));
4022 }
4023}
4024
4025static int last_matchgroup;
4026
4027struct name_list
4028{
4029 int flag;
4030 char *name;
4031};
4032
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004033static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034
4035/*
4036 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4037 */
4038 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004039syn_list_one(
4040 int id,
4041 int syncing, /* when TRUE: list syncing items */
4042 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043{
4044 int attr;
4045 int idx;
4046 int did_header = FALSE;
4047 synpat_T *spp;
4048 static struct name_list namelist1[] =
4049 {
4050 {HL_DISPLAY, "display"},
4051 {HL_CONTAINED, "contained"},
4052 {HL_ONELINE, "oneline"},
4053 {HL_KEEPEND, "keepend"},
4054 {HL_EXTEND, "extend"},
4055 {HL_EXCLUDENL, "excludenl"},
4056 {HL_TRANSP, "transparent"},
4057 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004058#ifdef FEAT_CONCEAL
4059 {HL_CONCEAL, "conceal"},
4060 {HL_CONCEALENDS, "concealends"},
4061#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 {0, NULL}
4063 };
4064 static struct name_list namelist2[] =
4065 {
4066 {HL_SKIPWHITE, "skipwhite"},
4067 {HL_SKIPNL, "skipnl"},
4068 {HL_SKIPEMPTY, "skipempty"},
4069 {0, NULL}
4070 };
4071
4072 attr = hl_attr(HLF_D); /* highlight like directories */
4073
4074 /* list the keywords for "id" */
4075 if (!syncing)
4076 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004077 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4078 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079 did_header, attr);
4080 }
4081
4082 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004083 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004085 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4087 continue;
4088
4089 (void)syn_list_header(did_header, 999, id);
4090 did_header = TRUE;
4091 last_matchgroup = 0;
4092 if (spp->sp_type == SPTYPE_MATCH)
4093 {
4094 put_pattern("match", ' ', spp, attr);
4095 msg_putchar(' ');
4096 }
4097 else if (spp->sp_type == SPTYPE_START)
4098 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004099 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4100 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4101 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4102 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4103 while (idx < curwin->w_s->b_syn_patterns.ga_len
4104 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4105 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106 --idx;
4107 msg_putchar(' ');
4108 }
4109 syn_list_flags(namelist1, spp->sp_flags, attr);
4110
4111 if (spp->sp_cont_list != NULL)
4112 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4113
4114 if (spp->sp_syn.cont_in_list != NULL)
4115 put_id_list((char_u *)"containedin",
4116 spp->sp_syn.cont_in_list, attr);
4117
4118 if (spp->sp_next_list != NULL)
4119 {
4120 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4121 syn_list_flags(namelist2, spp->sp_flags, attr);
4122 }
4123 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4124 {
4125 if (spp->sp_flags & HL_SYNC_HERE)
4126 msg_puts_attr((char_u *)"grouphere", attr);
4127 else
4128 msg_puts_attr((char_u *)"groupthere", attr);
4129 msg_putchar(' ');
4130 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004131 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4133 else
4134 MSG_PUTS("NONE");
4135 msg_putchar(' ');
4136 }
4137 }
4138
4139 /* list the link, if there is one */
4140 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4141 {
4142 (void)syn_list_header(did_header, 999, id);
4143 msg_puts_attr((char_u *)"links to", attr);
4144 msg_putchar(' ');
4145 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4146 }
4147}
4148
4149 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004150syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151{
4152 int i;
4153
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004154 for (i = 0; nlist[i].flag != 0; ++i)
4155 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004157 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 msg_putchar(' ');
4159 }
4160}
4161
4162/*
4163 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4164 */
4165 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004166syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167{
4168 int endcol = 15;
4169
4170 /* slight hack: roughly duplicate the guts of syn_list_header() */
4171 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004172 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173
4174 if (msg_col >= endcol) /* output at least one space */
4175 endcol = msg_col + 1;
4176 if (Columns <= endcol) /* avoid hang for tiny window */
4177 endcol = Columns - 1;
4178
4179 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004180 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004182 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 hl_attr(HLF_D));
4184 }
4185 else
4186 {
4187 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4188 msg_puts((char_u *)"=NONE");
4189 }
4190}
4191
4192 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004193put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194{
4195 short *p;
4196
4197 msg_puts_attr(name, attr);
4198 msg_putchar('=');
4199 for (p = list; *p; ++p)
4200 {
4201 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4202 {
4203 if (p[1])
4204 MSG_PUTS("ALLBUT");
4205 else
4206 MSG_PUTS("ALL");
4207 }
4208 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4209 {
4210 MSG_PUTS("TOP");
4211 }
4212 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4213 {
4214 MSG_PUTS("CONTAINED");
4215 }
4216 else if (*p >= SYNID_CLUSTER)
4217 {
4218 short scl_id = *p - SYNID_CLUSTER;
4219
4220 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004221 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 }
4223 else
4224 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4225 if (p[1])
4226 msg_putchar(',');
4227 }
4228 msg_putchar(' ');
4229}
4230
4231 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004232put_pattern(
4233 char *s,
4234 int c,
4235 synpat_T *spp,
4236 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004237{
4238 long n;
4239 int mask;
4240 int first;
4241 static char *sepchars = "/+=-#@\"|'^&";
4242 int i;
4243
4244 /* May have to write "matchgroup=group" */
4245 if (last_matchgroup != spp->sp_syn_match_id)
4246 {
4247 last_matchgroup = spp->sp_syn_match_id;
4248 msg_puts_attr((char_u *)"matchgroup", attr);
4249 msg_putchar('=');
4250 if (last_matchgroup == 0)
4251 msg_outtrans((char_u *)"NONE");
4252 else
4253 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4254 msg_putchar(' ');
4255 }
4256
4257 /* output the name of the pattern and an '=' or ' ' */
4258 msg_puts_attr((char_u *)s, attr);
4259 msg_putchar(c);
4260
4261 /* output the pattern, in between a char that is not in the pattern */
4262 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4263 if (sepchars[++i] == NUL)
4264 {
4265 i = 0; /* no good char found, just use the first one */
4266 break;
4267 }
4268 msg_putchar(sepchars[i]);
4269 msg_outtrans(spp->sp_pattern);
4270 msg_putchar(sepchars[i]);
4271
4272 /* output any pattern options */
4273 first = TRUE;
4274 for (i = 0; i < SPO_COUNT; ++i)
4275 {
4276 mask = (1 << i);
4277 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4278 {
4279 if (!first)
4280 msg_putchar(','); /* separate with commas */
4281 msg_puts((char_u *)spo_name_tab[i]);
4282 n = spp->sp_offsets[i];
4283 if (i != SPO_LC_OFF)
4284 {
4285 if (spp->sp_off_flags & mask)
4286 msg_putchar('s');
4287 else
4288 msg_putchar('e');
4289 if (n > 0)
4290 msg_putchar('+');
4291 }
4292 if (n || i == SPO_LC_OFF)
4293 msg_outnum(n);
4294 first = FALSE;
4295 }
4296 }
4297 msg_putchar(' ');
4298}
4299
4300/*
4301 * List or clear the keywords for one syntax group.
4302 * Return TRUE if the header has been printed.
4303 */
4304 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004305syn_list_keywords(
4306 int id,
4307 hashtab_T *ht,
4308 int did_header, /* header has already been printed */
4309 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004312 hashitem_T *hi;
4313 keyentry_T *kp;
4314 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 int prev_contained = 0;
4316 short *prev_next_list = NULL;
4317 short *prev_cont_in_list = NULL;
4318 int prev_skipnl = 0;
4319 int prev_skipwhite = 0;
4320 int prev_skipempty = 0;
4321
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322 /*
4323 * Unfortunately, this list of keywords is not sorted on alphabet but on
4324 * hash value...
4325 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004326 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 --todo;
4332 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 if (prev_contained != (kp->flags & HL_CONTAINED)
4337 || prev_skipnl != (kp->flags & HL_SKIPNL)
4338 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4339 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4340 || prev_cont_in_list != kp->k_syn.cont_in_list
4341 || prev_next_list != kp->next_list)
4342 outlen = 9999;
4343 else
4344 outlen = (int)STRLEN(kp->keyword);
4345 /* output "contained" and "nextgroup" on each line */
4346 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004348 prev_contained = 0;
4349 prev_next_list = NULL;
4350 prev_cont_in_list = NULL;
4351 prev_skipnl = 0;
4352 prev_skipwhite = 0;
4353 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 did_header = TRUE;
4356 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004364 put_id_list((char_u *)"containedin",
4365 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004367 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 if (kp->next_list != prev_next_list)
4370 {
4371 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4372 msg_putchar(' ');
4373 prev_next_list = kp->next_list;
4374 if (kp->flags & HL_SKIPNL)
4375 {
4376 msg_puts_attr((char_u *)"skipnl", attr);
4377 msg_putchar(' ');
4378 prev_skipnl = (kp->flags & HL_SKIPNL);
4379 }
4380 if (kp->flags & HL_SKIPWHITE)
4381 {
4382 msg_puts_attr((char_u *)"skipwhite", attr);
4383 msg_putchar(' ');
4384 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4385 }
4386 if (kp->flags & HL_SKIPEMPTY)
4387 {
4388 msg_puts_attr((char_u *)"skipempty", attr);
4389 msg_putchar(' ');
4390 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4391 }
4392 }
4393 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 }
4396 }
4397 }
4398
4399 return did_header;
4400}
4401
4402 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004403syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004404{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004405 hashitem_T *hi;
4406 keyentry_T *kp;
4407 keyentry_T *kp_prev;
4408 keyentry_T *kp_next;
4409 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410
Bram Moolenaardad6b692005-01-25 22:14:34 +00004411 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004412 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004413 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004414 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004415 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004417 --todo;
4418 kp_prev = NULL;
4419 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004421 if (kp->k_syn.id == id)
4422 {
4423 kp_next = kp->ke_next;
4424 if (kp_prev == NULL)
4425 {
4426 if (kp_next == NULL)
4427 hash_remove(ht, hi);
4428 else
4429 hi->hi_key = KE2HIKEY(kp_next);
4430 }
4431 else
4432 kp_prev->ke_next = kp_next;
4433 vim_free(kp->next_list);
4434 vim_free(kp->k_syn.cont_in_list);
4435 vim_free(kp);
4436 kp = kp_next;
4437 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 {
4440 kp_prev = kp;
4441 kp = kp->ke_next;
4442 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443 }
4444 }
4445 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447}
4448
4449/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004450 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451 */
4452 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004453clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004455 hashitem_T *hi;
4456 int todo;
4457 keyentry_T *kp;
4458 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004460 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004461 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004462 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004463 if (!HASHITEM_EMPTY(hi))
4464 {
4465 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004466 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004468 kp_next = kp->ke_next;
4469 vim_free(kp->next_list);
4470 vim_free(kp->k_syn.cont_in_list);
4471 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004473 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004475 hash_clear(ht);
4476 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477}
4478
4479/*
4480 * Add a keyword to the list of keywords.
4481 */
4482 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004483add_keyword(
4484 char_u *name, /* name of keyword */
4485 int id, /* group ID for this keyword */
4486 int flags, /* flags for this keyword */
4487 short *cont_in_list, /* containedin for this keyword */
4488 short *next_list, /* nextgroup for this keyword */
4489 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004491 keyentry_T *kp;
4492 hashtab_T *ht;
4493 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004494 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004495 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004496 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497
Bram Moolenaar860cae12010-06-05 23:22:07 +02004498 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 name_ic = str_foldcase(name, (int)STRLEN(name),
4500 name_folded, MAXKEYWLEN + 1);
4501 else
4502 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004503 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4504 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004506 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004507 kp->k_syn.id = id;
4508 kp->k_syn.inc_tag = current_syn_inc_tag;
4509 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004510 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004511 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004513 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004514 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515
Bram Moolenaar860cae12010-06-05 23:22:07 +02004516 if (curwin->w_s->b_syn_ic)
4517 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004519 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520
Bram Moolenaardad6b692005-01-25 22:14:34 +00004521 hash = hash_hash(kp->keyword);
4522 hi = hash_lookup(ht, kp->keyword, hash);
4523 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004525 /* new keyword, add to hashtable */
4526 kp->ke_next = NULL;
4527 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004529 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004531 /* keyword already exists, prepend to list */
4532 kp->ke_next = HI2KE(hi);
4533 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535}
4536
4537/*
4538 * Get the start and end of the group name argument.
4539 * Return a pointer to the first argument.
4540 * Return NULL if the end of the command was found instead of further args.
4541 */
4542 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004543get_group_name(
4544 char_u *arg, /* start of the argument */
4545 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546{
4547 char_u *rest;
4548
4549 *name_end = skiptowhite(arg);
4550 rest = skipwhite(*name_end);
4551
4552 /*
4553 * Check if there are enough arguments. The first argument may be a
4554 * pattern, where '|' is allowed, so only check for NUL.
4555 */
4556 if (ends_excmd(*arg) || *rest == NUL)
4557 return NULL;
4558 return rest;
4559}
4560
4561/*
4562 * Check for syntax command option arguments.
4563 * This can be called at any place in the list of arguments, and just picks
4564 * out the arguments that are known. Can be called several times in a row to
4565 * collect all options in between other arguments.
4566 * Return a pointer to the next argument (which isn't an option).
4567 * Return NULL for any error;
4568 */
4569 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004570get_syn_options(
4571 char_u *arg, /* next argument to be checked */
4572 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004573 int *conceal_char UNUSED,
4574 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576 char_u *gname_start, *gname;
4577 int syn_id;
4578 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004579 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 int i;
4581 int fidx;
4582 static struct flag
4583 {
4584 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004585 int argtype;
4586 int flags;
4587 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4588 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4589 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4590 {"eExXtTeEnNdD", 0, HL_EXTEND},
4591 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4592 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4593 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4594 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4595 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4596 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4597 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4598 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4599 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004600 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4601 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4602 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004603 {"cCoOnNtTaAiInNsS", 1, 0},
4604 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4605 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004607 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608
4609 if (arg == NULL) /* already detected error */
4610 return NULL;
4611
Bram Moolenaar860cae12010-06-05 23:22:07 +02004612#ifdef FEAT_CONCEAL
4613 if (curwin->w_s->b_syn_conceal)
4614 opt->flags |= HL_CONCEAL;
4615#endif
4616
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617 for (;;)
4618 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004619 /*
4620 * This is used very often when a large number of keywords is defined.
4621 * Need to skip quickly when no option name is found.
4622 * Also avoid tolower(), it's slow.
4623 */
4624 if (strchr(first_letters, *arg) == NULL)
4625 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626
4627 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4628 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004629 p = flagtab[fidx].name;
4630 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4631 if (arg[len] != p[i] && arg[len] != p[i + 1])
4632 break;
4633 if (p[i] == NUL && (vim_iswhite(arg[len])
4634 || (flagtab[fidx].argtype > 0
4635 ? arg[len] == '='
4636 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004638 if (opt->keyword
4639 && (flagtab[fidx].flags == HL_DISPLAY
4640 || flagtab[fidx].flags == HL_FOLD
4641 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004642 /* treat "display", "fold" and "extend" as a keyword */
4643 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004644 break;
4645 }
4646 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004647 if (fidx < 0) /* no match found */
4648 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004650 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004652 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004653 {
4654 EMSG(_("E395: contains argument not accepted here"));
4655 return NULL;
4656 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004657 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658 return NULL;
4659 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004660 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004661 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004662 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004663 return NULL;
4664 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004665 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004666 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004667 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668 return NULL;
4669 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004670 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4671 {
4672#ifdef FEAT_MBYTE
4673 /* cchar=? */
4674 if (has_mbyte)
4675 {
4676# ifdef FEAT_CONCEAL
4677 *conceal_char = mb_ptr2char(arg + 6);
4678# endif
4679 arg += mb_ptr2len(arg + 6) - 1;
4680 }
4681 else
4682#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004683 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004684#ifdef FEAT_CONCEAL
4685 *conceal_char = arg[6];
4686#else
4687 ;
4688#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004689 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004690#ifdef FEAT_CONCEAL
4691 if (!vim_isprintc_strict(*conceal_char))
4692 {
4693 EMSG(_("E844: invalid cchar value"));
4694 return NULL;
4695 }
4696#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004697 arg = skipwhite(arg + 7);
4698 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004700 {
4701 opt->flags |= flagtab[fidx].flags;
4702 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004704 if (flagtab[fidx].flags == HL_SYNC_HERE
4705 || flagtab[fidx].flags == HL_SYNC_THERE)
4706 {
4707 if (opt->sync_idx == NULL)
4708 {
4709 EMSG(_("E393: group[t]here not accepted here"));
4710 return NULL;
4711 }
4712 gname_start = arg;
4713 arg = skiptowhite(arg);
4714 if (gname_start == arg)
4715 return NULL;
4716 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4717 if (gname == NULL)
4718 return NULL;
4719 if (STRCMP(gname, "NONE") == 0)
4720 *opt->sync_idx = NONE_IDX;
4721 else
4722 {
4723 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004724 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4725 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4726 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004727 {
4728 *opt->sync_idx = i;
4729 break;
4730 }
4731 if (i < 0)
4732 {
4733 EMSG2(_("E394: Didn't find region item for %s"), gname);
4734 vim_free(gname);
4735 return NULL;
4736 }
4737 }
4738
4739 vim_free(gname);
4740 arg = skipwhite(arg);
4741 }
4742#ifdef FEAT_FOLDING
4743 else if (flagtab[fidx].flags == HL_FOLD
4744 && foldmethodIsSyntax(curwin))
4745 /* Need to update folds later. */
4746 foldUpdateAll(curwin);
4747#endif
4748 }
4749 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750
4751 return arg;
4752}
4753
4754/*
4755 * Adjustments to syntax item when declared in a ":syn include"'d file.
4756 * Set the contained flag, and if the item is not already contained, add it
4757 * to the specified top-level group, if any.
4758 */
4759 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004760syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004762 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004763 return;
4764 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004765 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 {
4767 /* We have to alloc this, because syn_combine_list() will free it. */
4768 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004769 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770
4771 if (grp_list != NULL)
4772 {
4773 grp_list[0] = id;
4774 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004775 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776 CLUSTER_ADD);
4777 }
4778 }
4779}
4780
4781/*
4782 * Handle ":syntax include [@{group-name}] filename" command.
4783 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004785syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786{
4787 char_u *arg = eap->arg;
4788 int sgl_id = 1;
4789 char_u *group_name_end;
4790 char_u *rest;
4791 char_u *errormsg = NULL;
4792 int prev_toplvl_grp;
4793 int prev_syn_inc_tag;
4794 int source = FALSE;
4795
4796 eap->nextcmd = find_nextcmd(arg);
4797 if (eap->skip)
4798 return;
4799
4800 if (arg[0] == '@')
4801 {
4802 ++arg;
4803 rest = get_group_name(arg, &group_name_end);
4804 if (rest == NULL)
4805 {
4806 EMSG((char_u *)_("E397: Filename required"));
4807 return;
4808 }
4809 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004810 if (sgl_id == 0)
4811 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812 /* separate_nextcmd() and expand_filename() depend on this */
4813 eap->arg = rest;
4814 }
4815
4816 /*
4817 * Everything that's left, up to the next command, should be the
4818 * filename to include.
4819 */
4820 eap->argt |= (XFILE | NOSPC);
4821 separate_nextcmd(eap);
4822 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4823 {
4824 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4825 * file. Need to expand the file name first. In other cases
4826 * ":runtime!" is used. */
4827 source = TRUE;
4828 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4829 {
4830 if (errormsg != NULL)
4831 EMSG(errormsg);
4832 return;
4833 }
4834 }
4835
4836 /*
4837 * Save and restore the existing top-level grouplist id and ":syn
4838 * include" tag around the actual inclusion.
4839 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004840 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4841 {
4842 EMSG((char_u *)_("E847: Too many syntax includes"));
4843 return;
4844 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004845 prev_syn_inc_tag = current_syn_inc_tag;
4846 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004847 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4848 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004849 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004850 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004852 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853 current_syn_inc_tag = prev_syn_inc_tag;
4854}
4855
4856/*
4857 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4858 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004860syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861{
4862 char_u *arg = eap->arg;
4863 char_u *group_name_end;
4864 int syn_id;
4865 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004866 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004868 char_u *kw;
4869 syn_opt_arg_T syn_opt_arg;
4870 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004871 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004872
4873 rest = get_group_name(arg, &group_name_end);
4874
4875 if (rest != NULL)
4876 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004877 if (eap->skip)
4878 syn_id = -1;
4879 else
4880 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004881 if (syn_id != 0)
4882 /* allocate a buffer, for removing backslashes in the keyword */
4883 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 if (keyword_copy != NULL)
4885 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004886 syn_opt_arg.flags = 0;
4887 syn_opt_arg.keyword = TRUE;
4888 syn_opt_arg.sync_idx = NULL;
4889 syn_opt_arg.has_cont_list = FALSE;
4890 syn_opt_arg.cont_in_list = NULL;
4891 syn_opt_arg.next_list = NULL;
4892
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 /*
4894 * The options given apply to ALL keywords, so all options must be
4895 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004898 cnt = 0;
4899 p = keyword_copy;
4900 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004902 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4903 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004904 if (rest == NULL || ends_excmd(*rest))
4905 break;
4906 /* Copy the keyword, removing backslashes, and add a NUL. */
4907 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004909 if (*rest == '\\' && rest[1] != NUL)
4910 ++rest;
4911 *p++ = *rest++;
4912 }
4913 *p++ = NUL;
4914 ++cnt;
4915 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004917 if (!eap->skip)
4918 {
4919 /* Adjust flags for use of ":syn include". */
4920 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4921
4922 /*
4923 * 2: Add an entry for each keyword.
4924 */
4925 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4926 {
4927 for (p = vim_strchr(kw, '['); ; )
4928 {
4929 if (p != NULL)
4930 *p = NUL;
4931 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004932 syn_opt_arg.cont_in_list,
4933 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004934 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004935 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004936 if (p[1] == NUL)
4937 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004938 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004939 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004940 }
4941 if (p[1] == ']')
4942 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004943 if (p[2] != NUL)
4944 {
4945 EMSG3(_("E890: trailing char after ']': %s]%s"),
4946 kw, &p[2]);
4947 goto error;
4948 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004949 kw = p + 1; /* skip over the "]" */
4950 break;
4951 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004952#ifdef FEAT_MBYTE
4953 if (has_mbyte)
4954 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004955 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004956
4957 mch_memmove(p, p + 1, l);
4958 p += l;
4959 }
4960 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004962 {
4963 p[0] = p[1];
4964 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004965 }
4966 }
4967 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004969error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004971 vim_free(syn_opt_arg.cont_in_list);
4972 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973 }
4974 }
4975
4976 if (rest != NULL)
4977 eap->nextcmd = check_nextcmd(rest);
4978 else
4979 EMSG2(_(e_invarg2), arg);
4980
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004981 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004982 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004983}
4984
4985/*
4986 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4987 *
4988 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4989 */
4990 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004991syn_cmd_match(
4992 exarg_T *eap,
4993 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994{
4995 char_u *arg = eap->arg;
4996 char_u *group_name_end;
4997 char_u *rest;
4998 synpat_T item; /* the item found in the line */
4999 int syn_id;
5000 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005001 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005003 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005004
5005 /* Isolate the group name, check for validity */
5006 rest = get_group_name(arg, &group_name_end);
5007
5008 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005009 syn_opt_arg.flags = 0;
5010 syn_opt_arg.keyword = FALSE;
5011 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5012 syn_opt_arg.has_cont_list = TRUE;
5013 syn_opt_arg.cont_list = NULL;
5014 syn_opt_arg.cont_in_list = NULL;
5015 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005016 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005017
5018 /* get the pattern. */
5019 init_syn_patterns();
5020 vim_memset(&item, 0, sizeof(item));
5021 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005022 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5023 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024
5025 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005026 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027
5028 if (rest != NULL) /* all arguments are valid */
5029 {
5030 /*
5031 * Check for trailing command and illegal trailing arguments.
5032 */
5033 eap->nextcmd = check_nextcmd(rest);
5034 if (!ends_excmd(*rest) || eap->skip)
5035 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005036 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037 && (syn_id = syn_check_group(arg,
5038 (int)(group_name_end - arg))) != 0)
5039 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005040 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005041 /*
5042 * Store the pattern in the syn_items list
5043 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005044 idx = curwin->w_s->b_syn_patterns.ga_len;
5045 SYN_ITEMS(curwin->w_s)[idx] = item;
5046 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5047 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5048 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5049 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5050 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5051 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5052 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5053 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005054 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005055#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005056 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005057#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005058 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005059 curwin->w_s->b_syn_containedin = TRUE;
5060 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5061 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005062
5063 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005064 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005065 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005067 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005068 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069#endif
5070
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005071 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005072 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005073 return; /* don't free the progs and patterns now */
5074 }
5075 }
5076
5077 /*
5078 * Something failed, free the allocated memory.
5079 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005080 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005082 vim_free(syn_opt_arg.cont_list);
5083 vim_free(syn_opt_arg.cont_in_list);
5084 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005085
5086 if (rest == NULL)
5087 EMSG2(_(e_invarg2), arg);
5088}
5089
5090/*
5091 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5092 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5093 */
5094 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005095syn_cmd_region(
5096 exarg_T *eap,
5097 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098{
5099 char_u *arg = eap->arg;
5100 char_u *group_name_end;
5101 char_u *rest; /* next arg, NULL on error */
5102 char_u *key_end;
5103 char_u *key = NULL;
5104 char_u *p;
5105 int item;
5106#define ITEM_START 0
5107#define ITEM_SKIP 1
5108#define ITEM_END 2
5109#define ITEM_MATCHGROUP 3
5110 struct pat_ptr
5111 {
5112 synpat_T *pp_synp; /* pointer to syn_pattern */
5113 int pp_matchgroup_id; /* matchgroup ID */
5114 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5115 } *(pat_ptrs[3]);
5116 /* patterns found in the line */
5117 struct pat_ptr *ppp;
5118 struct pat_ptr *ppp_next;
5119 int pat_count = 0; /* nr of syn_patterns found */
5120 int syn_id;
5121 int matchgroup_id = 0;
5122 int not_enough = FALSE; /* not enough arguments */
5123 int illegal = FALSE; /* illegal arguments */
5124 int success = FALSE;
5125 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005126 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005127 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128
5129 /* Isolate the group name, check for validity */
5130 rest = get_group_name(arg, &group_name_end);
5131
5132 pat_ptrs[0] = NULL;
5133 pat_ptrs[1] = NULL;
5134 pat_ptrs[2] = NULL;
5135
5136 init_syn_patterns();
5137
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005138 syn_opt_arg.flags = 0;
5139 syn_opt_arg.keyword = FALSE;
5140 syn_opt_arg.sync_idx = NULL;
5141 syn_opt_arg.has_cont_list = TRUE;
5142 syn_opt_arg.cont_list = NULL;
5143 syn_opt_arg.cont_in_list = NULL;
5144 syn_opt_arg.next_list = NULL;
5145
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146 /*
5147 * get the options, patterns and matchgroup.
5148 */
5149 while (rest != NULL && !ends_excmd(*rest))
5150 {
5151 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005152 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153 if (rest == NULL || ends_excmd(*rest))
5154 break;
5155
5156 /* must be a pattern or matchgroup then */
5157 key_end = rest;
5158 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5159 ++key_end;
5160 vim_free(key);
5161 key = vim_strnsave_up(rest, (int)(key_end - rest));
5162 if (key == NULL) /* out of memory */
5163 {
5164 rest = NULL;
5165 break;
5166 }
5167 if (STRCMP(key, "MATCHGROUP") == 0)
5168 item = ITEM_MATCHGROUP;
5169 else if (STRCMP(key, "START") == 0)
5170 item = ITEM_START;
5171 else if (STRCMP(key, "END") == 0)
5172 item = ITEM_END;
5173 else if (STRCMP(key, "SKIP") == 0)
5174 {
5175 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5176 {
5177 illegal = TRUE;
5178 break;
5179 }
5180 item = ITEM_SKIP;
5181 }
5182 else
5183 break;
5184 rest = skipwhite(key_end);
5185 if (*rest != '=')
5186 {
5187 rest = NULL;
5188 EMSG2(_("E398: Missing '=': %s"), arg);
5189 break;
5190 }
5191 rest = skipwhite(rest + 1);
5192 if (*rest == NUL)
5193 {
5194 not_enough = TRUE;
5195 break;
5196 }
5197
5198 if (item == ITEM_MATCHGROUP)
5199 {
5200 p = skiptowhite(rest);
5201 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5202 matchgroup_id = 0;
5203 else
5204 {
5205 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5206 if (matchgroup_id == 0)
5207 {
5208 illegal = TRUE;
5209 break;
5210 }
5211 }
5212 rest = skipwhite(p);
5213 }
5214 else
5215 {
5216 /*
5217 * Allocate room for a syn_pattern, and link it in the list of
5218 * syn_patterns for this item, at the start (because the list is
5219 * used from end to start).
5220 */
5221 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5222 if (ppp == NULL)
5223 {
5224 rest = NULL;
5225 break;
5226 }
5227 ppp->pp_next = pat_ptrs[item];
5228 pat_ptrs[item] = ppp;
5229 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5230 if (ppp->pp_synp == NULL)
5231 {
5232 rest = NULL;
5233 break;
5234 }
5235
5236 /*
5237 * Get the syntax pattern and the following offset(s).
5238 */
5239 /* Enable the appropriate \z specials. */
5240 if (item == ITEM_START)
5241 reg_do_extmatch = REX_SET;
5242 else if (item == ITEM_SKIP || item == ITEM_END)
5243 reg_do_extmatch = REX_USE;
5244 rest = get_syn_pattern(rest, ppp->pp_synp);
5245 reg_do_extmatch = 0;
5246 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005247 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005248 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5249 ppp->pp_matchgroup_id = matchgroup_id;
5250 ++pat_count;
5251 }
5252 }
5253 vim_free(key);
5254 if (illegal || not_enough)
5255 rest = NULL;
5256
5257 /*
5258 * Must have a "start" and "end" pattern.
5259 */
5260 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5261 pat_ptrs[ITEM_END] == NULL))
5262 {
5263 not_enough = TRUE;
5264 rest = NULL;
5265 }
5266
5267 if (rest != NULL)
5268 {
5269 /*
5270 * Check for trailing garbage or command.
5271 * If OK, add the item.
5272 */
5273 eap->nextcmd = check_nextcmd(rest);
5274 if (!ends_excmd(*rest) || eap->skip)
5275 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005276 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005277 && (syn_id = syn_check_group(arg,
5278 (int)(group_name_end - arg))) != 0)
5279 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005280 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005281 /*
5282 * Store the start/skip/end in the syn_items list
5283 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005284 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005285 for (item = ITEM_START; item <= ITEM_END; ++item)
5286 {
5287 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5288 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005289 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5290 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5291 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005292 (item == ITEM_START) ? SPTYPE_START :
5293 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005294 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5295 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005296 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5297 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005298 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005299 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005300#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005301 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005302#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 if (item == ITEM_START)
5304 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005305 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005306 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005307 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005308 syn_opt_arg.cont_in_list;
5309 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005310 curwin->w_s->b_syn_containedin = TRUE;
5311 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005312 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005313 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005314 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005315 ++idx;
5316#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005317 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005318 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319#endif
5320 }
5321 }
5322
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005323 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005324 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005325 success = TRUE; /* don't free the progs and patterns now */
5326 }
5327 }
5328
5329 /*
5330 * Free the allocated memory.
5331 */
5332 for (item = ITEM_START; item <= ITEM_END; ++item)
5333 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5334 {
5335 if (!success)
5336 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005337 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005338 vim_free(ppp->pp_synp->sp_pattern);
5339 }
5340 vim_free(ppp->pp_synp);
5341 ppp_next = ppp->pp_next;
5342 vim_free(ppp);
5343 }
5344
5345 if (!success)
5346 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005347 vim_free(syn_opt_arg.cont_list);
5348 vim_free(syn_opt_arg.cont_in_list);
5349 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005350 if (not_enough)
5351 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5352 else if (illegal || rest == NULL)
5353 EMSG2(_(e_invarg2), arg);
5354 }
5355}
5356
5357/*
5358 * A simple syntax group ID comparison function suitable for use in qsort()
5359 */
5360 static int
5361#ifdef __BORLANDC__
5362_RTLENTRYF
5363#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005364syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005365{
5366 const short *s1 = v1;
5367 const short *s2 = v2;
5368
5369 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5370}
5371
5372/*
5373 * Combines lists of syntax clusters.
5374 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5375 */
5376 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005377syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005378{
5379 int count1 = 0;
5380 int count2 = 0;
5381 short *g1;
5382 short *g2;
5383 short *clstr = NULL;
5384 int count;
5385 int round;
5386
5387 /*
5388 * Handle degenerate cases.
5389 */
5390 if (*clstr2 == NULL)
5391 return;
5392 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5393 {
5394 if (list_op == CLUSTER_REPLACE)
5395 vim_free(*clstr1);
5396 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5397 *clstr1 = *clstr2;
5398 else
5399 vim_free(*clstr2);
5400 return;
5401 }
5402
5403 for (g1 = *clstr1; *g1; g1++)
5404 ++count1;
5405 for (g2 = *clstr2; *g2; g2++)
5406 ++count2;
5407
5408 /*
5409 * For speed purposes, sort both lists.
5410 */
5411 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5412 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5413
5414 /*
5415 * We proceed in two passes; in round 1, we count the elements to place
5416 * in the new list, and in round 2, we allocate and populate the new
5417 * list. For speed, we use a mergesort-like method, adding the smaller
5418 * of the current elements in each list to the new list.
5419 */
5420 for (round = 1; round <= 2; round++)
5421 {
5422 g1 = *clstr1;
5423 g2 = *clstr2;
5424 count = 0;
5425
5426 /*
5427 * First, loop through the lists until one of them is empty.
5428 */
5429 while (*g1 && *g2)
5430 {
5431 /*
5432 * We always want to add from the first list.
5433 */
5434 if (*g1 < *g2)
5435 {
5436 if (round == 2)
5437 clstr[count] = *g1;
5438 count++;
5439 g1++;
5440 continue;
5441 }
5442 /*
5443 * We only want to add from the second list if we're adding the
5444 * lists.
5445 */
5446 if (list_op == CLUSTER_ADD)
5447 {
5448 if (round == 2)
5449 clstr[count] = *g2;
5450 count++;
5451 }
5452 if (*g1 == *g2)
5453 g1++;
5454 g2++;
5455 }
5456
5457 /*
5458 * Now add the leftovers from whichever list didn't get finished
5459 * first. As before, we only want to add from the second list if
5460 * we're adding the lists.
5461 */
5462 for (; *g1; g1++, count++)
5463 if (round == 2)
5464 clstr[count] = *g1;
5465 if (list_op == CLUSTER_ADD)
5466 for (; *g2; g2++, count++)
5467 if (round == 2)
5468 clstr[count] = *g2;
5469
5470 if (round == 1)
5471 {
5472 /*
5473 * If the group ended up empty, we don't need to allocate any
5474 * space for it.
5475 */
5476 if (count == 0)
5477 {
5478 clstr = NULL;
5479 break;
5480 }
5481 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5482 if (clstr == NULL)
5483 break;
5484 clstr[count] = 0;
5485 }
5486 }
5487
5488 /*
5489 * Finally, put the new list in place.
5490 */
5491 vim_free(*clstr1);
5492 vim_free(*clstr2);
5493 *clstr1 = clstr;
5494}
5495
5496/*
5497 * Lookup a syntax cluster name and return it's ID.
5498 * If it is not found, 0 is returned.
5499 */
5500 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005501syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502{
5503 int i;
5504 char_u *name_u;
5505
5506 /* Avoid using stricmp() too much, it's slow on some systems */
5507 name_u = vim_strsave_up(name);
5508 if (name_u == NULL)
5509 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005510 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5511 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5512 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513 break;
5514 vim_free(name_u);
5515 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5516}
5517
5518/*
5519 * Like syn_scl_name2id(), but take a pointer + length argument.
5520 */
5521 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005522syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005523{
5524 char_u *name;
5525 int id = 0;
5526
5527 name = vim_strnsave(linep, len);
5528 if (name != NULL)
5529 {
5530 id = syn_scl_name2id(name);
5531 vim_free(name);
5532 }
5533 return id;
5534}
5535
5536/*
5537 * Find syntax cluster name in the table and return it's ID.
5538 * The argument is a pointer to the name and the length of the name.
5539 * If it doesn't exist yet, a new entry is created.
5540 * Return 0 for failure.
5541 */
5542 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005543syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005544{
5545 int id;
5546 char_u *name;
5547
5548 name = vim_strnsave(pp, len);
5549 if (name == NULL)
5550 return 0;
5551
5552 id = syn_scl_name2id(name);
5553 if (id == 0) /* doesn't exist yet */
5554 id = syn_add_cluster(name);
5555 else
5556 vim_free(name);
5557 return id;
5558}
5559
5560/*
5561 * Add new syntax cluster and return it's ID.
5562 * "name" must be an allocated string, it will be consumed.
5563 * Return 0 for failure.
5564 */
5565 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005566syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005568 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569
5570 /*
5571 * First call for this growarray: init growing array.
5572 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005573 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005574 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005575 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5576 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577 }
5578
Bram Moolenaar42431a72011-04-01 14:44:59 +02005579 len = curwin->w_s->b_syn_clusters.ga_len;
5580 if (len >= MAX_CLUSTER_ID)
5581 {
5582 EMSG((char_u *)_("E848: Too many syntax clusters"));
5583 vim_free(name);
5584 return 0;
5585 }
5586
Bram Moolenaar071d4272004-06-13 20:20:40 +00005587 /*
5588 * Make room for at least one other cluster entry.
5589 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005590 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005591 {
5592 vim_free(name);
5593 return 0;
5594 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595
Bram Moolenaar860cae12010-06-05 23:22:07 +02005596 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5597 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5598 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5599 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5600 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601
Bram Moolenaar217ad922005-03-20 22:37:15 +00005602 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005603 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005604 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005605 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005606
Bram Moolenaar071d4272004-06-13 20:20:40 +00005607 return len + SYNID_CLUSTER;
5608}
5609
5610/*
5611 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5612 * [add={groupname},..] [remove={groupname},..]".
5613 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005615syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005616{
5617 char_u *arg = eap->arg;
5618 char_u *group_name_end;
5619 char_u *rest;
5620 int scl_id;
5621 short *clstr_list;
5622 int got_clstr = FALSE;
5623 int opt_len;
5624 int list_op;
5625
5626 eap->nextcmd = find_nextcmd(arg);
5627 if (eap->skip)
5628 return;
5629
5630 rest = get_group_name(arg, &group_name_end);
5631
5632 if (rest != NULL)
5633 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005634 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5635 if (scl_id == 0)
5636 return;
5637 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638
5639 for (;;)
5640 {
5641 if (STRNICMP(rest, "add", 3) == 0
5642 && (vim_iswhite(rest[3]) || rest[3] == '='))
5643 {
5644 opt_len = 3;
5645 list_op = CLUSTER_ADD;
5646 }
5647 else if (STRNICMP(rest, "remove", 6) == 0
5648 && (vim_iswhite(rest[6]) || rest[6] == '='))
5649 {
5650 opt_len = 6;
5651 list_op = CLUSTER_SUBTRACT;
5652 }
5653 else if (STRNICMP(rest, "contains", 8) == 0
5654 && (vim_iswhite(rest[8]) || rest[8] == '='))
5655 {
5656 opt_len = 8;
5657 list_op = CLUSTER_REPLACE;
5658 }
5659 else
5660 break;
5661
5662 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005663 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005664 {
5665 EMSG2(_(e_invarg2), rest);
5666 break;
5667 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005668 if (scl_id >= 0)
5669 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670 &clstr_list, list_op);
5671 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
5793 if (!ends_excmd(*end) && !vim_iswhite(*end))
5794 {
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 {
6014 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
6015 ;
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 {
6037 EMSG2(_("E408: %s must be first in contains list"), name + 1);
6038 failed = TRUE;
6039 vim_free(name);
6040 break;
6041 }
6042 if (name[1] == 'A')
6043 id = SYNID_ALLBUT;
6044 else if (name[1] == 'T')
6045 id = SYNID_TOP;
6046 else
6047 id = SYNID_CONTAINED;
6048 id += current_syn_inc_tag;
6049 }
6050 else if (name[1] == '@')
6051 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006052 if (skip)
6053 id = -1;
6054 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006055 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006056 }
6057 else
6058 {
6059 /*
6060 * Handle full group name.
6061 */
6062 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6063 id = syn_check_group(name + 1, (int)(end - p));
6064 else
6065 {
6066 /*
6067 * Handle match of regexp with group names.
6068 */
6069 *name = '^';
6070 STRCAT(name, "$");
6071 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6072 if (regmatch.regprog == NULL)
6073 {
6074 failed = TRUE;
6075 vim_free(name);
6076 break;
6077 }
6078
6079 regmatch.rm_ic = TRUE;
6080 id = 0;
6081 for (i = highlight_ga.ga_len; --i >= 0; )
6082 {
6083 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6084 (colnr_T)0))
6085 {
6086 if (round == 2)
6087 {
6088 /* Got more items than expected; can happen
6089 * when adding items that match:
6090 * "contains=a.*b,axb".
6091 * Go back to first round */
6092 if (count >= total_count)
6093 {
6094 vim_free(retval);
6095 round = 1;
6096 }
6097 else
6098 retval[count] = i + 1;
6099 }
6100 ++count;
6101 id = -1; /* remember that we found one */
6102 }
6103 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006104 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006105 }
6106 }
6107 vim_free(name);
6108 if (id == 0)
6109 {
6110 EMSG2(_("E409: Unknown group name: %s"), p);
6111 failed = TRUE;
6112 break;
6113 }
6114 if (id > 0)
6115 {
6116 if (round == 2)
6117 {
6118 /* Got more items than expected, go back to first round */
6119 if (count >= total_count)
6120 {
6121 vim_free(retval);
6122 round = 1;
6123 }
6124 else
6125 retval[count] = id;
6126 }
6127 ++count;
6128 }
6129 p = skipwhite(end);
6130 if (*p != ',')
6131 break;
6132 p = skipwhite(p + 1); /* skip comma in between arguments */
6133 }
6134 if (failed)
6135 break;
6136 if (round == 1)
6137 {
6138 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6139 if (retval == NULL)
6140 break;
6141 retval[count] = 0; /* zero means end of the list */
6142 total_count = count;
6143 }
6144 }
6145
6146 *arg = p;
6147 if (failed || retval == NULL)
6148 {
6149 vim_free(retval);
6150 return FAIL;
6151 }
6152
6153 if (*list == NULL)
6154 *list = retval;
6155 else
6156 vim_free(retval); /* list already found, don't overwrite it */
6157
6158 return OK;
6159}
6160
6161/*
6162 * Make a copy of an ID list.
6163 */
6164 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006165copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006166{
6167 int len;
6168 int count;
6169 short *retval;
6170
6171 if (list == NULL)
6172 return NULL;
6173
6174 for (count = 0; list[count]; ++count)
6175 ;
6176 len = (count + 1) * sizeof(short);
6177 retval = (short *)alloc((unsigned)len);
6178 if (retval != NULL)
6179 mch_memmove(retval, list, (size_t)len);
6180
6181 return retval;
6182}
6183
6184/*
6185 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6186 * "cur_si" can be NULL if not checking the "containedin" list.
6187 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6188 * the current item.
6189 * This function is called very often, keep it fast!!
6190 */
6191 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006192in_id_list(
6193 stateitem_T *cur_si, /* current item or NULL */
6194 short *list, /* id list */
6195 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6196 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006197{
6198 int retval;
6199 short *scl_list;
6200 short item;
6201 short id = ssp->id;
6202 static int depth = 0;
6203 int r;
6204
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006205 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006206 if (cur_si != NULL && ssp->cont_in_list != NULL
6207 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006208 {
6209 /* Ignore transparent items without a contains argument. Double check
6210 * that we don't go back past the first one. */
6211 while ((cur_si->si_flags & HL_TRANS_CONT)
6212 && cur_si > (stateitem_T *)(current_state.ga_data))
6213 --cur_si;
6214 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6215 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006216 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6217 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218 return TRUE;
6219 }
6220
6221 if (list == NULL)
6222 return FALSE;
6223
6224 /*
6225 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6226 * inside anything. Only allow not-contained groups.
6227 */
6228 if (list == ID_LIST_ALL)
6229 return !contained;
6230
6231 /*
6232 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6233 * contains list. We also require that "id" is at the same ":syn include"
6234 * level as the list.
6235 */
6236 item = *list;
6237 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6238 {
6239 if (item < SYNID_TOP)
6240 {
6241 /* ALL or ALLBUT: accept all groups in the same file */
6242 if (item - SYNID_ALLBUT != ssp->inc_tag)
6243 return FALSE;
6244 }
6245 else if (item < SYNID_CONTAINED)
6246 {
6247 /* TOP: accept all not-contained groups in the same file */
6248 if (item - SYNID_TOP != ssp->inc_tag || contained)
6249 return FALSE;
6250 }
6251 else
6252 {
6253 /* CONTAINED: accept all contained groups in the same file */
6254 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6255 return FALSE;
6256 }
6257 item = *++list;
6258 retval = FALSE;
6259 }
6260 else
6261 retval = TRUE;
6262
6263 /*
6264 * Return "retval" if id is in the contains list.
6265 */
6266 while (item != 0)
6267 {
6268 if (item == id)
6269 return retval;
6270 if (item >= SYNID_CLUSTER)
6271 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006272 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006273 /* restrict recursiveness to 30 to avoid an endless loop for a
6274 * cluster that includes itself (indirectly) */
6275 if (scl_list != NULL && depth < 30)
6276 {
6277 ++depth;
6278 r = in_id_list(NULL, scl_list, ssp, contained);
6279 --depth;
6280 if (r)
6281 return retval;
6282 }
6283 }
6284 item = *++list;
6285 }
6286 return !retval;
6287}
6288
6289struct subcommand
6290{
Bram Moolenaard99df422016-01-29 23:20:40 +01006291 char *name; /* subcommand name */
6292 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293};
6294
6295static struct subcommand subcommands[] =
6296{
6297 {"case", syn_cmd_case},
6298 {"clear", syn_cmd_clear},
6299 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006300 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006301 {"enable", syn_cmd_enable},
6302 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006303 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006304 {"keyword", syn_cmd_keyword},
6305 {"list", syn_cmd_list},
6306 {"manual", syn_cmd_manual},
6307 {"match", syn_cmd_match},
6308 {"on", syn_cmd_on},
6309 {"off", syn_cmd_off},
6310 {"region", syn_cmd_region},
6311 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006312 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006313 {"sync", syn_cmd_sync},
6314 {"", syn_cmd_list},
6315 {NULL, NULL}
6316};
6317
6318/*
6319 * ":syntax".
6320 * This searches the subcommands[] table for the subcommand name, and calls a
6321 * syntax_subcommand() function to do the rest.
6322 */
6323 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006324ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325{
6326 char_u *arg = eap->arg;
6327 char_u *subcmd_end;
6328 char_u *subcmd_name;
6329 int i;
6330
6331 syn_cmdlinep = eap->cmdlinep;
6332
6333 /* isolate subcommand name */
6334 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6335 ;
6336 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6337 if (subcmd_name != NULL)
6338 {
6339 if (eap->skip) /* skip error messages for all subcommands */
6340 ++emsg_skip;
6341 for (i = 0; ; ++i)
6342 {
6343 if (subcommands[i].name == NULL)
6344 {
6345 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6346 break;
6347 }
6348 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6349 {
6350 eap->arg = skipwhite(subcmd_end);
6351 (subcommands[i].func)(eap, FALSE);
6352 break;
6353 }
6354 }
6355 vim_free(subcmd_name);
6356 if (eap->skip)
6357 --emsg_skip;
6358 }
6359}
6360
Bram Moolenaar860cae12010-06-05 23:22:07 +02006361 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006362ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006363{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006364 char_u *old_value;
6365 char_u *new_value;
6366
Bram Moolenaar860cae12010-06-05 23:22:07 +02006367 if (curwin->w_s == &curwin->w_buffer->b_s)
6368 {
6369 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6370 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006371 hash_init(&curwin->w_s->b_keywtab);
6372 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006373#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006374 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006375 curwin->w_p_spell = FALSE; /* No spell checking */
6376 clear_string_option(&curwin->w_s->b_p_spc);
6377 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006378 clear_string_option(&curwin->w_s->b_p_spl);
6379#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006380 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006381 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006382
6383 /* save value of b:current_syntax */
6384 old_value = get_var_value((char_u *)"b:current_syntax");
6385 if (old_value != NULL)
6386 old_value = vim_strsave(old_value);
6387
Bram Moolenaard1413d92016-03-02 21:51:56 +01006388#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006389 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6390 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006391 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006392#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006393
6394 /* move value of b:current_syntax to w:current_syntax */
6395 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006396 if (new_value != NULL)
6397 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006398
6399 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006400 if (old_value == NULL)
6401 do_unlet((char_u *)"b:current_syntax", TRUE);
6402 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006403 {
6404 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6405 vim_free(old_value);
6406 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006407}
6408
6409 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006410syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006411{
6412 return (win->w_s->b_syn_patterns.ga_len != 0
6413 || win->w_s->b_syn_clusters.ga_len != 0
6414 || win->w_s->b_keywtab.ht_used > 0
6415 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006416}
6417
6418#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6419
6420static enum
6421{
6422 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006423 EXP_CASE, /* expand ":syn case" arguments */
6424 EXP_SPELL, /* expand ":syn spell" arguments */
6425 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426} expand_what;
6427
Bram Moolenaar4f688582007-07-24 12:34:30 +00006428/*
6429 * Reset include_link, include_default, include_none to 0.
6430 * Called when we are done expanding.
6431 */
6432 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006433reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006434{
6435 include_link = include_default = include_none = 0;
6436}
6437
6438/*
6439 * Handle command line completion for :match and :echohl command: Add "None"
6440 * as highlight group.
6441 */
6442 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006443set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006444{
6445 xp->xp_context = EXPAND_HIGHLIGHT;
6446 xp->xp_pattern = arg;
6447 include_none = 1;
6448}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006449
6450/*
6451 * Handle command line completion for :syntax command.
6452 */
6453 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006454set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006455{
6456 char_u *p;
6457
6458 /* Default: expand subcommands */
6459 xp->xp_context = EXPAND_SYNTAX;
6460 expand_what = EXP_SUBCMD;
6461 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006462 include_link = 0;
6463 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464
6465 /* (part of) subcommand already typed */
6466 if (*arg != NUL)
6467 {
6468 p = skiptowhite(arg);
6469 if (*p != NUL) /* past first word */
6470 {
6471 xp->xp_pattern = skipwhite(p);
6472 if (*skiptowhite(xp->xp_pattern) != NUL)
6473 xp->xp_context = EXPAND_NOTHING;
6474 else if (STRNICMP(arg, "case", p - arg) == 0)
6475 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006476 else if (STRNICMP(arg, "spell", p - arg) == 0)
6477 expand_what = EXP_SPELL;
6478 else if (STRNICMP(arg, "sync", p - arg) == 0)
6479 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6481 || STRNICMP(arg, "region", p - arg) == 0
6482 || STRNICMP(arg, "match", p - arg) == 0
6483 || STRNICMP(arg, "list", p - arg) == 0)
6484 xp->xp_context = EXPAND_HIGHLIGHT;
6485 else
6486 xp->xp_context = EXPAND_NOTHING;
6487 }
6488 }
6489}
6490
Bram Moolenaar071d4272004-06-13 20:20:40 +00006491/*
6492 * Function given to ExpandGeneric() to obtain the list syntax names for
6493 * expansion.
6494 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006495 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006496get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006497{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006498 switch (expand_what)
6499 {
6500 case EXP_SUBCMD:
6501 return (char_u *)subcommands[idx].name;
6502 case EXP_CASE:
6503 {
6504 static char *case_args[] = {"match", "ignore", NULL};
6505 return (char_u *)case_args[idx];
6506 }
6507 case EXP_SPELL:
6508 {
6509 static char *spell_args[] =
6510 {"toplevel", "notoplevel", "default", NULL};
6511 return (char_u *)spell_args[idx];
6512 }
6513 case EXP_SYNC:
6514 {
6515 static char *sync_args[] =
6516 {"ccomment", "clear", "fromstart",
6517 "linebreaks=", "linecont", "lines=", "match",
6518 "maxlines=", "minlines=", "region", NULL};
6519 return (char_u *)sync_args[idx];
6520 }
6521 }
6522 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523}
6524
6525#endif /* FEAT_CMDL_COMPL */
6526
Bram Moolenaar071d4272004-06-13 20:20:40 +00006527/*
6528 * Function called for expression evaluation: get syntax ID at file position.
6529 */
6530 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006531syn_get_id(
6532 win_T *wp,
6533 long lnum,
6534 colnr_T col,
6535 int trans, /* remove transparency */
6536 int *spellp, /* return: can do spell checking */
6537 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006538{
6539 /* When the position is not after the current position and in the same
6540 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006541 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006543 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006544 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006545 else if (wp->w_buffer == syn_buf
6546 && lnum == current_lnum
6547 && col > current_col)
6548 /* next_match may not be correct when moving around, e.g. with the
6549 * "skip" expression in searchpair() */
6550 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006551
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006552 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006553
6554 return (trans ? current_trans_id : current_id);
6555}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556
Bram Moolenaar860cae12010-06-05 23:22:07 +02006557#if defined(FEAT_CONCEAL) || defined(PROTO)
6558/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006559 * Get extra information about the syntax item. Must be called right after
6560 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006561 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006562 * Returns the current flags.
6563 */
6564 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006565get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006566{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006567 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006568 return current_flags;
6569}
6570
6571/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006572 * Return conceal substitution character
6573 */
6574 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006575syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006576{
6577 return current_sub_char;
6578}
6579#endif
6580
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006581#if defined(FEAT_EVAL) || defined(PROTO)
6582/*
6583 * Return the syntax ID at position "i" in the current stack.
6584 * The caller must have called syn_get_id() before to fill the stack.
6585 * Returns -1 when "i" is out of range.
6586 */
6587 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006588syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006589{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006590 if (i >= current_state.ga_len)
6591 {
6592 /* Need to invalidate the state, because we didn't properly finish it
6593 * for the last character, "keep_state" was TRUE. */
6594 invalidate_current_state();
6595 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006596 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006597 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006598 return CUR_STATE(i).si_id;
6599}
6600#endif
6601
Bram Moolenaar071d4272004-06-13 20:20:40 +00006602#if defined(FEAT_FOLDING) || defined(PROTO)
6603/*
6604 * Function called to get folding level for line "lnum" in window "wp".
6605 */
6606 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006607syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608{
6609 int level = 0;
6610 int i;
6611
6612 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006613 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006614 {
6615 syntax_start(wp, lnum);
6616
6617 for (i = 0; i < current_state.ga_len; ++i)
6618 if (CUR_STATE(i).si_flags & HL_FOLD)
6619 ++level;
6620 }
6621 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006622 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006623 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006624 if (level < 0)
6625 level = 0;
6626 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006627 return level;
6628}
6629#endif
6630
Bram Moolenaar01615492015-02-03 13:00:38 +01006631#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006632/*
6633 * ":syntime".
6634 */
6635 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006636ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006637{
6638 if (STRCMP(eap->arg, "on") == 0)
6639 syn_time_on = TRUE;
6640 else if (STRCMP(eap->arg, "off") == 0)
6641 syn_time_on = FALSE;
6642 else if (STRCMP(eap->arg, "clear") == 0)
6643 syntime_clear();
6644 else if (STRCMP(eap->arg, "report") == 0)
6645 syntime_report();
6646 else
6647 EMSG2(_(e_invarg2), eap->arg);
6648}
6649
6650 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006651syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006652{
6653 profile_zero(&st->total);
6654 profile_zero(&st->slowest);
6655 st->count = 0;
6656 st->match = 0;
6657}
6658
6659/*
6660 * Clear the syntax timing for the current buffer.
6661 */
6662 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006663syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006664{
6665 int idx;
6666 synpat_T *spp;
6667
6668 if (!syntax_present(curwin))
6669 {
6670 MSG(_(msg_no_items));
6671 return;
6672 }
6673 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6674 {
6675 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6676 syn_clear_time(&spp->sp_time);
6677 }
6678}
6679
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006680#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6681/*
6682 * Function given to ExpandGeneric() to obtain the possible arguments of the
6683 * ":syntime {on,off,clear,report}" command.
6684 */
6685 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006686get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006687{
6688 switch (idx)
6689 {
6690 case 0: return (char_u *)"on";
6691 case 1: return (char_u *)"off";
6692 case 2: return (char_u *)"clear";
6693 case 3: return (char_u *)"report";
6694 }
6695 return NULL;
6696}
6697#endif
6698
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006699typedef struct
6700{
6701 proftime_T total;
6702 int count;
6703 int match;
6704 proftime_T slowest;
6705 proftime_T average;
6706 int id;
6707 char_u *pattern;
6708} time_entry_T;
6709
6710 static int
6711#ifdef __BORLANDC__
6712_RTLENTRYF
6713#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006714syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006715{
6716 const time_entry_T *s1 = v1;
6717 const time_entry_T *s2 = v2;
6718
6719 return profile_cmp(&s1->total, &s2->total);
6720}
6721
6722/*
6723 * Clear the syntax timing for the current buffer.
6724 */
6725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006726syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006727{
6728 int idx;
6729 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006730# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006731 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006732# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006733 int len;
6734 proftime_T total_total;
6735 int total_count = 0;
6736 garray_T ga;
6737 time_entry_T *p;
6738
6739 if (!syntax_present(curwin))
6740 {
6741 MSG(_(msg_no_items));
6742 return;
6743 }
6744
6745 ga_init2(&ga, sizeof(time_entry_T), 50);
6746 profile_zero(&total_total);
6747 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6748 {
6749 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6750 if (spp->sp_time.count > 0)
6751 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006752 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006753 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6754 p->total = spp->sp_time.total;
6755 profile_add(&total_total, &spp->sp_time.total);
6756 p->count = spp->sp_time.count;
6757 p->match = spp->sp_time.match;
6758 total_count += spp->sp_time.count;
6759 p->slowest = spp->sp_time.slowest;
6760# ifdef FEAT_FLOAT
6761 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6762 p->average = tm;
6763# endif
6764 p->id = spp->sp_syn.id;
6765 p->pattern = spp->sp_pattern;
6766 ++ga.ga_len;
6767 }
6768 }
6769
Bram Moolenaara2162552017-01-08 17:46:20 +01006770 /* Sort on total time. Skip if there are no items to avoid passing NULL
6771 * pointer to qsort(). */
6772 if (ga.ga_len > 1)
6773 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006774 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006775
6776 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6777 MSG_PUTS("\n");
6778 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6779 {
6780 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6781 p = ((time_entry_T *)ga.ga_data) + idx;
6782
6783 MSG_PUTS(profile_msg(&p->total));
6784 MSG_PUTS(" "); /* make sure there is always a separating space */
6785 msg_advance(13);
6786 msg_outnum(p->count);
6787 MSG_PUTS(" ");
6788 msg_advance(20);
6789 msg_outnum(p->match);
6790 MSG_PUTS(" ");
6791 msg_advance(26);
6792 MSG_PUTS(profile_msg(&p->slowest));
6793 MSG_PUTS(" ");
6794 msg_advance(38);
6795# ifdef FEAT_FLOAT
6796 MSG_PUTS(profile_msg(&p->average));
6797 MSG_PUTS(" ");
6798# endif
6799 msg_advance(50);
6800 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6801 MSG_PUTS(" ");
6802
6803 msg_advance(69);
6804 if (Columns < 80)
6805 len = 20; /* will wrap anyway */
6806 else
6807 len = Columns - 70;
6808 if (len > (int)STRLEN(p->pattern))
6809 len = (int)STRLEN(p->pattern);
6810 msg_outtrans_len(p->pattern, len);
6811 MSG_PUTS("\n");
6812 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006813 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006814 if (!got_int)
6815 {
6816 MSG_PUTS("\n");
6817 MSG_PUTS(profile_msg(&total_total));
6818 msg_advance(13);
6819 msg_outnum(total_count);
6820 MSG_PUTS("\n");
6821 }
6822}
6823#endif
6824
Bram Moolenaar071d4272004-06-13 20:20:40 +00006825#endif /* FEAT_SYN_HL */
6826
Bram Moolenaar071d4272004-06-13 20:20:40 +00006827/**************************************
6828 * Highlighting stuff *
6829 **************************************/
6830
6831/*
6832 * The default highlight groups. These are compiled-in for fast startup and
6833 * they still work when the runtime files can't be found.
6834 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006835 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6836 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006837 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006838#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006839# define CENT(a, b) b
6840#else
6841# define CENT(a, b) a
6842#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006843static char *(highlight_init_both[]) =
6844 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006845 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6846 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6847 CENT("IncSearch term=reverse cterm=reverse",
6848 "IncSearch term=reverse cterm=reverse gui=reverse"),
6849 CENT("ModeMsg term=bold cterm=bold",
6850 "ModeMsg term=bold cterm=bold gui=bold"),
6851 CENT("NonText term=bold ctermfg=Blue",
6852 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6853 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6854 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6855 CENT("StatusLineNC term=reverse cterm=reverse",
6856 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006857 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006858#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006859 CENT("VertSplit term=reverse cterm=reverse",
6860 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006861#endif
6862#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006863 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6864 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006865#endif
6866#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006867 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6868 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006869#endif
6870#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006871 CENT("PmenuSbar ctermbg=Grey",
6872 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006873#endif
6874#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006875 CENT("TabLineSel term=bold cterm=bold",
6876 "TabLineSel term=bold cterm=bold gui=bold"),
6877 CENT("TabLineFill term=reverse cterm=reverse",
6878 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006879#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006880#ifdef FEAT_GUI
6881 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006882 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006883#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006884 NULL
6885 };
6886
6887static char *(highlight_init_light[]) =
6888 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006889 CENT("Directory term=bold ctermfg=DarkBlue",
6890 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6891 CENT("LineNr term=underline ctermfg=Brown",
6892 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006893 CENT("CursorLineNr term=bold ctermfg=Brown",
6894 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006895 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6896 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6897 CENT("Question term=standout ctermfg=DarkGreen",
6898 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6899 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6900 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006902 CENT("SpellBad term=reverse ctermbg=LightRed",
6903 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6904 CENT("SpellCap term=reverse ctermbg=LightBlue",
6905 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6906 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6907 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6908 CENT("SpellLocal term=underline ctermbg=Cyan",
6909 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006910#endif
6911#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006912 CENT("PmenuThumb ctermbg=Black",
6913 "PmenuThumb ctermbg=Black guibg=Black"),
6914 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6915 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6916 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6917 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006918#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006919 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6920 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6921 CENT("Title term=bold ctermfg=DarkMagenta",
6922 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6923 CENT("WarningMsg term=standout ctermfg=DarkRed",
6924 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006925#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006926 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6927 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006928#endif
6929#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006930 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6931 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6932 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6933 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006934#endif
6935#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006936 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6937 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006938#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006939 CENT("Visual term=reverse",
6940 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006941#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006942 CENT("DiffAdd term=bold ctermbg=LightBlue",
6943 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6944 CENT("DiffChange term=bold ctermbg=LightMagenta",
6945 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6946 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6947 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006948#endif
6949#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006950 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6951 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006952#endif
6953#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006954 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006955 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006956 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006957 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006958 CENT("ColorColumn term=reverse ctermbg=LightRed",
6959 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006960#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006961#ifdef FEAT_CONCEAL
6962 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6963 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6964#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006965#ifdef FEAT_AUTOCMD
6966 CENT("MatchParen term=reverse ctermbg=Cyan",
6967 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6968#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006969#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006970 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006971#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006972 NULL
6973 };
6974
6975static char *(highlight_init_dark[]) =
6976 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006977 CENT("Directory term=bold ctermfg=LightCyan",
6978 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6979 CENT("LineNr term=underline ctermfg=Yellow",
6980 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006981 CENT("CursorLineNr term=bold ctermfg=Yellow",
6982 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006983 CENT("MoreMsg term=bold ctermfg=LightGreen",
6984 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6985 CENT("Question term=standout ctermfg=LightGreen",
6986 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6987 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6988 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6989 CENT("SpecialKey term=bold ctermfg=LightBlue",
6990 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006991#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006992 CENT("SpellBad term=reverse ctermbg=Red",
6993 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6994 CENT("SpellCap term=reverse ctermbg=Blue",
6995 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6996 CENT("SpellRare term=reverse ctermbg=Magenta",
6997 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6998 CENT("SpellLocal term=underline ctermbg=Cyan",
6999 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007000#endif
7001#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007002 CENT("PmenuThumb ctermbg=White",
7003 "PmenuThumb ctermbg=White guibg=White"),
7004 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7005 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007006 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7007 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007008#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007009 CENT("Title term=bold ctermfg=LightMagenta",
7010 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7011 CENT("WarningMsg term=standout ctermfg=LightRed",
7012 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007013#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007014 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7015 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007016#endif
7017#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007018 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7019 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7020 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7021 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007022#endif
7023#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007024 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7025 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007026#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007027 CENT("Visual term=reverse",
7028 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007029#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007030 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7031 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7032 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7033 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7034 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7035 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007036#endif
7037#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007038 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7039 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007040#endif
7041#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007042 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007043 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007044 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007045 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007046 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7047 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007048#endif
7049#ifdef FEAT_AUTOCMD
7050 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7051 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007052#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007053#ifdef FEAT_CONCEAL
7054 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7055 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7056#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007057#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007058 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007059#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007060 NULL
7061 };
7062
7063 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007064init_highlight(
7065 int both, /* include groups where 'bg' doesn't matter */
7066 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007067{
7068 int i;
7069 char **pp;
7070 static int had_both = FALSE;
7071#ifdef FEAT_EVAL
7072 char_u *p;
7073
7074 /*
7075 * Try finding the color scheme file. Used when a color file was loaded
7076 * and 'background' or 't_Co' is changed.
7077 */
7078 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007079 if (p != NULL)
7080 {
7081 /* The value of g:colors_name could be freed when sourcing the script,
7082 * making "p" invalid, so copy it. */
7083 char_u *copy_p = vim_strsave(p);
7084 int r;
7085
7086 if (copy_p != NULL)
7087 {
7088 r = load_colors(copy_p);
7089 vim_free(copy_p);
7090 if (r == OK)
7091 return;
7092 }
7093 }
7094
Bram Moolenaar071d4272004-06-13 20:20:40 +00007095#endif
7096
7097 /*
7098 * Didn't use a color file, use the compiled-in colors.
7099 */
7100 if (both)
7101 {
7102 had_both = TRUE;
7103 pp = highlight_init_both;
7104 for (i = 0; pp[i] != NULL; ++i)
7105 do_highlight((char_u *)pp[i], reset, TRUE);
7106 }
7107 else if (!had_both)
7108 /* Don't do anything before the call with both == TRUE from main().
7109 * Not everything has been setup then, and that call will overrule
7110 * everything anyway. */
7111 return;
7112
7113 if (*p_bg == 'l')
7114 pp = highlight_init_light;
7115 else
7116 pp = highlight_init_dark;
7117 for (i = 0; pp[i] != NULL; ++i)
7118 do_highlight((char_u *)pp[i], reset, TRUE);
7119
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007120 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007121 * depend on the number of colors available.
7122 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007123 * to avoid Statement highlighted text disappears.
7124 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007125 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007126 do_highlight((char_u *)(*p_bg == 'l'
7127 ? "Visual cterm=NONE ctermbg=LightGrey"
7128 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007129 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007130 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007131 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7132 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007133 if (*p_bg == 'l')
7134 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7135 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007136
Bram Moolenaar071d4272004-06-13 20:20:40 +00007137#ifdef FEAT_SYN_HL
7138 /*
7139 * If syntax highlighting is enabled load the highlighting for it.
7140 */
7141 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007142 {
7143 static int recursive = 0;
7144
7145 if (recursive >= 5)
7146 EMSG(_("E679: recursive loop loading syncolor.vim"));
7147 else
7148 {
7149 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007150 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007151 --recursive;
7152 }
7153 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007154#endif
7155}
7156
7157/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007158 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007159 * Return OK for success, FAIL for failure.
7160 */
7161 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007162load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007163{
7164 char_u *buf;
7165 int retval = FAIL;
7166 static int recursive = FALSE;
7167
7168 /* When being called recursively, this is probably because setting
7169 * 'background' caused the highlighting to be reloaded. This means it is
7170 * working, thus we should return OK. */
7171 if (recursive)
7172 return OK;
7173
7174 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007175 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007176 if (buf != NULL)
7177 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007178 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007179 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007180 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007181#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007182 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007183#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007184 }
7185 recursive = FALSE;
7186
7187 return retval;
7188}
7189
7190/*
7191 * Handle the ":highlight .." command.
7192 * When using ":hi clear" this is called recursively for each group with
7193 * "forceit" and "init" both TRUE.
7194 */
7195 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007196do_highlight(
7197 char_u *line,
7198 int forceit,
7199 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007200{
7201 char_u *name_end;
7202 char_u *p;
7203 char_u *linep;
7204 char_u *key_start;
7205 char_u *arg_start;
7206 char_u *key = NULL, *arg = NULL;
7207 long i;
7208 int off;
7209 int len;
7210 int attr;
7211 int id;
7212 int idx;
7213 int dodefault = FALSE;
7214 int doclear = FALSE;
7215 int dolink = FALSE;
7216 int error = FALSE;
7217 int color;
7218 int is_normal_group = FALSE; /* "Normal" group */
7219#ifdef FEAT_GUI_X11
7220 int is_menu_group = FALSE; /* "Menu" group */
7221 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7222 int is_tooltip_group = FALSE; /* "Tooltip" group */
7223 int do_colors = FALSE; /* need to update colors? */
7224#else
7225# define is_menu_group 0
7226# define is_tooltip_group 0
7227#endif
7228
7229 /*
7230 * If no argument, list current highlighting.
7231 */
7232 if (ends_excmd(*line))
7233 {
7234 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7235 /* TODO: only call when the group has attributes set */
7236 highlight_list_one((int)i);
7237 return;
7238 }
7239
7240 /*
7241 * Isolate the name.
7242 */
7243 name_end = skiptowhite(line);
7244 linep = skipwhite(name_end);
7245
7246 /*
7247 * Check for "default" argument.
7248 */
7249 if (STRNCMP(line, "default", name_end - line) == 0)
7250 {
7251 dodefault = TRUE;
7252 line = linep;
7253 name_end = skiptowhite(line);
7254 linep = skipwhite(name_end);
7255 }
7256
7257 /*
7258 * Check for "clear" or "link" argument.
7259 */
7260 if (STRNCMP(line, "clear", name_end - line) == 0)
7261 doclear = TRUE;
7262 if (STRNCMP(line, "link", name_end - line) == 0)
7263 dolink = TRUE;
7264
7265 /*
7266 * ":highlight {group-name}": list highlighting for one group.
7267 */
7268 if (!doclear && !dolink && ends_excmd(*linep))
7269 {
7270 id = syn_namen2id(line, (int)(name_end - line));
7271 if (id == 0)
7272 EMSG2(_("E411: highlight group not found: %s"), line);
7273 else
7274 highlight_list_one(id);
7275 return;
7276 }
7277
7278 /*
7279 * Handle ":highlight link {from} {to}" command.
7280 */
7281 if (dolink)
7282 {
7283 char_u *from_start = linep;
7284 char_u *from_end;
7285 char_u *to_start;
7286 char_u *to_end;
7287 int from_id;
7288 int to_id;
7289
7290 from_end = skiptowhite(from_start);
7291 to_start = skipwhite(from_end);
7292 to_end = skiptowhite(to_start);
7293
7294 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7295 {
7296 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7297 from_start);
7298 return;
7299 }
7300
7301 if (!ends_excmd(*skipwhite(to_end)))
7302 {
7303 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7304 return;
7305 }
7306
7307 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7308 if (STRNCMP(to_start, "NONE", 4) == 0)
7309 to_id = 0;
7310 else
7311 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7312
7313 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7314 {
7315 /*
7316 * Don't allow a link when there already is some highlighting
7317 * for the group, unless '!' is used
7318 */
7319 if (to_id > 0 && !forceit && !init
7320 && hl_has_settings(from_id - 1, dodefault))
7321 {
7322 if (sourcing_name == NULL && !dodefault)
7323 EMSG(_("E414: group has settings, highlight link ignored"));
7324 }
7325 else
7326 {
7327 if (!init)
7328 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7329 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007330#ifdef FEAT_EVAL
7331 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7332#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01007333 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007334 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007335 }
7336 }
7337
7338 /* Only call highlight_changed() once, after sourcing a syntax file */
7339 need_highlight_changed = TRUE;
7340
7341 return;
7342 }
7343
7344 if (doclear)
7345 {
7346 /*
7347 * ":highlight clear [group]" command.
7348 */
7349 line = linep;
7350 if (ends_excmd(*line))
7351 {
7352#ifdef FEAT_GUI
7353 /* First, we do not destroy the old values, but allocate the new
7354 * ones and update the display. THEN we destroy the old values.
7355 * If we destroy the old values first, then the old values
7356 * (such as GuiFont's or GuiFontset's) will still be displayed but
7357 * invalid because they were free'd.
7358 */
7359 if (gui.in_use)
7360 {
7361# ifdef FEAT_BEVAL_TIP
7362 gui_init_tooltip_font();
7363# endif
7364# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7365 gui_init_menu_font();
7366# endif
7367 }
7368# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7369 gui_mch_def_colors();
7370# endif
7371# ifdef FEAT_GUI_X11
7372# ifdef FEAT_MENU
7373
7374 /* This only needs to be done when there is no Menu highlight
7375 * group defined by default, which IS currently the case.
7376 */
7377 gui_mch_new_menu_colors();
7378# endif
7379 if (gui.in_use)
7380 {
7381 gui_new_scrollbar_colors();
7382# ifdef FEAT_BEVAL
7383 gui_mch_new_tooltip_colors();
7384# endif
7385# ifdef FEAT_MENU
7386 gui_mch_new_menu_font();
7387# endif
7388 }
7389# endif
7390
7391 /* Ok, we're done allocating the new default graphics items.
7392 * The screen should already be refreshed at this point.
7393 * It is now Ok to clear out the old data.
7394 */
7395#endif
7396#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007397 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007398#endif
7399 restore_cterm_colors();
7400
7401 /*
7402 * Clear all default highlight groups and load the defaults.
7403 */
7404 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7405 highlight_clear(idx);
7406 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007407#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007408 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007409 highlight_gui_started();
7410#endif
7411 highlight_changed();
7412 redraw_later_clear();
7413 return;
7414 }
7415 name_end = skiptowhite(line);
7416 linep = skipwhite(name_end);
7417 }
7418
7419 /*
7420 * Find the group name in the table. If it does not exist yet, add it.
7421 */
7422 id = syn_check_group(line, (int)(name_end - line));
7423 if (id == 0) /* failed (out of memory) */
7424 return;
7425 idx = id - 1; /* index is ID minus one */
7426
7427 /* Return if "default" was used and the group already has settings. */
7428 if (dodefault && hl_has_settings(idx, TRUE))
7429 return;
7430
7431 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7432 is_normal_group = TRUE;
7433#ifdef FEAT_GUI_X11
7434 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7435 is_menu_group = TRUE;
7436 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7437 is_scrollbar_group = TRUE;
7438 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7439 is_tooltip_group = TRUE;
7440#endif
7441
7442 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7443 if (doclear || (forceit && init))
7444 {
7445 highlight_clear(idx);
7446 if (!doclear)
7447 HL_TABLE()[idx].sg_set = 0;
7448 }
7449
7450 if (!doclear)
7451 while (!ends_excmd(*linep))
7452 {
7453 key_start = linep;
7454 if (*linep == '=')
7455 {
7456 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7457 error = TRUE;
7458 break;
7459 }
7460
7461 /*
7462 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7463 * "guibg").
7464 */
7465 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7466 ++linep;
7467 vim_free(key);
7468 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7469 if (key == NULL)
7470 {
7471 error = TRUE;
7472 break;
7473 }
7474 linep = skipwhite(linep);
7475
7476 if (STRCMP(key, "NONE") == 0)
7477 {
7478 if (!init || HL_TABLE()[idx].sg_set == 0)
7479 {
7480 if (!init)
7481 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7482 highlight_clear(idx);
7483 }
7484 continue;
7485 }
7486
7487 /*
7488 * Check for the equal sign.
7489 */
7490 if (*linep != '=')
7491 {
7492 EMSG2(_("E416: missing equal sign: %s"), key_start);
7493 error = TRUE;
7494 break;
7495 }
7496 ++linep;
7497
7498 /*
7499 * Isolate the argument.
7500 */
7501 linep = skipwhite(linep);
7502 if (*linep == '\'') /* guifg='color name' */
7503 {
7504 arg_start = ++linep;
7505 linep = vim_strchr(linep, '\'');
7506 if (linep == NULL)
7507 {
7508 EMSG2(_(e_invarg2), key_start);
7509 error = TRUE;
7510 break;
7511 }
7512 }
7513 else
7514 {
7515 arg_start = linep;
7516 linep = skiptowhite(linep);
7517 }
7518 if (linep == arg_start)
7519 {
7520 EMSG2(_("E417: missing argument: %s"), key_start);
7521 error = TRUE;
7522 break;
7523 }
7524 vim_free(arg);
7525 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7526 if (arg == NULL)
7527 {
7528 error = TRUE;
7529 break;
7530 }
7531 if (*linep == '\'')
7532 ++linep;
7533
7534 /*
7535 * Store the argument.
7536 */
7537 if ( STRCMP(key, "TERM") == 0
7538 || STRCMP(key, "CTERM") == 0
7539 || STRCMP(key, "GUI") == 0)
7540 {
7541 attr = 0;
7542 off = 0;
7543 while (arg[off] != NUL)
7544 {
7545 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7546 {
7547 len = (int)STRLEN(hl_name_table[i]);
7548 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7549 {
7550 attr |= hl_attr_table[i];
7551 off += len;
7552 break;
7553 }
7554 }
7555 if (i < 0)
7556 {
7557 EMSG2(_("E418: Illegal value: %s"), arg);
7558 error = TRUE;
7559 break;
7560 }
7561 if (arg[off] == ',') /* another one follows */
7562 ++off;
7563 }
7564 if (error)
7565 break;
7566 if (*key == 'T')
7567 {
7568 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7569 {
7570 if (!init)
7571 HL_TABLE()[idx].sg_set |= SG_TERM;
7572 HL_TABLE()[idx].sg_term = attr;
7573 }
7574 }
7575 else if (*key == 'C')
7576 {
7577 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7578 {
7579 if (!init)
7580 HL_TABLE()[idx].sg_set |= SG_CTERM;
7581 HL_TABLE()[idx].sg_cterm = attr;
7582 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7583 }
7584 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007585#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007586 else
7587 {
7588 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7589 {
7590 if (!init)
7591 HL_TABLE()[idx].sg_set |= SG_GUI;
7592 HL_TABLE()[idx].sg_gui = attr;
7593 }
7594 }
7595#endif
7596 }
7597 else if (STRCMP(key, "FONT") == 0)
7598 {
7599 /* in non-GUI fonts are simply ignored */
7600#ifdef FEAT_GUI
7601 if (!gui.shell_created)
7602 {
7603 /* GUI not started yet, always accept the name. */
7604 vim_free(HL_TABLE()[idx].sg_font_name);
7605 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7606 }
7607 else
7608 {
7609 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7610# ifdef FEAT_XFONTSET
7611 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7612# endif
7613 /* First, save the current font/fontset.
7614 * Then try to allocate the font/fontset.
7615 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7616 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7617 */
7618
7619 HL_TABLE()[idx].sg_font = NOFONT;
7620# ifdef FEAT_XFONTSET
7621 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7622# endif
7623 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007624 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007625
7626# ifdef FEAT_XFONTSET
7627 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7628 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007629 /* New fontset was accepted. Free the old one, if there
7630 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007631 gui_mch_free_fontset(temp_sg_fontset);
7632 vim_free(HL_TABLE()[idx].sg_font_name);
7633 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7634 }
7635 else
7636 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7637# endif
7638 if (HL_TABLE()[idx].sg_font != NOFONT)
7639 {
7640 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007641 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007642 gui_mch_free_font(temp_sg_font);
7643 vim_free(HL_TABLE()[idx].sg_font_name);
7644 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7645 }
7646 else
7647 HL_TABLE()[idx].sg_font = temp_sg_font;
7648 }
7649#endif
7650 }
7651 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7652 {
7653 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7654 {
7655 if (!init)
7656 HL_TABLE()[idx].sg_set |= SG_CTERM;
7657
7658 /* When setting the foreground color, and previously the "bold"
7659 * flag was set for a light color, reset it now */
7660 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7661 {
7662 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7663 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7664 }
7665
7666 if (VIM_ISDIGIT(*arg))
7667 color = atoi((char *)arg);
7668 else if (STRICMP(arg, "fg") == 0)
7669 {
7670 if (cterm_normal_fg_color)
7671 color = cterm_normal_fg_color - 1;
7672 else
7673 {
7674 EMSG(_("E419: FG color unknown"));
7675 error = TRUE;
7676 break;
7677 }
7678 }
7679 else if (STRICMP(arg, "bg") == 0)
7680 {
7681 if (cterm_normal_bg_color > 0)
7682 color = cterm_normal_bg_color - 1;
7683 else
7684 {
7685 EMSG(_("E420: BG color unknown"));
7686 error = TRUE;
7687 break;
7688 }
7689 }
7690 else
7691 {
7692 static char *(color_names[28]) = {
7693 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7694 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7695 "Gray", "Grey",
7696 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7697 "Blue", "LightBlue", "Green", "LightGreen",
7698 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7699 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7700 static int color_numbers_16[28] = {0, 1, 2, 3,
7701 4, 5, 6, 6,
7702 7, 7,
7703 7, 7, 8, 8,
7704 9, 9, 10, 10,
7705 11, 11, 12, 12, 13,
7706 13, 14, 14, 15, -1};
7707 /* for xterm with 88 colors... */
7708 static int color_numbers_88[28] = {0, 4, 2, 6,
7709 1, 5, 32, 72,
7710 84, 84,
7711 7, 7, 82, 82,
7712 12, 43, 10, 61,
7713 14, 63, 9, 74, 13,
7714 75, 11, 78, 15, -1};
7715 /* for xterm with 256 colors... */
7716 static int color_numbers_256[28] = {0, 4, 2, 6,
7717 1, 5, 130, 130,
7718 248, 248,
7719 7, 7, 242, 242,
7720 12, 81, 10, 121,
7721 14, 159, 9, 224, 13,
7722 225, 11, 229, 15, -1};
7723 /* for terminals with less than 16 colors... */
7724 static int color_numbers_8[28] = {0, 4, 2, 6,
7725 1, 5, 3, 3,
7726 7, 7,
7727 7, 7, 0+8, 0+8,
7728 4+8, 4+8, 2+8, 2+8,
7729 6+8, 6+8, 1+8, 1+8, 5+8,
7730 5+8, 3+8, 3+8, 7+8, -1};
7731#if defined(__QNXNTO__)
7732 static int *color_numbers_8_qansi = color_numbers_8;
7733 /* On qnx, the 8 & 16 color arrays are the same */
7734 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7735 color_numbers_8_qansi = color_numbers_16;
7736#endif
7737
7738 /* reduce calls to STRICMP a bit, it can be slow */
7739 off = TOUPPER_ASC(*arg);
7740 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7741 if (off == color_names[i][0]
7742 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7743 break;
7744 if (i < 0)
7745 {
7746 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7747 error = TRUE;
7748 break;
7749 }
7750
7751 /* Use the _16 table to check if its a valid color name. */
7752 color = color_numbers_16[i];
7753 if (color >= 0)
7754 {
7755 if (t_colors == 8)
7756 {
7757 /* t_Co is 8: use the 8 colors table */
7758#if defined(__QNXNTO__)
7759 color = color_numbers_8_qansi[i];
7760#else
7761 color = color_numbers_8[i];
7762#endif
7763 if (key[5] == 'F')
7764 {
7765 /* set/reset bold attribute to get light foreground
7766 * colors (on some terminals, e.g. "linux") */
7767 if (color & 8)
7768 {
7769 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7770 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7771 }
7772 else
7773 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7774 }
7775 color &= 7; /* truncate to 8 colors */
7776 }
7777 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007778 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779 {
7780 /*
7781 * Guess: if the termcap entry ends in 'm', it is
7782 * probably an xterm-like terminal. Use the changed
7783 * order for colors.
7784 */
7785 if (*T_CAF != NUL)
7786 p = T_CAF;
7787 else
7788 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007789 if (*p != NUL && (t_colors > 256
7790 || *(p + STRLEN(p) - 1) == 'm'))
7791 {
7792 if (t_colors == 88)
7793 color = color_numbers_88[i];
7794 else if (t_colors >= 256)
7795 color = color_numbers_256[i];
7796 else
7797 color = color_numbers_8[i];
7798 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007799 }
7800 }
7801 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007802 /* Add one to the argument, to avoid zero. Zero is used for
7803 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804 if (key[5] == 'F')
7805 {
7806 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7807 if (is_normal_group)
7808 {
7809 cterm_normal_fg_color = color + 1;
7810 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7811#ifdef FEAT_GUI
7812 /* Don't do this if the GUI is used. */
7813 if (!gui.in_use && !gui.starting)
7814#endif
7815 {
7816 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007817 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818 term_fg_color(color);
7819 }
7820 }
7821 }
7822 else
7823 {
7824 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7825 if (is_normal_group)
7826 {
7827 cterm_normal_bg_color = color + 1;
7828#ifdef FEAT_GUI
7829 /* Don't mess with 'background' if the GUI is used. */
7830 if (!gui.in_use && !gui.starting)
7831#endif
7832 {
7833 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007834 if (color >= 0)
7835 {
7836 if (termcap_active)
7837 term_bg_color(color);
7838 if (t_colors < 16)
7839 i = (color == 0 || color == 4);
7840 else
7841 i = (color < 7 || color == 8);
7842 /* Set the 'background' option if the value is
7843 * wrong. */
7844 if (i != (*p_bg == 'd'))
7845 set_option_value((char_u *)"bg", 0L,
7846 i ? (char_u *)"dark"
7847 : (char_u *)"light", 0);
7848 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007849 }
7850 }
7851 }
7852 }
7853 }
7854 else if (STRCMP(key, "GUIFG") == 0)
7855 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007856#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007857 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007859 if (!init)
7860 HL_TABLE()[idx].sg_set |= SG_GUI;
7861
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007862# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007863 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007864 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007865 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007866 {
7867 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007868# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007869 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7870 if (STRCMP(arg, "NONE"))
7871 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7872 else
7873 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007874# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007875# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007876 if (is_menu_group)
7877 gui.menu_fg_pixel = i;
7878 if (is_scrollbar_group)
7879 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007880# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007881 if (is_tooltip_group)
7882 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007883# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007884 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007885# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007886 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007887# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007888 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007889#endif
7890 }
7891 else if (STRCMP(key, "GUIBG") == 0)
7892 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007893#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007894 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007895 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007896 if (!init)
7897 HL_TABLE()[idx].sg_set |= SG_GUI;
7898
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007899# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007900 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007901 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007902 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007903 {
7904 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007905# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007906 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7907 if (STRCMP(arg, "NONE") != 0)
7908 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7909 else
7910 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007911# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007912# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007913 if (is_menu_group)
7914 gui.menu_bg_pixel = i;
7915 if (is_scrollbar_group)
7916 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007917# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007918 if (is_tooltip_group)
7919 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007920# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007921 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007922# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007923 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007924# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007925 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007926#endif
7927 }
7928 else if (STRCMP(key, "GUISP") == 0)
7929 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007930#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007931 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7932 {
7933 if (!init)
7934 HL_TABLE()[idx].sg_set |= SG_GUI;
7935
Bram Moolenaar61623362010-07-14 22:04:22 +02007936# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007937 i = color_name2handle(arg);
7938 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7939 {
7940 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007941# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007942 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7943 if (STRCMP(arg, "NONE") != 0)
7944 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7945 else
7946 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007947# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007948 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007949# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007950 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007951#endif
7952 }
7953 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7954 {
7955 char_u buf[100];
7956 char_u *tname;
7957
7958 if (!init)
7959 HL_TABLE()[idx].sg_set |= SG_TERM;
7960
7961 /*
7962 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007963 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007964 */
7965 if (STRNCMP(arg, "t_", 2) == 0)
7966 {
7967 off = 0;
7968 buf[0] = 0;
7969 while (arg[off] != NUL)
7970 {
7971 /* Isolate one termcap name */
7972 for (len = 0; arg[off + len] &&
7973 arg[off + len] != ','; ++len)
7974 ;
7975 tname = vim_strnsave(arg + off, len);
7976 if (tname == NULL) /* out of memory */
7977 {
7978 error = TRUE;
7979 break;
7980 }
7981 /* lookup the escape sequence for the item */
7982 p = get_term_code(tname);
7983 vim_free(tname);
7984 if (p == NULL) /* ignore non-existing things */
7985 p = (char_u *)"";
7986
7987 /* Append it to the already found stuff */
7988 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7989 {
7990 EMSG2(_("E422: terminal code too long: %s"), arg);
7991 error = TRUE;
7992 break;
7993 }
7994 STRCAT(buf, p);
7995
7996 /* Advance to the next item */
7997 off += len;
7998 if (arg[off] == ',') /* another one follows */
7999 ++off;
8000 }
8001 }
8002 else
8003 {
8004 /*
8005 * Copy characters from arg[] to buf[], translating <> codes.
8006 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008007 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008008 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008009 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008010 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008011 off += len;
8012 else /* copy as normal char */
8013 buf[off++] = *p++;
8014 }
8015 buf[off] = NUL;
8016 }
8017 if (error)
8018 break;
8019
8020 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8021 p = NULL;
8022 else
8023 p = vim_strsave(buf);
8024 if (key[2] == 'A')
8025 {
8026 vim_free(HL_TABLE()[idx].sg_start);
8027 HL_TABLE()[idx].sg_start = p;
8028 }
8029 else
8030 {
8031 vim_free(HL_TABLE()[idx].sg_stop);
8032 HL_TABLE()[idx].sg_stop = p;
8033 }
8034 }
8035 else
8036 {
8037 EMSG2(_("E423: Illegal argument: %s"), key_start);
8038 error = TRUE;
8039 break;
8040 }
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008041 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008042
8043 /*
8044 * When highlighting has been given for a group, don't link it.
8045 */
8046 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8047 HL_TABLE()[idx].sg_link = 0;
8048
8049 /*
8050 * Continue with next argument.
8051 */
8052 linep = skipwhite(linep);
8053 }
8054
8055 /*
8056 * If there is an error, and it's a new entry, remove it from the table.
8057 */
8058 if (error && idx == highlight_ga.ga_len)
8059 syn_unadd_group();
8060 else
8061 {
8062 if (is_normal_group)
8063 {
8064 HL_TABLE()[idx].sg_term_attr = 0;
8065 HL_TABLE()[idx].sg_cterm_attr = 0;
8066#ifdef FEAT_GUI
8067 HL_TABLE()[idx].sg_gui_attr = 0;
8068 /*
8069 * Need to update all groups, because they might be using "bg"
8070 * and/or "fg", which have been changed now.
8071 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008072#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008073#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008074 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008075 highlight_gui_started();
8076#endif
8077 }
8078#ifdef FEAT_GUI_X11
8079# ifdef FEAT_MENU
8080 else if (is_menu_group)
8081 {
8082 if (gui.in_use && do_colors)
8083 gui_mch_new_menu_colors();
8084 }
8085# endif
8086 else if (is_scrollbar_group)
8087 {
8088 if (gui.in_use && do_colors)
8089 gui_new_scrollbar_colors();
8090 }
8091# ifdef FEAT_BEVAL
8092 else if (is_tooltip_group)
8093 {
8094 if (gui.in_use && do_colors)
8095 gui_mch_new_tooltip_colors();
8096 }
8097# endif
8098#endif
8099 else
8100 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008101#ifdef FEAT_EVAL
8102 HL_TABLE()[idx].sg_scriptID = current_SID;
8103#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008104 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008105 }
8106 vim_free(key);
8107 vim_free(arg);
8108
8109 /* Only call highlight_changed() once, after sourcing a syntax file */
8110 need_highlight_changed = TRUE;
8111}
8112
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008113#if defined(EXITFREE) || defined(PROTO)
8114 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008115free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008116{
8117 int i;
8118
8119 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008120 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008121 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008122 vim_free(HL_TABLE()[i].sg_name);
8123 vim_free(HL_TABLE()[i].sg_name_u);
8124 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008125 ga_clear(&highlight_ga);
8126}
8127#endif
8128
Bram Moolenaar071d4272004-06-13 20:20:40 +00008129/*
8130 * Reset the cterm colors to what they were before Vim was started, if
8131 * possible. Otherwise reset them to zero.
8132 */
8133 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008134restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008135{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008136#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008137 /* Since t_me has been set, this probably means that the user
8138 * wants to use this as default colors. Need to reset default
8139 * background/foreground colors. */
8140 mch_set_normal_colors();
8141#else
8142 cterm_normal_fg_color = 0;
8143 cterm_normal_fg_bold = 0;
8144 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008145# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008146 cterm_normal_fg_gui_color = INVALCOLOR;
8147 cterm_normal_bg_gui_color = INVALCOLOR;
8148# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149#endif
8150}
8151
8152/*
8153 * Return TRUE if highlight group "idx" has any settings.
8154 * When "check_link" is TRUE also check for an existing link.
8155 */
8156 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008157hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008158{
8159 return ( HL_TABLE()[idx].sg_term_attr != 0
8160 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008161 || HL_TABLE()[idx].sg_cterm_fg != 0
8162 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008163#ifdef FEAT_GUI
8164 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008165 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8166 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8167 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8168 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169#endif
8170 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8171}
8172
8173/*
8174 * Clear highlighting for one group.
8175 */
8176 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008177highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008178{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008179 HL_TABLE()[idx].sg_cleared = TRUE;
8180
Bram Moolenaar071d4272004-06-13 20:20:40 +00008181 HL_TABLE()[idx].sg_term = 0;
8182 vim_free(HL_TABLE()[idx].sg_start);
8183 HL_TABLE()[idx].sg_start = NULL;
8184 vim_free(HL_TABLE()[idx].sg_stop);
8185 HL_TABLE()[idx].sg_stop = NULL;
8186 HL_TABLE()[idx].sg_term_attr = 0;
8187 HL_TABLE()[idx].sg_cterm = 0;
8188 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8189 HL_TABLE()[idx].sg_cterm_fg = 0;
8190 HL_TABLE()[idx].sg_cterm_bg = 0;
8191 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008192#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008194 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8195 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008196 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8197 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008198 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8199 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008200#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008201#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008202 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8203 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008204#endif
8205#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008206 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008207 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8208 HL_TABLE()[idx].sg_font = NOFONT;
8209# ifdef FEAT_XFONTSET
8210 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8211 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8212# endif
8213 vim_free(HL_TABLE()[idx].sg_font_name);
8214 HL_TABLE()[idx].sg_font_name = NULL;
8215 HL_TABLE()[idx].sg_gui_attr = 0;
8216#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008217#ifdef FEAT_EVAL
8218 /* Clear the script ID only when there is no link, since that is not
8219 * cleared. */
8220 if (HL_TABLE()[idx].sg_link == 0)
8221 HL_TABLE()[idx].sg_scriptID = 0;
8222#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223}
8224
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008225#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226/*
8227 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008228 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008229 * "Tooltip" colors.
8230 */
8231 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008232set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008234#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008235# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008236 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008239 if (set_group_colors((char_u *)"Normal",
8240 &gui.norm_pixel, &gui.back_pixel,
8241 FALSE, TRUE, FALSE))
8242 {
8243 gui_mch_new_colors();
8244 must_redraw = CLEAR;
8245 }
8246# ifdef FEAT_GUI_X11
8247 if (set_group_colors((char_u *)"Menu",
8248 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8249 TRUE, FALSE, FALSE))
8250 {
8251# ifdef FEAT_MENU
8252 gui_mch_new_menu_colors();
8253# endif
8254 must_redraw = CLEAR;
8255 }
8256# ifdef FEAT_BEVAL
8257 if (set_group_colors((char_u *)"Tooltip",
8258 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8259 FALSE, FALSE, TRUE))
8260 {
8261# ifdef FEAT_TOOLBAR
8262 gui_mch_new_tooltip_colors();
8263# endif
8264 must_redraw = CLEAR;
8265 }
8266# endif
8267 if (set_group_colors((char_u *)"Scrollbar",
8268 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8269 FALSE, FALSE, FALSE))
8270 {
8271 gui_new_scrollbar_colors();
8272 must_redraw = CLEAR;
8273 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008275 }
8276#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008277#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008278# ifdef FEAT_GUI
8279 else
8280# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008281 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008282 int idx;
8283
8284 idx = syn_name2id((char_u *)"Normal") - 1;
8285 if (idx >= 0)
8286 {
8287 gui_do_one_color(idx, FALSE, FALSE);
8288
8289 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8290 {
8291 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8292 must_redraw = CLEAR;
8293 }
8294 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8295 {
8296 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8297 must_redraw = CLEAR;
8298 }
8299 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008300 }
8301#endif
8302}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008303#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008304
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008305#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008306/*
8307 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8308 */
8309 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008310set_group_colors(
8311 char_u *name,
8312 guicolor_T *fgp,
8313 guicolor_T *bgp,
8314 int do_menu,
8315 int use_norm,
8316 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008317{
8318 int idx;
8319
8320 idx = syn_name2id(name) - 1;
8321 if (idx >= 0)
8322 {
8323 gui_do_one_color(idx, do_menu, do_tooltip);
8324
8325 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8326 *fgp = HL_TABLE()[idx].sg_gui_fg;
8327 else if (use_norm)
8328 *fgp = gui.def_norm_pixel;
8329 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8330 *bgp = HL_TABLE()[idx].sg_gui_bg;
8331 else if (use_norm)
8332 *bgp = gui.def_back_pixel;
8333 return TRUE;
8334 }
8335 return FALSE;
8336}
8337
8338/*
8339 * Get the font of the "Normal" group.
8340 * Returns "" when it's not found or not set.
8341 */
8342 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008343hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008344{
8345 int id;
8346 char_u *s;
8347
8348 id = syn_name2id((char_u *)"Normal");
8349 if (id > 0)
8350 {
8351 s = HL_TABLE()[id - 1].sg_font_name;
8352 if (s != NULL)
8353 return s;
8354 }
8355 return (char_u *)"";
8356}
8357
8358/*
8359 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8360 * actually chosen to be used.
8361 */
8362 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008363hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364{
8365 int id;
8366
8367 id = syn_name2id((char_u *)"Normal");
8368 if (id > 0)
8369 {
8370 vim_free(HL_TABLE()[id - 1].sg_font_name);
8371 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8372 }
8373}
8374
8375/*
8376 * Set background color for "Normal" group. Called by gui_set_bg_color()
8377 * when the color is known.
8378 */
8379 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008380hl_set_bg_color_name(
8381 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008382{
8383 int id;
8384
8385 if (name != NULL)
8386 {
8387 id = syn_name2id((char_u *)"Normal");
8388 if (id > 0)
8389 {
8390 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8391 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8392 }
8393 }
8394}
8395
8396/*
8397 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8398 * when the color is known.
8399 */
8400 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008401hl_set_fg_color_name(
8402 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008403{
8404 int id;
8405
8406 if (name != NULL)
8407 {
8408 id = syn_name2id((char_u *)"Normal");
8409 if (id > 0)
8410 {
8411 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8412 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8413 }
8414 }
8415}
8416
8417/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008418 * Return the handle for a font name.
8419 * Returns NOFONT when failed.
8420 */
8421 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008422font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008423{
8424 if (STRCMP(name, "NONE") == 0)
8425 return NOFONT;
8426
8427 return gui_mch_get_font(name, TRUE);
8428}
8429
8430# ifdef FEAT_XFONTSET
8431/*
8432 * Return the handle for a fontset name.
8433 * Returns NOFONTSET when failed.
8434 */
8435 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008436fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008437{
8438 if (STRCMP(name, "NONE") == 0)
8439 return NOFONTSET;
8440
8441 return gui_mch_get_fontset(name, TRUE, fixed_width);
8442}
8443# endif
8444
8445/*
8446 * Get the font or fontset for one highlight group.
8447 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008448 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008449hl_do_font(
8450 int idx,
8451 char_u *arg,
8452 int do_normal, /* set normal font */
8453 int do_menu UNUSED, /* set menu font */
8454 int do_tooltip UNUSED, /* set tooltip font */
8455 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008456{
8457# ifdef FEAT_XFONTSET
8458 /* If 'guifontset' is not empty, first try using the name as a
8459 * fontset. If that doesn't work, use it as a font name. */
8460 if (*p_guifontset != NUL
8461# ifdef FONTSET_ALWAYS
8462 || do_menu
8463# endif
8464# ifdef FEAT_BEVAL_TIP
8465 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8466 || do_tooltip
8467# endif
8468 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008469 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008470 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008471 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008472 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8473# ifdef FONTSET_ALWAYS
8474 || do_menu
8475# endif
8476# ifdef FEAT_BEVAL_TIP
8477 || do_tooltip
8478# endif
8479 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008480 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008481 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8482 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008483 /* If it worked and it's the Normal group, use it as the normal
8484 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008485 if (do_normal)
8486 gui_init_font(arg, TRUE);
8487# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8488 if (do_menu)
8489 {
8490# ifdef FONTSET_ALWAYS
8491 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8492# else
8493 /* YIKES! This is a bug waiting to crash the program */
8494 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8495# endif
8496 gui_mch_new_menu_font();
8497 }
8498# ifdef FEAT_BEVAL
8499 if (do_tooltip)
8500 {
8501 /* The Athena widget set cannot currently handle switching between
8502 * displaying a single font and a fontset.
8503 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008504 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505 * XFontStruct is used.
8506 */
8507 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8508 gui_mch_new_tooltip_font();
8509 }
8510# endif
8511# endif
8512 }
8513 else
8514# endif
8515 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008516 if (free_font)
8517 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8519 /* If it worked and it's the Normal group, use it as the
8520 * normal font. Same for the Menu group. */
8521 if (HL_TABLE()[idx].sg_font != NOFONT)
8522 {
8523 if (do_normal)
8524 gui_init_font(arg, FALSE);
8525#ifndef FONTSET_ALWAYS
8526# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8527 if (do_menu)
8528 {
8529 gui.menu_font = HL_TABLE()[idx].sg_font;
8530 gui_mch_new_menu_font();
8531 }
8532# endif
8533#endif
8534 }
8535 }
8536}
8537
8538#endif /* FEAT_GUI */
8539
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008540#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008541/*
8542 * Return the handle for a color name.
8543 * Returns INVALCOLOR when failed.
8544 */
8545 static guicolor_T
8546color_name2handle(char_u *name)
8547{
8548 if (STRCMP(name, "NONE") == 0)
8549 return INVALCOLOR;
8550
8551 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8552 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008553#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008554 if (gui.in_use)
8555#endif
8556#ifdef FEAT_GUI
8557 return gui.norm_pixel;
8558#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008559#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008560 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008561 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008562 /* Guess that the foreground is black or white. */
8563 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008564#endif
8565 }
8566 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8567 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008568#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008569 if (gui.in_use)
8570#endif
8571#ifdef FEAT_GUI
8572 return gui.back_pixel;
8573#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008574#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008575 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008576 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008577 /* Guess that the background is white or black. */
8578 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008579#endif
8580 }
8581
8582 return GUI_GET_COLOR(name);
8583}
8584#endif
8585
Bram Moolenaar071d4272004-06-13 20:20:40 +00008586/*
8587 * Table with the specifications for an attribute number.
8588 * Note that this table is used by ALL buffers. This is required because the
8589 * GUI can redraw at any time for any buffer.
8590 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008591static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008592
8593#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8594
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008595static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008596
8597#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8598
8599#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008600static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601
8602#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8603#endif
8604
8605/*
8606 * Return the attr number for a set of colors and font.
8607 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8608 * if the combination is new.
8609 * Return 0 for error (no more room).
8610 */
8611 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008612get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613{
8614 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008615 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008616 static int recursive = FALSE;
8617
8618 /*
8619 * Init the table, in case it wasn't done yet.
8620 */
8621 table->ga_itemsize = sizeof(attrentry_T);
8622 table->ga_growsize = 7;
8623
8624 /*
8625 * Try to find an entry with the same specifications.
8626 */
8627 for (i = 0; i < table->ga_len; ++i)
8628 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008629 taep = &(((attrentry_T *)table->ga_data)[i]);
8630 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008631 && (
8632#ifdef FEAT_GUI
8633 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008634 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8635 && aep->ae_u.gui.bg_color
8636 == taep->ae_u.gui.bg_color
8637 && aep->ae_u.gui.sp_color
8638 == taep->ae_u.gui.sp_color
8639 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008640# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008641 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008642# endif
8643 ))
8644 ||
8645#endif
8646 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008647 && (aep->ae_u.term.start == NULL)
8648 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008649 && (aep->ae_u.term.start == NULL
8650 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008651 taep->ae_u.term.start) == 0)
8652 && (aep->ae_u.term.stop == NULL)
8653 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008654 && (aep->ae_u.term.stop == NULL
8655 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008656 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008657 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008658 && aep->ae_u.cterm.fg_color
8659 == taep->ae_u.cterm.fg_color
8660 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008661 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008662#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008663 && aep->ae_u.cterm.fg_rgb
8664 == taep->ae_u.cterm.fg_rgb
8665 && aep->ae_u.cterm.bg_rgb
8666 == taep->ae_u.cterm.bg_rgb
8667#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008668 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669
8670 return i + ATTR_OFF;
8671 }
8672
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008673 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008674 {
8675 /*
8676 * Running out of attribute entries! remove all attributes, and
8677 * compute new ones for all groups.
8678 * When called recursively, we are really out of numbers.
8679 */
8680 if (recursive)
8681 {
8682 EMSG(_("E424: Too many different highlighting attributes in use"));
8683 return 0;
8684 }
8685 recursive = TRUE;
8686
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008687 clear_hl_tables();
8688
Bram Moolenaar071d4272004-06-13 20:20:40 +00008689 must_redraw = CLEAR;
8690
8691 for (i = 0; i < highlight_ga.ga_len; ++i)
8692 set_hl_attr(i);
8693
8694 recursive = FALSE;
8695 }
8696
8697 /*
8698 * This is a new combination of colors and font, add an entry.
8699 */
8700 if (ga_grow(table, 1) == FAIL)
8701 return 0;
8702
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008703 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8704 vim_memset(taep, 0, sizeof(attrentry_T));
8705 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008706#ifdef FEAT_GUI
8707 if (table == &gui_attr_table)
8708 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008709 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8710 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8711 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8712 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008713# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008714 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008715# endif
8716 }
8717#endif
8718 if (table == &term_attr_table)
8719 {
8720 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008721 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008722 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008723 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008724 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008725 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008726 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008727 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008728 }
8729 else if (table == &cterm_attr_table)
8730 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008731 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8732 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008733#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008734 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8735 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8736#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008737 }
8738 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008739 return (table->ga_len - 1 + ATTR_OFF);
8740}
8741
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008742/*
8743 * Clear all highlight tables.
8744 */
8745 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008746clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008747{
8748 int i;
8749 attrentry_T *taep;
8750
8751#ifdef FEAT_GUI
8752 ga_clear(&gui_attr_table);
8753#endif
8754 for (i = 0; i < term_attr_table.ga_len; ++i)
8755 {
8756 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8757 vim_free(taep->ae_u.term.start);
8758 vim_free(taep->ae_u.term.stop);
8759 }
8760 ga_clear(&term_attr_table);
8761 ga_clear(&cterm_attr_table);
8762}
8763
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008764#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008765/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008766 * Combine special attributes (e.g., for spelling) with other attributes
8767 * (e.g., for syntax highlighting).
8768 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008769 * This creates a new group when required.
8770 * Since we expect there to be few spelling mistakes we don't cache the
8771 * result.
8772 * Return the resulting attributes.
8773 */
8774 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008775hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008776{
8777 attrentry_T *char_aep = NULL;
8778 attrentry_T *spell_aep;
8779 attrentry_T new_en;
8780
8781 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008782 return prim_attr;
8783 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8784 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008785#ifdef FEAT_GUI
8786 if (gui.in_use)
8787 {
8788 if (char_attr > HL_ALL)
8789 char_aep = syn_gui_attr2entry(char_attr);
8790 if (char_aep != NULL)
8791 new_en = *char_aep;
8792 else
8793 {
8794 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008795 new_en.ae_u.gui.fg_color = INVALCOLOR;
8796 new_en.ae_u.gui.bg_color = INVALCOLOR;
8797 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008798 if (char_attr <= HL_ALL)
8799 new_en.ae_attr = char_attr;
8800 }
8801
Bram Moolenaar30abd282005-06-22 22:35:10 +00008802 if (prim_attr <= HL_ALL)
8803 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008804 else
8805 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008806 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008807 if (spell_aep != NULL)
8808 {
8809 new_en.ae_attr |= spell_aep->ae_attr;
8810 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8811 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8812 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8813 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8814 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8815 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8816 if (spell_aep->ae_u.gui.font != NOFONT)
8817 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8818# ifdef FEAT_XFONTSET
8819 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8820 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8821# endif
8822 }
8823 }
8824 return get_attr_entry(&gui_attr_table, &new_en);
8825 }
8826#endif
8827
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008828 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008829 {
8830 if (char_attr > HL_ALL)
8831 char_aep = syn_cterm_attr2entry(char_attr);
8832 if (char_aep != NULL)
8833 new_en = *char_aep;
8834 else
8835 {
8836 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008837#ifdef FEAT_TERMGUICOLORS
8838 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8839 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8840#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008841 if (char_attr <= HL_ALL)
8842 new_en.ae_attr = char_attr;
8843 }
8844
Bram Moolenaar30abd282005-06-22 22:35:10 +00008845 if (prim_attr <= HL_ALL)
8846 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008847 else
8848 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008849 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008850 if (spell_aep != NULL)
8851 {
8852 new_en.ae_attr |= spell_aep->ae_attr;
8853 if (spell_aep->ae_u.cterm.fg_color > 0)
8854 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8855 if (spell_aep->ae_u.cterm.bg_color > 0)
8856 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008857#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008858 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008859 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008860 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008861 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8862#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008863 }
8864 }
8865 return get_attr_entry(&cterm_attr_table, &new_en);
8866 }
8867
8868 if (char_attr > HL_ALL)
8869 char_aep = syn_term_attr2entry(char_attr);
8870 if (char_aep != NULL)
8871 new_en = *char_aep;
8872 else
8873 {
8874 vim_memset(&new_en, 0, sizeof(new_en));
8875 if (char_attr <= HL_ALL)
8876 new_en.ae_attr = char_attr;
8877 }
8878
Bram Moolenaar30abd282005-06-22 22:35:10 +00008879 if (prim_attr <= HL_ALL)
8880 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008881 else
8882 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008883 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008884 if (spell_aep != NULL)
8885 {
8886 new_en.ae_attr |= spell_aep->ae_attr;
8887 if (spell_aep->ae_u.term.start != NULL)
8888 {
8889 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8890 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8891 }
8892 }
8893 }
8894 return get_attr_entry(&term_attr_table, &new_en);
8895}
8896#endif
8897
Bram Moolenaar071d4272004-06-13 20:20:40 +00008898#ifdef FEAT_GUI
8899
8900 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008901syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008902{
8903 attr -= ATTR_OFF;
8904 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8905 return NULL;
8906 return &(GUI_ATTR_ENTRY(attr));
8907}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008908#endif /* FEAT_GUI */
8909
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008910/*
8911 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8912 * Only to be used when "attr" > HL_ALL.
8913 */
8914 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008915syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008916{
8917 attrentry_T *aep;
8918
8919#ifdef FEAT_GUI
8920 if (gui.in_use)
8921 aep = syn_gui_attr2entry(attr);
8922 else
8923#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008924 if (IS_CTERM)
8925 aep = syn_cterm_attr2entry(attr);
8926 else
8927 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008928
8929 if (aep == NULL) /* highlighting not set */
8930 return 0;
8931 return aep->ae_attr;
8932}
8933
8934
Bram Moolenaar071d4272004-06-13 20:20:40 +00008935 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008936syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008937{
8938 attr -= ATTR_OFF;
8939 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8940 return NULL;
8941 return &(TERM_ATTR_ENTRY(attr));
8942}
8943
8944 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008945syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008946{
8947 attr -= ATTR_OFF;
8948 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8949 return NULL;
8950 return &(CTERM_ATTR_ENTRY(attr));
8951}
8952
8953#define LIST_ATTR 1
8954#define LIST_STRING 2
8955#define LIST_INT 3
8956
8957 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008958highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008959{
8960 struct hl_group *sgp;
8961 int didh = FALSE;
8962
8963 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8964
8965 didh = highlight_list_arg(id, didh, LIST_ATTR,
8966 sgp->sg_term, NULL, "term");
8967 didh = highlight_list_arg(id, didh, LIST_STRING,
8968 0, sgp->sg_start, "start");
8969 didh = highlight_list_arg(id, didh, LIST_STRING,
8970 0, sgp->sg_stop, "stop");
8971
8972 didh = highlight_list_arg(id, didh, LIST_ATTR,
8973 sgp->sg_cterm, NULL, "cterm");
8974 didh = highlight_list_arg(id, didh, LIST_INT,
8975 sgp->sg_cterm_fg, NULL, "ctermfg");
8976 didh = highlight_list_arg(id, didh, LIST_INT,
8977 sgp->sg_cterm_bg, NULL, "ctermbg");
8978
Bram Moolenaar61623362010-07-14 22:04:22 +02008979#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008980 didh = highlight_list_arg(id, didh, LIST_ATTR,
8981 sgp->sg_gui, NULL, "gui");
8982 didh = highlight_list_arg(id, didh, LIST_STRING,
8983 0, sgp->sg_gui_fg_name, "guifg");
8984 didh = highlight_list_arg(id, didh, LIST_STRING,
8985 0, sgp->sg_gui_bg_name, "guibg");
8986 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008987 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008988#endif
8989#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008990 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008991 0, sgp->sg_font_name, "font");
8992#endif
8993
Bram Moolenaar661b1822005-07-28 22:36:45 +00008994 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008995 {
8996 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008997 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008998 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8999 msg_putchar(' ');
9000 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9001 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009002
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009003 if (!didh)
9004 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009005#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009006 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009007 last_set_msg(sgp->sg_scriptID);
9008#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009009}
9010
9011 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009012highlight_list_arg(
9013 int id,
9014 int didh,
9015 int type,
9016 int iarg,
9017 char_u *sarg,
9018 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009019{
9020 char_u buf[100];
9021 char_u *ts;
9022 int i;
9023
Bram Moolenaar661b1822005-07-28 22:36:45 +00009024 if (got_int)
9025 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009026 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9027 {
9028 ts = buf;
9029 if (type == LIST_INT)
9030 sprintf((char *)buf, "%d", iarg - 1);
9031 else if (type == LIST_STRING)
9032 ts = sarg;
9033 else /* type == LIST_ATTR */
9034 {
9035 buf[0] = NUL;
9036 for (i = 0; hl_attr_table[i] != 0; ++i)
9037 {
9038 if (iarg & hl_attr_table[i])
9039 {
9040 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009041 vim_strcat(buf, (char_u *)",", 100);
9042 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009043 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9044 }
9045 }
9046 }
9047
9048 (void)syn_list_header(didh,
9049 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9050 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009051 if (!got_int)
9052 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009053 if (*name != NUL)
9054 {
9055 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
9056 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
9057 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009058 msg_outtrans(ts);
9059 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009060 }
9061 return didh;
9062}
9063
9064#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9065/*
9066 * Return "1" if highlight group "id" has attribute "flag".
9067 * Return NULL otherwise.
9068 */
9069 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009070highlight_has_attr(
9071 int id,
9072 int flag,
9073 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009074{
9075 int attr;
9076
9077 if (id <= 0 || id > highlight_ga.ga_len)
9078 return NULL;
9079
Bram Moolenaar61623362010-07-14 22:04:22 +02009080#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009081 if (modec == 'g')
9082 attr = HL_TABLE()[id - 1].sg_gui;
9083 else
9084#endif
9085 if (modec == 'c')
9086 attr = HL_TABLE()[id - 1].sg_cterm;
9087 else
9088 attr = HL_TABLE()[id - 1].sg_term;
9089
9090 if (attr & flag)
9091 return (char_u *)"1";
9092 return NULL;
9093}
9094#endif
9095
9096#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9097/*
9098 * Return color name of highlight group "id".
9099 */
9100 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009101highlight_color(
9102 int id,
9103 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9104 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009105{
9106 static char_u name[20];
9107 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009108 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009109 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009110 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009111
9112 if (id <= 0 || id > highlight_ga.ga_len)
9113 return NULL;
9114
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009115 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009116 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009117 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009118 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009119 font = TRUE;
9120 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009121 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009122 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9123 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009124 if (modec == 'g')
9125 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009126# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009127# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009128 /* return font name */
9129 if (font)
9130 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009131# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009132
Bram Moolenaar071d4272004-06-13 20:20:40 +00009133 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009134 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009135 {
9136 guicolor_T color;
9137 long_u rgb;
9138 static char_u buf[10];
9139
9140 if (fg)
9141 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009142 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009143# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009144 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009145# else
9146 color = INVALCOLOR;
9147# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009148 else
9149 color = HL_TABLE()[id - 1].sg_gui_bg;
9150 if (color == INVALCOLOR)
9151 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009152 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009153 sprintf((char *)buf, "#%02x%02x%02x",
9154 (unsigned)(rgb >> 16),
9155 (unsigned)(rgb >> 8) & 255,
9156 (unsigned)rgb & 255);
9157 return buf;
9158 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009159# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009160 if (fg)
9161 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009162 if (sp)
9163 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009164 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9165 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009166 if (font || sp)
9167 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009168 if (modec == 'c')
9169 {
9170 if (fg)
9171 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9172 else
9173 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009174 if (n < 0)
9175 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009176 sprintf((char *)name, "%d", n);
9177 return name;
9178 }
9179 /* term doesn't have color */
9180 return NULL;
9181}
9182#endif
9183
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009184#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009185 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009186 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009187/*
9188 * Return color name of highlight group "id" as RGB value.
9189 */
9190 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009191highlight_gui_color_rgb(
9192 int id,
9193 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009194{
9195 guicolor_T color;
9196
9197 if (id <= 0 || id > highlight_ga.ga_len)
9198 return 0L;
9199
9200 if (fg)
9201 color = HL_TABLE()[id - 1].sg_gui_fg;
9202 else
9203 color = HL_TABLE()[id - 1].sg_gui_bg;
9204
9205 if (color == INVALCOLOR)
9206 return 0L;
9207
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009208 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009209}
9210#endif
9211
9212/*
9213 * Output the syntax list header.
9214 * Return TRUE when started a new line.
9215 */
9216 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009217syn_list_header(
9218 int did_header, /* did header already */
9219 int outlen, /* length of string that comes */
9220 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009221{
9222 int endcol = 19;
9223 int newline = TRUE;
9224
9225 if (!did_header)
9226 {
9227 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009228 if (got_int)
9229 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009230 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9231 endcol = 15;
9232 }
9233 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009234 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009235 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009236 if (got_int)
9237 return TRUE;
9238 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009239 else
9240 {
9241 if (msg_col >= endcol) /* wrap around is like starting a new line */
9242 newline = FALSE;
9243 }
9244
9245 if (msg_col >= endcol) /* output at least one space */
9246 endcol = msg_col + 1;
9247 if (Columns <= endcol) /* avoid hang for tiny window */
9248 endcol = Columns - 1;
9249
9250 msg_advance(endcol);
9251
9252 /* Show "xxx" with the attributes. */
9253 if (!did_header)
9254 {
9255 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9256 msg_putchar(' ');
9257 }
9258
9259 return newline;
9260}
9261
9262/*
9263 * Set the attribute numbers for a highlight group.
9264 * Called after one of the attributes has changed.
9265 */
9266 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009267set_hl_attr(
9268 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009269{
9270 attrentry_T at_en;
9271 struct hl_group *sgp = HL_TABLE() + idx;
9272
9273 /* The "Normal" group doesn't need an attribute number */
9274 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9275 return;
9276
9277#ifdef FEAT_GUI
9278 /*
9279 * For the GUI mode: If there are other than "normal" highlighting
9280 * attributes, need to allocate an attr number.
9281 */
9282 if (sgp->sg_gui_fg == INVALCOLOR
9283 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009284 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009285 && sgp->sg_font == NOFONT
9286# ifdef FEAT_XFONTSET
9287 && sgp->sg_fontset == NOFONTSET
9288# endif
9289 )
9290 {
9291 sgp->sg_gui_attr = sgp->sg_gui;
9292 }
9293 else
9294 {
9295 at_en.ae_attr = sgp->sg_gui;
9296 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9297 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009298 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009299 at_en.ae_u.gui.font = sgp->sg_font;
9300# ifdef FEAT_XFONTSET
9301 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9302# endif
9303 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9304 }
9305#endif
9306 /*
9307 * For the term mode: If there are other than "normal" highlighting
9308 * attributes, need to allocate an attr number.
9309 */
9310 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9311 sgp->sg_term_attr = sgp->sg_term;
9312 else
9313 {
9314 at_en.ae_attr = sgp->sg_term;
9315 at_en.ae_u.term.start = sgp->sg_start;
9316 at_en.ae_u.term.stop = sgp->sg_stop;
9317 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9318 }
9319
9320 /*
9321 * For the color term mode: If there are other than "normal"
9322 * highlighting attributes, need to allocate an attr number.
9323 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009324 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009325# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009326 && sgp->sg_gui_fg == INVALCOLOR
9327 && sgp->sg_gui_bg == INVALCOLOR
9328# endif
9329 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009330 sgp->sg_cterm_attr = sgp->sg_cterm;
9331 else
9332 {
9333 at_en.ae_attr = sgp->sg_cterm;
9334 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9335 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009336# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009337 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9338 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009339# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009340 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9341 }
9342}
9343
9344/*
9345 * Lookup a highlight group name and return it's ID.
9346 * If it is not found, 0 is returned.
9347 */
9348 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009349syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009350{
9351 int i;
9352 char_u name_u[200];
9353
9354 /* Avoid using stricmp() too much, it's slow on some systems */
9355 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9356 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009357 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009358 vim_strup(name_u);
9359 for (i = highlight_ga.ga_len; --i >= 0; )
9360 if (HL_TABLE()[i].sg_name_u != NULL
9361 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9362 break;
9363 return i + 1;
9364}
9365
9366#if defined(FEAT_EVAL) || defined(PROTO)
9367/*
9368 * Return TRUE if highlight group "name" exists.
9369 */
9370 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009371highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009372{
9373 return (syn_name2id(name) > 0);
9374}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009375
9376# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9377/*
9378 * Return the name of highlight group "id".
9379 * When not a valid ID return an empty string.
9380 */
9381 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009382syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009383{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009384 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009385 return (char_u *)"";
9386 return HL_TABLE()[id - 1].sg_name;
9387}
9388# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009389#endif
9390
9391/*
9392 * Like syn_name2id(), but take a pointer + length argument.
9393 */
9394 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009395syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009396{
9397 char_u *name;
9398 int id = 0;
9399
9400 name = vim_strnsave(linep, len);
9401 if (name != NULL)
9402 {
9403 id = syn_name2id(name);
9404 vim_free(name);
9405 }
9406 return id;
9407}
9408
9409/*
9410 * Find highlight group name in the table and return it's ID.
9411 * The argument is a pointer to the name and the length of the name.
9412 * If it doesn't exist yet, a new entry is created.
9413 * Return 0 for failure.
9414 */
9415 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009416syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009417{
9418 int id;
9419 char_u *name;
9420
9421 name = vim_strnsave(pp, len);
9422 if (name == NULL)
9423 return 0;
9424
9425 id = syn_name2id(name);
9426 if (id == 0) /* doesn't exist yet */
9427 id = syn_add_group(name);
9428 else
9429 vim_free(name);
9430 return id;
9431}
9432
9433/*
9434 * Add new highlight group and return it's ID.
9435 * "name" must be an allocated string, it will be consumed.
9436 * Return 0 for failure.
9437 */
9438 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009439syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009440{
9441 char_u *p;
9442
9443 /* Check that the name is ASCII letters, digits and underscore. */
9444 for (p = name; *p != NUL; ++p)
9445 {
9446 if (!vim_isprintc(*p))
9447 {
9448 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009449 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009450 return 0;
9451 }
9452 else if (!ASCII_ISALNUM(*p) && *p != '_')
9453 {
9454 /* This is an error, but since there previously was no check only
9455 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009456 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009457 MSG(_("W18: Invalid character in group name"));
9458 break;
9459 }
9460 }
9461
9462 /*
9463 * First call for this growarray: init growing array.
9464 */
9465 if (highlight_ga.ga_data == NULL)
9466 {
9467 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9468 highlight_ga.ga_growsize = 10;
9469 }
9470
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009471 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009472 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009473 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009474 vim_free(name);
9475 return 0;
9476 }
9477
Bram Moolenaar071d4272004-06-13 20:20:40 +00009478 /*
9479 * Make room for at least one other syntax_highlight entry.
9480 */
9481 if (ga_grow(&highlight_ga, 1) == FAIL)
9482 {
9483 vim_free(name);
9484 return 0;
9485 }
9486
9487 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9488 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9489 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009490#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009491 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9492 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009493# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009494 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009495# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009496#endif
9497 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009498
9499 return highlight_ga.ga_len; /* ID is index plus one */
9500}
9501
9502/*
9503 * When, just after calling syn_add_group(), an error is discovered, this
9504 * function deletes the new name.
9505 */
9506 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009507syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009508{
9509 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009510 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9511 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9512}
9513
9514/*
9515 * Translate a group ID to highlight attributes.
9516 */
9517 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009518syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009519{
9520 int attr;
9521 struct hl_group *sgp;
9522
9523 hl_id = syn_get_final_id(hl_id);
9524 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9525
9526#ifdef FEAT_GUI
9527 /*
9528 * Only use GUI attr when the GUI is being used.
9529 */
9530 if (gui.in_use)
9531 attr = sgp->sg_gui_attr;
9532 else
9533#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009534 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009535 attr = sgp->sg_cterm_attr;
9536 else
9537 attr = sgp->sg_term_attr;
9538
9539 return attr;
9540}
9541
9542#ifdef FEAT_GUI
9543/*
9544 * Get the GUI colors and attributes for a group ID.
9545 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9546 */
9547 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009548syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009549{
9550 struct hl_group *sgp;
9551
9552 hl_id = syn_get_final_id(hl_id);
9553 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9554
9555 *fgp = sgp->sg_gui_fg;
9556 *bgp = sgp->sg_gui_bg;
9557 return sgp->sg_gui;
9558}
9559#endif
9560
9561/*
9562 * Translate a group ID to the final group ID (following links).
9563 */
9564 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009565syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009566{
9567 int count;
9568 struct hl_group *sgp;
9569
9570 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9571 return 0; /* Can be called from eval!! */
9572
9573 /*
9574 * Follow links until there is no more.
9575 * Look out for loops! Break after 100 links.
9576 */
9577 for (count = 100; --count >= 0; )
9578 {
9579 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9580 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9581 break;
9582 hl_id = sgp->sg_link;
9583 }
9584
9585 return hl_id;
9586}
9587
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009588#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009589/*
9590 * Call this function just after the GUI has started.
9591 * It finds the font and color handles for the highlighting groups.
9592 */
9593 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009594highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009595{
9596 int idx;
9597
9598 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009599# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9600# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009601 if (USE_24BIT)
9602# endif
9603 set_normal_colors();
9604# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009605
9606 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9607 gui_do_one_color(idx, FALSE, FALSE);
9608
9609 highlight_changed();
9610}
9611
9612 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009613gui_do_one_color(
9614 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009615 int do_menu UNUSED, /* TRUE: might set the menu font */
9616 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009617{
9618 int didit = FALSE;
9619
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009620# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009621# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009622 if (gui.in_use)
9623# endif
9624 if (HL_TABLE()[idx].sg_font_name != NULL)
9625 {
9626 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009627 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009628 didit = TRUE;
9629 }
9630# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009631 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9632 {
9633 HL_TABLE()[idx].sg_gui_fg =
9634 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9635 didit = TRUE;
9636 }
9637 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9638 {
9639 HL_TABLE()[idx].sg_gui_bg =
9640 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9641 didit = TRUE;
9642 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009643# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009644 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9645 {
9646 HL_TABLE()[idx].sg_gui_sp =
9647 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9648 didit = TRUE;
9649 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009650# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009651 if (didit) /* need to get a new attr number */
9652 set_hl_attr(idx);
9653}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009654#endif
9655
9656/*
9657 * Translate the 'highlight' option into attributes in highlight_attr[] and
9658 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9659 * corresponding highlights to use on top of HLF_SNC is computed.
9660 * Called only when the 'highlight' option has been changed and upon first
9661 * screen redraw after any :highlight command.
9662 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9663 */
9664 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009665highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009666{
9667 int hlf;
9668 int i;
9669 char_u *p;
9670 int attr;
9671 char_u *end;
9672 int id;
9673#ifdef USER_HIGHLIGHT
9674 char_u userhl[10];
9675# ifdef FEAT_STL_OPT
9676 int id_SNC = -1;
9677 int id_S = -1;
9678 int hlcnt;
9679# endif
9680#endif
9681 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9682
9683 need_highlight_changed = FALSE;
9684
9685 /*
9686 * Clear all attributes.
9687 */
9688 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9689 highlight_attr[hlf] = 0;
9690
9691 /*
9692 * First set all attributes to their default value.
9693 * Then use the attributes from the 'highlight' option.
9694 */
9695 for (i = 0; i < 2; ++i)
9696 {
9697 if (i)
9698 p = p_hl;
9699 else
9700 p = get_highlight_default();
9701 if (p == NULL) /* just in case */
9702 continue;
9703
9704 while (*p)
9705 {
9706 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9707 if (hl_flags[hlf] == *p)
9708 break;
9709 ++p;
9710 if (hlf == (int)HLF_COUNT || *p == NUL)
9711 return FAIL;
9712
9713 /*
9714 * Allow several hl_flags to be combined, like "bu" for
9715 * bold-underlined.
9716 */
9717 attr = 0;
9718 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9719 {
9720 if (vim_iswhite(*p)) /* ignore white space */
9721 continue;
9722
9723 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9724 return FAIL;
9725
9726 switch (*p)
9727 {
9728 case 'b': attr |= HL_BOLD;
9729 break;
9730 case 'i': attr |= HL_ITALIC;
9731 break;
9732 case '-':
9733 case 'n': /* no highlighting */
9734 break;
9735 case 'r': attr |= HL_INVERSE;
9736 break;
9737 case 's': attr |= HL_STANDOUT;
9738 break;
9739 case 'u': attr |= HL_UNDERLINE;
9740 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009741 case 'c': attr |= HL_UNDERCURL;
9742 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009743 case ':': ++p; /* highlight group name */
9744 if (attr || *p == NUL) /* no combinations */
9745 return FAIL;
9746 end = vim_strchr(p, ',');
9747 if (end == NULL)
9748 end = p + STRLEN(p);
9749 id = syn_check_group(p, (int)(end - p));
9750 if (id == 0)
9751 return FAIL;
9752 attr = syn_id2attr(id);
9753 p = end - 1;
9754#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9755 if (hlf == (int)HLF_SNC)
9756 id_SNC = syn_get_final_id(id);
9757 else if (hlf == (int)HLF_S)
9758 id_S = syn_get_final_id(id);
9759#endif
9760 break;
9761 default: return FAIL;
9762 }
9763 }
9764 highlight_attr[hlf] = attr;
9765
9766 p = skip_to_option_part(p); /* skip comma and spaces */
9767 }
9768 }
9769
9770#ifdef USER_HIGHLIGHT
9771 /* Setup the user highlights
9772 *
9773 * Temporarily utilize 10 more hl entries. Have to be in there
9774 * simultaneously in case of table overflows in get_attr_entry()
9775 */
9776# ifdef FEAT_STL_OPT
9777 if (ga_grow(&highlight_ga, 10) == FAIL)
9778 return FAIL;
9779 hlcnt = highlight_ga.ga_len;
9780 if (id_S == 0)
9781 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009782 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009783 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9784 id_S = hlcnt + 10;
9785 }
9786# endif
9787 for (i = 0; i < 9; i++)
9788 {
9789 sprintf((char *)userhl, "User%d", i + 1);
9790 id = syn_name2id(userhl);
9791 if (id == 0)
9792 {
9793 highlight_user[i] = 0;
9794# ifdef FEAT_STL_OPT
9795 highlight_stlnc[i] = 0;
9796# endif
9797 }
9798 else
9799 {
9800# ifdef FEAT_STL_OPT
9801 struct hl_group *hlt = HL_TABLE();
9802# endif
9803
9804 highlight_user[i] = syn_id2attr(id);
9805# ifdef FEAT_STL_OPT
9806 if (id_SNC == 0)
9807 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009808 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009809 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9810 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009811# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009812 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9813# endif
9814 }
9815 else
9816 mch_memmove(&hlt[hlcnt + i],
9817 &hlt[id_SNC - 1],
9818 sizeof(struct hl_group));
9819 hlt[hlcnt + i].sg_link = 0;
9820
9821 /* Apply difference between UserX and HLF_S to HLF_SNC */
9822 hlt[hlcnt + i].sg_term ^=
9823 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9824 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9825 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9826 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9827 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9828 hlt[hlcnt + i].sg_cterm ^=
9829 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9830 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9831 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9832 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9833 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009834# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009835 hlt[hlcnt + i].sg_gui ^=
9836 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009837# endif
9838# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009839 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9840 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9841 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9842 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009843 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9844 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009845 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9846 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9847# ifdef FEAT_XFONTSET
9848 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9849 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9850# endif
9851# endif
9852 highlight_ga.ga_len = hlcnt + i + 1;
9853 set_hl_attr(hlcnt + i); /* At long last we can apply */
9854 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9855# endif
9856 }
9857 }
9858# ifdef FEAT_STL_OPT
9859 highlight_ga.ga_len = hlcnt;
9860# endif
9861
9862#endif /* USER_HIGHLIGHT */
9863
9864 return OK;
9865}
9866
Bram Moolenaar4f688582007-07-24 12:34:30 +00009867#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009868
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009869static void highlight_list(void);
9870static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009871
9872/*
9873 * Handle command line completion for :highlight command.
9874 */
9875 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009876set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009877{
9878 char_u *p;
9879
9880 /* Default: expand group names */
9881 xp->xp_context = EXPAND_HIGHLIGHT;
9882 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009883 include_link = 2;
9884 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009885
9886 /* (part of) subcommand already typed */
9887 if (*arg != NUL)
9888 {
9889 p = skiptowhite(arg);
9890 if (*p != NUL) /* past "default" or group name */
9891 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009892 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009893 if (STRNCMP("default", arg, p - arg) == 0)
9894 {
9895 arg = skipwhite(p);
9896 xp->xp_pattern = arg;
9897 p = skiptowhite(arg);
9898 }
9899 if (*p != NUL) /* past group name */
9900 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009901 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009902 if (arg[1] == 'i' && arg[0] == 'N')
9903 highlight_list();
9904 if (STRNCMP("link", arg, p - arg) == 0
9905 || STRNCMP("clear", arg, p - arg) == 0)
9906 {
9907 xp->xp_pattern = skipwhite(p);
9908 p = skiptowhite(xp->xp_pattern);
9909 if (*p != NUL) /* past first group name */
9910 {
9911 xp->xp_pattern = skipwhite(p);
9912 p = skiptowhite(xp->xp_pattern);
9913 }
9914 }
9915 if (*p != NUL) /* past group name(s) */
9916 xp->xp_context = EXPAND_NOTHING;
9917 }
9918 }
9919 }
9920}
9921
9922/*
9923 * List highlighting matches in a nice way.
9924 */
9925 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009926highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009927{
9928 int i;
9929
9930 for (i = 10; --i >= 0; )
9931 highlight_list_two(i, hl_attr(HLF_D));
9932 for (i = 40; --i >= 0; )
9933 highlight_list_two(99, 0);
9934}
9935
9936 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009937highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009938{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009939 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009940 msg_clr_eos();
9941 out_flush();
9942 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9943}
9944
9945#endif /* FEAT_CMDL_COMPL */
9946
9947#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9948 || defined(FEAT_SIGNS) || defined(PROTO)
9949/*
9950 * Function given to ExpandGeneric() to obtain the list of group names.
9951 * Also used for synIDattr() function.
9952 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009953 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009954get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009955{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009956#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009957 if (idx == highlight_ga.ga_len && include_none != 0)
9958 return (char_u *)"none";
9959 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009960 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009961 if (idx == highlight_ga.ga_len + include_none + include_default
9962 && include_link != 0)
9963 return (char_u *)"link";
9964 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9965 && include_link != 0)
9966 return (char_u *)"clear";
9967#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01009968 if (idx < 0)
9969 return NULL;
9970 /* Items are never removed from the table, skip the ones that were cleared.
9971 */
9972 while (idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
9973 ++idx;
9974 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009975 return NULL;
9976 return HL_TABLE()[idx].sg_name;
9977}
9978#endif
9979
Bram Moolenaar4f688582007-07-24 12:34:30 +00009980#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009981/*
9982 * Free all the highlight group fonts.
9983 * Used when quitting for systems which need it.
9984 */
9985 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009986free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009987{
9988 int idx;
9989
9990 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9991 {
9992 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9993 HL_TABLE()[idx].sg_font = NOFONT;
9994# ifdef FEAT_XFONTSET
9995 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9996 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9997# endif
9998 }
9999
10000 gui_mch_free_font(gui.norm_font);
10001# ifdef FEAT_XFONTSET
10002 gui_mch_free_fontset(gui.fontset);
10003# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010004# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010005 gui_mch_free_font(gui.bold_font);
10006 gui_mch_free_font(gui.ital_font);
10007 gui_mch_free_font(gui.boldital_font);
10008# endif
10009}
10010#endif
10011
10012/**************************************
10013 * End of Highlighting stuff *
10014 **************************************/