blob: 027f2a6ae51c5188b3d5db0e95042b322d49778a [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010025 int sg_cleared; /* "hi clear" was used */
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/* for normal terminals */
27 int sg_term; /* "term=" highlighting attributes */
28 char_u *sg_start; /* terminal string for start highl */
29 char_u *sg_stop; /* terminal string for stop highl */
30 int sg_term_attr; /* Screen attr for term mode */
31/* for color terminals */
32 int sg_cterm; /* "cterm=" highlighting attr */
33 int sg_cterm_bold; /* bold attr was set for light color */
34 int sg_cterm_fg; /* terminal fg color number + 1 */
35 int sg_cterm_bg; /* terminal bg color number + 1 */
36 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000037/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000040 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020041#endif
42#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 GuiFont sg_font; /* GUI font handle */
45#ifdef FEAT_XFONTSET
46 GuiFontset sg_fontset; /* GUI fontset handle */
47#endif
48 char_u *sg_font_name; /* GUI font or fontset name */
49 int sg_gui_attr; /* Screen attr for GUI mode */
50#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020051#if defined(FEAT_GUI) || defined(FEAT_EVAL)
52/* Store the sp color name for the GUI or synIDattr() */
53 int sg_gui; /* "gui=" highlighting attributes */
54 char_u *sg_gui_fg_name;/* GUI foreground color name */
55 char_u *sg_gui_bg_name;/* GUI background color name */
56 char_u *sg_gui_sp_name;/* GUI special color name */
57#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 int sg_link; /* link to this highlight group ID */
59 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000060#ifdef FEAT_EVAL
61 scid_T sg_scriptID; /* script in which the group was last set */
62#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000063};
64
65#define SG_TERM 1 /* term has been set */
66#define SG_CTERM 2 /* cterm has been set */
67#define SG_GUI 4 /* gui has been set */
68#define SG_LINK 8 /* link has been set */
69
70static garray_T highlight_ga; /* highlight groups for 'highlight' option */
71
72#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
73
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020074#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000077/* Flags to indicate an additional string for highlight name completion. */
78static int include_none = 0; /* when 1 include "None" */
79static int include_default = 0; /* when 1 include "default" */
80static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000081#endif
82
83/*
84 * The "term", "cterm" and "gui" arguments can be any combination of the
85 * following names, separated by commas (but no spaces!).
86 */
87static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {"bold", "standout", "underline", "undercurl",
89 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000092
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010093static int get_attr_entry(garray_T *table, attrentry_T *aep);
94static void syn_unadd_group(void);
95static void set_hl_attr(int idx);
96static void highlight_list_one(int id);
97static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
98static int syn_add_group(char_u *name);
99static int syn_list_header(int did_header, int outlen, int id);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100105static guicolor_T color_name2handle(char_u *name);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200106#endif
107#ifdef FEAT_GUI
108static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100109static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100111static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100113static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#endif
115
116/*
117 * An attribute number is the index in attr_table plus ATTR_OFF.
118 */
119#define ATTR_OFF (HL_ALL + 1)
120
121#if defined(FEAT_SYN_HL) || defined(PROTO)
122
123#define SYN_NAMELEN 50 /* maximum length of a syntax name */
124
125/* different types of offsets that are possible */
126#define SPO_MS_OFF 0 /* match start offset */
127#define SPO_ME_OFF 1 /* match end offset */
128#define SPO_HS_OFF 2 /* highl. start offset */
129#define SPO_HE_OFF 3 /* highl. end offset */
130#define SPO_RS_OFF 4 /* region start offset */
131#define SPO_RE_OFF 5 /* region end offset */
132#define SPO_LC_OFF 6 /* leading context offset */
133#define SPO_COUNT 7
134
135static char *(spo_name_tab[SPO_COUNT]) =
136 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
137
138/*
139 * The patterns that are being searched for are stored in a syn_pattern.
140 * A match item consists of one pattern.
141 * A start/end item consists of n start patterns and m end patterns.
142 * A start/skip/end item consists of n start patterns, one skip pattern and m
143 * end patterns.
144 * For the latter two, the patterns are always consecutive: start-skip-end.
145 *
146 * A character offset can be given for the matched text (_m_start and _m_end)
147 * and for the actually highlighted text (_h_start and _h_end).
148 */
149typedef struct syn_pattern
150{
151 char sp_type; /* see SPTYPE_ defines below */
152 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200153 int sp_flags; /* see HL_ defines below */
154#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200155 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200156#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157 struct sp_syn sp_syn; /* struct passed to in_id_list() */
158 short sp_syn_match_id; /* highlight group ID of pattern */
159 char_u *sp_pattern; /* regexp to match, pattern */
160 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200161#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200162 syn_time_T sp_time;
163#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000164 int sp_ic; /* ignore-case flag for sp_prog */
165 short sp_off_flags; /* see below */
166 int sp_offsets[SPO_COUNT]; /* offsets */
167 short *sp_cont_list; /* cont. group IDs, if non-zero */
168 short *sp_next_list; /* next group IDs, if non-zero */
169 int sp_sync_idx; /* sync item index (syncing only) */
170 int sp_line_id; /* ID of last line where tried */
171 int sp_startcol; /* next match in sp_line_id line */
172} synpat_T;
173
174/* The sp_off_flags are computed like this:
175 * offset from the start of the matched text: (1 << SPO_XX_OFF)
176 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
177 * When both are present, only one is used.
178 */
179
180#define SPTYPE_MATCH 1 /* match keyword with this group ID */
181#define SPTYPE_START 2 /* match a regexp, start of item */
182#define SPTYPE_END 3 /* match a regexp, end of item */
183#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
184
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185
186#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
187
188#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
189
190/*
191 * Flags for b_syn_sync_flags:
192 */
193#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
194#define SF_MATCH 0x02 /* sync by matching a pattern */
195
196#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
197
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198#define MAXKEYWLEN 80 /* maximum length of a keyword */
199
200/*
201 * The attributes of the syntax item that has been recognized.
202 */
203static int current_attr = 0; /* attr of current syntax word */
204#ifdef FEAT_EVAL
205static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000206static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200208#ifdef FEAT_CONCEAL
209static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200210static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200211static int current_sub_char = 0;
212#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215{
216 char_u *scl_name; /* syntax cluster name */
217 char_u *scl_name_u; /* uppercase of scl_name */
218 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000219} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220
221/*
222 * Methods of combining two clusters
223 */
224#define CLUSTER_REPLACE 1 /* replace first list with second */
225#define CLUSTER_ADD 2 /* add second list to first */
226#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
227
Bram Moolenaar217ad922005-03-20 22:37:15 +0000228#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229
230/*
231 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200232 * 0 - 19999 normal syntax groups
233 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
234 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
235 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
236 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200238#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200239#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
240#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
241#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
242
Bram Moolenaar42431a72011-04-01 14:44:59 +0200243#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
244#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000245
246/*
247 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
248 * expand_filename(). Most of the other syntax commands don't need it, so
249 * instead of passing it to them, we stow it here.
250 */
251static char_u **syn_cmdlinep;
252
253/*
254 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200255 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000256 * rules in each ":syn include"'d file.
257 */
258static int current_syn_inc_tag = 0;
259static int running_syn_inc_tag = 0;
260
261/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000262 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
263 * This avoids adding a pointer to the hashtable item.
264 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
265 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
266 * HI2KE() converts a hashitem pointer to a var pointer.
267 */
268static keyentry_T dumkey;
269#define KE2HIKEY(kp) ((kp)->keyword)
270#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
271#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
272
273/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 * To reduce the time spent in keepend(), remember at which level in the state
275 * stack the first item with "keepend" is present. When "-1", there is no
276 * "keepend" on the stack.
277 */
278static int keepend_level = -1;
279
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200280static char msg_no_items[] = N_("No Syntax items defined for this buffer");
281
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282/*
283 * For the current state we need to remember more than just the idx.
284 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
285 * (The end positions have the column number of the next char)
286 */
287typedef struct state_item
288{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000289 int si_idx; /* index of syntax pattern or
290 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000292 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_m_lnum; /* lnum of the match */
294 int si_m_startcol; /* starting column of the match */
295 lpos_T si_m_endpos; /* just after end posn of the match */
296 lpos_T si_h_startpos; /* start position of the highlighting */
297 lpos_T si_h_endpos; /* end position of the highlighting */
298 lpos_T si_eoe_pos; /* end position of end pattern */
299 int si_end_idx; /* group ID for end pattern or zero */
300 int si_ends; /* if match ends before si_m_endpos */
301 int si_attr; /* attributes in this state */
302 long si_flags; /* HL_HAS_EOL flag in this state, and
303 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200304#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200305 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200306 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200307#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 short *si_cont_list; /* list of contained groups */
309 short *si_next_list; /* nextgroup IDs after this item ends */
310 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
311 * pattern */
312} stateitem_T;
313
314#define KEYWORD_IDX -1 /* value of si_idx for keywords */
315#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
316 but contained groups */
317
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200318#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100319static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#endif
321
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000323 * Struct to reduce the number of arguments to get_syn_options(), it's used
324 * very often.
325 */
326typedef struct
327{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000328 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000329 int keyword; /* TRUE for ":syn keyword" */
330 int *sync_idx; /* syntax item for "grouphere" argument, NULL
331 if not allowed */
332 char has_cont_list; /* TRUE if "cont_list" can be used */
333 short *cont_list; /* group IDs for "contains" argument */
334 short *cont_in_list; /* group IDs for "containedin" argument */
335 short *next_list; /* group IDs for "nextgroup" argument */
336} syn_opt_arg_T;
337
338/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339 * The next possible match in the current line for any pattern is remembered,
340 * to avoid having to try for a match in each column.
341 * If next_match_idx == -1, not tried (in this line) yet.
342 * If next_match_col == MAXCOL, no match found in this line.
343 * (All end positions have the column of the char after the end)
344 */
345static int next_match_col; /* column for start of next match */
346static lpos_T next_match_m_endpos; /* position for end of next match */
347static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
348static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
349static int next_match_idx; /* index of matched item */
350static long next_match_flags; /* flags for next match */
351static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
352static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
353static int next_match_end_idx; /* ID of group for end pattn or zero */
354static reg_extmatch_T *next_match_extmatch = NULL;
355
356/*
357 * A state stack is an array of integers or stateitem_T, stored in a
358 * garray_T. A state stack is invalid if it's itemsize entry is zero.
359 */
360#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
361#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
362
363/*
364 * The current state (within the line) of the recognition engine.
365 * When current_state.ga_itemsize is 0 the current state is invalid.
366 */
367static win_T *syn_win; /* current window for highlighting */
368static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200369static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370static linenr_T current_lnum = 0; /* lnum of current state */
371static colnr_T current_col = 0; /* column of current state */
372static int current_state_stored = 0; /* TRUE if stored current state
373 * after setting current_finished */
374static int current_finished = 0; /* current line has been finished */
375static garray_T current_state /* current stack of state_items */
376 = {0, 0, 0, 0, NULL};
377static short *current_next_list = NULL; /* when non-zero, nextgroup list */
378static int current_next_flags = 0; /* flags for current_next_list */
379static int current_line_id = 0; /* unique number for current line */
380
381#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
382
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100383static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100384static void save_chartab(char_u *chartab);
385static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100386static int syn_match_linecont(linenr_T lnum);
387static void syn_start_line(void);
388static void syn_update_ends(int startofline);
389static void syn_stack_alloc(void);
390static int syn_stack_cleanup(void);
391static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
392static synstate_T *syn_stack_find_entry(linenr_T lnum);
393static synstate_T *store_current_state(void);
394static void load_current_state(synstate_T *from);
395static void invalidate_current_state(void);
396static int syn_stack_equal(synstate_T *sp);
397static void validate_current_state(void);
398static int syn_finish_line(int syncing);
399static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
400static int did_match_already(int idx, garray_T *gap);
401static stateitem_T *push_next_match(stateitem_T *cur_si);
402static void check_state_ends(void);
403static void update_si_attr(int idx);
404static void check_keepend(void);
405static void update_si_end(stateitem_T *sip, int startcol, int force);
406static short *copy_id_list(short *list);
407static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
408static int push_current_state(int idx);
409static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200410#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100411static void syn_clear_time(syn_time_T *tt);
412static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200413#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100414static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200415#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100416static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200417#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100418static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200419static int syn_time_on = FALSE;
420# define IF_SYN_TIME(p) (p)
421#else
422# define IF_SYN_TIME(p) NULL
423typedef int syn_time_T;
424#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100426static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
427static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
428static void clear_syn_state(synstate_T *p);
429static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100431static void limit_pos(lpos_T *pos, lpos_T *limit);
432static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
433static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
434static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
435static char_u *syn_getcurline(void);
436static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
437static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
438static void syn_cmd_case(exarg_T *eap, int syncing);
439static void syn_cmd_spell(exarg_T *eap, int syncing);
440static void syntax_sync_clear(void);
441static void syn_remove_pattern(synblock_T *block, int idx);
442static void syn_clear_pattern(synblock_T *block, int i);
443static void syn_clear_cluster(synblock_T *block, int i);
444static void syn_cmd_clear(exarg_T *eap, int syncing);
445static void syn_cmd_conceal(exarg_T *eap, int syncing);
446static void syn_clear_one(int id, int syncing);
447static void syn_cmd_on(exarg_T *eap, int syncing);
448static void syn_cmd_enable(exarg_T *eap, int syncing);
449static void syn_cmd_reset(exarg_T *eap, int syncing);
450static void syn_cmd_manual(exarg_T *eap, int syncing);
451static void syn_cmd_off(exarg_T *eap, int syncing);
452static void syn_cmd_onoff(exarg_T *eap, char *name);
453static void syn_cmd_list(exarg_T *eap, int syncing);
454static void syn_lines_msg(void);
455static void syn_match_msg(void);
456static void syn_stack_free_block(synblock_T *block);
457static void syn_list_one(int id, int syncing, int link_only);
458static void syn_list_cluster(int id);
459static void put_id_list(char_u *name, short *list, int attr);
460static void put_pattern(char *s, int c, synpat_T *spp, int attr);
461static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
462static void syn_clear_keyword(int id, hashtab_T *ht);
463static void clear_keywtab(hashtab_T *ht);
464static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
465static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100466static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100467static void syn_cmd_include(exarg_T *eap, int syncing);
468static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
469static void syn_cmd_keyword(exarg_T *eap, int syncing);
470static void syn_cmd_match(exarg_T *eap, int syncing);
471static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100473static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100475static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100477static void syn_cmd_cluster(exarg_T *eap, int syncing);
478static int syn_scl_name2id(char_u *name);
479static int syn_scl_namen2id(char_u *linep, int len);
480static int syn_check_cluster(char_u *pp, int len);
481static int syn_add_cluster(char_u *name);
482static void init_syn_patterns(void);
483static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
484static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100485static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100486static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
487static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000488
489/*
490 * Start the syntax recognition for a line. This function is normally called
491 * from the screen updating, once for each displayed line.
492 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
493 * it. Careful: curbuf and curwin are likely to point to another buffer and
494 * window.
495 */
496 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100497syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498{
499 synstate_T *p;
500 synstate_T *last_valid = NULL;
501 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000502 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503 linenr_T parsed_lnum;
504 linenr_T first_stored;
505 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100506 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200508#ifdef FEAT_CONCEAL
509 current_sub_char = NUL;
510#endif
511
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 /*
513 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000514 * Also do this when a change was made, the current state may be invalid
515 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200517 if (syn_block != wp->w_s
518 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100519 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 {
521 invalidate_current_state();
522 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200523 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100525 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 syn_win = wp;
527
528 /*
529 * Allocate syntax stack when needed.
530 */
531 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000533 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200534 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535
536 /*
537 * If the state of the end of the previous line is useful, store it.
538 */
539 if (VALID_STATE(&current_state)
540 && current_lnum < lnum
541 && current_lnum < syn_buf->b_ml.ml_line_count)
542 {
543 (void)syn_finish_line(FALSE);
544 if (!current_state_stored)
545 {
546 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000547 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 }
549
550 /*
551 * If the current_lnum is now the same as "lnum", keep the current
552 * state (this happens very often!). Otherwise invalidate
553 * current_state and figure it out below.
554 */
555 if (current_lnum != lnum)
556 invalidate_current_state();
557 }
558 else
559 invalidate_current_state();
560
561 /*
562 * Try to synchronize from a saved state in b_sst_array[].
563 * Only do this if lnum is not before and not to far beyond a saved state.
564 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200565 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 {
567 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200568 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 {
570 if (p->sst_lnum > lnum)
571 break;
572 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
573 {
574 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200575 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576 last_min_valid = p;
577 }
578 }
579 if (last_min_valid != NULL)
580 load_current_state(last_min_valid);
581 }
582
583 /*
584 * If "lnum" is before or far beyond a line with a saved state, need to
585 * re-synchronize.
586 */
587 if (INVALID_STATE(&current_state))
588 {
589 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200590 if (current_lnum == 1)
591 /* First line is always valid, no matter "minlines". */
592 first_stored = 1;
593 else
594 /* Need to parse "minlines" lines before state can be considered
595 * valid to store. */
596 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597 }
598 else
599 first_stored = current_lnum;
600
601 /*
602 * Advance from the sync point or saved state until the current line.
603 * Save some entries for syncing with later on.
604 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200605 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000606 dist = 999999;
607 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200608 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000609 while (current_lnum < lnum)
610 {
611 syn_start_line();
612 (void)syn_finish_line(FALSE);
613 ++current_lnum;
614
615 /* If we parsed at least "minlines" lines or started at a valid
616 * state, the current state is considered valid. */
617 if (current_lnum >= first_stored)
618 {
619 /* Check if the saved state entry is for the current line and is
620 * equal to the current state. If so, then validate all saved
621 * states that depended on a change before the parsed line. */
622 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000623 prev = syn_stack_find_entry(current_lnum - 1);
624 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200625 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000627 sp = prev;
628 while (sp != NULL && sp->sst_lnum < current_lnum)
629 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 if (sp != NULL
631 && sp->sst_lnum == current_lnum
632 && syn_stack_equal(sp))
633 {
634 parsed_lnum = current_lnum;
635 prev = sp;
636 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
637 {
638 if (sp->sst_lnum <= lnum)
639 /* valid state before desired line, use this one */
640 prev = sp;
641 else if (sp->sst_change_lnum == 0)
642 /* past saved states depending on change, break here. */
643 break;
644 sp->sst_change_lnum = 0;
645 sp = sp->sst_next;
646 }
647 load_current_state(prev);
648 }
649 /* Store the state at this line when it's the first one, the line
650 * where we start parsing, or some distance from the previously
651 * saved state. But only when parsed at least 'minlines'. */
652 else if (prev == NULL
653 || current_lnum == lnum
654 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000655 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 }
657
658 /* This can take a long time: break when CTRL-C pressed. The current
659 * state will be wrong then. */
660 line_breakcheck();
661 if (got_int)
662 {
663 current_lnum = lnum;
664 break;
665 }
666 }
667
668 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669}
670
671/*
672 * We cannot simply discard growarrays full of state_items or buf_states; we
673 * have to manually release their extmatch pointers first.
674 */
675 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100676clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677{
678 int i;
679 garray_T *gap;
680
681 if (p->sst_stacksize > SST_FIX_STATES)
682 {
683 gap = &(p->sst_union.sst_ga);
684 for (i = 0; i < gap->ga_len; i++)
685 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
686 ga_clear(gap);
687 }
688 else
689 {
690 for (i = 0; i < p->sst_stacksize; i++)
691 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
692 }
693}
694
695/*
696 * Cleanup the current_state stack.
697 */
698 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100699clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700{
701 int i;
702 stateitem_T *sip;
703
704 sip = (stateitem_T *)(current_state.ga_data);
705 for (i = 0; i < current_state.ga_len; i++)
706 unref_extmatch(sip[i].si_extmatch);
707 ga_clear(&current_state);
708}
709
710/*
711 * Try to find a synchronisation point for line "lnum".
712 *
713 * This sets current_lnum and the current state. One of three methods is
714 * used:
715 * 1. Search backwards for the end of a C-comment.
716 * 2. Search backwards for given sync patterns.
717 * 3. Simply start on a given number of lines above "lnum".
718 */
719 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100720syn_sync(
721 win_T *wp,
722 linenr_T start_lnum,
723 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000724{
725 buf_T *curbuf_save;
726 win_T *curwin_save;
727 pos_T cursor_save;
728 int idx;
729 linenr_T lnum;
730 linenr_T end_lnum;
731 linenr_T break_lnum;
732 int had_sync_point;
733 stateitem_T *cur_si;
734 synpat_T *spp;
735 char_u *line;
736 int found_flags = 0;
737 int found_match_idx = 0;
738 linenr_T found_current_lnum = 0;
739 int found_current_col= 0;
740 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000741 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742
743 /*
744 * Clear any current state that might be hanging around.
745 */
746 invalidate_current_state();
747
748 /*
749 * Start at least "minlines" back. Default starting point for parsing is
750 * there.
751 * Start further back, to avoid that scrolling backwards will result in
752 * resyncing for every line. Now it resyncs only one out of N lines,
753 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
754 * Watch out for overflow when minlines is MAXLNUM.
755 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200756 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 start_lnum = 1;
758 else
759 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200760 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200762 else if (syn_block->b_syn_sync_minlines < 10)
763 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200765 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
766 if (syn_block->b_syn_sync_maxlines != 0
767 && lnum > syn_block->b_syn_sync_maxlines)
768 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000769 if (lnum >= start_lnum)
770 start_lnum = 1;
771 else
772 start_lnum -= lnum;
773 }
774 current_lnum = start_lnum;
775
776 /*
777 * 1. Search backwards for the end of a C-style comment.
778 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200779 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000780 {
781 /* Need to make syn_buf the current buffer for a moment, to be able to
782 * use find_start_comment(). */
783 curwin_save = curwin;
784 curwin = wp;
785 curbuf_save = curbuf;
786 curbuf = syn_buf;
787
788 /*
789 * Skip lines that end in a backslash.
790 */
791 for ( ; start_lnum > 1; --start_lnum)
792 {
793 line = ml_get(start_lnum - 1);
794 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
795 break;
796 }
797 current_lnum = start_lnum;
798
799 /* set cursor to start of search */
800 cursor_save = wp->w_cursor;
801 wp->w_cursor.lnum = start_lnum;
802 wp->w_cursor.col = 0;
803
804 /*
805 * If the line is inside a comment, need to find the syntax item that
806 * defines the comment.
807 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
808 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200809 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000810 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200811 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
812 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
813 == syn_block->b_syn_sync_id
814 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000815 {
816 validate_current_state();
817 if (push_current_state(idx) == OK)
818 update_si_attr(current_state.ga_len - 1);
819 break;
820 }
821 }
822
823 /* restore cursor and buffer */
824 wp->w_cursor = cursor_save;
825 curwin = curwin_save;
826 curbuf = curbuf_save;
827 }
828
829 /*
830 * 2. Search backwards for given sync patterns.
831 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200832 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000833 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200834 if (syn_block->b_syn_sync_maxlines != 0
835 && start_lnum > syn_block->b_syn_sync_maxlines)
836 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000837 else
838 break_lnum = 0;
839
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000840 found_m_endpos.lnum = 0;
841 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000842 end_lnum = start_lnum;
843 lnum = start_lnum;
844 while (--lnum > break_lnum)
845 {
846 /* This can take a long time: break when CTRL-C pressed. */
847 line_breakcheck();
848 if (got_int)
849 {
850 invalidate_current_state();
851 current_lnum = start_lnum;
852 break;
853 }
854
855 /* Check if we have run into a valid saved state stack now. */
856 if (last_valid != NULL && lnum == last_valid->sst_lnum)
857 {
858 load_current_state(last_valid);
859 break;
860 }
861
862 /*
863 * Check if the previous line has the line-continuation pattern.
864 */
865 if (lnum > 1 && syn_match_linecont(lnum - 1))
866 continue;
867
868 /*
869 * Start with nothing on the state stack
870 */
871 validate_current_state();
872
873 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
874 {
875 syn_start_line();
876 for (;;)
877 {
878 had_sync_point = syn_finish_line(TRUE);
879 /*
880 * When a sync point has been found, remember where, and
881 * continue to look for another one, further on in the line.
882 */
883 if (had_sync_point && current_state.ga_len)
884 {
885 cur_si = &CUR_STATE(current_state.ga_len - 1);
886 if (cur_si->si_m_endpos.lnum > start_lnum)
887 {
888 /* ignore match that goes to after where started */
889 current_lnum = end_lnum;
890 break;
891 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000892 if (cur_si->si_idx < 0)
893 {
894 /* Cannot happen? */
895 found_flags = 0;
896 found_match_idx = KEYWORD_IDX;
897 }
898 else
899 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200900 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000901 found_flags = spp->sp_flags;
902 found_match_idx = spp->sp_sync_idx;
903 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 found_current_lnum = current_lnum;
905 found_current_col = current_col;
906 found_m_endpos = cur_si->si_m_endpos;
907 /*
908 * Continue after the match (be aware of a zero-length
909 * match).
910 */
911 if (found_m_endpos.lnum > current_lnum)
912 {
913 current_lnum = found_m_endpos.lnum;
914 current_col = found_m_endpos.col;
915 if (current_lnum >= end_lnum)
916 break;
917 }
918 else if (found_m_endpos.col > current_col)
919 current_col = found_m_endpos.col;
920 else
921 ++current_col;
922
923 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000924 * an item that ends here, need to do that now. Be
925 * careful not to go past the NUL. */
926 prev_current_col = current_col;
927 if (syn_getcurline()[current_col] != NUL)
928 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000929 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000930 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931 }
932 else
933 break;
934 }
935 }
936
937 /*
938 * If a sync point was encountered, break here.
939 */
940 if (found_flags)
941 {
942 /*
943 * Put the item that was specified by the sync point on the
944 * state stack. If there was no item specified, make the
945 * state stack empty.
946 */
947 clear_current_state();
948 if (found_match_idx >= 0
949 && push_current_state(found_match_idx) == OK)
950 update_si_attr(current_state.ga_len - 1);
951
952 /*
953 * When using "grouphere", continue from the sync point
954 * match, until the end of the line. Parsing starts at
955 * the next line.
956 * For "groupthere" the parsing starts at start_lnum.
957 */
958 if (found_flags & HL_SYNC_HERE)
959 {
960 if (current_state.ga_len)
961 {
962 cur_si = &CUR_STATE(current_state.ga_len - 1);
963 cur_si->si_h_startpos.lnum = found_current_lnum;
964 cur_si->si_h_startpos.col = found_current_col;
965 update_si_end(cur_si, (int)current_col, TRUE);
966 check_keepend();
967 }
968 current_col = found_m_endpos.col;
969 current_lnum = found_m_endpos.lnum;
970 (void)syn_finish_line(FALSE);
971 ++current_lnum;
972 }
973 else
974 current_lnum = start_lnum;
975
976 break;
977 }
978
979 end_lnum = lnum;
980 invalidate_current_state();
981 }
982
983 /* Ran into start of the file or exceeded maximum number of lines */
984 if (lnum <= break_lnum)
985 {
986 invalidate_current_state();
987 current_lnum = break_lnum + 1;
988 }
989 }
990
991 validate_current_state();
992}
993
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100994 static void
995save_chartab(char_u *chartab)
996{
997 if (syn_block->b_syn_isk != empty_option)
998 {
999 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1000 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1001 (size_t)32);
1002 }
1003}
1004
1005 static void
1006restore_chartab(char_u *chartab)
1007{
1008 if (syn_win->w_s->b_syn_isk != empty_option)
1009 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1010}
1011
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012/*
1013 * Return TRUE if the line-continuation pattern matches in line "lnum".
1014 */
1015 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001016syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001017{
1018 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001019 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001020 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021
Bram Moolenaar860cae12010-06-05 23:22:07 +02001022 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001023 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001024 /* use syntax iskeyword option */
1025 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001026 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1027 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001028 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001029 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001030 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001031 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001032 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001033 }
1034 return FALSE;
1035}
1036
1037/*
1038 * Prepare the current state for the start of a line.
1039 */
1040 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001041syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042{
1043 current_finished = FALSE;
1044 current_col = 0;
1045
1046 /*
1047 * Need to update the end of a start/skip/end that continues from the
1048 * previous line and regions that have "keepend".
1049 */
1050 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001051 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001053 check_state_ends();
1054 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055
1056 next_match_idx = -1;
1057 ++current_line_id;
1058}
1059
1060/*
1061 * Check for items in the stack that need their end updated.
1062 * When "startofline" is TRUE the last item is always updated.
1063 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1064 */
1065 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001066syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067{
1068 stateitem_T *cur_si;
1069 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001070 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001071
1072 if (startofline)
1073 {
1074 /* Check for a match carried over from a previous line with a
1075 * contained region. The match ends as soon as the region ends. */
1076 for (i = 0; i < current_state.ga_len; ++i)
1077 {
1078 cur_si = &CUR_STATE(i);
1079 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001080 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081 == SPTYPE_MATCH
1082 && cur_si->si_m_endpos.lnum < current_lnum)
1083 {
1084 cur_si->si_flags |= HL_MATCHCONT;
1085 cur_si->si_m_endpos.lnum = 0;
1086 cur_si->si_m_endpos.col = 0;
1087 cur_si->si_h_endpos = cur_si->si_m_endpos;
1088 cur_si->si_ends = TRUE;
1089 }
1090 }
1091 }
1092
1093 /*
1094 * Need to update the end of a start/skip/end that continues from the
1095 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001096 * influence contained items. If we've just removed "extend"
1097 * (startofline == 0) then we should update ends of normal regions
1098 * contained inside "keepend" because "extend" could have extended
1099 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 * Then check for items ending in column 0.
1101 */
1102 i = current_state.ga_len - 1;
1103 if (keepend_level >= 0)
1104 for ( ; i > keepend_level; --i)
1105 if (CUR_STATE(i).si_flags & HL_EXTEND)
1106 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001107
1108 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001109 for ( ; i < current_state.ga_len; ++i)
1110 {
1111 cur_si = &CUR_STATE(i);
1112 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001113 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 || (i == current_state.ga_len - 1 && startofline))
1115 {
1116 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1117 cur_si->si_h_startpos.lnum = current_lnum;
1118
1119 if (!(cur_si->si_flags & HL_MATCHCONT))
1120 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001121
1122 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1123 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 }
1125 }
1126 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127}
1128
1129/****************************************
1130 * Handling of the state stack cache.
1131 */
1132
1133/*
1134 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1135 *
1136 * To speed up syntax highlighting, the state stack for the start of some
1137 * lines is cached. These entries can be used to start parsing at that point.
1138 *
1139 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1140 * valid entries. b_sst_first points to the first one, then follow sst_next.
1141 * The entries are sorted on line number. The first entry is often for line 2
1142 * (line 1 always starts with an empty stack).
1143 * There is also a list for free entries. This construction is used to avoid
1144 * having to allocate and free memory blocks too often.
1145 *
1146 * When making changes to the buffer, this is logged in b_mod_*. When calling
1147 * update_screen() to update the display, it will call
1148 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1149 * entries. The entries which are inside the changed area are removed,
1150 * because they must be recomputed. Entries below the changed have their line
1151 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1152 * set to indicate that a check must be made if the changed lines would change
1153 * the cached entry.
1154 *
1155 * When later displaying lines, an entry is stored for each line. Displayed
1156 * lines are likely to be displayed again, in which case the state at the
1157 * start of the line is needed.
1158 * For not displayed lines, an entry is stored for every so many lines. These
1159 * entries will be used e.g., when scrolling backwards. The distance between
1160 * entries depends on the number of lines in the buffer. For small buffers
1161 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1162 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1163 */
1164
Bram Moolenaar860cae12010-06-05 23:22:07 +02001165 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001166syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167{
1168 synstate_T *p;
1169
1170 if (block->b_sst_array != NULL)
1171 {
1172 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1173 clear_syn_state(p);
1174 vim_free(block->b_sst_array);
1175 block->b_sst_array = NULL;
1176 block->b_sst_len = 0;
1177 }
1178}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179/*
1180 * Free b_sst_array[] for buffer "buf".
1181 * Used when syntax items changed to force resyncing everywhere.
1182 */
1183 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001184syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001186#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001188#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189
Bram Moolenaar860cae12010-06-05 23:22:07 +02001190 syn_stack_free_block(block);
1191
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192#ifdef FEAT_FOLDING
1193 /* When using "syntax" fold method, must update all folds. */
1194 FOR_ALL_WINDOWS(wp)
1195 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001196 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197 foldUpdateAll(wp);
1198 }
1199#endif
1200}
1201
1202/*
1203 * Allocate the syntax state stack for syn_buf when needed.
1204 * If the number of entries in b_sst_array[] is much too big or a bit too
1205 * small, reallocate it.
1206 * Also used to allocate b_sst_array[] for the first time.
1207 */
1208 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001209syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210{
1211 long len;
1212 synstate_T *to, *from;
1213 synstate_T *sstp;
1214
1215 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1216 if (len < SST_MIN_ENTRIES)
1217 len = SST_MIN_ENTRIES;
1218 else if (len > SST_MAX_ENTRIES)
1219 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001220 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 {
1222 /* Allocate 50% too much, to avoid reallocating too often. */
1223 len = syn_buf->b_ml.ml_line_count;
1224 len = (len + len / 2) / SST_DIST + Rows * 2;
1225 if (len < SST_MIN_ENTRIES)
1226 len = SST_MIN_ENTRIES;
1227 else if (len > SST_MAX_ENTRIES)
1228 len = SST_MAX_ENTRIES;
1229
Bram Moolenaar860cae12010-06-05 23:22:07 +02001230 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 {
1232 /* When shrinking the array, cleanup the existing stack.
1233 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001234 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 && syn_stack_cleanup())
1236 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1238 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 }
1240
1241 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1242 if (sstp == NULL) /* out of memory! */
1243 return;
1244
1245 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001246 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 {
1248 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 from = from->sst_next)
1251 {
1252 ++to;
1253 *to = *from;
1254 to->sst_next = to + 1;
1255 }
1256 }
1257 if (to != sstp - 1)
1258 {
1259 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001260 syn_block->b_sst_first = sstp;
1261 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 }
1263 else
1264 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 syn_block->b_sst_first = NULL;
1266 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 }
1268
1269 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001270 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271 while (++to < sstp + len)
1272 to->sst_next = to + 1;
1273 (sstp + len - 1)->sst_next = NULL;
1274
Bram Moolenaar860cae12010-06-05 23:22:07 +02001275 vim_free(syn_block->b_sst_array);
1276 syn_block->b_sst_array = sstp;
1277 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 }
1279}
1280
1281/*
1282 * Check for changes in a buffer to affect stored syntax states. Uses the
1283 * b_mod_* fields.
1284 * Called from update_screen(), before screen is being updated, once for each
1285 * displayed buffer.
1286 */
1287 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001288syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001290 win_T *wp;
1291
1292 syn_stack_apply_changes_block(&buf->b_s, buf);
1293
1294 FOR_ALL_WINDOWS(wp)
1295 {
1296 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1297 syn_stack_apply_changes_block(wp->w_s, buf);
1298 }
1299}
1300
1301 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001302syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001303{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 synstate_T *p, *prev, *np;
1305 linenr_T n;
1306
Bram Moolenaar860cae12010-06-05 23:22:07 +02001307 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 return;
1309
1310 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
1315 n = p->sst_lnum + buf->b_mod_xlines;
1316 if (n <= buf->b_mod_bot)
1317 {
1318 /* this state is inside the changed area, remove it */
1319 np = p->sst_next;
1320 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001321 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 else
1323 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001324 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001325 p = np;
1326 continue;
1327 }
1328 /* This state is below the changed area. Remember the line
1329 * that needs to be parsed before this entry can be made valid
1330 * again. */
1331 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1332 {
1333 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1334 p->sst_change_lnum += buf->b_mod_xlines;
1335 else
1336 p->sst_change_lnum = buf->b_mod_top;
1337 }
1338 if (p->sst_change_lnum == 0
1339 || p->sst_change_lnum < buf->b_mod_bot)
1340 p->sst_change_lnum = buf->b_mod_bot;
1341
1342 p->sst_lnum = n;
1343 }
1344 prev = p;
1345 p = p->sst_next;
1346 }
1347}
1348
1349/*
1350 * Reduce the number of entries in the state stack for syn_buf.
1351 * Returns TRUE if at least one entry was freed.
1352 */
1353 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001354syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355{
1356 synstate_T *p, *prev;
1357 disptick_T tick;
1358 int above;
1359 int dist;
1360 int retval = FALSE;
1361
Bram Moolenaar860cae12010-06-05 23:22:07 +02001362 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 return retval;
1364
1365 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001366 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001367 dist = 999999;
1368 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001369 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370
1371 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001372 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 * be removed. Set "above" when the "tick" for the oldest entry is above
1374 * "b_sst_lasttick" (the display tick wraps around).
1375 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001378 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1380 {
1381 if (prev->sst_lnum + dist > p->sst_lnum)
1382 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001383 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 {
1385 if (!above || p->sst_tick < tick)
1386 tick = p->sst_tick;
1387 above = TRUE;
1388 }
1389 else if (!above && p->sst_tick < tick)
1390 tick = p->sst_tick;
1391 }
1392 }
1393
1394 /*
1395 * Go through the list to make the entries for the oldest tick at an
1396 * interval of several lines.
1397 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001398 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1400 {
1401 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1402 {
1403 /* Move this entry from used list to free list */
1404 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001405 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406 p = prev;
1407 retval = TRUE;
1408 }
1409 }
1410 return retval;
1411}
1412
1413/*
1414 * Free the allocated memory for a syn_state item.
1415 * Move the entry into the free list.
1416 */
1417 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001418syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419{
1420 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001421 p->sst_next = block->b_sst_firstfree;
1422 block->b_sst_firstfree = p;
1423 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424}
1425
1426/*
1427 * Find an entry in the list of state stacks at or before "lnum".
1428 * Returns NULL when there is no entry or the first entry is after "lnum".
1429 */
1430 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001431syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432{
1433 synstate_T *p, *prev;
1434
1435 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001436 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 {
1438 if (p->sst_lnum == lnum)
1439 return p;
1440 if (p->sst_lnum > lnum)
1441 break;
1442 }
1443 return prev;
1444}
1445
1446/*
1447 * Try saving the current state in b_sst_array[].
1448 * The current state must be valid for the start of the current_lnum line!
1449 */
1450 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001451store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001452{
1453 int i;
1454 synstate_T *p;
1455 bufstate_T *bp;
1456 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001457 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458
1459 /*
1460 * If the current state contains a start or end pattern that continues
1461 * from the previous line, we can't use it. Don't store it then.
1462 */
1463 for (i = current_state.ga_len - 1; i >= 0; --i)
1464 {
1465 cur_si = &CUR_STATE(i);
1466 if (cur_si->si_h_startpos.lnum >= current_lnum
1467 || cur_si->si_m_endpos.lnum >= current_lnum
1468 || cur_si->si_h_endpos.lnum >= current_lnum
1469 || (cur_si->si_end_idx
1470 && cur_si->si_eoe_pos.lnum >= current_lnum))
1471 break;
1472 }
1473 if (i >= 0)
1474 {
1475 if (sp != NULL)
1476 {
1477 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001478 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001480 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 else
1482 {
1483 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001484 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 if (p->sst_next == sp)
1486 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001487 if (p != NULL) /* just in case */
1488 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001490 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 sp = NULL;
1492 }
1493 }
1494 else if (sp == NULL || sp->sst_lnum != current_lnum)
1495 {
1496 /*
1497 * Add a new entry
1498 */
1499 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001500 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 {
1502 (void)syn_stack_cleanup();
1503 /* "sp" may have been moved to the freelist now */
1504 sp = syn_stack_find_entry(current_lnum);
1505 }
1506 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001507 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 sp = NULL;
1509 else
1510 {
1511 /* Take the first item from the free list and put it in the used
1512 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001513 p = syn_block->b_sst_firstfree;
1514 syn_block->b_sst_firstfree = p->sst_next;
1515 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001516 if (sp == NULL)
1517 {
1518 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001519 p->sst_next = syn_block->b_sst_first;
1520 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 }
1522 else
1523 {
1524 /* insert in list after *sp */
1525 p->sst_next = sp->sst_next;
1526 sp->sst_next = p;
1527 }
1528 sp = p;
1529 sp->sst_stacksize = 0;
1530 sp->sst_lnum = current_lnum;
1531 }
1532 }
1533 if (sp != NULL)
1534 {
1535 /* When overwriting an existing state stack, clear it first */
1536 clear_syn_state(sp);
1537 sp->sst_stacksize = current_state.ga_len;
1538 if (current_state.ga_len > SST_FIX_STATES)
1539 {
1540 /* Need to clear it, might be something remaining from when the
1541 * length was less than SST_FIX_STATES. */
1542 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1543 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1544 sp->sst_stacksize = 0;
1545 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001547 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1548 }
1549 else
1550 bp = sp->sst_union.sst_stack;
1551 for (i = 0; i < sp->sst_stacksize; ++i)
1552 {
1553 bp[i].bs_idx = CUR_STATE(i).si_idx;
1554 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001555#ifdef FEAT_CONCEAL
1556 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1557 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1558#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001559 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1560 }
1561 sp->sst_next_flags = current_next_flags;
1562 sp->sst_next_list = current_next_list;
1563 sp->sst_tick = display_tick;
1564 sp->sst_change_lnum = 0;
1565 }
1566 current_state_stored = TRUE;
1567 return sp;
1568}
1569
1570/*
1571 * Copy a state stack from "from" in b_sst_array[] to current_state;
1572 */
1573 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001574load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575{
1576 int i;
1577 bufstate_T *bp;
1578
1579 clear_current_state();
1580 validate_current_state();
1581 keepend_level = -1;
1582 if (from->sst_stacksize
1583 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1584 {
1585 if (from->sst_stacksize > SST_FIX_STATES)
1586 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1587 else
1588 bp = from->sst_union.sst_stack;
1589 for (i = 0; i < from->sst_stacksize; ++i)
1590 {
1591 CUR_STATE(i).si_idx = bp[i].bs_idx;
1592 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001593#ifdef FEAT_CONCEAL
1594 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1595 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1596#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1598 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1599 keepend_level = i;
1600 CUR_STATE(i).si_ends = FALSE;
1601 CUR_STATE(i).si_m_lnum = 0;
1602 if (CUR_STATE(i).si_idx >= 0)
1603 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001604 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001605 else
1606 CUR_STATE(i).si_next_list = NULL;
1607 update_si_attr(i);
1608 }
1609 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610 }
1611 current_next_list = from->sst_next_list;
1612 current_next_flags = from->sst_next_flags;
1613 current_lnum = from->sst_lnum;
1614}
1615
1616/*
1617 * Compare saved state stack "*sp" with the current state.
1618 * Return TRUE when they are equal.
1619 */
1620 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001621syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001622{
1623 int i, j;
1624 bufstate_T *bp;
1625 reg_extmatch_T *six, *bsx;
1626
1627 /* First a quick check if the stacks have the same size end nextlist. */
1628 if (sp->sst_stacksize == current_state.ga_len
1629 && sp->sst_next_list == current_next_list)
1630 {
1631 /* Need to compare all states on both stacks. */
1632 if (sp->sst_stacksize > SST_FIX_STATES)
1633 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1634 else
1635 bp = sp->sst_union.sst_stack;
1636
1637 for (i = current_state.ga_len; --i >= 0; )
1638 {
1639 /* If the item has another index the state is different. */
1640 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1641 break;
1642 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1643 {
1644 /* When the extmatch pointers are different, the strings in
1645 * them can still be the same. Check if the extmatch
1646 * references are equal. */
1647 bsx = bp[i].bs_extmatch;
1648 six = CUR_STATE(i).si_extmatch;
1649 /* If one of the extmatch pointers is NULL the states are
1650 * different. */
1651 if (bsx == NULL || six == NULL)
1652 break;
1653 for (j = 0; j < NSUBEXP; ++j)
1654 {
1655 /* Check each referenced match string. They must all be
1656 * equal. */
1657 if (bsx->matches[j] != six->matches[j])
1658 {
1659 /* If the pointer is different it can still be the
1660 * same text. Compare the strings, ignore case when
1661 * the start item has the sp_ic flag set. */
1662 if (bsx->matches[j] == NULL
1663 || six->matches[j] == NULL)
1664 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001665 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001666 ? MB_STRICMP(bsx->matches[j],
1667 six->matches[j]) != 0
1668 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1669 break;
1670 }
1671 }
1672 if (j != NSUBEXP)
1673 break;
1674 }
1675 }
1676 if (i < 0)
1677 return TRUE;
1678 }
1679 return FALSE;
1680}
1681
1682/*
1683 * We stop parsing syntax above line "lnum". If the stored state at or below
1684 * this line depended on a change before it, it now depends on the line below
1685 * the last parsed line.
1686 * The window looks like this:
1687 * line which changed
1688 * displayed line
1689 * displayed line
1690 * lnum -> line below window
1691 */
1692 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001693syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694{
1695 synstate_T *sp;
1696
1697 sp = syn_stack_find_entry(lnum);
1698 if (sp != NULL && sp->sst_lnum < lnum)
1699 sp = sp->sst_next;
1700
1701 if (sp != NULL && sp->sst_change_lnum != 0)
1702 sp->sst_change_lnum = lnum;
1703}
1704
1705/*
1706 * End of handling of the state stack.
1707 ****************************************/
1708
1709 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001710invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711{
1712 clear_current_state();
1713 current_state.ga_itemsize = 0; /* mark current_state invalid */
1714 current_next_list = NULL;
1715 keepend_level = -1;
1716}
1717
1718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001719validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720{
1721 current_state.ga_itemsize = sizeof(stateitem_T);
1722 current_state.ga_growsize = 3;
1723}
1724
1725/*
1726 * Return TRUE if the syntax at start of lnum changed since last time.
1727 * This will only be called just after get_syntax_attr() for the previous
1728 * line, to check if the next line needs to be redrawn too.
1729 */
1730 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001731syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001732{
1733 int retval = TRUE;
1734 synstate_T *sp;
1735
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 /*
1737 * Check the state stack when:
1738 * - lnum is just below the previously syntaxed line.
1739 * - lnum is not before the lines with saved states.
1740 * - lnum is not past the lines with saved states.
1741 * - lnum is at or before the last changed line.
1742 */
1743 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1744 {
1745 sp = syn_stack_find_entry(lnum);
1746 if (sp != NULL && sp->sst_lnum == lnum)
1747 {
1748 /*
1749 * finish the previous line (needed when not all of the line was
1750 * drawn)
1751 */
1752 (void)syn_finish_line(FALSE);
1753
1754 /*
1755 * Compare the current state with the previously saved state of
1756 * the line.
1757 */
1758 if (syn_stack_equal(sp))
1759 retval = FALSE;
1760
1761 /*
1762 * Store the current state in b_sst_array[] for later use.
1763 */
1764 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001765 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 }
1767 }
1768
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 return retval;
1770}
1771
1772/*
1773 * Finish the current line.
1774 * This doesn't return any attributes, it only gets the state at the end of
1775 * the line. It can start anywhere in the line, as long as the current state
1776 * is valid.
1777 */
1778 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001779syn_finish_line(
1780 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781{
1782 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001783 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784
1785 if (!current_finished)
1786 {
1787 while (!current_finished)
1788 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001789 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790 /*
1791 * When syncing, and found some item, need to check the item.
1792 */
1793 if (syncing && current_state.ga_len)
1794 {
1795 /*
1796 * Check for match with sync item.
1797 */
1798 cur_si = &CUR_STATE(current_state.ga_len - 1);
1799 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001800 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1802 return TRUE;
1803
1804 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001805 * that ends here, need to do that now. Be careful not to go
1806 * past the NUL. */
1807 prev_current_col = current_col;
1808 if (syn_getcurline()[current_col] != NUL)
1809 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001811 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 }
1813 ++current_col;
1814 }
1815 }
1816 return FALSE;
1817}
1818
1819/*
1820 * Return highlight attributes for next character.
1821 * Must first call syntax_start() once for the line.
1822 * "col" is normally 0 for the first use in a line, and increments by one each
1823 * time. It's allowed to skip characters and to stop before the end of the
1824 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001825 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1826 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 */
1828 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001829get_syntax_attr(
1830 colnr_T col,
1831 int *can_spell,
1832 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833{
1834 int attr = 0;
1835
Bram Moolenaar349955a2007-08-14 21:07:36 +00001836 if (can_spell != NULL)
1837 /* Default: Only do spelling when there is no @Spell cluster or when
1838 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001839 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1840 ? (syn_block->b_spell_cluster_id == 0)
1841 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001842
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001844 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845 return 0;
1846
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001847 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001848 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001849 {
1850 clear_current_state();
1851#ifdef FEAT_EVAL
1852 current_id = 0;
1853 current_trans_id = 0;
1854#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001855#ifdef FEAT_CONCEAL
1856 current_flags = 0;
1857#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001858 return 0;
1859 }
1860
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 /* Make sure current_state is valid */
1862 if (INVALID_STATE(&current_state))
1863 validate_current_state();
1864
1865 /*
1866 * Skip from the current column to "col", get the attributes for "col".
1867 */
1868 while (current_col <= col)
1869 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001870 attr = syn_current_attr(FALSE, TRUE, can_spell,
1871 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001872 ++current_col;
1873 }
1874
Bram Moolenaar071d4272004-06-13 20:20:40 +00001875 return attr;
1876}
1877
1878/*
1879 * Get syntax attributes for current_lnum, current_col.
1880 */
1881 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001882syn_current_attr(
1883 int syncing, /* When 1: called for syncing */
1884 int displaying, /* result will be displayed */
1885 int *can_spell, /* return: do spell checking */
1886 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887{
1888 int syn_id;
1889 lpos_T endpos; /* was: char_u *endp; */
1890 lpos_T hl_startpos; /* was: int hl_startcol; */
1891 lpos_T hl_endpos;
1892 lpos_T eos_pos; /* end-of-start match (start region) */
1893 lpos_T eoe_pos; /* end-of-end pattern */
1894 int end_idx; /* group ID for end pattern */
1895 int idx;
1896 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001897 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898 int startcol;
1899 int endcol;
1900 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001901 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902 short *next_list;
1903 int found_match; /* found usable match */
1904 static int try_next_column = FALSE; /* must try in next col */
1905 int do_keywords;
1906 regmmatch_T regmatch;
1907 lpos_T pos;
1908 int lc_col;
1909 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001910 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911 char_u *line; /* current line. NOTE: becomes invalid after
1912 looking for a pattern match! */
1913
1914 /* variables for zero-width matches that have a "nextgroup" argument */
1915 int keep_next_list;
1916 int zero_width_next_list = FALSE;
1917 garray_T zero_width_next_ga;
1918
1919 /*
1920 * No character, no attributes! Past end of line?
1921 * Do try matching with an empty line (could be the start of a region).
1922 */
1923 line = syn_getcurline();
1924 if (line[current_col] == NUL && current_col != 0)
1925 {
1926 /*
1927 * If we found a match after the last column, use it.
1928 */
1929 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1930 && next_match_col != MAXCOL)
1931 (void)push_next_match(NULL);
1932
1933 current_finished = TRUE;
1934 current_state_stored = FALSE;
1935 return 0;
1936 }
1937
1938 /* if the current or next character is NUL, we will finish the line now */
1939 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1940 {
1941 current_finished = TRUE;
1942 current_state_stored = FALSE;
1943 }
1944
1945 /*
1946 * When in the previous column there was a match but it could not be used
1947 * (empty match or already matched in this column) need to try again in
1948 * the next column.
1949 */
1950 if (try_next_column)
1951 {
1952 next_match_idx = -1;
1953 try_next_column = FALSE;
1954 }
1955
1956 /* Only check for keywords when not syncing and there are some. */
1957 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001958 && (syn_block->b_keywtab.ht_used > 0
1959 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960
1961 /* Init the list of zero-width matches with a nextlist. This is used to
1962 * avoid matching the same item in the same position twice. */
1963 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1964
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001965 /* use syntax iskeyword option */
1966 save_chartab(buf_chartab);
1967
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968 /*
1969 * Repeat matching keywords and patterns, to find contained items at the
1970 * same column. This stops when there are no extra matches at the current
1971 * column.
1972 */
1973 do
1974 {
1975 found_match = FALSE;
1976 keep_next_list = FALSE;
1977 syn_id = 0;
1978
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001979
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980 /*
1981 * 1. Check for a current state.
1982 * Only when there is no current state, or if the current state may
1983 * contain other things, we need to check for keywords and patterns.
1984 * Always need to check for contained items if some item has the
1985 * "containedin" argument (takes extra time!).
1986 */
1987 if (current_state.ga_len)
1988 cur_si = &CUR_STATE(current_state.ga_len - 1);
1989 else
1990 cur_si = NULL;
1991
Bram Moolenaar860cae12010-06-05 23:22:07 +02001992 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001993 || cur_si->si_cont_list != NULL)
1994 {
1995 /*
1996 * 2. Check for keywords, if on a keyword char after a non-keyword
1997 * char. Don't do this when syncing.
1998 */
1999 if (do_keywords)
2000 {
2001 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002002 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002004 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005#ifdef FEAT_MBYTE
2006 - (has_mbyte
2007 ? (*mb_head_off)(line, line + current_col - 1)
2008 : 0)
2009#endif
2010 , syn_buf)))
2011 {
2012 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002013 &endcol, &flags, &next_list, cur_si,
2014 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002015 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 {
2017 if (push_current_state(KEYWORD_IDX) == OK)
2018 {
2019 cur_si = &CUR_STATE(current_state.ga_len - 1);
2020 cur_si->si_m_startcol = current_col;
2021 cur_si->si_h_startpos.lnum = current_lnum;
2022 cur_si->si_h_startpos.col = 0; /* starts right away */
2023 cur_si->si_m_endpos.lnum = current_lnum;
2024 cur_si->si_m_endpos.col = endcol;
2025 cur_si->si_h_endpos.lnum = current_lnum;
2026 cur_si->si_h_endpos.col = endcol;
2027 cur_si->si_ends = TRUE;
2028 cur_si->si_end_idx = 0;
2029 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002030#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002031 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002032 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002033 if (current_state.ga_len > 1)
2034 cur_si->si_flags |=
2035 CUR_STATE(current_state.ga_len - 2).si_flags
2036 & HL_CONCEAL;
2037#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038 cur_si->si_id = syn_id;
2039 cur_si->si_trans_id = syn_id;
2040 if (flags & HL_TRANSP)
2041 {
2042 if (current_state.ga_len < 2)
2043 {
2044 cur_si->si_attr = 0;
2045 cur_si->si_trans_id = 0;
2046 }
2047 else
2048 {
2049 cur_si->si_attr = CUR_STATE(
2050 current_state.ga_len - 2).si_attr;
2051 cur_si->si_trans_id = CUR_STATE(
2052 current_state.ga_len - 2).si_trans_id;
2053 }
2054 }
2055 else
2056 cur_si->si_attr = syn_id2attr(syn_id);
2057 cur_si->si_cont_list = NULL;
2058 cur_si->si_next_list = next_list;
2059 check_keepend();
2060 }
2061 else
2062 vim_free(next_list);
2063 }
2064 }
2065 }
2066
2067 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002068 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002070 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002071 {
2072 /*
2073 * If we didn't check for a match yet, or we are past it, check
2074 * for any match with a pattern.
2075 */
2076 if (next_match_idx < 0 || next_match_col < (int)current_col)
2077 {
2078 /*
2079 * Check all relevant patterns for a match at this
2080 * position. This is complicated, because matching with a
2081 * pattern takes quite a bit of time, thus we want to
2082 * avoid doing it when it's not needed.
2083 */
2084 next_match_idx = 0; /* no match in this line yet */
2085 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002086 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002088 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 if ( spp->sp_syncing == syncing
2090 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2091 && (spp->sp_type == SPTYPE_MATCH
2092 || spp->sp_type == SPTYPE_START)
2093 && (current_next_list != NULL
2094 ? in_id_list(NULL, current_next_list,
2095 &spp->sp_syn, 0)
2096 : (cur_si == NULL
2097 ? !(spp->sp_flags & HL_CONTAINED)
2098 : in_id_list(cur_si,
2099 cur_si->si_cont_list, &spp->sp_syn,
2100 spp->sp_flags & HL_CONTAINED))))
2101 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002102 int r;
2103
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 /* If we already tried matching in this line, and
2105 * there isn't a match before next_match_col, skip
2106 * this item. */
2107 if (spp->sp_line_id == current_line_id
2108 && spp->sp_startcol >= next_match_col)
2109 continue;
2110 spp->sp_line_id = current_line_id;
2111
2112 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2113 if (lc_col < 0)
2114 lc_col = 0;
2115
2116 regmatch.rmm_ic = spp->sp_ic;
2117 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002118 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002119 current_lnum,
2120 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002121 IF_SYN_TIME(&spp->sp_time));
2122 spp->sp_prog = regmatch.regprog;
2123 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002124 {
2125 /* no match in this line, try another one */
2126 spp->sp_startcol = MAXCOL;
2127 continue;
2128 }
2129
2130 /*
2131 * Compute the first column of the match.
2132 */
2133 syn_add_start_off(&pos, &regmatch,
2134 spp, SPO_MS_OFF, -1);
2135 if (pos.lnum > current_lnum)
2136 {
2137 /* must have used end of match in a next line,
2138 * we can't handle that */
2139 spp->sp_startcol = MAXCOL;
2140 continue;
2141 }
2142 startcol = pos.col;
2143
2144 /* remember the next column where this pattern
2145 * matches in the current line */
2146 spp->sp_startcol = startcol;
2147
2148 /*
2149 * If a previously found match starts at a lower
2150 * column number, don't use this one.
2151 */
2152 if (startcol >= next_match_col)
2153 continue;
2154
2155 /*
2156 * If we matched this pattern at this position
2157 * before, skip it. Must retry in the next
2158 * column, because it may match from there.
2159 */
2160 if (did_match_already(idx, &zero_width_next_ga))
2161 {
2162 try_next_column = TRUE;
2163 continue;
2164 }
2165
2166 endpos.lnum = regmatch.endpos[0].lnum;
2167 endpos.col = regmatch.endpos[0].col;
2168
2169 /* Compute the highlight start. */
2170 syn_add_start_off(&hl_startpos, &regmatch,
2171 spp, SPO_HS_OFF, -1);
2172
2173 /* Compute the region start. */
2174 /* Default is to use the end of the match. */
2175 syn_add_end_off(&eos_pos, &regmatch,
2176 spp, SPO_RS_OFF, 0);
2177
2178 /*
2179 * Grab the external submatches before they get
2180 * overwritten. Reference count doesn't change.
2181 */
2182 unref_extmatch(cur_extmatch);
2183 cur_extmatch = re_extmatch_out;
2184 re_extmatch_out = NULL;
2185
2186 flags = 0;
2187 eoe_pos.lnum = 0; /* avoid warning */
2188 eoe_pos.col = 0;
2189 end_idx = 0;
2190 hl_endpos.lnum = 0;
2191
2192 /*
2193 * For a "oneline" the end must be found in the
2194 * same line too. Search for it after the end of
2195 * the match with the start pattern. Set the
2196 * resulting end positions at the same time.
2197 */
2198 if (spp->sp_type == SPTYPE_START
2199 && (spp->sp_flags & HL_ONELINE))
2200 {
2201 lpos_T startpos;
2202
2203 startpos = endpos;
2204 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2205 &flags, &eoe_pos, &end_idx, cur_extmatch);
2206 if (endpos.lnum == 0)
2207 continue; /* not found */
2208 }
2209
2210 /*
2211 * For a "match" the size must be > 0 after the
2212 * end offset needs has been added. Except when
2213 * syncing.
2214 */
2215 else if (spp->sp_type == SPTYPE_MATCH)
2216 {
2217 syn_add_end_off(&hl_endpos, &regmatch, spp,
2218 SPO_HE_OFF, 0);
2219 syn_add_end_off(&endpos, &regmatch, spp,
2220 SPO_ME_OFF, 0);
2221 if (endpos.lnum == current_lnum
2222 && (int)endpos.col + syncing < startcol)
2223 {
2224 /*
2225 * If an empty string is matched, may need
2226 * to try matching again at next column.
2227 */
2228 if (regmatch.startpos[0].col
2229 == regmatch.endpos[0].col)
2230 try_next_column = TRUE;
2231 continue;
2232 }
2233 }
2234
2235 /*
2236 * keep the best match so far in next_match_*
2237 */
2238 /* Highlighting must start after startpos and end
2239 * before endpos. */
2240 if (hl_startpos.lnum == current_lnum
2241 && (int)hl_startpos.col < startcol)
2242 hl_startpos.col = startcol;
2243 limit_pos_zero(&hl_endpos, &endpos);
2244
2245 next_match_idx = idx;
2246 next_match_col = startcol;
2247 next_match_m_endpos = endpos;
2248 next_match_h_endpos = hl_endpos;
2249 next_match_h_startpos = hl_startpos;
2250 next_match_flags = flags;
2251 next_match_eos_pos = eos_pos;
2252 next_match_eoe_pos = eoe_pos;
2253 next_match_end_idx = end_idx;
2254 unref_extmatch(next_match_extmatch);
2255 next_match_extmatch = cur_extmatch;
2256 cur_extmatch = NULL;
2257 }
2258 }
2259 }
2260
2261 /*
2262 * If we found a match at the current column, use it.
2263 */
2264 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2265 {
2266 synpat_T *lspp;
2267
2268 /* When a zero-width item matched which has a nextgroup,
2269 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002270 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 if (next_match_m_endpos.lnum == current_lnum
2272 && next_match_m_endpos.col == current_col
2273 && lspp->sp_next_list != NULL)
2274 {
2275 current_next_list = lspp->sp_next_list;
2276 current_next_flags = lspp->sp_flags;
2277 keep_next_list = TRUE;
2278 zero_width_next_list = TRUE;
2279
2280 /* Add the index to a list, so that we can check
2281 * later that we don't match it again (and cause an
2282 * endless loop). */
2283 if (ga_grow(&zero_width_next_ga, 1) == OK)
2284 {
2285 ((int *)(zero_width_next_ga.ga_data))
2286 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 }
2288 next_match_idx = -1;
2289 }
2290 else
2291 cur_si = push_next_match(cur_si);
2292 found_match = TRUE;
2293 }
2294 }
2295 }
2296
2297 /*
2298 * Handle searching for nextgroup match.
2299 */
2300 if (current_next_list != NULL && !keep_next_list)
2301 {
2302 /*
2303 * If a nextgroup was not found, continue looking for one if:
2304 * - this is an empty line and the "skipempty" option was given
2305 * - we are on white space and the "skipwhite" option was given
2306 */
2307 if (!found_match)
2308 {
2309 line = syn_getcurline();
2310 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002311 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 || ((current_next_flags & HL_SKIPEMPTY)
2313 && *line == NUL))
2314 break;
2315 }
2316
2317 /*
2318 * If a nextgroup was found: Use it, and continue looking for
2319 * contained matches.
2320 * If a nextgroup was not found: Continue looking for a normal
2321 * match.
2322 * When did set current_next_list for a zero-width item and no
2323 * match was found don't loop (would get stuck).
2324 */
2325 current_next_list = NULL;
2326 next_match_idx = -1;
2327 if (!zero_width_next_list)
2328 found_match = TRUE;
2329 }
2330
2331 } while (found_match);
2332
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002333 restore_chartab(buf_chartab);
2334
Bram Moolenaar071d4272004-06-13 20:20:40 +00002335 /*
2336 * Use attributes from the current state, if within its highlighting.
2337 * If not, use attributes from the current-but-one state, etc.
2338 */
2339 current_attr = 0;
2340#ifdef FEAT_EVAL
2341 current_id = 0;
2342 current_trans_id = 0;
2343#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002344#ifdef FEAT_CONCEAL
2345 current_flags = 0;
2346#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002347 if (cur_si != NULL)
2348 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002349#ifndef FEAT_EVAL
2350 int current_trans_id = 0;
2351#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2353 {
2354 sip = &CUR_STATE(idx);
2355 if ((current_lnum > sip->si_h_startpos.lnum
2356 || (current_lnum == sip->si_h_startpos.lnum
2357 && current_col >= sip->si_h_startpos.col))
2358 && (sip->si_h_endpos.lnum == 0
2359 || current_lnum < sip->si_h_endpos.lnum
2360 || (current_lnum == sip->si_h_endpos.lnum
2361 && current_col < sip->si_h_endpos.col)))
2362 {
2363 current_attr = sip->si_attr;
2364#ifdef FEAT_EVAL
2365 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002367 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002368#ifdef FEAT_CONCEAL
2369 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002370 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002371 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002372#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 break;
2374 }
2375 }
2376
Bram Moolenaar217ad922005-03-20 22:37:15 +00002377 if (can_spell != NULL)
2378 {
2379 struct sp_syn sps;
2380
2381 /*
2382 * set "can_spell" to TRUE if spell checking is supposed to be
2383 * done in the current item.
2384 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002385 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002386 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002387 /* There is no @Spell cluster: Do spelling for items without
2388 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002389 if (syn_block->b_nospell_cluster_id == 0
2390 || current_trans_id == 0)
2391 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002392 else
2393 {
2394 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002395 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002396 sps.cont_in_list = NULL;
2397 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2398 }
2399 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002400 else
2401 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002402 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002403 * the @Spell cluster. But not when @NoSpell is also there.
2404 * At the toplevel only spell check when ":syn spell toplevel"
2405 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002406 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002407 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002408 else
2409 {
2410 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002411 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002412 sps.cont_in_list = NULL;
2413 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2414
Bram Moolenaar860cae12010-06-05 23:22:07 +02002415 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002416 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002417 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002418 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2419 *can_spell = FALSE;
2420 }
2421 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002422 }
2423 }
2424
2425
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426 /*
2427 * Check for end of current state (and the states before it) at the
2428 * next column. Don't do this for syncing, because we would miss a
2429 * single character match.
2430 * First check if the current state ends at the current column. It
2431 * may be for an empty match and a containing item might end in the
2432 * current column.
2433 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002434 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435 {
2436 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002437 if (current_state.ga_len > 0
2438 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002439 {
2440 ++current_col;
2441 check_state_ends();
2442 --current_col;
2443 }
2444 }
2445 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002446 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002447 /* Default: Only do spelling when there is no @Spell cluster or when
2448 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002449 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2450 ? (syn_block->b_spell_cluster_id == 0)
2451 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002453 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454 if (current_next_list != NULL
2455 && syn_getcurline()[current_col + 1] == NUL
2456 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2457 current_next_list = NULL;
2458
2459 if (zero_width_next_ga.ga_len > 0)
2460 ga_clear(&zero_width_next_ga);
2461
2462 /* No longer need external matches. But keep next_match_extmatch. */
2463 unref_extmatch(re_extmatch_out);
2464 re_extmatch_out = NULL;
2465 unref_extmatch(cur_extmatch);
2466
2467 return current_attr;
2468}
2469
2470
2471/*
2472 * Check if we already matched pattern "idx" at the current column.
2473 */
2474 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002475did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002476{
2477 int i;
2478
2479 for (i = current_state.ga_len; --i >= 0; )
2480 if (CUR_STATE(i).si_m_startcol == (int)current_col
2481 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2482 && CUR_STATE(i).si_idx == idx)
2483 return TRUE;
2484
2485 /* Zero-width matches with a nextgroup argument are not put on the syntax
2486 * stack, and can only be matched once anyway. */
2487 for (i = gap->ga_len; --i >= 0; )
2488 if (((int *)(gap->ga_data))[i] == idx)
2489 return TRUE;
2490
2491 return FALSE;
2492}
2493
2494/*
2495 * Push the next match onto the stack.
2496 */
2497 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002498push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002499{
2500 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002501#ifdef FEAT_CONCEAL
2502 int save_flags;
2503#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504
Bram Moolenaar860cae12010-06-05 23:22:07 +02002505 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002506
2507 /*
2508 * Push the item in current_state stack;
2509 */
2510 if (push_current_state(next_match_idx) == OK)
2511 {
2512 /*
2513 * If it's a start-skip-end type that crosses lines, figure out how
2514 * much it continues in this line. Otherwise just fill in the length.
2515 */
2516 cur_si = &CUR_STATE(current_state.ga_len - 1);
2517 cur_si->si_h_startpos = next_match_h_startpos;
2518 cur_si->si_m_startcol = current_col;
2519 cur_si->si_m_lnum = current_lnum;
2520 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002521#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002522 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002523 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002524 if (current_state.ga_len > 1)
2525 cur_si->si_flags |=
2526 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2527#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002528 cur_si->si_next_list = spp->sp_next_list;
2529 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2530 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2531 {
2532 /* Try to find the end pattern in the current line */
2533 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2534 check_keepend();
2535 }
2536 else
2537 {
2538 cur_si->si_m_endpos = next_match_m_endpos;
2539 cur_si->si_h_endpos = next_match_h_endpos;
2540 cur_si->si_ends = TRUE;
2541 cur_si->si_flags |= next_match_flags;
2542 cur_si->si_eoe_pos = next_match_eoe_pos;
2543 cur_si->si_end_idx = next_match_end_idx;
2544 }
2545 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2546 keepend_level = current_state.ga_len - 1;
2547 check_keepend();
2548 update_si_attr(current_state.ga_len - 1);
2549
Bram Moolenaar860cae12010-06-05 23:22:07 +02002550#ifdef FEAT_CONCEAL
2551 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2552#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553 /*
2554 * If the start pattern has another highlight group, push another item
2555 * on the stack for the start pattern.
2556 */
2557 if ( spp->sp_type == SPTYPE_START
2558 && spp->sp_syn_match_id != 0
2559 && push_current_state(next_match_idx) == OK)
2560 {
2561 cur_si = &CUR_STATE(current_state.ga_len - 1);
2562 cur_si->si_h_startpos = next_match_h_startpos;
2563 cur_si->si_m_startcol = current_col;
2564 cur_si->si_m_lnum = current_lnum;
2565 cur_si->si_m_endpos = next_match_eos_pos;
2566 cur_si->si_h_endpos = next_match_eos_pos;
2567 cur_si->si_ends = TRUE;
2568 cur_si->si_end_idx = 0;
2569 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002570#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002571 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002572 cur_si->si_flags |= save_flags;
2573 if (cur_si->si_flags & HL_CONCEALENDS)
2574 cur_si->si_flags |= HL_CONCEAL;
2575#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002576 cur_si->si_next_list = NULL;
2577 check_keepend();
2578 update_si_attr(current_state.ga_len - 1);
2579 }
2580 }
2581
2582 next_match_idx = -1; /* try other match next time */
2583
2584 return cur_si;
2585}
2586
2587/*
2588 * Check for end of current state (and the states before it).
2589 */
2590 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002591check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592{
2593 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002594 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595
2596 cur_si = &CUR_STATE(current_state.ga_len - 1);
2597 for (;;)
2598 {
2599 if (cur_si->si_ends
2600 && (cur_si->si_m_endpos.lnum < current_lnum
2601 || (cur_si->si_m_endpos.lnum == current_lnum
2602 && cur_si->si_m_endpos.col <= current_col)))
2603 {
2604 /*
2605 * If there is an end pattern group ID, highlight the end pattern
2606 * now. No need to pop the current item from the stack.
2607 * Only do this if the end pattern continues beyond the current
2608 * position.
2609 */
2610 if (cur_si->si_end_idx
2611 && (cur_si->si_eoe_pos.lnum > current_lnum
2612 || (cur_si->si_eoe_pos.lnum == current_lnum
2613 && cur_si->si_eoe_pos.col > current_col)))
2614 {
2615 cur_si->si_idx = cur_si->si_end_idx;
2616 cur_si->si_end_idx = 0;
2617 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2618 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2619 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002620#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002621 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002622 if (cur_si->si_flags & HL_CONCEALENDS)
2623 cur_si->si_flags |= HL_CONCEAL;
2624#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002625 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002626
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002627 /* nextgroup= should not match in the end pattern */
2628 current_next_list = NULL;
2629
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002630 /* what matches next may be different now, clear it */
2631 next_match_idx = 0;
2632 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002633 break;
2634 }
2635 else
2636 {
2637 /* handle next_list, unless at end of line and no "skipnl" or
2638 * "skipempty" */
2639 current_next_list = cur_si->si_next_list;
2640 current_next_flags = cur_si->si_flags;
2641 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2642 && syn_getcurline()[current_col] == NUL)
2643 current_next_list = NULL;
2644
2645 /* When the ended item has "extend", another item with
2646 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002647 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648
2649 pop_current_state();
2650
2651 if (current_state.ga_len == 0)
2652 break;
2653
Bram Moolenaar81993f42008-01-11 20:27:45 +00002654 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002655 {
2656 syn_update_ends(FALSE);
2657 if (current_state.ga_len == 0)
2658 break;
2659 }
2660
2661 cur_si = &CUR_STATE(current_state.ga_len - 1);
2662
2663 /*
2664 * Only for a region the search for the end continues after
2665 * the end of the contained item. If the contained match
2666 * included the end-of-line, break here, the region continues.
2667 * Don't do this when:
2668 * - "keepend" is used for the contained item
2669 * - not at the end of the line (could be end="x$"me=e-1).
2670 * - "excludenl" is used (HL_HAS_EOL won't be set)
2671 */
2672 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002673 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002674 == SPTYPE_START
2675 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2676 {
2677 update_si_end(cur_si, (int)current_col, TRUE);
2678 check_keepend();
2679 if ((current_next_flags & HL_HAS_EOL)
2680 && keepend_level < 0
2681 && syn_getcurline()[current_col] == NUL)
2682 break;
2683 }
2684 }
2685 }
2686 else
2687 break;
2688 }
2689}
2690
2691/*
2692 * Update an entry in the current_state stack for a match or region. This
2693 * fills in si_attr, si_next_list and si_cont_list.
2694 */
2695 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002696update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697{
2698 stateitem_T *sip = &CUR_STATE(idx);
2699 synpat_T *spp;
2700
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002701 /* This should not happen... */
2702 if (sip->si_idx < 0)
2703 return;
2704
Bram Moolenaar860cae12010-06-05 23:22:07 +02002705 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706 if (sip->si_flags & HL_MATCH)
2707 sip->si_id = spp->sp_syn_match_id;
2708 else
2709 sip->si_id = spp->sp_syn.id;
2710 sip->si_attr = syn_id2attr(sip->si_id);
2711 sip->si_trans_id = sip->si_id;
2712 if (sip->si_flags & HL_MATCH)
2713 sip->si_cont_list = NULL;
2714 else
2715 sip->si_cont_list = spp->sp_cont_list;
2716
2717 /*
2718 * For transparent items, take attr from outer item.
2719 * Also take cont_list, if there is none.
2720 * Don't do this for the matchgroup of a start or end pattern.
2721 */
2722 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2723 {
2724 if (idx == 0)
2725 {
2726 sip->si_attr = 0;
2727 sip->si_trans_id = 0;
2728 if (sip->si_cont_list == NULL)
2729 sip->si_cont_list = ID_LIST_ALL;
2730 }
2731 else
2732 {
2733 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2734 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002735 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2736 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002737 if (sip->si_cont_list == NULL)
2738 {
2739 sip->si_flags |= HL_TRANS_CONT;
2740 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2741 }
2742 }
2743 }
2744}
2745
2746/*
2747 * Check the current stack for patterns with "keepend" flag.
2748 * Propagate the match-end to contained items, until a "skipend" item is found.
2749 */
2750 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002751check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752{
2753 int i;
2754 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002755 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 stateitem_T *sip;
2757
2758 /*
2759 * This check can consume a lot of time; only do it from the level where
2760 * there really is a keepend.
2761 */
2762 if (keepend_level < 0)
2763 return;
2764
2765 /*
2766 * Find the last index of an "extend" item. "keepend" items before that
2767 * won't do anything. If there is no "extend" item "i" will be
2768 * "keepend_level" and all "keepend" items will work normally.
2769 */
2770 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2771 if (CUR_STATE(i).si_flags & HL_EXTEND)
2772 break;
2773
2774 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002775 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002776 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002777 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778 for ( ; i < current_state.ga_len; ++i)
2779 {
2780 sip = &CUR_STATE(i);
2781 if (maxpos.lnum != 0)
2782 {
2783 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002784 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002785 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2786 sip->si_ends = TRUE;
2787 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002788 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2789 {
2790 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 || maxpos.lnum > sip->si_m_endpos.lnum
2792 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002793 && maxpos.col > sip->si_m_endpos.col))
2794 maxpos = sip->si_m_endpos;
2795 if (maxpos_h.lnum == 0
2796 || maxpos_h.lnum > sip->si_h_endpos.lnum
2797 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2798 && maxpos_h.col > sip->si_h_endpos.col))
2799 maxpos_h = sip->si_h_endpos;
2800 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002801 }
2802}
2803
2804/*
2805 * Update an entry in the current_state stack for a start-skip-end pattern.
2806 * This finds the end of the current item, if it's in the current line.
2807 *
2808 * Return the flags for the matched END.
2809 */
2810 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002811update_si_end(
2812 stateitem_T *sip,
2813 int startcol, /* where to start searching for the end */
2814 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002815{
2816 lpos_T startpos;
2817 lpos_T endpos;
2818 lpos_T hl_endpos;
2819 lpos_T end_endpos;
2820 int end_idx;
2821
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002822 /* return quickly for a keyword */
2823 if (sip->si_idx < 0)
2824 return;
2825
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826 /* Don't update when it's already done. Can be a match of an end pattern
2827 * that started in a previous line. Watch out: can also be a "keepend"
2828 * from a containing item. */
2829 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2830 return;
2831
2832 /*
2833 * We need to find the end of the region. It may continue in the next
2834 * line.
2835 */
2836 end_idx = 0;
2837 startpos.lnum = current_lnum;
2838 startpos.col = startcol;
2839 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2840 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2841
2842 if (endpos.lnum == 0)
2843 {
2844 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002845 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846 {
2847 /* a "oneline" never continues in the next line */
2848 sip->si_ends = TRUE;
2849 sip->si_m_endpos.lnum = current_lnum;
2850 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2851 }
2852 else
2853 {
2854 /* continues in the next line */
2855 sip->si_ends = FALSE;
2856 sip->si_m_endpos.lnum = 0;
2857 }
2858 sip->si_h_endpos = sip->si_m_endpos;
2859 }
2860 else
2861 {
2862 /* match within this line */
2863 sip->si_m_endpos = endpos;
2864 sip->si_h_endpos = hl_endpos;
2865 sip->si_eoe_pos = end_endpos;
2866 sip->si_ends = TRUE;
2867 sip->si_end_idx = end_idx;
2868 }
2869}
2870
2871/*
2872 * Add a new state to the current state stack.
2873 * It is cleared and the index set to "idx".
2874 * Return FAIL if it's not possible (out of memory).
2875 */
2876 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002877push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878{
2879 if (ga_grow(&current_state, 1) == FAIL)
2880 return FAIL;
2881 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2882 CUR_STATE(current_state.ga_len).si_idx = idx;
2883 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 return OK;
2885}
2886
2887/*
2888 * Remove a state from the current_state stack.
2889 */
2890 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002891pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892{
2893 if (current_state.ga_len)
2894 {
2895 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2896 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002897 }
2898 /* after the end of a pattern, try matching a keyword or pattern */
2899 next_match_idx = -1;
2900
2901 /* if first state with "keepend" is popped, reset keepend_level */
2902 if (keepend_level >= current_state.ga_len)
2903 keepend_level = -1;
2904}
2905
2906/*
2907 * Find the end of a start/skip/end syntax region after "startpos".
2908 * Only checks one line.
2909 * Also handles a match item that continued from a previous line.
2910 * If not found, the syntax item continues in the next line. m_endpos->lnum
2911 * will be 0.
2912 * If found, the end of the region and the end of the highlighting is
2913 * computed.
2914 */
2915 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002916find_endpos(
2917 int idx, /* index of the pattern */
2918 lpos_T *startpos, /* where to start looking for an END match */
2919 lpos_T *m_endpos, /* return: end of match */
2920 lpos_T *hl_endpos, /* return: end of highlighting */
2921 long *flagsp, /* return: flags of matching END */
2922 lpos_T *end_endpos, /* return: end of end pattern match */
2923 int *end_idx, /* return: group ID for end pat. match, or 0 */
2924 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925{
2926 colnr_T matchcol;
2927 synpat_T *spp, *spp_skip;
2928 int start_idx;
2929 int best_idx;
2930 regmmatch_T regmatch;
2931 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2932 lpos_T pos;
2933 char_u *line;
2934 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002935 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002936
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002937 /* just in case we are invoked for a keyword */
2938 if (idx < 0)
2939 return;
2940
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 /*
2942 * Check for being called with a START pattern.
2943 * Can happen with a match that continues to the next line, because it
2944 * contained a region.
2945 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002946 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947 if (spp->sp_type != SPTYPE_START)
2948 {
2949 *hl_endpos = *startpos;
2950 return;
2951 }
2952
2953 /*
2954 * Find the SKIP or first END pattern after the last START pattern.
2955 */
2956 for (;;)
2957 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002958 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 if (spp->sp_type != SPTYPE_START)
2960 break;
2961 ++idx;
2962 }
2963
2964 /*
2965 * Lookup the SKIP pattern (if present)
2966 */
2967 if (spp->sp_type == SPTYPE_SKIP)
2968 {
2969 spp_skip = spp;
2970 ++idx;
2971 }
2972 else
2973 spp_skip = NULL;
2974
2975 /* Setup external matches for syn_regexec(). */
2976 unref_extmatch(re_extmatch_in);
2977 re_extmatch_in = ref_extmatch(start_ext);
2978
2979 matchcol = startpos->col; /* start looking for a match at sstart */
2980 start_idx = idx; /* remember the first END pattern. */
2981 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002982
2983 /* use syntax iskeyword option */
2984 save_chartab(buf_chartab);
2985
Bram Moolenaar071d4272004-06-13 20:20:40 +00002986 for (;;)
2987 {
2988 /*
2989 * Find end pattern that matches first after "matchcol".
2990 */
2991 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002992 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002993 {
2994 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002995 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996
Bram Moolenaar860cae12010-06-05 23:22:07 +02002997 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002998 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2999 break;
3000 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3001 if (lc_col < 0)
3002 lc_col = 0;
3003
3004 regmatch.rmm_ic = spp->sp_ic;
3005 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003006 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3007 IF_SYN_TIME(&spp->sp_time));
3008 spp->sp_prog = regmatch.regprog;
3009 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010 {
3011 if (best_idx == -1 || regmatch.startpos[0].col
3012 < best_regmatch.startpos[0].col)
3013 {
3014 best_idx = idx;
3015 best_regmatch.startpos[0] = regmatch.startpos[0];
3016 best_regmatch.endpos[0] = regmatch.endpos[0];
3017 }
3018 }
3019 }
3020
3021 /*
3022 * If all end patterns have been tried, and there is no match, the
3023 * item continues until end-of-line.
3024 */
3025 if (best_idx == -1)
3026 break;
3027
3028 /*
3029 * If the skip pattern matches before the end pattern,
3030 * continue searching after the skip pattern.
3031 */
3032 if (spp_skip != NULL)
3033 {
3034 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003035 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003036
3037 if (lc_col < 0)
3038 lc_col = 0;
3039 regmatch.rmm_ic = spp_skip->sp_ic;
3040 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003041 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3042 IF_SYN_TIME(&spp_skip->sp_time));
3043 spp_skip->sp_prog = regmatch.regprog;
3044 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045 <= best_regmatch.startpos[0].col)
3046 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003047 int line_len;
3048
Bram Moolenaar071d4272004-06-13 20:20:40 +00003049 /* Add offset to skip pattern match */
3050 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3051
3052 /* If the skip pattern goes on to the next line, there is no
3053 * match with an end pattern in this line. */
3054 if (pos.lnum > startpos->lnum)
3055 break;
3056
3057 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003058 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003059
3060 /* take care of an empty match or negative offset */
3061 if (pos.col <= matchcol)
3062 ++matchcol;
3063 else if (pos.col <= regmatch.endpos[0].col)
3064 matchcol = pos.col;
3065 else
3066 /* Be careful not to jump over the NUL at the end-of-line */
3067 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003068 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003069 ++matchcol)
3070 ;
3071
3072 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003073 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074 break;
3075
3076 continue; /* start with first end pattern again */
3077 }
3078 }
3079
3080 /*
3081 * Match from start pattern to end pattern.
3082 * Correct for match and highlight offset of end pattern.
3083 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003084 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003085 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3086 /* can't end before the start */
3087 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3088 m_endpos->col = startpos->col;
3089
3090 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3091 /* can't end before the start */
3092 if (end_endpos->lnum == startpos->lnum
3093 && end_endpos->col < startpos->col)
3094 end_endpos->col = startpos->col;
3095 /* can't end after the match */
3096 limit_pos(end_endpos, m_endpos);
3097
3098 /*
3099 * If the end group is highlighted differently, adjust the pointers.
3100 */
3101 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3102 {
3103 *end_idx = best_idx;
3104 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3105 {
3106 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3107 hl_endpos->col = best_regmatch.endpos[0].col;
3108 }
3109 else
3110 {
3111 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3112 hl_endpos->col = best_regmatch.startpos[0].col;
3113 }
3114 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3115
3116 /* can't end before the start */
3117 if (hl_endpos->lnum == startpos->lnum
3118 && hl_endpos->col < startpos->col)
3119 hl_endpos->col = startpos->col;
3120 limit_pos(hl_endpos, m_endpos);
3121
3122 /* now the match ends where the highlighting ends, it is turned
3123 * into the matchgroup for the end */
3124 *m_endpos = *hl_endpos;
3125 }
3126 else
3127 {
3128 *end_idx = 0;
3129 *hl_endpos = *end_endpos;
3130 }
3131
3132 *flagsp = spp->sp_flags;
3133
3134 had_match = TRUE;
3135 break;
3136 }
3137
3138 /* no match for an END pattern in this line */
3139 if (!had_match)
3140 m_endpos->lnum = 0;
3141
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003142 restore_chartab(buf_chartab);
3143
Bram Moolenaar071d4272004-06-13 20:20:40 +00003144 /* Remove external matches. */
3145 unref_extmatch(re_extmatch_in);
3146 re_extmatch_in = NULL;
3147}
3148
3149/*
3150 * Limit "pos" not to be after "limit".
3151 */
3152 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003153limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003154{
3155 if (pos->lnum > limit->lnum)
3156 *pos = *limit;
3157 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3158 pos->col = limit->col;
3159}
3160
3161/*
3162 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3163 */
3164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003165limit_pos_zero(
3166 lpos_T *pos,
3167 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003168{
3169 if (pos->lnum == 0)
3170 *pos = *limit;
3171 else
3172 limit_pos(pos, limit);
3173}
3174
3175/*
3176 * Add offset to matched text for end of match or highlight.
3177 */
3178 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003179syn_add_end_off(
3180 lpos_T *result, /* returned position */
3181 regmmatch_T *regmatch, /* start/end of match */
3182 synpat_T *spp, /* matched pattern */
3183 int idx, /* index of offset */
3184 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185{
3186 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003187 int off;
3188 char_u *base;
3189 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190
3191 if (spp->sp_off_flags & (1 << idx))
3192 {
3193 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003194 col = regmatch->startpos[0].col;
3195 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 }
3197 else
3198 {
3199 result->lnum = regmatch->endpos[0].lnum;
3200 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003201 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003203 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3204 * is a matchgroup. Watch out for match with last NL in the buffer. */
3205 if (result->lnum > syn_buf->b_ml.ml_line_count)
3206 col = 0;
3207 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003208 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003209 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3210 p = base + col;
3211 if (off > 0)
3212 {
3213 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003214 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003215 }
3216 else if (off < 0)
3217 {
3218 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003219 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 }
3221 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003222 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003223 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003224}
3225
3226/*
3227 * Add offset to matched text for start of match or highlight.
3228 * Avoid resulting column to become negative.
3229 */
3230 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003231syn_add_start_off(
3232 lpos_T *result, /* returned position */
3233 regmmatch_T *regmatch, /* start/end of match */
3234 synpat_T *spp,
3235 int idx,
3236 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003237{
3238 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003239 int off;
3240 char_u *base;
3241 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003242
3243 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3244 {
3245 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003246 col = regmatch->endpos[0].col;
3247 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248 }
3249 else
3250 {
3251 result->lnum = regmatch->startpos[0].lnum;
3252 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003253 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003255 if (result->lnum > syn_buf->b_ml.ml_line_count)
3256 {
3257 /* a "\n" at the end of the pattern may take us below the last line */
3258 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003259 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003260 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003261 if (off != 0)
3262 {
3263 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3264 p = base + col;
3265 if (off > 0)
3266 {
3267 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003268 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003269 }
3270 else if (off < 0)
3271 {
3272 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003273 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003274 }
3275 col = (int)(p - base);
3276 }
3277 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278}
3279
3280/*
3281 * Get current line in syntax buffer.
3282 */
3283 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003284syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285{
3286 return ml_get_buf(syn_buf, current_lnum, FALSE);
3287}
3288
3289/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003290 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291 * Returns TRUE when there is a match.
3292 */
3293 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003294syn_regexec(
3295 regmmatch_T *rmp,
3296 linenr_T lnum,
3297 colnr_T col,
3298 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003300 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003301#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003302 proftime_T pt;
3303
3304 if (syn_time_on)
3305 profile_start(&pt);
3306#endif
3307
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003308 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003309 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3310
Bram Moolenaarf7512552013-06-06 14:55:19 +02003311#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003312 if (syn_time_on)
3313 {
3314 profile_end(&pt);
3315 profile_add(&st->total, &pt);
3316 if (profile_cmp(&pt, &st->slowest) < 0)
3317 st->slowest = pt;
3318 ++st->count;
3319 if (r > 0)
3320 ++st->match;
3321 }
3322#endif
3323
3324 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325 {
3326 rmp->startpos[0].lnum += lnum;
3327 rmp->endpos[0].lnum += lnum;
3328 return TRUE;
3329 }
3330 return FALSE;
3331}
3332
3333/*
3334 * Check one position in a line for a matching keyword.
3335 * The caller must check if a keyword can start at startcol.
3336 * Return it's ID if found, 0 otherwise.
3337 */
3338 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003339check_keyword_id(
3340 char_u *line,
3341 int startcol, /* position in line to check for keyword */
3342 int *endcolp, /* return: character after found keyword */
3343 long *flagsp, /* return: flags of matching keyword */
3344 short **next_listp, /* return: next_list of matching keyword */
3345 stateitem_T *cur_si, /* item at the top of the stack */
3346 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003348 keyentry_T *kp;
3349 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003351 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003353 hashtab_T *ht;
3354 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355
3356 /* Find first character after the keyword. First character was already
3357 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003358 kwp = line + startcol;
3359 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360 do
3361 {
3362#ifdef FEAT_MBYTE
3363 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003364 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365 else
3366#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003367 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003369 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370
Bram Moolenaardad6b692005-01-25 22:14:34 +00003371 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372 return 0;
3373
3374 /*
3375 * Must make a copy of the keyword, so we can add a NUL and make it
3376 * lowercase.
3377 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003378 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379
3380 /*
3381 * Try twice:
3382 * 1. matching case
3383 * 2. ignoring case
3384 */
3385 for (round = 1; round <= 2; ++round)
3386 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003387 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003390 if (round == 2) /* ignore case */
3391 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392
3393 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003394 * Find keywords that match. There can be several with different
3395 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396 * When current_next_list is non-zero accept only that group, otherwise:
3397 * Accept a not-contained keyword at toplevel.
3398 * Accept a keyword at other levels only if it is in the contains list.
3399 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003400 hi = hash_find(ht, keyword);
3401 if (!HASHITEM_EMPTY(hi))
3402 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003403 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003404 if (current_next_list != 0
3405 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3406 : (cur_si == NULL
3407 ? !(kp->flags & HL_CONTAINED)
3408 : in_id_list(cur_si, cur_si->si_cont_list,
3409 &kp->k_syn, kp->flags & HL_CONTAINED)))
3410 {
3411 *endcolp = startcol + kwlen;
3412 *flagsp = kp->flags;
3413 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003414#ifdef FEAT_CONCEAL
3415 *ccharp = kp->k_char;
3416#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003417 return kp->k_syn.id;
3418 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003419 }
3420 }
3421 return 0;
3422}
3423
3424/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003425 * Handle ":syntax conceal" command.
3426 */
3427 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003428syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003429{
3430#ifdef FEAT_CONCEAL
3431 char_u *arg = eap->arg;
3432 char_u *next;
3433
3434 eap->nextcmd = find_nextcmd(arg);
3435 if (eap->skip)
3436 return;
3437
3438 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003439 if (*arg == NUL)
3440 {
3441 if (curwin->w_s->b_syn_conceal)
3442 MSG(_("syn conceal on"));
3443 else
3444 MSG(_("syn conceal off"));
3445 }
3446 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003447 curwin->w_s->b_syn_conceal = TRUE;
3448 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3449 curwin->w_s->b_syn_conceal = FALSE;
3450 else
3451 EMSG2(_("E390: Illegal argument: %s"), arg);
3452#endif
3453}
3454
3455/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456 * Handle ":syntax case" command.
3457 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003459syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460{
3461 char_u *arg = eap->arg;
3462 char_u *next;
3463
3464 eap->nextcmd = find_nextcmd(arg);
3465 if (eap->skip)
3466 return;
3467
3468 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003469 if (*arg == NUL)
3470 {
3471 if (curwin->w_s->b_syn_ic)
3472 MSG(_("syntax case ignore"));
3473 else
3474 MSG(_("syntax case match"));
3475 }
3476 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480 else
3481 EMSG2(_("E390: Illegal argument: %s"), arg);
3482}
3483
3484/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003485 * Handle ":syntax spell" command.
3486 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003487 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003488syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003489{
3490 char_u *arg = eap->arg;
3491 char_u *next;
3492
3493 eap->nextcmd = find_nextcmd(arg);
3494 if (eap->skip)
3495 return;
3496
3497 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003498 if (*arg == NUL)
3499 {
3500 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3501 MSG(_("syntax spell toplevel"));
3502 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3503 MSG(_("syntax spell notoplevel"));
3504 else
3505 MSG(_("syntax spell default"));
3506 }
3507 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003509 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003511 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003512 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003513 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003514 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003515 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003516 return;
3517 }
3518
3519 /* assume spell checking changed, force a redraw */
3520 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003521}
3522
3523/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003524 * Handle ":syntax iskeyword" command.
3525 */
3526 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003527syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003528{
3529 char_u *arg = eap->arg;
3530 char_u save_chartab[32];
3531 char_u *save_isk;
3532
3533 if (eap->skip)
3534 return;
3535
3536 arg = skipwhite(arg);
3537 if (*arg == NUL)
3538 {
3539 MSG_PUTS("\n");
3540 MSG_PUTS(_("syntax iskeyword "));
3541 if (curwin->w_s->b_syn_isk != empty_option)
3542 msg_outtrans(curwin->w_s->b_syn_isk);
3543 else
3544 msg_outtrans((char_u *)"not set");
3545 }
3546 else
3547 {
3548 if (STRNICMP(arg, "clear", 5) == 0)
3549 {
3550 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3551 (size_t)32);
3552 clear_string_option(&curwin->w_s->b_syn_isk);
3553 }
3554 else
3555 {
3556 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3557 save_isk = curbuf->b_p_isk;
3558 curbuf->b_p_isk = vim_strsave(arg);
3559
3560 buf_init_chartab(curbuf, FALSE);
3561 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3562 (size_t)32);
3563 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3564 clear_string_option(&curwin->w_s->b_syn_isk);
3565 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3566 curbuf->b_p_isk = save_isk;
3567 }
3568 }
3569 redraw_win_later(curwin, NOT_VALID);
3570}
3571
3572/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003573 * Clear all syntax info for one buffer.
3574 */
3575 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003576syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577{
3578 int i;
3579
Bram Moolenaar860cae12010-06-05 23:22:07 +02003580 block->b_syn_error = FALSE; /* clear previous error */
3581 block->b_syn_ic = FALSE; /* Use case, by default */
3582 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3583 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003584#ifdef FEAT_CONCEAL
3585 block->b_syn_conceal = FALSE;
3586#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587
3588 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 clear_keywtab(&block->b_keywtab);
3590 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591
3592 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003593 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3594 syn_clear_pattern(block, i);
3595 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596
3597 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003598 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3599 syn_clear_cluster(block, i);
3600 ga_clear(&block->b_syn_clusters);
3601 block->b_spell_cluster_id = 0;
3602 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603
Bram Moolenaar860cae12010-06-05 23:22:07 +02003604 block->b_syn_sync_flags = 0;
3605 block->b_syn_sync_minlines = 0;
3606 block->b_syn_sync_maxlines = 0;
3607 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608
Bram Moolenaar473de612013-06-08 18:19:48 +02003609 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003610 block->b_syn_linecont_prog = NULL;
3611 vim_free(block->b_syn_linecont_pat);
3612 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003616 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617
3618 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003619 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003621
3622 /* Reset the counter for ":syn include" */
3623 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624}
3625
3626/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003627 * Get rid of ownsyntax for window "wp".
3628 */
3629 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003630reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003631{
3632 if (wp->w_s != &wp->w_buffer->b_s)
3633 {
3634 syntax_clear(wp->w_s);
3635 vim_free(wp->w_s);
3636 wp->w_s = &wp->w_buffer->b_s;
3637 }
3638}
3639
3640/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641 * Clear syncing info for one buffer.
3642 */
3643 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003644syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645{
3646 int i;
3647
3648 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003649 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3650 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3651 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652
Bram Moolenaar860cae12010-06-05 23:22:07 +02003653 curwin->w_s->b_syn_sync_flags = 0;
3654 curwin->w_s->b_syn_sync_minlines = 0;
3655 curwin->w_s->b_syn_sync_maxlines = 0;
3656 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657
Bram Moolenaar473de612013-06-08 18:19:48 +02003658 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003659 curwin->w_s->b_syn_linecont_prog = NULL;
3660 vim_free(curwin->w_s->b_syn_linecont_pat);
3661 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003662 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003664 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665}
3666
3667/*
3668 * Remove one pattern from the buffer's pattern list.
3669 */
3670 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003671syn_remove_pattern(
3672 synblock_T *block,
3673 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674{
3675 synpat_T *spp;
3676
Bram Moolenaar860cae12010-06-05 23:22:07 +02003677 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678#ifdef FEAT_FOLDING
3679 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003680 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003682 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003684 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3685 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686}
3687
3688/*
3689 * Clear and free one syntax pattern. When clearing all, must be called from
3690 * last to first!
3691 */
3692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003693syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003695 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003696 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003698 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003699 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003700 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3701 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3702 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703 }
3704}
3705
3706/*
3707 * Clear and free one syntax cluster.
3708 */
3709 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003710syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003712 vim_free(SYN_CLSTR(block)[i].scl_name);
3713 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3714 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715}
3716
3717/*
3718 * Handle ":syntax clear" command.
3719 */
3720 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003721syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722{
3723 char_u *arg = eap->arg;
3724 char_u *arg_end;
3725 int id;
3726
3727 eap->nextcmd = find_nextcmd(arg);
3728 if (eap->skip)
3729 return;
3730
3731 /*
3732 * We have to disable this within ":syn include @group filename",
3733 * because otherwise @group would get deleted.
3734 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3735 * clear".
3736 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003737 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738 return;
3739
3740 if (ends_excmd(*arg))
3741 {
3742 /*
3743 * No argument: Clear all syntax items.
3744 */
3745 if (syncing)
3746 syntax_sync_clear();
3747 else
3748 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003749 syntax_clear(curwin->w_s);
3750 if (curwin->w_s == &curwin->w_buffer->b_s)
3751 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003752 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 }
3754 }
3755 else
3756 {
3757 /*
3758 * Clear the group IDs that are in the argument.
3759 */
3760 while (!ends_excmd(*arg))
3761 {
3762 arg_end = skiptowhite(arg);
3763 if (*arg == '@')
3764 {
3765 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3766 if (id == 0)
3767 {
3768 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3769 break;
3770 }
3771 else
3772 {
3773 /*
3774 * We can't physically delete a cluster without changing
3775 * the IDs of other clusters, so we do the next best thing
3776 * and make it empty.
3777 */
3778 short scl_id = id - SYNID_CLUSTER;
3779
Bram Moolenaar860cae12010-06-05 23:22:07 +02003780 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3781 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 }
3783 }
3784 else
3785 {
3786 id = syn_namen2id(arg, (int)(arg_end - arg));
3787 if (id == 0)
3788 {
3789 EMSG2(_(e_nogroup), arg);
3790 break;
3791 }
3792 else
3793 syn_clear_one(id, syncing);
3794 }
3795 arg = skipwhite(arg_end);
3796 }
3797 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003798 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003799 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800}
3801
3802/*
3803 * Clear one syntax group for the current buffer.
3804 */
3805 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003806syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807{
3808 synpat_T *spp;
3809 int idx;
3810
3811 /* Clear keywords only when not ":syn sync clear group-name" */
3812 if (!syncing)
3813 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003814 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3815 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 }
3817
3818 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003819 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003821 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3823 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003824 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 }
3826}
3827
3828/*
3829 * Handle ":syntax on" command.
3830 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003832syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833{
3834 syn_cmd_onoff(eap, "syntax");
3835}
3836
3837/*
3838 * Handle ":syntax enable" command.
3839 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003841syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842{
3843 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3844 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003845 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846}
3847
3848/*
3849 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003850 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003853syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854{
3855 eap->nextcmd = check_nextcmd(eap->arg);
3856 if (!eap->skip)
3857 {
3858 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3859 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003860 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003861 }
3862}
3863
3864/*
3865 * Handle ":syntax manual" command.
3866 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003868syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869{
3870 syn_cmd_onoff(eap, "manual");
3871}
3872
3873/*
3874 * Handle ":syntax off" command.
3875 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003877syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003878{
3879 syn_cmd_onoff(eap, "nosyntax");
3880}
3881
3882 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003883syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884{
3885 char_u buf[100];
3886
3887 eap->nextcmd = check_nextcmd(eap->arg);
3888 if (!eap->skip)
3889 {
3890 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003891 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 do_cmdline_cmd(buf);
3893 }
3894}
3895
3896/*
3897 * Handle ":syntax [list]" command: list current syntax words.
3898 */
3899 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003900syn_cmd_list(
3901 exarg_T *eap,
3902 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903{
3904 char_u *arg = eap->arg;
3905 int id;
3906 char_u *arg_end;
3907
3908 eap->nextcmd = find_nextcmd(arg);
3909 if (eap->skip)
3910 return;
3911
Bram Moolenaar860cae12010-06-05 23:22:07 +02003912 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003914 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003915 return;
3916 }
3917
3918 if (syncing)
3919 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003920 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 {
3922 MSG_PUTS(_("syncing on C-style comments"));
3923 syn_lines_msg();
3924 syn_match_msg();
3925 return;
3926 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003929 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 MSG_PUTS(_("no syncing"));
3931 else
3932 {
3933 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003934 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 MSG_PUTS(_(" lines before top line"));
3936 syn_match_msg();
3937 }
3938 return;
3939 }
3940 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003941 if (curwin->w_s->b_syn_sync_minlines > 0
3942 || curwin->w_s->b_syn_sync_maxlines > 0
3943 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944 {
3945 MSG_PUTS(_("\nsyncing on items"));
3946 syn_lines_msg();
3947 syn_match_msg();
3948 }
3949 }
3950 else
3951 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3952 if (ends_excmd(*arg))
3953 {
3954 /*
3955 * No argument: List all group IDs and all syntax clusters.
3956 */
3957 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3958 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003959 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960 syn_list_cluster(id);
3961 }
3962 else
3963 {
3964 /*
3965 * List the group IDs and syntax clusters that are in the argument.
3966 */
3967 while (!ends_excmd(*arg) && !got_int)
3968 {
3969 arg_end = skiptowhite(arg);
3970 if (*arg == '@')
3971 {
3972 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3973 if (id == 0)
3974 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3975 else
3976 syn_list_cluster(id - SYNID_CLUSTER);
3977 }
3978 else
3979 {
3980 id = syn_namen2id(arg, (int)(arg_end - arg));
3981 if (id == 0)
3982 EMSG2(_(e_nogroup), arg);
3983 else
3984 syn_list_one(id, syncing, TRUE);
3985 }
3986 arg = skipwhite(arg_end);
3987 }
3988 }
3989 eap->nextcmd = check_nextcmd(arg);
3990}
3991
3992 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003993syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003995 if (curwin->w_s->b_syn_sync_maxlines > 0
3996 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997 {
3998 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003999 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000 {
4001 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004002 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4003 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004 MSG_PUTS(", ");
4005 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004006 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007 {
4008 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004009 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010 }
4011 MSG_PUTS(_(" lines before top line"));
4012 }
4013}
4014
4015 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004016syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004018 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 {
4020 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004021 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004022 MSG_PUTS(_(" line breaks"));
4023 }
4024}
4025
4026static int last_matchgroup;
4027
4028struct name_list
4029{
4030 int flag;
4031 char *name;
4032};
4033
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004034static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035
4036/*
4037 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4038 */
4039 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004040syn_list_one(
4041 int id,
4042 int syncing, /* when TRUE: list syncing items */
4043 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044{
4045 int attr;
4046 int idx;
4047 int did_header = FALSE;
4048 synpat_T *spp;
4049 static struct name_list namelist1[] =
4050 {
4051 {HL_DISPLAY, "display"},
4052 {HL_CONTAINED, "contained"},
4053 {HL_ONELINE, "oneline"},
4054 {HL_KEEPEND, "keepend"},
4055 {HL_EXTEND, "extend"},
4056 {HL_EXCLUDENL, "excludenl"},
4057 {HL_TRANSP, "transparent"},
4058 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004059#ifdef FEAT_CONCEAL
4060 {HL_CONCEAL, "conceal"},
4061 {HL_CONCEALENDS, "concealends"},
4062#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063 {0, NULL}
4064 };
4065 static struct name_list namelist2[] =
4066 {
4067 {HL_SKIPWHITE, "skipwhite"},
4068 {HL_SKIPNL, "skipnl"},
4069 {HL_SKIPEMPTY, "skipempty"},
4070 {0, NULL}
4071 };
4072
Bram Moolenaar8820b482017-03-16 17:23:31 +01004073 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004074
4075 /* list the keywords for "id" */
4076 if (!syncing)
4077 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004078 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4079 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004080 did_header, attr);
4081 }
4082
4083 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004084 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004086 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4088 continue;
4089
4090 (void)syn_list_header(did_header, 999, id);
4091 did_header = TRUE;
4092 last_matchgroup = 0;
4093 if (spp->sp_type == SPTYPE_MATCH)
4094 {
4095 put_pattern("match", ' ', spp, attr);
4096 msg_putchar(' ');
4097 }
4098 else if (spp->sp_type == SPTYPE_START)
4099 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004100 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4101 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4102 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4103 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4104 while (idx < curwin->w_s->b_syn_patterns.ga_len
4105 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4106 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107 --idx;
4108 msg_putchar(' ');
4109 }
4110 syn_list_flags(namelist1, spp->sp_flags, attr);
4111
4112 if (spp->sp_cont_list != NULL)
4113 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4114
4115 if (spp->sp_syn.cont_in_list != NULL)
4116 put_id_list((char_u *)"containedin",
4117 spp->sp_syn.cont_in_list, attr);
4118
4119 if (spp->sp_next_list != NULL)
4120 {
4121 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4122 syn_list_flags(namelist2, spp->sp_flags, attr);
4123 }
4124 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4125 {
4126 if (spp->sp_flags & HL_SYNC_HERE)
4127 msg_puts_attr((char_u *)"grouphere", attr);
4128 else
4129 msg_puts_attr((char_u *)"groupthere", attr);
4130 msg_putchar(' ');
4131 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004132 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4134 else
4135 MSG_PUTS("NONE");
4136 msg_putchar(' ');
4137 }
4138 }
4139
4140 /* list the link, if there is one */
4141 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4142 {
4143 (void)syn_list_header(did_header, 999, id);
4144 msg_puts_attr((char_u *)"links to", attr);
4145 msg_putchar(' ');
4146 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4147 }
4148}
4149
4150 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004151syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152{
4153 int i;
4154
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004155 for (i = 0; nlist[i].flag != 0; ++i)
4156 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004158 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 msg_putchar(' ');
4160 }
4161}
4162
4163/*
4164 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4165 */
4166 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004167syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168{
4169 int endcol = 15;
4170
4171 /* slight hack: roughly duplicate the guts of syn_list_header() */
4172 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004173 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174
4175 if (msg_col >= endcol) /* output at least one space */
4176 endcol = msg_col + 1;
4177 if (Columns <= endcol) /* avoid hang for tiny window */
4178 endcol = Columns - 1;
4179
4180 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004181 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004183 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004184 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 }
4186 else
4187 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004188 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 msg_puts((char_u *)"=NONE");
4190 }
4191}
4192
4193 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004194put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195{
4196 short *p;
4197
4198 msg_puts_attr(name, attr);
4199 msg_putchar('=');
4200 for (p = list; *p; ++p)
4201 {
4202 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4203 {
4204 if (p[1])
4205 MSG_PUTS("ALLBUT");
4206 else
4207 MSG_PUTS("ALL");
4208 }
4209 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4210 {
4211 MSG_PUTS("TOP");
4212 }
4213 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4214 {
4215 MSG_PUTS("CONTAINED");
4216 }
4217 else if (*p >= SYNID_CLUSTER)
4218 {
4219 short scl_id = *p - SYNID_CLUSTER;
4220
4221 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004222 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 }
4224 else
4225 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4226 if (p[1])
4227 msg_putchar(',');
4228 }
4229 msg_putchar(' ');
4230}
4231
4232 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004233put_pattern(
4234 char *s,
4235 int c,
4236 synpat_T *spp,
4237 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004238{
4239 long n;
4240 int mask;
4241 int first;
4242 static char *sepchars = "/+=-#@\"|'^&";
4243 int i;
4244
4245 /* May have to write "matchgroup=group" */
4246 if (last_matchgroup != spp->sp_syn_match_id)
4247 {
4248 last_matchgroup = spp->sp_syn_match_id;
4249 msg_puts_attr((char_u *)"matchgroup", attr);
4250 msg_putchar('=');
4251 if (last_matchgroup == 0)
4252 msg_outtrans((char_u *)"NONE");
4253 else
4254 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4255 msg_putchar(' ');
4256 }
4257
4258 /* output the name of the pattern and an '=' or ' ' */
4259 msg_puts_attr((char_u *)s, attr);
4260 msg_putchar(c);
4261
4262 /* output the pattern, in between a char that is not in the pattern */
4263 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4264 if (sepchars[++i] == NUL)
4265 {
4266 i = 0; /* no good char found, just use the first one */
4267 break;
4268 }
4269 msg_putchar(sepchars[i]);
4270 msg_outtrans(spp->sp_pattern);
4271 msg_putchar(sepchars[i]);
4272
4273 /* output any pattern options */
4274 first = TRUE;
4275 for (i = 0; i < SPO_COUNT; ++i)
4276 {
4277 mask = (1 << i);
4278 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4279 {
4280 if (!first)
4281 msg_putchar(','); /* separate with commas */
4282 msg_puts((char_u *)spo_name_tab[i]);
4283 n = spp->sp_offsets[i];
4284 if (i != SPO_LC_OFF)
4285 {
4286 if (spp->sp_off_flags & mask)
4287 msg_putchar('s');
4288 else
4289 msg_putchar('e');
4290 if (n > 0)
4291 msg_putchar('+');
4292 }
4293 if (n || i == SPO_LC_OFF)
4294 msg_outnum(n);
4295 first = FALSE;
4296 }
4297 }
4298 msg_putchar(' ');
4299}
4300
4301/*
4302 * List or clear the keywords for one syntax group.
4303 * Return TRUE if the header has been printed.
4304 */
4305 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004306syn_list_keywords(
4307 int id,
4308 hashtab_T *ht,
4309 int did_header, /* header has already been printed */
4310 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004313 hashitem_T *hi;
4314 keyentry_T *kp;
4315 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 int prev_contained = 0;
4317 short *prev_next_list = NULL;
4318 short *prev_cont_in_list = NULL;
4319 int prev_skipnl = 0;
4320 int prev_skipwhite = 0;
4321 int prev_skipempty = 0;
4322
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323 /*
4324 * Unfortunately, this list of keywords is not sorted on alphabet but on
4325 * hash value...
4326 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004327 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 --todo;
4333 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 if (prev_contained != (kp->flags & HL_CONTAINED)
4338 || prev_skipnl != (kp->flags & HL_SKIPNL)
4339 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4340 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4341 || prev_cont_in_list != kp->k_syn.cont_in_list
4342 || prev_next_list != kp->next_list)
4343 outlen = 9999;
4344 else
4345 outlen = (int)STRLEN(kp->keyword);
4346 /* output "contained" and "nextgroup" on each line */
4347 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 prev_contained = 0;
4350 prev_next_list = NULL;
4351 prev_cont_in_list = NULL;
4352 prev_skipnl = 0;
4353 prev_skipwhite = 0;
4354 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 did_header = TRUE;
4357 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 put_id_list((char_u *)"containedin",
4366 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370 if (kp->next_list != prev_next_list)
4371 {
4372 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4373 msg_putchar(' ');
4374 prev_next_list = kp->next_list;
4375 if (kp->flags & HL_SKIPNL)
4376 {
4377 msg_puts_attr((char_u *)"skipnl", attr);
4378 msg_putchar(' ');
4379 prev_skipnl = (kp->flags & HL_SKIPNL);
4380 }
4381 if (kp->flags & HL_SKIPWHITE)
4382 {
4383 msg_puts_attr((char_u *)"skipwhite", attr);
4384 msg_putchar(' ');
4385 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4386 }
4387 if (kp->flags & HL_SKIPEMPTY)
4388 {
4389 msg_puts_attr((char_u *)"skipempty", attr);
4390 msg_putchar(' ');
4391 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4392 }
4393 }
4394 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 }
4397 }
4398 }
4399
4400 return did_header;
4401}
4402
4403 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004404syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004406 hashitem_T *hi;
4407 keyentry_T *kp;
4408 keyentry_T *kp_prev;
4409 keyentry_T *kp_next;
4410 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411
Bram Moolenaardad6b692005-01-25 22:14:34 +00004412 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004413 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004414 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004416 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004417 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004418 --todo;
4419 kp_prev = NULL;
4420 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004421 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004422 if (kp->k_syn.id == id)
4423 {
4424 kp_next = kp->ke_next;
4425 if (kp_prev == NULL)
4426 {
4427 if (kp_next == NULL)
4428 hash_remove(ht, hi);
4429 else
4430 hi->hi_key = KE2HIKEY(kp_next);
4431 }
4432 else
4433 kp_prev->ke_next = kp_next;
4434 vim_free(kp->next_list);
4435 vim_free(kp->k_syn.cont_in_list);
4436 vim_free(kp);
4437 kp = kp_next;
4438 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 {
4441 kp_prev = kp;
4442 kp = kp->ke_next;
4443 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444 }
4445 }
4446 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448}
4449
4450/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004451 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452 */
4453 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004454clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004456 hashitem_T *hi;
4457 int todo;
4458 keyentry_T *kp;
4459 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004461 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004462 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 if (!HASHITEM_EMPTY(hi))
4465 {
4466 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004467 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004469 kp_next = kp->ke_next;
4470 vim_free(kp->next_list);
4471 vim_free(kp->k_syn.cont_in_list);
4472 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004474 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004476 hash_clear(ht);
4477 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478}
4479
4480/*
4481 * Add a keyword to the list of keywords.
4482 */
4483 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004484add_keyword(
4485 char_u *name, /* name of keyword */
4486 int id, /* group ID for this keyword */
4487 int flags, /* flags for this keyword */
4488 short *cont_in_list, /* containedin for this keyword */
4489 short *next_list, /* nextgroup for this keyword */
4490 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004492 keyentry_T *kp;
4493 hashtab_T *ht;
4494 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004495 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004496 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498
Bram Moolenaar860cae12010-06-05 23:22:07 +02004499 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004500 name_ic = str_foldcase(name, (int)STRLEN(name),
4501 name_folded, MAXKEYWLEN + 1);
4502 else
4503 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004504 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4505 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004507 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004508 kp->k_syn.id = id;
4509 kp->k_syn.inc_tag = current_syn_inc_tag;
4510 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004511 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004512 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004514 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004515 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516
Bram Moolenaar860cae12010-06-05 23:22:07 +02004517 if (curwin->w_s->b_syn_ic)
4518 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004520 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521
Bram Moolenaardad6b692005-01-25 22:14:34 +00004522 hash = hash_hash(kp->keyword);
4523 hi = hash_lookup(ht, kp->keyword, hash);
4524 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004526 /* new keyword, add to hashtable */
4527 kp->ke_next = NULL;
4528 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004530 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004532 /* keyword already exists, prepend to list */
4533 kp->ke_next = HI2KE(hi);
4534 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004536}
4537
4538/*
4539 * Get the start and end of the group name argument.
4540 * Return a pointer to the first argument.
4541 * Return NULL if the end of the command was found instead of further args.
4542 */
4543 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004544get_group_name(
4545 char_u *arg, /* start of the argument */
4546 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547{
4548 char_u *rest;
4549
4550 *name_end = skiptowhite(arg);
4551 rest = skipwhite(*name_end);
4552
4553 /*
4554 * Check if there are enough arguments. The first argument may be a
4555 * pattern, where '|' is allowed, so only check for NUL.
4556 */
4557 if (ends_excmd(*arg) || *rest == NUL)
4558 return NULL;
4559 return rest;
4560}
4561
4562/*
4563 * Check for syntax command option arguments.
4564 * This can be called at any place in the list of arguments, and just picks
4565 * out the arguments that are known. Can be called several times in a row to
4566 * collect all options in between other arguments.
4567 * Return a pointer to the next argument (which isn't an option).
4568 * Return NULL for any error;
4569 */
4570 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004571get_syn_options(
4572 char_u *arg, /* next argument to be checked */
4573 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004574 int *conceal_char UNUSED,
4575 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577 char_u *gname_start, *gname;
4578 int syn_id;
4579 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004580 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004581 int i;
4582 int fidx;
4583 static struct flag
4584 {
4585 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004586 int argtype;
4587 int flags;
4588 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4589 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4590 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4591 {"eExXtTeEnNdD", 0, HL_EXTEND},
4592 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4593 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4594 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4595 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4596 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4597 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4598 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4599 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4600 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004601 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4602 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4603 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004604 {"cCoOnNtTaAiInNsS", 1, 0},
4605 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4606 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004608 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609
4610 if (arg == NULL) /* already detected error */
4611 return NULL;
4612
Bram Moolenaar860cae12010-06-05 23:22:07 +02004613#ifdef FEAT_CONCEAL
4614 if (curwin->w_s->b_syn_conceal)
4615 opt->flags |= HL_CONCEAL;
4616#endif
4617
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618 for (;;)
4619 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004620 /*
4621 * This is used very often when a large number of keywords is defined.
4622 * Need to skip quickly when no option name is found.
4623 * Also avoid tolower(), it's slow.
4624 */
4625 if (strchr(first_letters, *arg) == NULL)
4626 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004627
4628 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4629 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004630 p = flagtab[fidx].name;
4631 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4632 if (arg[len] != p[i] && arg[len] != p[i + 1])
4633 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004634 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004635 || (flagtab[fidx].argtype > 0
4636 ? arg[len] == '='
4637 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004639 if (opt->keyword
4640 && (flagtab[fidx].flags == HL_DISPLAY
4641 || flagtab[fidx].flags == HL_FOLD
4642 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 /* treat "display", "fold" and "extend" as a keyword */
4644 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004645 break;
4646 }
4647 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004648 if (fidx < 0) /* no match found */
4649 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004651 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004653 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004654 {
4655 EMSG(_("E395: contains argument not accepted here"));
4656 return NULL;
4657 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004658 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659 return NULL;
4660 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004661 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004663 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664 return NULL;
4665 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004666 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004668 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 return NULL;
4670 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004671 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4672 {
4673#ifdef FEAT_MBYTE
4674 /* cchar=? */
4675 if (has_mbyte)
4676 {
4677# ifdef FEAT_CONCEAL
4678 *conceal_char = mb_ptr2char(arg + 6);
4679# endif
4680 arg += mb_ptr2len(arg + 6) - 1;
4681 }
4682 else
4683#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004684 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004685#ifdef FEAT_CONCEAL
4686 *conceal_char = arg[6];
4687#else
4688 ;
4689#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004690 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004691#ifdef FEAT_CONCEAL
4692 if (!vim_isprintc_strict(*conceal_char))
4693 {
4694 EMSG(_("E844: invalid cchar value"));
4695 return NULL;
4696 }
4697#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004698 arg = skipwhite(arg + 7);
4699 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004701 {
4702 opt->flags |= flagtab[fidx].flags;
4703 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004705 if (flagtab[fidx].flags == HL_SYNC_HERE
4706 || flagtab[fidx].flags == HL_SYNC_THERE)
4707 {
4708 if (opt->sync_idx == NULL)
4709 {
4710 EMSG(_("E393: group[t]here not accepted here"));
4711 return NULL;
4712 }
4713 gname_start = arg;
4714 arg = skiptowhite(arg);
4715 if (gname_start == arg)
4716 return NULL;
4717 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4718 if (gname == NULL)
4719 return NULL;
4720 if (STRCMP(gname, "NONE") == 0)
4721 *opt->sync_idx = NONE_IDX;
4722 else
4723 {
4724 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004725 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4726 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4727 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004728 {
4729 *opt->sync_idx = i;
4730 break;
4731 }
4732 if (i < 0)
4733 {
4734 EMSG2(_("E394: Didn't find region item for %s"), gname);
4735 vim_free(gname);
4736 return NULL;
4737 }
4738 }
4739
4740 vim_free(gname);
4741 arg = skipwhite(arg);
4742 }
4743#ifdef FEAT_FOLDING
4744 else if (flagtab[fidx].flags == HL_FOLD
4745 && foldmethodIsSyntax(curwin))
4746 /* Need to update folds later. */
4747 foldUpdateAll(curwin);
4748#endif
4749 }
4750 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004751
4752 return arg;
4753}
4754
4755/*
4756 * Adjustments to syntax item when declared in a ":syn include"'d file.
4757 * Set the contained flag, and if the item is not already contained, add it
4758 * to the specified top-level group, if any.
4759 */
4760 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004761syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004763 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764 return;
4765 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004766 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004767 {
4768 /* We have to alloc this, because syn_combine_list() will free it. */
4769 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004770 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004771
4772 if (grp_list != NULL)
4773 {
4774 grp_list[0] = id;
4775 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004776 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004777 CLUSTER_ADD);
4778 }
4779 }
4780}
4781
4782/*
4783 * Handle ":syntax include [@{group-name}] filename" command.
4784 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004786syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787{
4788 char_u *arg = eap->arg;
4789 int sgl_id = 1;
4790 char_u *group_name_end;
4791 char_u *rest;
4792 char_u *errormsg = NULL;
4793 int prev_toplvl_grp;
4794 int prev_syn_inc_tag;
4795 int source = FALSE;
4796
4797 eap->nextcmd = find_nextcmd(arg);
4798 if (eap->skip)
4799 return;
4800
4801 if (arg[0] == '@')
4802 {
4803 ++arg;
4804 rest = get_group_name(arg, &group_name_end);
4805 if (rest == NULL)
4806 {
4807 EMSG((char_u *)_("E397: Filename required"));
4808 return;
4809 }
4810 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004811 if (sgl_id == 0)
4812 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 /* separate_nextcmd() and expand_filename() depend on this */
4814 eap->arg = rest;
4815 }
4816
4817 /*
4818 * Everything that's left, up to the next command, should be the
4819 * filename to include.
4820 */
4821 eap->argt |= (XFILE | NOSPC);
4822 separate_nextcmd(eap);
4823 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4824 {
4825 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4826 * file. Need to expand the file name first. In other cases
4827 * ":runtime!" is used. */
4828 source = TRUE;
4829 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4830 {
4831 if (errormsg != NULL)
4832 EMSG(errormsg);
4833 return;
4834 }
4835 }
4836
4837 /*
4838 * Save and restore the existing top-level grouplist id and ":syn
4839 * include" tag around the actual inclusion.
4840 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004841 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4842 {
4843 EMSG((char_u *)_("E847: Too many syntax includes"));
4844 return;
4845 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004846 prev_syn_inc_tag = current_syn_inc_tag;
4847 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004848 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4849 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004850 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004851 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004852 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004853 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004854 current_syn_inc_tag = prev_syn_inc_tag;
4855}
4856
4857/*
4858 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4859 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004861syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004862{
4863 char_u *arg = eap->arg;
4864 char_u *group_name_end;
4865 int syn_id;
4866 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004867 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004868 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004869 char_u *kw;
4870 syn_opt_arg_T syn_opt_arg;
4871 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004872 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004873
4874 rest = get_group_name(arg, &group_name_end);
4875
4876 if (rest != NULL)
4877 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004878 if (eap->skip)
4879 syn_id = -1;
4880 else
4881 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004882 if (syn_id != 0)
4883 /* allocate a buffer, for removing backslashes in the keyword */
4884 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 if (keyword_copy != NULL)
4886 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004887 syn_opt_arg.flags = 0;
4888 syn_opt_arg.keyword = TRUE;
4889 syn_opt_arg.sync_idx = NULL;
4890 syn_opt_arg.has_cont_list = FALSE;
4891 syn_opt_arg.cont_in_list = NULL;
4892 syn_opt_arg.next_list = NULL;
4893
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894 /*
4895 * The options given apply to ALL keywords, so all options must be
4896 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004899 cnt = 0;
4900 p = keyword_copy;
4901 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004903 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4904 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004905 if (rest == NULL || ends_excmd(*rest))
4906 break;
4907 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004908 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004910 if (*rest == '\\' && rest[1] != NUL)
4911 ++rest;
4912 *p++ = *rest++;
4913 }
4914 *p++ = NUL;
4915 ++cnt;
4916 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004918 if (!eap->skip)
4919 {
4920 /* Adjust flags for use of ":syn include". */
4921 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4922
4923 /*
4924 * 2: Add an entry for each keyword.
4925 */
4926 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4927 {
4928 for (p = vim_strchr(kw, '['); ; )
4929 {
4930 if (p != NULL)
4931 *p = NUL;
4932 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004933 syn_opt_arg.cont_in_list,
4934 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004935 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004936 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004937 if (p[1] == NUL)
4938 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004939 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004940 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004941 }
4942 if (p[1] == ']')
4943 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004944 if (p[2] != NUL)
4945 {
4946 EMSG3(_("E890: trailing char after ']': %s]%s"),
4947 kw, &p[2]);
4948 goto error;
4949 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004950 kw = p + 1; /* skip over the "]" */
4951 break;
4952 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004953#ifdef FEAT_MBYTE
4954 if (has_mbyte)
4955 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004956 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004957
4958 mch_memmove(p, p + 1, l);
4959 p += l;
4960 }
4961 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004962#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004963 {
4964 p[0] = p[1];
4965 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966 }
4967 }
4968 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004970error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004971 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004972 vim_free(syn_opt_arg.cont_in_list);
4973 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004974 }
4975 }
4976
4977 if (rest != NULL)
4978 eap->nextcmd = check_nextcmd(rest);
4979 else
4980 EMSG2(_(e_invarg2), arg);
4981
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004982 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004983 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004984}
4985
4986/*
4987 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4988 *
4989 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4990 */
4991 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004992syn_cmd_match(
4993 exarg_T *eap,
4994 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995{
4996 char_u *arg = eap->arg;
4997 char_u *group_name_end;
4998 char_u *rest;
4999 synpat_T item; /* the item found in the line */
5000 int syn_id;
5001 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005002 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005004 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005
5006 /* Isolate the group name, check for validity */
5007 rest = get_group_name(arg, &group_name_end);
5008
5009 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005010 syn_opt_arg.flags = 0;
5011 syn_opt_arg.keyword = FALSE;
5012 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5013 syn_opt_arg.has_cont_list = TRUE;
5014 syn_opt_arg.cont_list = NULL;
5015 syn_opt_arg.cont_in_list = NULL;
5016 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005017 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005018
5019 /* get the pattern. */
5020 init_syn_patterns();
5021 vim_memset(&item, 0, sizeof(item));
5022 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005023 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5024 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025
5026 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005027 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028
5029 if (rest != NULL) /* all arguments are valid */
5030 {
5031 /*
5032 * Check for trailing command and illegal trailing arguments.
5033 */
5034 eap->nextcmd = check_nextcmd(rest);
5035 if (!ends_excmd(*rest) || eap->skip)
5036 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005037 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005038 && (syn_id = syn_check_group(arg,
5039 (int)(group_name_end - arg))) != 0)
5040 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005041 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005042 /*
5043 * Store the pattern in the syn_items list
5044 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005045 idx = curwin->w_s->b_syn_patterns.ga_len;
5046 SYN_ITEMS(curwin->w_s)[idx] = item;
5047 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5048 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5049 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5050 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5051 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5052 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5053 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5054 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005055 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005056#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005057 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005058#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005059 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005060 curwin->w_s->b_syn_containedin = TRUE;
5061 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5062 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063
5064 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005065 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005066 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005068 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005069 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070#endif
5071
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005072 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005073 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 return; /* don't free the progs and patterns now */
5075 }
5076 }
5077
5078 /*
5079 * Something failed, free the allocated memory.
5080 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005081 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005082 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005083 vim_free(syn_opt_arg.cont_list);
5084 vim_free(syn_opt_arg.cont_in_list);
5085 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005086
5087 if (rest == NULL)
5088 EMSG2(_(e_invarg2), arg);
5089}
5090
5091/*
5092 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5093 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5094 */
5095 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005096syn_cmd_region(
5097 exarg_T *eap,
5098 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099{
5100 char_u *arg = eap->arg;
5101 char_u *group_name_end;
5102 char_u *rest; /* next arg, NULL on error */
5103 char_u *key_end;
5104 char_u *key = NULL;
5105 char_u *p;
5106 int item;
5107#define ITEM_START 0
5108#define ITEM_SKIP 1
5109#define ITEM_END 2
5110#define ITEM_MATCHGROUP 3
5111 struct pat_ptr
5112 {
5113 synpat_T *pp_synp; /* pointer to syn_pattern */
5114 int pp_matchgroup_id; /* matchgroup ID */
5115 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5116 } *(pat_ptrs[3]);
5117 /* patterns found in the line */
5118 struct pat_ptr *ppp;
5119 struct pat_ptr *ppp_next;
5120 int pat_count = 0; /* nr of syn_patterns found */
5121 int syn_id;
5122 int matchgroup_id = 0;
5123 int not_enough = FALSE; /* not enough arguments */
5124 int illegal = FALSE; /* illegal arguments */
5125 int success = FALSE;
5126 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005127 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005128 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005129
5130 /* Isolate the group name, check for validity */
5131 rest = get_group_name(arg, &group_name_end);
5132
5133 pat_ptrs[0] = NULL;
5134 pat_ptrs[1] = NULL;
5135 pat_ptrs[2] = NULL;
5136
5137 init_syn_patterns();
5138
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005139 syn_opt_arg.flags = 0;
5140 syn_opt_arg.keyword = FALSE;
5141 syn_opt_arg.sync_idx = NULL;
5142 syn_opt_arg.has_cont_list = TRUE;
5143 syn_opt_arg.cont_list = NULL;
5144 syn_opt_arg.cont_in_list = NULL;
5145 syn_opt_arg.next_list = NULL;
5146
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147 /*
5148 * get the options, patterns and matchgroup.
5149 */
5150 while (rest != NULL && !ends_excmd(*rest))
5151 {
5152 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005153 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 if (rest == NULL || ends_excmd(*rest))
5155 break;
5156
5157 /* must be a pattern or matchgroup then */
5158 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005159 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005160 ++key_end;
5161 vim_free(key);
5162 key = vim_strnsave_up(rest, (int)(key_end - rest));
5163 if (key == NULL) /* out of memory */
5164 {
5165 rest = NULL;
5166 break;
5167 }
5168 if (STRCMP(key, "MATCHGROUP") == 0)
5169 item = ITEM_MATCHGROUP;
5170 else if (STRCMP(key, "START") == 0)
5171 item = ITEM_START;
5172 else if (STRCMP(key, "END") == 0)
5173 item = ITEM_END;
5174 else if (STRCMP(key, "SKIP") == 0)
5175 {
5176 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5177 {
5178 illegal = TRUE;
5179 break;
5180 }
5181 item = ITEM_SKIP;
5182 }
5183 else
5184 break;
5185 rest = skipwhite(key_end);
5186 if (*rest != '=')
5187 {
5188 rest = NULL;
5189 EMSG2(_("E398: Missing '=': %s"), arg);
5190 break;
5191 }
5192 rest = skipwhite(rest + 1);
5193 if (*rest == NUL)
5194 {
5195 not_enough = TRUE;
5196 break;
5197 }
5198
5199 if (item == ITEM_MATCHGROUP)
5200 {
5201 p = skiptowhite(rest);
5202 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5203 matchgroup_id = 0;
5204 else
5205 {
5206 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5207 if (matchgroup_id == 0)
5208 {
5209 illegal = TRUE;
5210 break;
5211 }
5212 }
5213 rest = skipwhite(p);
5214 }
5215 else
5216 {
5217 /*
5218 * Allocate room for a syn_pattern, and link it in the list of
5219 * syn_patterns for this item, at the start (because the list is
5220 * used from end to start).
5221 */
5222 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5223 if (ppp == NULL)
5224 {
5225 rest = NULL;
5226 break;
5227 }
5228 ppp->pp_next = pat_ptrs[item];
5229 pat_ptrs[item] = ppp;
5230 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5231 if (ppp->pp_synp == NULL)
5232 {
5233 rest = NULL;
5234 break;
5235 }
5236
5237 /*
5238 * Get the syntax pattern and the following offset(s).
5239 */
5240 /* Enable the appropriate \z specials. */
5241 if (item == ITEM_START)
5242 reg_do_extmatch = REX_SET;
5243 else if (item == ITEM_SKIP || item == ITEM_END)
5244 reg_do_extmatch = REX_USE;
5245 rest = get_syn_pattern(rest, ppp->pp_synp);
5246 reg_do_extmatch = 0;
5247 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005248 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5250 ppp->pp_matchgroup_id = matchgroup_id;
5251 ++pat_count;
5252 }
5253 }
5254 vim_free(key);
5255 if (illegal || not_enough)
5256 rest = NULL;
5257
5258 /*
5259 * Must have a "start" and "end" pattern.
5260 */
5261 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5262 pat_ptrs[ITEM_END] == NULL))
5263 {
5264 not_enough = TRUE;
5265 rest = NULL;
5266 }
5267
5268 if (rest != NULL)
5269 {
5270 /*
5271 * Check for trailing garbage or command.
5272 * If OK, add the item.
5273 */
5274 eap->nextcmd = check_nextcmd(rest);
5275 if (!ends_excmd(*rest) || eap->skip)
5276 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005277 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005278 && (syn_id = syn_check_group(arg,
5279 (int)(group_name_end - arg))) != 0)
5280 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005281 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282 /*
5283 * Store the start/skip/end in the syn_items list
5284 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005285 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005286 for (item = ITEM_START; item <= ITEM_END; ++item)
5287 {
5288 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5289 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005290 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5291 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5292 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005293 (item == ITEM_START) ? SPTYPE_START :
5294 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005295 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5296 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005297 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5298 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005299 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005300 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005301#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005302 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005303#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005304 if (item == ITEM_START)
5305 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005306 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005307 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005308 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005309 syn_opt_arg.cont_in_list;
5310 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005311 curwin->w_s->b_syn_containedin = TRUE;
5312 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005313 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005315 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005316 ++idx;
5317#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005318 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005319 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005320#endif
5321 }
5322 }
5323
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005324 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005325 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005326 success = TRUE; /* don't free the progs and patterns now */
5327 }
5328 }
5329
5330 /*
5331 * Free the allocated memory.
5332 */
5333 for (item = ITEM_START; item <= ITEM_END; ++item)
5334 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5335 {
5336 if (!success)
5337 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005338 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005339 vim_free(ppp->pp_synp->sp_pattern);
5340 }
5341 vim_free(ppp->pp_synp);
5342 ppp_next = ppp->pp_next;
5343 vim_free(ppp);
5344 }
5345
5346 if (!success)
5347 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005348 vim_free(syn_opt_arg.cont_list);
5349 vim_free(syn_opt_arg.cont_in_list);
5350 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 if (not_enough)
5352 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5353 else if (illegal || rest == NULL)
5354 EMSG2(_(e_invarg2), arg);
5355 }
5356}
5357
5358/*
5359 * A simple syntax group ID comparison function suitable for use in qsort()
5360 */
5361 static int
5362#ifdef __BORLANDC__
5363_RTLENTRYF
5364#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005365syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005366{
5367 const short *s1 = v1;
5368 const short *s2 = v2;
5369
5370 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5371}
5372
5373/*
5374 * Combines lists of syntax clusters.
5375 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5376 */
5377 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005378syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005379{
5380 int count1 = 0;
5381 int count2 = 0;
5382 short *g1;
5383 short *g2;
5384 short *clstr = NULL;
5385 int count;
5386 int round;
5387
5388 /*
5389 * Handle degenerate cases.
5390 */
5391 if (*clstr2 == NULL)
5392 return;
5393 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5394 {
5395 if (list_op == CLUSTER_REPLACE)
5396 vim_free(*clstr1);
5397 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5398 *clstr1 = *clstr2;
5399 else
5400 vim_free(*clstr2);
5401 return;
5402 }
5403
5404 for (g1 = *clstr1; *g1; g1++)
5405 ++count1;
5406 for (g2 = *clstr2; *g2; g2++)
5407 ++count2;
5408
5409 /*
5410 * For speed purposes, sort both lists.
5411 */
5412 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5413 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5414
5415 /*
5416 * We proceed in two passes; in round 1, we count the elements to place
5417 * in the new list, and in round 2, we allocate and populate the new
5418 * list. For speed, we use a mergesort-like method, adding the smaller
5419 * of the current elements in each list to the new list.
5420 */
5421 for (round = 1; round <= 2; round++)
5422 {
5423 g1 = *clstr1;
5424 g2 = *clstr2;
5425 count = 0;
5426
5427 /*
5428 * First, loop through the lists until one of them is empty.
5429 */
5430 while (*g1 && *g2)
5431 {
5432 /*
5433 * We always want to add from the first list.
5434 */
5435 if (*g1 < *g2)
5436 {
5437 if (round == 2)
5438 clstr[count] = *g1;
5439 count++;
5440 g1++;
5441 continue;
5442 }
5443 /*
5444 * We only want to add from the second list if we're adding the
5445 * lists.
5446 */
5447 if (list_op == CLUSTER_ADD)
5448 {
5449 if (round == 2)
5450 clstr[count] = *g2;
5451 count++;
5452 }
5453 if (*g1 == *g2)
5454 g1++;
5455 g2++;
5456 }
5457
5458 /*
5459 * Now add the leftovers from whichever list didn't get finished
5460 * first. As before, we only want to add from the second list if
5461 * we're adding the lists.
5462 */
5463 for (; *g1; g1++, count++)
5464 if (round == 2)
5465 clstr[count] = *g1;
5466 if (list_op == CLUSTER_ADD)
5467 for (; *g2; g2++, count++)
5468 if (round == 2)
5469 clstr[count] = *g2;
5470
5471 if (round == 1)
5472 {
5473 /*
5474 * If the group ended up empty, we don't need to allocate any
5475 * space for it.
5476 */
5477 if (count == 0)
5478 {
5479 clstr = NULL;
5480 break;
5481 }
5482 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5483 if (clstr == NULL)
5484 break;
5485 clstr[count] = 0;
5486 }
5487 }
5488
5489 /*
5490 * Finally, put the new list in place.
5491 */
5492 vim_free(*clstr1);
5493 vim_free(*clstr2);
5494 *clstr1 = clstr;
5495}
5496
5497/*
5498 * Lookup a syntax cluster name and return it's ID.
5499 * If it is not found, 0 is returned.
5500 */
5501 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005502syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005503{
5504 int i;
5505 char_u *name_u;
5506
5507 /* Avoid using stricmp() too much, it's slow on some systems */
5508 name_u = vim_strsave_up(name);
5509 if (name_u == NULL)
5510 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005511 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5512 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5513 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005514 break;
5515 vim_free(name_u);
5516 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5517}
5518
5519/*
5520 * Like syn_scl_name2id(), but take a pointer + length argument.
5521 */
5522 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005523syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524{
5525 char_u *name;
5526 int id = 0;
5527
5528 name = vim_strnsave(linep, len);
5529 if (name != NULL)
5530 {
5531 id = syn_scl_name2id(name);
5532 vim_free(name);
5533 }
5534 return id;
5535}
5536
5537/*
5538 * Find syntax cluster name in the table and return it's ID.
5539 * The argument is a pointer to the name and the length of the name.
5540 * If it doesn't exist yet, a new entry is created.
5541 * Return 0 for failure.
5542 */
5543 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005544syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005545{
5546 int id;
5547 char_u *name;
5548
5549 name = vim_strnsave(pp, len);
5550 if (name == NULL)
5551 return 0;
5552
5553 id = syn_scl_name2id(name);
5554 if (id == 0) /* doesn't exist yet */
5555 id = syn_add_cluster(name);
5556 else
5557 vim_free(name);
5558 return id;
5559}
5560
5561/*
5562 * Add new syntax cluster and return it's ID.
5563 * "name" must be an allocated string, it will be consumed.
5564 * Return 0 for failure.
5565 */
5566 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005567syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005569 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005570
5571 /*
5572 * First call for this growarray: init growing array.
5573 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005574 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005575 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005576 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5577 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 }
5579
Bram Moolenaar42431a72011-04-01 14:44:59 +02005580 len = curwin->w_s->b_syn_clusters.ga_len;
5581 if (len >= MAX_CLUSTER_ID)
5582 {
5583 EMSG((char_u *)_("E848: Too many syntax clusters"));
5584 vim_free(name);
5585 return 0;
5586 }
5587
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588 /*
5589 * Make room for at least one other cluster entry.
5590 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005591 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592 {
5593 vim_free(name);
5594 return 0;
5595 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005596
Bram Moolenaar860cae12010-06-05 23:22:07 +02005597 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5598 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5599 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5600 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5601 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602
Bram Moolenaar217ad922005-03-20 22:37:15 +00005603 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005604 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005605 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005606 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005607
Bram Moolenaar071d4272004-06-13 20:20:40 +00005608 return len + SYNID_CLUSTER;
5609}
5610
5611/*
5612 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5613 * [add={groupname},..] [remove={groupname},..]".
5614 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005615 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005616syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005617{
5618 char_u *arg = eap->arg;
5619 char_u *group_name_end;
5620 char_u *rest;
5621 int scl_id;
5622 short *clstr_list;
5623 int got_clstr = FALSE;
5624 int opt_len;
5625 int list_op;
5626
5627 eap->nextcmd = find_nextcmd(arg);
5628 if (eap->skip)
5629 return;
5630
5631 rest = get_group_name(arg, &group_name_end);
5632
5633 if (rest != NULL)
5634 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005635 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5636 if (scl_id == 0)
5637 return;
5638 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639
5640 for (;;)
5641 {
5642 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005643 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005644 {
5645 opt_len = 3;
5646 list_op = CLUSTER_ADD;
5647 }
5648 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005649 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005650 {
5651 opt_len = 6;
5652 list_op = CLUSTER_SUBTRACT;
5653 }
5654 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005655 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005656 {
5657 opt_len = 8;
5658 list_op = CLUSTER_REPLACE;
5659 }
5660 else
5661 break;
5662
5663 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005664 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 {
5666 EMSG2(_(e_invarg2), rest);
5667 break;
5668 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005669 if (scl_id >= 0)
5670 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005672 else
5673 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005674 got_clstr = TRUE;
5675 }
5676
5677 if (got_clstr)
5678 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005679 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005680 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681 }
5682 }
5683
5684 if (!got_clstr)
5685 EMSG(_("E400: No cluster specified"));
5686 if (rest == NULL || !ends_excmd(*rest))
5687 EMSG2(_(e_invarg2), arg);
5688}
5689
5690/*
5691 * On first call for current buffer: Init growing array.
5692 */
5693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005694init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005696 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5697 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698}
5699
5700/*
5701 * Get one pattern for a ":syntax match" or ":syntax region" command.
5702 * Stores the pattern and program in a synpat_T.
5703 * Returns a pointer to the next argument, or NULL in case of an error.
5704 */
5705 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005706get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707{
5708 char_u *end;
5709 int *p;
5710 int idx;
5711 char_u *cpo_save;
5712
5713 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005714 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005715 return NULL;
5716
5717 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5718 if (*end != *arg) /* end delimiter not found */
5719 {
5720 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5721 return NULL;
5722 }
5723 /* store the pattern and compiled regexp program */
5724 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5725 return NULL;
5726
5727 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5728 cpo_save = p_cpo;
5729 p_cpo = (char_u *)"";
5730 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5731 p_cpo = cpo_save;
5732
5733 if (ci->sp_prog == NULL)
5734 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005735 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005736#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005737 syn_clear_time(&ci->sp_time);
5738#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739
5740 /*
5741 * Check for a match, highlight or region offset.
5742 */
5743 ++end;
5744 do
5745 {
5746 for (idx = SPO_COUNT; --idx >= 0; )
5747 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5748 break;
5749 if (idx >= 0)
5750 {
5751 p = &(ci->sp_offsets[idx]);
5752 if (idx != SPO_LC_OFF)
5753 switch (end[3])
5754 {
5755 case 's': break;
5756 case 'b': break;
5757 case 'e': idx += SPO_COUNT; break;
5758 default: idx = -1; break;
5759 }
5760 if (idx >= 0)
5761 {
5762 ci->sp_off_flags |= (1 << idx);
5763 if (idx == SPO_LC_OFF) /* lc=99 */
5764 {
5765 end += 3;
5766 *p = getdigits(&end);
5767
5768 /* "lc=" offset automatically sets "ms=" offset */
5769 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5770 {
5771 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5772 ci->sp_offsets[SPO_MS_OFF] = *p;
5773 }
5774 }
5775 else /* yy=x+99 */
5776 {
5777 end += 4;
5778 if (*end == '+')
5779 {
5780 ++end;
5781 *p = getdigits(&end); /* positive offset */
5782 }
5783 else if (*end == '-')
5784 {
5785 ++end;
5786 *p = -getdigits(&end); /* negative offset */
5787 }
5788 }
5789 if (*end != ',')
5790 break;
5791 ++end;
5792 }
5793 }
5794 } while (idx >= 0);
5795
Bram Moolenaar1c465442017-03-12 20:10:05 +01005796 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005797 {
5798 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5799 return NULL;
5800 }
5801 return skipwhite(end);
5802}
5803
5804/*
5805 * Handle ":syntax sync .." command.
5806 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005807 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005808syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005809{
5810 char_u *arg_start = eap->arg;
5811 char_u *arg_end;
5812 char_u *key = NULL;
5813 char_u *next_arg;
5814 int illegal = FALSE;
5815 int finished = FALSE;
5816 long n;
5817 char_u *cpo_save;
5818
5819 if (ends_excmd(*arg_start))
5820 {
5821 syn_cmd_list(eap, TRUE);
5822 return;
5823 }
5824
5825 while (!ends_excmd(*arg_start))
5826 {
5827 arg_end = skiptowhite(arg_start);
5828 next_arg = skipwhite(arg_end);
5829 vim_free(key);
5830 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5831 if (STRCMP(key, "CCOMMENT") == 0)
5832 {
5833 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005834 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835 if (!ends_excmd(*next_arg))
5836 {
5837 arg_end = skiptowhite(next_arg);
5838 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005839 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 (int)(arg_end - next_arg));
5841 next_arg = skipwhite(arg_end);
5842 }
5843 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005844 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005845 }
5846 else if ( STRNCMP(key, "LINES", 5) == 0
5847 || STRNCMP(key, "MINLINES", 8) == 0
5848 || STRNCMP(key, "MAXLINES", 8) == 0
5849 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5850 {
5851 if (key[4] == 'S')
5852 arg_end = key + 6;
5853 else if (key[0] == 'L')
5854 arg_end = key + 11;
5855 else
5856 arg_end = key + 9;
5857 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5858 {
5859 illegal = TRUE;
5860 break;
5861 }
5862 n = getdigits(&arg_end);
5863 if (!eap->skip)
5864 {
5865 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005866 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005870 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005871 }
5872 }
5873 else if (STRCMP(key, "FROMSTART") == 0)
5874 {
5875 if (!eap->skip)
5876 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005877 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5878 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879 }
5880 }
5881 else if (STRCMP(key, "LINECONT") == 0)
5882 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005883 if (*next_arg == NUL) /* missing pattern */
5884 {
5885 illegal = TRUE;
5886 break;
5887 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005888 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005889 {
5890 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5891 finished = TRUE;
5892 break;
5893 }
5894 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5895 if (*arg_end != *next_arg) /* end delimiter not found */
5896 {
5897 illegal = TRUE;
5898 break;
5899 }
5900
5901 if (!eap->skip)
5902 {
5903 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005904 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905 (int)(arg_end - next_arg - 1))) == NULL)
5906 {
5907 finished = TRUE;
5908 break;
5909 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005910 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911
5912 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5913 cpo_save = p_cpo;
5914 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005915 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005916 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005918#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005919 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5920#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005921
Bram Moolenaar860cae12010-06-05 23:22:07 +02005922 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005923 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005924 vim_free(curwin->w_s->b_syn_linecont_pat);
5925 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005926 finished = TRUE;
5927 break;
5928 }
5929 }
5930 next_arg = skipwhite(arg_end + 1);
5931 }
5932 else
5933 {
5934 eap->arg = next_arg;
5935 if (STRCMP(key, "MATCH") == 0)
5936 syn_cmd_match(eap, TRUE);
5937 else if (STRCMP(key, "REGION") == 0)
5938 syn_cmd_region(eap, TRUE);
5939 else if (STRCMP(key, "CLEAR") == 0)
5940 syn_cmd_clear(eap, TRUE);
5941 else
5942 illegal = TRUE;
5943 finished = TRUE;
5944 break;
5945 }
5946 arg_start = next_arg;
5947 }
5948 vim_free(key);
5949 if (illegal)
5950 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5951 else if (!finished)
5952 {
5953 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005954 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005955 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005956 }
5957}
5958
5959/*
5960 * Convert a line of highlight group names into a list of group ID numbers.
5961 * "arg" should point to the "contains" or "nextgroup" keyword.
5962 * "arg" is advanced to after the last group name.
5963 * Careful: the argument is modified (NULs added).
5964 * returns FAIL for some error, OK for success.
5965 */
5966 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005967get_id_list(
5968 char_u **arg,
5969 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005970 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005971 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005972 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005973{
5974 char_u *p = NULL;
5975 char_u *end;
5976 int round;
5977 int count;
5978 int total_count = 0;
5979 short *retval = NULL;
5980 char_u *name;
5981 regmatch_T regmatch;
5982 int id;
5983 int i;
5984 int failed = FALSE;
5985
5986 /*
5987 * We parse the list twice:
5988 * round == 1: count the number of items, allocate the array.
5989 * round == 2: fill the array with the items.
5990 * In round 1 new groups may be added, causing the number of items to
5991 * grow when a regexp is used. In that case round 1 is done once again.
5992 */
5993 for (round = 1; round <= 2; ++round)
5994 {
5995 /*
5996 * skip "contains"
5997 */
5998 p = skipwhite(*arg + keylen);
5999 if (*p != '=')
6000 {
6001 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6002 break;
6003 }
6004 p = skipwhite(p + 1);
6005 if (ends_excmd(*p))
6006 {
6007 EMSG2(_("E406: Empty argument: %s"), *arg);
6008 break;
6009 }
6010
6011 /*
6012 * parse the arguments after "contains"
6013 */
6014 count = 0;
6015 while (!ends_excmd(*p))
6016 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006017 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006018 ;
6019 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6020 if (name == NULL)
6021 {
6022 failed = TRUE;
6023 break;
6024 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006025 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006026 if ( STRCMP(name + 1, "ALLBUT") == 0
6027 || STRCMP(name + 1, "ALL") == 0
6028 || STRCMP(name + 1, "TOP") == 0
6029 || STRCMP(name + 1, "CONTAINED") == 0)
6030 {
6031 if (TOUPPER_ASC(**arg) != 'C')
6032 {
6033 EMSG2(_("E407: %s not allowed here"), name + 1);
6034 failed = TRUE;
6035 vim_free(name);
6036 break;
6037 }
6038 if (count != 0)
6039 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006040 EMSG2(_("E408: %s must be first in contains list"),
6041 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006042 failed = TRUE;
6043 vim_free(name);
6044 break;
6045 }
6046 if (name[1] == 'A')
6047 id = SYNID_ALLBUT;
6048 else if (name[1] == 'T')
6049 id = SYNID_TOP;
6050 else
6051 id = SYNID_CONTAINED;
6052 id += current_syn_inc_tag;
6053 }
6054 else if (name[1] == '@')
6055 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006056 if (skip)
6057 id = -1;
6058 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006059 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006060 }
6061 else
6062 {
6063 /*
6064 * Handle full group name.
6065 */
6066 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6067 id = syn_check_group(name + 1, (int)(end - p));
6068 else
6069 {
6070 /*
6071 * Handle match of regexp with group names.
6072 */
6073 *name = '^';
6074 STRCAT(name, "$");
6075 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6076 if (regmatch.regprog == NULL)
6077 {
6078 failed = TRUE;
6079 vim_free(name);
6080 break;
6081 }
6082
6083 regmatch.rm_ic = TRUE;
6084 id = 0;
6085 for (i = highlight_ga.ga_len; --i >= 0; )
6086 {
6087 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6088 (colnr_T)0))
6089 {
6090 if (round == 2)
6091 {
6092 /* Got more items than expected; can happen
6093 * when adding items that match:
6094 * "contains=a.*b,axb".
6095 * Go back to first round */
6096 if (count >= total_count)
6097 {
6098 vim_free(retval);
6099 round = 1;
6100 }
6101 else
6102 retval[count] = i + 1;
6103 }
6104 ++count;
6105 id = -1; /* remember that we found one */
6106 }
6107 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006108 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006109 }
6110 }
6111 vim_free(name);
6112 if (id == 0)
6113 {
6114 EMSG2(_("E409: Unknown group name: %s"), p);
6115 failed = TRUE;
6116 break;
6117 }
6118 if (id > 0)
6119 {
6120 if (round == 2)
6121 {
6122 /* Got more items than expected, go back to first round */
6123 if (count >= total_count)
6124 {
6125 vim_free(retval);
6126 round = 1;
6127 }
6128 else
6129 retval[count] = id;
6130 }
6131 ++count;
6132 }
6133 p = skipwhite(end);
6134 if (*p != ',')
6135 break;
6136 p = skipwhite(p + 1); /* skip comma in between arguments */
6137 }
6138 if (failed)
6139 break;
6140 if (round == 1)
6141 {
6142 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6143 if (retval == NULL)
6144 break;
6145 retval[count] = 0; /* zero means end of the list */
6146 total_count = count;
6147 }
6148 }
6149
6150 *arg = p;
6151 if (failed || retval == NULL)
6152 {
6153 vim_free(retval);
6154 return FAIL;
6155 }
6156
6157 if (*list == NULL)
6158 *list = retval;
6159 else
6160 vim_free(retval); /* list already found, don't overwrite it */
6161
6162 return OK;
6163}
6164
6165/*
6166 * Make a copy of an ID list.
6167 */
6168 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006169copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006170{
6171 int len;
6172 int count;
6173 short *retval;
6174
6175 if (list == NULL)
6176 return NULL;
6177
6178 for (count = 0; list[count]; ++count)
6179 ;
6180 len = (count + 1) * sizeof(short);
6181 retval = (short *)alloc((unsigned)len);
6182 if (retval != NULL)
6183 mch_memmove(retval, list, (size_t)len);
6184
6185 return retval;
6186}
6187
6188/*
6189 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6190 * "cur_si" can be NULL if not checking the "containedin" list.
6191 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6192 * the current item.
6193 * This function is called very often, keep it fast!!
6194 */
6195 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006196in_id_list(
6197 stateitem_T *cur_si, /* current item or NULL */
6198 short *list, /* id list */
6199 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6200 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006201{
6202 int retval;
6203 short *scl_list;
6204 short item;
6205 short id = ssp->id;
6206 static int depth = 0;
6207 int r;
6208
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006209 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006210 if (cur_si != NULL && ssp->cont_in_list != NULL
6211 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006212 {
6213 /* Ignore transparent items without a contains argument. Double check
6214 * that we don't go back past the first one. */
6215 while ((cur_si->si_flags & HL_TRANS_CONT)
6216 && cur_si > (stateitem_T *)(current_state.ga_data))
6217 --cur_si;
6218 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6219 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006220 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6221 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222 return TRUE;
6223 }
6224
6225 if (list == NULL)
6226 return FALSE;
6227
6228 /*
6229 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6230 * inside anything. Only allow not-contained groups.
6231 */
6232 if (list == ID_LIST_ALL)
6233 return !contained;
6234
6235 /*
6236 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6237 * contains list. We also require that "id" is at the same ":syn include"
6238 * level as the list.
6239 */
6240 item = *list;
6241 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6242 {
6243 if (item < SYNID_TOP)
6244 {
6245 /* ALL or ALLBUT: accept all groups in the same file */
6246 if (item - SYNID_ALLBUT != ssp->inc_tag)
6247 return FALSE;
6248 }
6249 else if (item < SYNID_CONTAINED)
6250 {
6251 /* TOP: accept all not-contained groups in the same file */
6252 if (item - SYNID_TOP != ssp->inc_tag || contained)
6253 return FALSE;
6254 }
6255 else
6256 {
6257 /* CONTAINED: accept all contained groups in the same file */
6258 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6259 return FALSE;
6260 }
6261 item = *++list;
6262 retval = FALSE;
6263 }
6264 else
6265 retval = TRUE;
6266
6267 /*
6268 * Return "retval" if id is in the contains list.
6269 */
6270 while (item != 0)
6271 {
6272 if (item == id)
6273 return retval;
6274 if (item >= SYNID_CLUSTER)
6275 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006276 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006277 /* restrict recursiveness to 30 to avoid an endless loop for a
6278 * cluster that includes itself (indirectly) */
6279 if (scl_list != NULL && depth < 30)
6280 {
6281 ++depth;
6282 r = in_id_list(NULL, scl_list, ssp, contained);
6283 --depth;
6284 if (r)
6285 return retval;
6286 }
6287 }
6288 item = *++list;
6289 }
6290 return !retval;
6291}
6292
6293struct subcommand
6294{
Bram Moolenaard99df422016-01-29 23:20:40 +01006295 char *name; /* subcommand name */
6296 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297};
6298
6299static struct subcommand subcommands[] =
6300{
6301 {"case", syn_cmd_case},
6302 {"clear", syn_cmd_clear},
6303 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006304 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305 {"enable", syn_cmd_enable},
6306 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006307 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006308 {"keyword", syn_cmd_keyword},
6309 {"list", syn_cmd_list},
6310 {"manual", syn_cmd_manual},
6311 {"match", syn_cmd_match},
6312 {"on", syn_cmd_on},
6313 {"off", syn_cmd_off},
6314 {"region", syn_cmd_region},
6315 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006316 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317 {"sync", syn_cmd_sync},
6318 {"", syn_cmd_list},
6319 {NULL, NULL}
6320};
6321
6322/*
6323 * ":syntax".
6324 * This searches the subcommands[] table for the subcommand name, and calls a
6325 * syntax_subcommand() function to do the rest.
6326 */
6327 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006328ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006329{
6330 char_u *arg = eap->arg;
6331 char_u *subcmd_end;
6332 char_u *subcmd_name;
6333 int i;
6334
6335 syn_cmdlinep = eap->cmdlinep;
6336
6337 /* isolate subcommand name */
6338 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6339 ;
6340 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6341 if (subcmd_name != NULL)
6342 {
6343 if (eap->skip) /* skip error messages for all subcommands */
6344 ++emsg_skip;
6345 for (i = 0; ; ++i)
6346 {
6347 if (subcommands[i].name == NULL)
6348 {
6349 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6350 break;
6351 }
6352 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6353 {
6354 eap->arg = skipwhite(subcmd_end);
6355 (subcommands[i].func)(eap, FALSE);
6356 break;
6357 }
6358 }
6359 vim_free(subcmd_name);
6360 if (eap->skip)
6361 --emsg_skip;
6362 }
6363}
6364
Bram Moolenaar860cae12010-06-05 23:22:07 +02006365 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006366ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006368 char_u *old_value;
6369 char_u *new_value;
6370
Bram Moolenaar860cae12010-06-05 23:22:07 +02006371 if (curwin->w_s == &curwin->w_buffer->b_s)
6372 {
6373 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6374 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006375 hash_init(&curwin->w_s->b_keywtab);
6376 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006377#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006378 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006379 curwin->w_p_spell = FALSE; /* No spell checking */
6380 clear_string_option(&curwin->w_s->b_p_spc);
6381 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006382 clear_string_option(&curwin->w_s->b_p_spl);
6383#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006384 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006385 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006386
6387 /* save value of b:current_syntax */
6388 old_value = get_var_value((char_u *)"b:current_syntax");
6389 if (old_value != NULL)
6390 old_value = vim_strsave(old_value);
6391
Bram Moolenaard1413d92016-03-02 21:51:56 +01006392#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006393 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6394 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006395 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006396#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006397
6398 /* move value of b:current_syntax to w:current_syntax */
6399 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006400 if (new_value != NULL)
6401 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006402
6403 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006404 if (old_value == NULL)
6405 do_unlet((char_u *)"b:current_syntax", TRUE);
6406 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006407 {
6408 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6409 vim_free(old_value);
6410 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006411}
6412
6413 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006414syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006415{
6416 return (win->w_s->b_syn_patterns.ga_len != 0
6417 || win->w_s->b_syn_clusters.ga_len != 0
6418 || win->w_s->b_keywtab.ht_used > 0
6419 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420}
6421
6422#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6423
6424static enum
6425{
6426 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006427 EXP_CASE, /* expand ":syn case" arguments */
6428 EXP_SPELL, /* expand ":syn spell" arguments */
6429 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006430} expand_what;
6431
Bram Moolenaar4f688582007-07-24 12:34:30 +00006432/*
6433 * Reset include_link, include_default, include_none to 0.
6434 * Called when we are done expanding.
6435 */
6436 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006437reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006438{
6439 include_link = include_default = include_none = 0;
6440}
6441
6442/*
6443 * Handle command line completion for :match and :echohl command: Add "None"
6444 * as highlight group.
6445 */
6446 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006447set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006448{
6449 xp->xp_context = EXPAND_HIGHLIGHT;
6450 xp->xp_pattern = arg;
6451 include_none = 1;
6452}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006453
6454/*
6455 * Handle command line completion for :syntax command.
6456 */
6457 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006458set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459{
6460 char_u *p;
6461
6462 /* Default: expand subcommands */
6463 xp->xp_context = EXPAND_SYNTAX;
6464 expand_what = EXP_SUBCMD;
6465 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006466 include_link = 0;
6467 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006468
6469 /* (part of) subcommand already typed */
6470 if (*arg != NUL)
6471 {
6472 p = skiptowhite(arg);
6473 if (*p != NUL) /* past first word */
6474 {
6475 xp->xp_pattern = skipwhite(p);
6476 if (*skiptowhite(xp->xp_pattern) != NUL)
6477 xp->xp_context = EXPAND_NOTHING;
6478 else if (STRNICMP(arg, "case", p - arg) == 0)
6479 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006480 else if (STRNICMP(arg, "spell", p - arg) == 0)
6481 expand_what = EXP_SPELL;
6482 else if (STRNICMP(arg, "sync", p - arg) == 0)
6483 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6485 || STRNICMP(arg, "region", p - arg) == 0
6486 || STRNICMP(arg, "match", p - arg) == 0
6487 || STRNICMP(arg, "list", p - arg) == 0)
6488 xp->xp_context = EXPAND_HIGHLIGHT;
6489 else
6490 xp->xp_context = EXPAND_NOTHING;
6491 }
6492 }
6493}
6494
Bram Moolenaar071d4272004-06-13 20:20:40 +00006495/*
6496 * Function given to ExpandGeneric() to obtain the list syntax names for
6497 * expansion.
6498 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006500get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006502 switch (expand_what)
6503 {
6504 case EXP_SUBCMD:
6505 return (char_u *)subcommands[idx].name;
6506 case EXP_CASE:
6507 {
6508 static char *case_args[] = {"match", "ignore", NULL};
6509 return (char_u *)case_args[idx];
6510 }
6511 case EXP_SPELL:
6512 {
6513 static char *spell_args[] =
6514 {"toplevel", "notoplevel", "default", NULL};
6515 return (char_u *)spell_args[idx];
6516 }
6517 case EXP_SYNC:
6518 {
6519 static char *sync_args[] =
6520 {"ccomment", "clear", "fromstart",
6521 "linebreaks=", "linecont", "lines=", "match",
6522 "maxlines=", "minlines=", "region", NULL};
6523 return (char_u *)sync_args[idx];
6524 }
6525 }
6526 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006527}
6528
6529#endif /* FEAT_CMDL_COMPL */
6530
Bram Moolenaar071d4272004-06-13 20:20:40 +00006531/*
6532 * Function called for expression evaluation: get syntax ID at file position.
6533 */
6534 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006535syn_get_id(
6536 win_T *wp,
6537 long lnum,
6538 colnr_T col,
6539 int trans, /* remove transparency */
6540 int *spellp, /* return: can do spell checking */
6541 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542{
6543 /* When the position is not after the current position and in the same
6544 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006545 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006546 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006547 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006548 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006549 else if (wp->w_buffer == syn_buf
6550 && lnum == current_lnum
6551 && col > current_col)
6552 /* next_match may not be correct when moving around, e.g. with the
6553 * "skip" expression in searchpair() */
6554 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006555
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006556 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557
6558 return (trans ? current_trans_id : current_id);
6559}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560
Bram Moolenaar860cae12010-06-05 23:22:07 +02006561#if defined(FEAT_CONCEAL) || defined(PROTO)
6562/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006563 * Get extra information about the syntax item. Must be called right after
6564 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006565 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006566 * Returns the current flags.
6567 */
6568 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006569get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006570{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006571 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006572 return current_flags;
6573}
6574
6575/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006576 * Return conceal substitution character
6577 */
6578 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006579syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006580{
6581 return current_sub_char;
6582}
6583#endif
6584
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006585#if defined(FEAT_EVAL) || defined(PROTO)
6586/*
6587 * Return the syntax ID at position "i" in the current stack.
6588 * The caller must have called syn_get_id() before to fill the stack.
6589 * Returns -1 when "i" is out of range.
6590 */
6591 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006592syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006593{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006594 if (i >= current_state.ga_len)
6595 {
6596 /* Need to invalidate the state, because we didn't properly finish it
6597 * for the last character, "keep_state" was TRUE. */
6598 invalidate_current_state();
6599 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006600 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006601 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006602 return CUR_STATE(i).si_id;
6603}
6604#endif
6605
Bram Moolenaar071d4272004-06-13 20:20:40 +00006606#if defined(FEAT_FOLDING) || defined(PROTO)
6607/*
6608 * Function called to get folding level for line "lnum" in window "wp".
6609 */
6610 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006611syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006612{
6613 int level = 0;
6614 int i;
6615
6616 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006617 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006618 {
6619 syntax_start(wp, lnum);
6620
6621 for (i = 0; i < current_state.ga_len; ++i)
6622 if (CUR_STATE(i).si_flags & HL_FOLD)
6623 ++level;
6624 }
6625 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006626 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006627 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006628 if (level < 0)
6629 level = 0;
6630 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006631 return level;
6632}
6633#endif
6634
Bram Moolenaar01615492015-02-03 13:00:38 +01006635#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006636/*
6637 * ":syntime".
6638 */
6639 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006640ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006641{
6642 if (STRCMP(eap->arg, "on") == 0)
6643 syn_time_on = TRUE;
6644 else if (STRCMP(eap->arg, "off") == 0)
6645 syn_time_on = FALSE;
6646 else if (STRCMP(eap->arg, "clear") == 0)
6647 syntime_clear();
6648 else if (STRCMP(eap->arg, "report") == 0)
6649 syntime_report();
6650 else
6651 EMSG2(_(e_invarg2), eap->arg);
6652}
6653
6654 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006655syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006656{
6657 profile_zero(&st->total);
6658 profile_zero(&st->slowest);
6659 st->count = 0;
6660 st->match = 0;
6661}
6662
6663/*
6664 * Clear the syntax timing for the current buffer.
6665 */
6666 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006667syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006668{
6669 int idx;
6670 synpat_T *spp;
6671
6672 if (!syntax_present(curwin))
6673 {
6674 MSG(_(msg_no_items));
6675 return;
6676 }
6677 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6678 {
6679 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6680 syn_clear_time(&spp->sp_time);
6681 }
6682}
6683
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006684#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6685/*
6686 * Function given to ExpandGeneric() to obtain the possible arguments of the
6687 * ":syntime {on,off,clear,report}" command.
6688 */
6689 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006690get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006691{
6692 switch (idx)
6693 {
6694 case 0: return (char_u *)"on";
6695 case 1: return (char_u *)"off";
6696 case 2: return (char_u *)"clear";
6697 case 3: return (char_u *)"report";
6698 }
6699 return NULL;
6700}
6701#endif
6702
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006703typedef struct
6704{
6705 proftime_T total;
6706 int count;
6707 int match;
6708 proftime_T slowest;
6709 proftime_T average;
6710 int id;
6711 char_u *pattern;
6712} time_entry_T;
6713
6714 static int
6715#ifdef __BORLANDC__
6716_RTLENTRYF
6717#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006718syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006719{
6720 const time_entry_T *s1 = v1;
6721 const time_entry_T *s2 = v2;
6722
6723 return profile_cmp(&s1->total, &s2->total);
6724}
6725
6726/*
6727 * Clear the syntax timing for the current buffer.
6728 */
6729 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006730syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006731{
6732 int idx;
6733 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006734# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006735 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006736# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006737 int len;
6738 proftime_T total_total;
6739 int total_count = 0;
6740 garray_T ga;
6741 time_entry_T *p;
6742
6743 if (!syntax_present(curwin))
6744 {
6745 MSG(_(msg_no_items));
6746 return;
6747 }
6748
6749 ga_init2(&ga, sizeof(time_entry_T), 50);
6750 profile_zero(&total_total);
6751 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6752 {
6753 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6754 if (spp->sp_time.count > 0)
6755 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006756 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006757 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6758 p->total = spp->sp_time.total;
6759 profile_add(&total_total, &spp->sp_time.total);
6760 p->count = spp->sp_time.count;
6761 p->match = spp->sp_time.match;
6762 total_count += spp->sp_time.count;
6763 p->slowest = spp->sp_time.slowest;
6764# ifdef FEAT_FLOAT
6765 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6766 p->average = tm;
6767# endif
6768 p->id = spp->sp_syn.id;
6769 p->pattern = spp->sp_pattern;
6770 ++ga.ga_len;
6771 }
6772 }
6773
Bram Moolenaara2162552017-01-08 17:46:20 +01006774 /* Sort on total time. Skip if there are no items to avoid passing NULL
6775 * pointer to qsort(). */
6776 if (ga.ga_len > 1)
6777 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006778 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006779
6780 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6781 MSG_PUTS("\n");
6782 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6783 {
6784 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6785 p = ((time_entry_T *)ga.ga_data) + idx;
6786
6787 MSG_PUTS(profile_msg(&p->total));
6788 MSG_PUTS(" "); /* make sure there is always a separating space */
6789 msg_advance(13);
6790 msg_outnum(p->count);
6791 MSG_PUTS(" ");
6792 msg_advance(20);
6793 msg_outnum(p->match);
6794 MSG_PUTS(" ");
6795 msg_advance(26);
6796 MSG_PUTS(profile_msg(&p->slowest));
6797 MSG_PUTS(" ");
6798 msg_advance(38);
6799# ifdef FEAT_FLOAT
6800 MSG_PUTS(profile_msg(&p->average));
6801 MSG_PUTS(" ");
6802# endif
6803 msg_advance(50);
6804 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6805 MSG_PUTS(" ");
6806
6807 msg_advance(69);
6808 if (Columns < 80)
6809 len = 20; /* will wrap anyway */
6810 else
6811 len = Columns - 70;
6812 if (len > (int)STRLEN(p->pattern))
6813 len = (int)STRLEN(p->pattern);
6814 msg_outtrans_len(p->pattern, len);
6815 MSG_PUTS("\n");
6816 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006817 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006818 if (!got_int)
6819 {
6820 MSG_PUTS("\n");
6821 MSG_PUTS(profile_msg(&total_total));
6822 msg_advance(13);
6823 msg_outnum(total_count);
6824 MSG_PUTS("\n");
6825 }
6826}
6827#endif
6828
Bram Moolenaar071d4272004-06-13 20:20:40 +00006829#endif /* FEAT_SYN_HL */
6830
Bram Moolenaar071d4272004-06-13 20:20:40 +00006831/**************************************
6832 * Highlighting stuff *
6833 **************************************/
6834
6835/*
6836 * The default highlight groups. These are compiled-in for fast startup and
6837 * they still work when the runtime files can't be found.
6838 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006839 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6840 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006841 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006842#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006843# define CENT(a, b) b
6844#else
6845# define CENT(a, b) a
6846#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006847static char *(highlight_init_both[]) =
6848 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006849 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6850 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6851 CENT("IncSearch term=reverse cterm=reverse",
6852 "IncSearch term=reverse cterm=reverse gui=reverse"),
6853 CENT("ModeMsg term=bold cterm=bold",
6854 "ModeMsg term=bold cterm=bold gui=bold"),
6855 CENT("NonText term=bold ctermfg=Blue",
6856 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6857 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6858 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6859 CENT("StatusLineNC term=reverse cterm=reverse",
6860 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006861 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006862#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006863 CENT("VertSplit term=reverse cterm=reverse",
6864 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006865#endif
6866#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006867 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6868 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006869#endif
6870#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006871 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6872 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006873#endif
6874#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006875 CENT("PmenuSbar ctermbg=Grey",
6876 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006877#endif
6878#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006879 CENT("TabLineSel term=bold cterm=bold",
6880 "TabLineSel term=bold cterm=bold gui=bold"),
6881 CENT("TabLineFill term=reverse cterm=reverse",
6882 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006883#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006884#ifdef FEAT_GUI
6885 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006886 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006887#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006888 NULL
6889 };
6890
6891static char *(highlight_init_light[]) =
6892 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006893 CENT("Directory term=bold ctermfg=DarkBlue",
6894 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6895 CENT("LineNr term=underline ctermfg=Brown",
6896 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006897 CENT("CursorLineNr term=bold ctermfg=Brown",
6898 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006899 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6900 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6901 CENT("Question term=standout ctermfg=DarkGreen",
6902 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6903 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6904 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006905#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006906 CENT("SpellBad term=reverse ctermbg=LightRed",
6907 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6908 CENT("SpellCap term=reverse ctermbg=LightBlue",
6909 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6910 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6911 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6912 CENT("SpellLocal term=underline ctermbg=Cyan",
6913 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006914#endif
6915#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006916 CENT("PmenuThumb ctermbg=Black",
6917 "PmenuThumb ctermbg=Black guibg=Black"),
6918 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6919 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6920 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6921 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006922#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006923 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6924 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6925 CENT("Title term=bold ctermfg=DarkMagenta",
6926 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6927 CENT("WarningMsg term=standout ctermfg=DarkRed",
6928 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006929#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006930 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6931 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006932#endif
6933#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006934 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6935 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6936 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6937 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006938#endif
6939#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006940 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6941 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006942#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006943 CENT("Visual term=reverse",
6944 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006945#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006946 CENT("DiffAdd term=bold ctermbg=LightBlue",
6947 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6948 CENT("DiffChange term=bold ctermbg=LightMagenta",
6949 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6950 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6951 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006952#endif
6953#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006954 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6955 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006956#endif
6957#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006958 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006959 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006960 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006961 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006962 CENT("ColorColumn term=reverse ctermbg=LightRed",
6963 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006964#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006965#ifdef FEAT_CONCEAL
6966 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6967 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6968#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006969#ifdef FEAT_AUTOCMD
6970 CENT("MatchParen term=reverse ctermbg=Cyan",
6971 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6972#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006973#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006974 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006975#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006976 NULL
6977 };
6978
6979static char *(highlight_init_dark[]) =
6980 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006981 CENT("Directory term=bold ctermfg=LightCyan",
6982 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6983 CENT("LineNr term=underline ctermfg=Yellow",
6984 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006985 CENT("CursorLineNr term=bold ctermfg=Yellow",
6986 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006987 CENT("MoreMsg term=bold ctermfg=LightGreen",
6988 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6989 CENT("Question term=standout ctermfg=LightGreen",
6990 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6991 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6992 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6993 CENT("SpecialKey term=bold ctermfg=LightBlue",
6994 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006995#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006996 CENT("SpellBad term=reverse ctermbg=Red",
6997 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6998 CENT("SpellCap term=reverse ctermbg=Blue",
6999 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7000 CENT("SpellRare term=reverse ctermbg=Magenta",
7001 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7002 CENT("SpellLocal term=underline ctermbg=Cyan",
7003 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007004#endif
7005#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007006 CENT("PmenuThumb ctermbg=White",
7007 "PmenuThumb ctermbg=White guibg=White"),
7008 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7009 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007010 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7011 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007012#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007013 CENT("Title term=bold ctermfg=LightMagenta",
7014 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7015 CENT("WarningMsg term=standout ctermfg=LightRed",
7016 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007017#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007018 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7019 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007020#endif
7021#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007022 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7023 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7024 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7025 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007026#endif
7027#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007028 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7029 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007030#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007031 CENT("Visual term=reverse",
7032 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007033#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007034 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7035 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7036 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7037 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7038 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7039 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007040#endif
7041#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007042 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7043 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007044#endif
7045#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007046 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007047 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007048 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007049 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007050 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7051 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007052#endif
7053#ifdef FEAT_AUTOCMD
7054 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7055 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007056#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007057#ifdef FEAT_CONCEAL
7058 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7059 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7060#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007061#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007062 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007063#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064 NULL
7065 };
7066
7067 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007068init_highlight(
7069 int both, /* include groups where 'bg' doesn't matter */
7070 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007071{
7072 int i;
7073 char **pp;
7074 static int had_both = FALSE;
7075#ifdef FEAT_EVAL
7076 char_u *p;
7077
7078 /*
7079 * Try finding the color scheme file. Used when a color file was loaded
7080 * and 'background' or 't_Co' is changed.
7081 */
7082 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007083 if (p != NULL)
7084 {
7085 /* The value of g:colors_name could be freed when sourcing the script,
7086 * making "p" invalid, so copy it. */
7087 char_u *copy_p = vim_strsave(p);
7088 int r;
7089
7090 if (copy_p != NULL)
7091 {
7092 r = load_colors(copy_p);
7093 vim_free(copy_p);
7094 if (r == OK)
7095 return;
7096 }
7097 }
7098
Bram Moolenaar071d4272004-06-13 20:20:40 +00007099#endif
7100
7101 /*
7102 * Didn't use a color file, use the compiled-in colors.
7103 */
7104 if (both)
7105 {
7106 had_both = TRUE;
7107 pp = highlight_init_both;
7108 for (i = 0; pp[i] != NULL; ++i)
7109 do_highlight((char_u *)pp[i], reset, TRUE);
7110 }
7111 else if (!had_both)
7112 /* Don't do anything before the call with both == TRUE from main().
7113 * Not everything has been setup then, and that call will overrule
7114 * everything anyway. */
7115 return;
7116
7117 if (*p_bg == 'l')
7118 pp = highlight_init_light;
7119 else
7120 pp = highlight_init_dark;
7121 for (i = 0; pp[i] != NULL; ++i)
7122 do_highlight((char_u *)pp[i], reset, TRUE);
7123
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007124 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007125 * depend on the number of colors available.
7126 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007127 * to avoid Statement highlighted text disappears.
7128 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007129 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007130 do_highlight((char_u *)(*p_bg == 'l'
7131 ? "Visual cterm=NONE ctermbg=LightGrey"
7132 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007133 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007134 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007135 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7136 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007137 if (*p_bg == 'l')
7138 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7139 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007140
Bram Moolenaar071d4272004-06-13 20:20:40 +00007141#ifdef FEAT_SYN_HL
7142 /*
7143 * If syntax highlighting is enabled load the highlighting for it.
7144 */
7145 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007146 {
7147 static int recursive = 0;
7148
7149 if (recursive >= 5)
7150 EMSG(_("E679: recursive loop loading syncolor.vim"));
7151 else
7152 {
7153 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007154 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007155 --recursive;
7156 }
7157 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007158#endif
7159}
7160
7161/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007162 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007163 * Return OK for success, FAIL for failure.
7164 */
7165 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007166load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007167{
7168 char_u *buf;
7169 int retval = FAIL;
7170 static int recursive = FALSE;
7171
7172 /* When being called recursively, this is probably because setting
7173 * 'background' caused the highlighting to be reloaded. This means it is
7174 * working, thus we should return OK. */
7175 if (recursive)
7176 return OK;
7177
7178 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007179 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007180 if (buf != NULL)
7181 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007182 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007183 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007184 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007185#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007186 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007187#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007188 }
7189 recursive = FALSE;
7190
7191 return retval;
7192}
7193
7194/*
7195 * Handle the ":highlight .." command.
7196 * When using ":hi clear" this is called recursively for each group with
7197 * "forceit" and "init" both TRUE.
7198 */
7199 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007200do_highlight(
7201 char_u *line,
7202 int forceit,
7203 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007204{
7205 char_u *name_end;
7206 char_u *p;
7207 char_u *linep;
7208 char_u *key_start;
7209 char_u *arg_start;
7210 char_u *key = NULL, *arg = NULL;
7211 long i;
7212 int off;
7213 int len;
7214 int attr;
7215 int id;
7216 int idx;
7217 int dodefault = FALSE;
7218 int doclear = FALSE;
7219 int dolink = FALSE;
7220 int error = FALSE;
7221 int color;
7222 int is_normal_group = FALSE; /* "Normal" group */
7223#ifdef FEAT_GUI_X11
7224 int is_menu_group = FALSE; /* "Menu" group */
7225 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7226 int is_tooltip_group = FALSE; /* "Tooltip" group */
7227 int do_colors = FALSE; /* need to update colors? */
7228#else
7229# define is_menu_group 0
7230# define is_tooltip_group 0
7231#endif
7232
7233 /*
7234 * If no argument, list current highlighting.
7235 */
7236 if (ends_excmd(*line))
7237 {
7238 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7239 /* TODO: only call when the group has attributes set */
7240 highlight_list_one((int)i);
7241 return;
7242 }
7243
7244 /*
7245 * Isolate the name.
7246 */
7247 name_end = skiptowhite(line);
7248 linep = skipwhite(name_end);
7249
7250 /*
7251 * Check for "default" argument.
7252 */
7253 if (STRNCMP(line, "default", name_end - line) == 0)
7254 {
7255 dodefault = TRUE;
7256 line = linep;
7257 name_end = skiptowhite(line);
7258 linep = skipwhite(name_end);
7259 }
7260
7261 /*
7262 * Check for "clear" or "link" argument.
7263 */
7264 if (STRNCMP(line, "clear", name_end - line) == 0)
7265 doclear = TRUE;
7266 if (STRNCMP(line, "link", name_end - line) == 0)
7267 dolink = TRUE;
7268
7269 /*
7270 * ":highlight {group-name}": list highlighting for one group.
7271 */
7272 if (!doclear && !dolink && ends_excmd(*linep))
7273 {
7274 id = syn_namen2id(line, (int)(name_end - line));
7275 if (id == 0)
7276 EMSG2(_("E411: highlight group not found: %s"), line);
7277 else
7278 highlight_list_one(id);
7279 return;
7280 }
7281
7282 /*
7283 * Handle ":highlight link {from} {to}" command.
7284 */
7285 if (dolink)
7286 {
7287 char_u *from_start = linep;
7288 char_u *from_end;
7289 char_u *to_start;
7290 char_u *to_end;
7291 int from_id;
7292 int to_id;
7293
7294 from_end = skiptowhite(from_start);
7295 to_start = skipwhite(from_end);
7296 to_end = skiptowhite(to_start);
7297
7298 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7299 {
7300 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7301 from_start);
7302 return;
7303 }
7304
7305 if (!ends_excmd(*skipwhite(to_end)))
7306 {
7307 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7308 return;
7309 }
7310
7311 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7312 if (STRNCMP(to_start, "NONE", 4) == 0)
7313 to_id = 0;
7314 else
7315 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7316
7317 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7318 {
7319 /*
7320 * Don't allow a link when there already is some highlighting
7321 * for the group, unless '!' is used
7322 */
7323 if (to_id > 0 && !forceit && !init
7324 && hl_has_settings(from_id - 1, dodefault))
7325 {
7326 if (sourcing_name == NULL && !dodefault)
7327 EMSG(_("E414: group has settings, highlight link ignored"));
7328 }
7329 else
7330 {
7331 if (!init)
7332 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7333 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007334#ifdef FEAT_EVAL
7335 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7336#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01007337 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007338 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007339 }
7340 }
7341
7342 /* Only call highlight_changed() once, after sourcing a syntax file */
7343 need_highlight_changed = TRUE;
7344
7345 return;
7346 }
7347
7348 if (doclear)
7349 {
7350 /*
7351 * ":highlight clear [group]" command.
7352 */
7353 line = linep;
7354 if (ends_excmd(*line))
7355 {
7356#ifdef FEAT_GUI
7357 /* First, we do not destroy the old values, but allocate the new
7358 * ones and update the display. THEN we destroy the old values.
7359 * If we destroy the old values first, then the old values
7360 * (such as GuiFont's or GuiFontset's) will still be displayed but
7361 * invalid because they were free'd.
7362 */
7363 if (gui.in_use)
7364 {
7365# ifdef FEAT_BEVAL_TIP
7366 gui_init_tooltip_font();
7367# endif
7368# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7369 gui_init_menu_font();
7370# endif
7371 }
7372# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7373 gui_mch_def_colors();
7374# endif
7375# ifdef FEAT_GUI_X11
7376# ifdef FEAT_MENU
7377
7378 /* This only needs to be done when there is no Menu highlight
7379 * group defined by default, which IS currently the case.
7380 */
7381 gui_mch_new_menu_colors();
7382# endif
7383 if (gui.in_use)
7384 {
7385 gui_new_scrollbar_colors();
7386# ifdef FEAT_BEVAL
7387 gui_mch_new_tooltip_colors();
7388# endif
7389# ifdef FEAT_MENU
7390 gui_mch_new_menu_font();
7391# endif
7392 }
7393# endif
7394
7395 /* Ok, we're done allocating the new default graphics items.
7396 * The screen should already be refreshed at this point.
7397 * It is now Ok to clear out the old data.
7398 */
7399#endif
7400#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007401 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007402#endif
7403 restore_cterm_colors();
7404
7405 /*
7406 * Clear all default highlight groups and load the defaults.
7407 */
7408 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7409 highlight_clear(idx);
7410 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007411#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007412 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007413 highlight_gui_started();
7414#endif
7415 highlight_changed();
7416 redraw_later_clear();
7417 return;
7418 }
7419 name_end = skiptowhite(line);
7420 linep = skipwhite(name_end);
7421 }
7422
7423 /*
7424 * Find the group name in the table. If it does not exist yet, add it.
7425 */
7426 id = syn_check_group(line, (int)(name_end - line));
7427 if (id == 0) /* failed (out of memory) */
7428 return;
7429 idx = id - 1; /* index is ID minus one */
7430
7431 /* Return if "default" was used and the group already has settings. */
7432 if (dodefault && hl_has_settings(idx, TRUE))
7433 return;
7434
7435 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7436 is_normal_group = TRUE;
7437#ifdef FEAT_GUI_X11
7438 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7439 is_menu_group = TRUE;
7440 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7441 is_scrollbar_group = TRUE;
7442 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7443 is_tooltip_group = TRUE;
7444#endif
7445
7446 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7447 if (doclear || (forceit && init))
7448 {
7449 highlight_clear(idx);
7450 if (!doclear)
7451 HL_TABLE()[idx].sg_set = 0;
7452 }
7453
7454 if (!doclear)
7455 while (!ends_excmd(*linep))
7456 {
7457 key_start = linep;
7458 if (*linep == '=')
7459 {
7460 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7461 error = TRUE;
7462 break;
7463 }
7464
7465 /*
7466 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7467 * "guibg").
7468 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007469 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007470 ++linep;
7471 vim_free(key);
7472 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7473 if (key == NULL)
7474 {
7475 error = TRUE;
7476 break;
7477 }
7478 linep = skipwhite(linep);
7479
7480 if (STRCMP(key, "NONE") == 0)
7481 {
7482 if (!init || HL_TABLE()[idx].sg_set == 0)
7483 {
7484 if (!init)
7485 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7486 highlight_clear(idx);
7487 }
7488 continue;
7489 }
7490
7491 /*
7492 * Check for the equal sign.
7493 */
7494 if (*linep != '=')
7495 {
7496 EMSG2(_("E416: missing equal sign: %s"), key_start);
7497 error = TRUE;
7498 break;
7499 }
7500 ++linep;
7501
7502 /*
7503 * Isolate the argument.
7504 */
7505 linep = skipwhite(linep);
7506 if (*linep == '\'') /* guifg='color name' */
7507 {
7508 arg_start = ++linep;
7509 linep = vim_strchr(linep, '\'');
7510 if (linep == NULL)
7511 {
7512 EMSG2(_(e_invarg2), key_start);
7513 error = TRUE;
7514 break;
7515 }
7516 }
7517 else
7518 {
7519 arg_start = linep;
7520 linep = skiptowhite(linep);
7521 }
7522 if (linep == arg_start)
7523 {
7524 EMSG2(_("E417: missing argument: %s"), key_start);
7525 error = TRUE;
7526 break;
7527 }
7528 vim_free(arg);
7529 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7530 if (arg == NULL)
7531 {
7532 error = TRUE;
7533 break;
7534 }
7535 if (*linep == '\'')
7536 ++linep;
7537
7538 /*
7539 * Store the argument.
7540 */
7541 if ( STRCMP(key, "TERM") == 0
7542 || STRCMP(key, "CTERM") == 0
7543 || STRCMP(key, "GUI") == 0)
7544 {
7545 attr = 0;
7546 off = 0;
7547 while (arg[off] != NUL)
7548 {
7549 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7550 {
7551 len = (int)STRLEN(hl_name_table[i]);
7552 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7553 {
7554 attr |= hl_attr_table[i];
7555 off += len;
7556 break;
7557 }
7558 }
7559 if (i < 0)
7560 {
7561 EMSG2(_("E418: Illegal value: %s"), arg);
7562 error = TRUE;
7563 break;
7564 }
7565 if (arg[off] == ',') /* another one follows */
7566 ++off;
7567 }
7568 if (error)
7569 break;
7570 if (*key == 'T')
7571 {
7572 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7573 {
7574 if (!init)
7575 HL_TABLE()[idx].sg_set |= SG_TERM;
7576 HL_TABLE()[idx].sg_term = attr;
7577 }
7578 }
7579 else if (*key == 'C')
7580 {
7581 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7582 {
7583 if (!init)
7584 HL_TABLE()[idx].sg_set |= SG_CTERM;
7585 HL_TABLE()[idx].sg_cterm = attr;
7586 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7587 }
7588 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007589#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007590 else
7591 {
7592 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7593 {
7594 if (!init)
7595 HL_TABLE()[idx].sg_set |= SG_GUI;
7596 HL_TABLE()[idx].sg_gui = attr;
7597 }
7598 }
7599#endif
7600 }
7601 else if (STRCMP(key, "FONT") == 0)
7602 {
7603 /* in non-GUI fonts are simply ignored */
7604#ifdef FEAT_GUI
7605 if (!gui.shell_created)
7606 {
7607 /* GUI not started yet, always accept the name. */
7608 vim_free(HL_TABLE()[idx].sg_font_name);
7609 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7610 }
7611 else
7612 {
7613 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7614# ifdef FEAT_XFONTSET
7615 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7616# endif
7617 /* First, save the current font/fontset.
7618 * Then try to allocate the font/fontset.
7619 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7620 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7621 */
7622
7623 HL_TABLE()[idx].sg_font = NOFONT;
7624# ifdef FEAT_XFONTSET
7625 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7626# endif
7627 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007628 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007629
7630# ifdef FEAT_XFONTSET
7631 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7632 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007633 /* New fontset was accepted. Free the old one, if there
7634 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007635 gui_mch_free_fontset(temp_sg_fontset);
7636 vim_free(HL_TABLE()[idx].sg_font_name);
7637 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7638 }
7639 else
7640 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7641# endif
7642 if (HL_TABLE()[idx].sg_font != NOFONT)
7643 {
7644 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007645 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007646 gui_mch_free_font(temp_sg_font);
7647 vim_free(HL_TABLE()[idx].sg_font_name);
7648 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7649 }
7650 else
7651 HL_TABLE()[idx].sg_font = temp_sg_font;
7652 }
7653#endif
7654 }
7655 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7656 {
7657 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7658 {
7659 if (!init)
7660 HL_TABLE()[idx].sg_set |= SG_CTERM;
7661
7662 /* When setting the foreground color, and previously the "bold"
7663 * flag was set for a light color, reset it now */
7664 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7665 {
7666 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7667 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7668 }
7669
7670 if (VIM_ISDIGIT(*arg))
7671 color = atoi((char *)arg);
7672 else if (STRICMP(arg, "fg") == 0)
7673 {
7674 if (cterm_normal_fg_color)
7675 color = cterm_normal_fg_color - 1;
7676 else
7677 {
7678 EMSG(_("E419: FG color unknown"));
7679 error = TRUE;
7680 break;
7681 }
7682 }
7683 else if (STRICMP(arg, "bg") == 0)
7684 {
7685 if (cterm_normal_bg_color > 0)
7686 color = cterm_normal_bg_color - 1;
7687 else
7688 {
7689 EMSG(_("E420: BG color unknown"));
7690 error = TRUE;
7691 break;
7692 }
7693 }
7694 else
7695 {
7696 static char *(color_names[28]) = {
7697 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7698 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7699 "Gray", "Grey",
7700 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7701 "Blue", "LightBlue", "Green", "LightGreen",
7702 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7703 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7704 static int color_numbers_16[28] = {0, 1, 2, 3,
7705 4, 5, 6, 6,
7706 7, 7,
7707 7, 7, 8, 8,
7708 9, 9, 10, 10,
7709 11, 11, 12, 12, 13,
7710 13, 14, 14, 15, -1};
7711 /* for xterm with 88 colors... */
7712 static int color_numbers_88[28] = {0, 4, 2, 6,
7713 1, 5, 32, 72,
7714 84, 84,
7715 7, 7, 82, 82,
7716 12, 43, 10, 61,
7717 14, 63, 9, 74, 13,
7718 75, 11, 78, 15, -1};
7719 /* for xterm with 256 colors... */
7720 static int color_numbers_256[28] = {0, 4, 2, 6,
7721 1, 5, 130, 130,
7722 248, 248,
7723 7, 7, 242, 242,
7724 12, 81, 10, 121,
7725 14, 159, 9, 224, 13,
7726 225, 11, 229, 15, -1};
7727 /* for terminals with less than 16 colors... */
7728 static int color_numbers_8[28] = {0, 4, 2, 6,
7729 1, 5, 3, 3,
7730 7, 7,
7731 7, 7, 0+8, 0+8,
7732 4+8, 4+8, 2+8, 2+8,
7733 6+8, 6+8, 1+8, 1+8, 5+8,
7734 5+8, 3+8, 3+8, 7+8, -1};
7735#if defined(__QNXNTO__)
7736 static int *color_numbers_8_qansi = color_numbers_8;
7737 /* On qnx, the 8 & 16 color arrays are the same */
7738 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7739 color_numbers_8_qansi = color_numbers_16;
7740#endif
7741
7742 /* reduce calls to STRICMP a bit, it can be slow */
7743 off = TOUPPER_ASC(*arg);
7744 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7745 if (off == color_names[i][0]
7746 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7747 break;
7748 if (i < 0)
7749 {
7750 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7751 error = TRUE;
7752 break;
7753 }
7754
7755 /* Use the _16 table to check if its a valid color name. */
7756 color = color_numbers_16[i];
7757 if (color >= 0)
7758 {
7759 if (t_colors == 8)
7760 {
7761 /* t_Co is 8: use the 8 colors table */
7762#if defined(__QNXNTO__)
7763 color = color_numbers_8_qansi[i];
7764#else
7765 color = color_numbers_8[i];
7766#endif
7767 if (key[5] == 'F')
7768 {
7769 /* set/reset bold attribute to get light foreground
7770 * colors (on some terminals, e.g. "linux") */
7771 if (color & 8)
7772 {
7773 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7774 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7775 }
7776 else
7777 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7778 }
7779 color &= 7; /* truncate to 8 colors */
7780 }
7781 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007782 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007783 {
7784 /*
7785 * Guess: if the termcap entry ends in 'm', it is
7786 * probably an xterm-like terminal. Use the changed
7787 * order for colors.
7788 */
7789 if (*T_CAF != NUL)
7790 p = T_CAF;
7791 else
7792 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007793 if (*p != NUL && (t_colors > 256
7794 || *(p + STRLEN(p) - 1) == 'm'))
7795 {
7796 if (t_colors == 88)
7797 color = color_numbers_88[i];
7798 else if (t_colors >= 256)
7799 color = color_numbers_256[i];
7800 else
7801 color = color_numbers_8[i];
7802 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007803 }
7804 }
7805 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007806 /* Add one to the argument, to avoid zero. Zero is used for
7807 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 if (key[5] == 'F')
7809 {
7810 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7811 if (is_normal_group)
7812 {
7813 cterm_normal_fg_color = color + 1;
7814 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7815#ifdef FEAT_GUI
7816 /* Don't do this if the GUI is used. */
7817 if (!gui.in_use && !gui.starting)
7818#endif
7819 {
7820 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007821 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007822 term_fg_color(color);
7823 }
7824 }
7825 }
7826 else
7827 {
7828 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7829 if (is_normal_group)
7830 {
7831 cterm_normal_bg_color = color + 1;
7832#ifdef FEAT_GUI
7833 /* Don't mess with 'background' if the GUI is used. */
7834 if (!gui.in_use && !gui.starting)
7835#endif
7836 {
7837 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007838 if (color >= 0)
7839 {
7840 if (termcap_active)
7841 term_bg_color(color);
7842 if (t_colors < 16)
7843 i = (color == 0 || color == 4);
7844 else
7845 i = (color < 7 || color == 8);
7846 /* Set the 'background' option if the value is
7847 * wrong. */
7848 if (i != (*p_bg == 'd'))
7849 set_option_value((char_u *)"bg", 0L,
7850 i ? (char_u *)"dark"
7851 : (char_u *)"light", 0);
7852 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007853 }
7854 }
7855 }
7856 }
7857 }
7858 else if (STRCMP(key, "GUIFG") == 0)
7859 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007860#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007861 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007862 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007863 if (!init)
7864 HL_TABLE()[idx].sg_set |= SG_GUI;
7865
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007866# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007867 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007868 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007869 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007870 {
7871 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007872# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007873 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7874 if (STRCMP(arg, "NONE"))
7875 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7876 else
7877 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007878# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007879# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007880 if (is_menu_group)
7881 gui.menu_fg_pixel = i;
7882 if (is_scrollbar_group)
7883 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007884# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007885 if (is_tooltip_group)
7886 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007887# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007888 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007889# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007890 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007891# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007892 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007893#endif
7894 }
7895 else if (STRCMP(key, "GUIBG") == 0)
7896 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007897#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007898 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007899 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007900 if (!init)
7901 HL_TABLE()[idx].sg_set |= SG_GUI;
7902
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007903# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007904 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007905 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007906 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007907 {
7908 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007909# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007910 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7911 if (STRCMP(arg, "NONE") != 0)
7912 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7913 else
7914 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007915# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007916# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007917 if (is_menu_group)
7918 gui.menu_bg_pixel = i;
7919 if (is_scrollbar_group)
7920 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007921# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007922 if (is_tooltip_group)
7923 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007924# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007925 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007926# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007927 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007928# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007929 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007930#endif
7931 }
7932 else if (STRCMP(key, "GUISP") == 0)
7933 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007934#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007935 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7936 {
7937 if (!init)
7938 HL_TABLE()[idx].sg_set |= SG_GUI;
7939
Bram Moolenaar61623362010-07-14 22:04:22 +02007940# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007941 i = color_name2handle(arg);
7942 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7943 {
7944 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007945# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007946 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7947 if (STRCMP(arg, "NONE") != 0)
7948 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7949 else
7950 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007951# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007952 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007953# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007954 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007955#endif
7956 }
7957 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7958 {
7959 char_u buf[100];
7960 char_u *tname;
7961
7962 if (!init)
7963 HL_TABLE()[idx].sg_set |= SG_TERM;
7964
7965 /*
7966 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007967 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007968 */
7969 if (STRNCMP(arg, "t_", 2) == 0)
7970 {
7971 off = 0;
7972 buf[0] = 0;
7973 while (arg[off] != NUL)
7974 {
7975 /* Isolate one termcap name */
7976 for (len = 0; arg[off + len] &&
7977 arg[off + len] != ','; ++len)
7978 ;
7979 tname = vim_strnsave(arg + off, len);
7980 if (tname == NULL) /* out of memory */
7981 {
7982 error = TRUE;
7983 break;
7984 }
7985 /* lookup the escape sequence for the item */
7986 p = get_term_code(tname);
7987 vim_free(tname);
7988 if (p == NULL) /* ignore non-existing things */
7989 p = (char_u *)"";
7990
7991 /* Append it to the already found stuff */
7992 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7993 {
7994 EMSG2(_("E422: terminal code too long: %s"), arg);
7995 error = TRUE;
7996 break;
7997 }
7998 STRCAT(buf, p);
7999
8000 /* Advance to the next item */
8001 off += len;
8002 if (arg[off] == ',') /* another one follows */
8003 ++off;
8004 }
8005 }
8006 else
8007 {
8008 /*
8009 * Copy characters from arg[] to buf[], translating <> codes.
8010 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008011 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008012 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008013 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008014 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008015 off += len;
8016 else /* copy as normal char */
8017 buf[off++] = *p++;
8018 }
8019 buf[off] = NUL;
8020 }
8021 if (error)
8022 break;
8023
8024 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8025 p = NULL;
8026 else
8027 p = vim_strsave(buf);
8028 if (key[2] == 'A')
8029 {
8030 vim_free(HL_TABLE()[idx].sg_start);
8031 HL_TABLE()[idx].sg_start = p;
8032 }
8033 else
8034 {
8035 vim_free(HL_TABLE()[idx].sg_stop);
8036 HL_TABLE()[idx].sg_stop = p;
8037 }
8038 }
8039 else
8040 {
8041 EMSG2(_("E423: Illegal argument: %s"), key_start);
8042 error = TRUE;
8043 break;
8044 }
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008045 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008046
8047 /*
8048 * When highlighting has been given for a group, don't link it.
8049 */
8050 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8051 HL_TABLE()[idx].sg_link = 0;
8052
8053 /*
8054 * Continue with next argument.
8055 */
8056 linep = skipwhite(linep);
8057 }
8058
8059 /*
8060 * If there is an error, and it's a new entry, remove it from the table.
8061 */
8062 if (error && idx == highlight_ga.ga_len)
8063 syn_unadd_group();
8064 else
8065 {
8066 if (is_normal_group)
8067 {
8068 HL_TABLE()[idx].sg_term_attr = 0;
8069 HL_TABLE()[idx].sg_cterm_attr = 0;
8070#ifdef FEAT_GUI
8071 HL_TABLE()[idx].sg_gui_attr = 0;
8072 /*
8073 * Need to update all groups, because they might be using "bg"
8074 * and/or "fg", which have been changed now.
8075 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008076#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008077#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008078 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008079 highlight_gui_started();
8080#endif
8081 }
8082#ifdef FEAT_GUI_X11
8083# ifdef FEAT_MENU
8084 else if (is_menu_group)
8085 {
8086 if (gui.in_use && do_colors)
8087 gui_mch_new_menu_colors();
8088 }
8089# endif
8090 else if (is_scrollbar_group)
8091 {
8092 if (gui.in_use && do_colors)
8093 gui_new_scrollbar_colors();
8094 }
8095# ifdef FEAT_BEVAL
8096 else if (is_tooltip_group)
8097 {
8098 if (gui.in_use && do_colors)
8099 gui_mch_new_tooltip_colors();
8100 }
8101# endif
8102#endif
8103 else
8104 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008105#ifdef FEAT_EVAL
8106 HL_TABLE()[idx].sg_scriptID = current_SID;
8107#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008108 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109 }
8110 vim_free(key);
8111 vim_free(arg);
8112
8113 /* Only call highlight_changed() once, after sourcing a syntax file */
8114 need_highlight_changed = TRUE;
8115}
8116
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008117#if defined(EXITFREE) || defined(PROTO)
8118 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008119free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008120{
8121 int i;
8122
8123 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008124 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008125 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008126 vim_free(HL_TABLE()[i].sg_name);
8127 vim_free(HL_TABLE()[i].sg_name_u);
8128 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008129 ga_clear(&highlight_ga);
8130}
8131#endif
8132
Bram Moolenaar071d4272004-06-13 20:20:40 +00008133/*
8134 * Reset the cterm colors to what they were before Vim was started, if
8135 * possible. Otherwise reset them to zero.
8136 */
8137 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008138restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008139{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008140#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 /* Since t_me has been set, this probably means that the user
8142 * wants to use this as default colors. Need to reset default
8143 * background/foreground colors. */
8144 mch_set_normal_colors();
8145#else
8146 cterm_normal_fg_color = 0;
8147 cterm_normal_fg_bold = 0;
8148 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008149# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008150 cterm_normal_fg_gui_color = INVALCOLOR;
8151 cterm_normal_bg_gui_color = INVALCOLOR;
8152# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008153#endif
8154}
8155
8156/*
8157 * Return TRUE if highlight group "idx" has any settings.
8158 * When "check_link" is TRUE also check for an existing link.
8159 */
8160 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008161hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008162{
8163 return ( HL_TABLE()[idx].sg_term_attr != 0
8164 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008165 || HL_TABLE()[idx].sg_cterm_fg != 0
8166 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008167#ifdef FEAT_GUI
8168 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008169 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8170 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8171 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008172 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008173#endif
8174 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8175}
8176
8177/*
8178 * Clear highlighting for one group.
8179 */
8180 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008181highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008183 HL_TABLE()[idx].sg_cleared = TRUE;
8184
Bram Moolenaar071d4272004-06-13 20:20:40 +00008185 HL_TABLE()[idx].sg_term = 0;
8186 vim_free(HL_TABLE()[idx].sg_start);
8187 HL_TABLE()[idx].sg_start = NULL;
8188 vim_free(HL_TABLE()[idx].sg_stop);
8189 HL_TABLE()[idx].sg_stop = NULL;
8190 HL_TABLE()[idx].sg_term_attr = 0;
8191 HL_TABLE()[idx].sg_cterm = 0;
8192 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8193 HL_TABLE()[idx].sg_cterm_fg = 0;
8194 HL_TABLE()[idx].sg_cterm_bg = 0;
8195 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008196#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008198 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8199 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8201 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008202 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8203 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008204#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008205#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008206 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8207 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008208#endif
8209#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008210 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8212 HL_TABLE()[idx].sg_font = NOFONT;
8213# ifdef FEAT_XFONTSET
8214 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8215 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8216# endif
8217 vim_free(HL_TABLE()[idx].sg_font_name);
8218 HL_TABLE()[idx].sg_font_name = NULL;
8219 HL_TABLE()[idx].sg_gui_attr = 0;
8220#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008221#ifdef FEAT_EVAL
8222 /* Clear the script ID only when there is no link, since that is not
8223 * cleared. */
8224 if (HL_TABLE()[idx].sg_link == 0)
8225 HL_TABLE()[idx].sg_scriptID = 0;
8226#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227}
8228
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008229#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008230/*
8231 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008232 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233 * "Tooltip" colors.
8234 */
8235 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008236set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008238#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008239# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008240 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008241# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008242 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008243 if (set_group_colors((char_u *)"Normal",
8244 &gui.norm_pixel, &gui.back_pixel,
8245 FALSE, TRUE, FALSE))
8246 {
8247 gui_mch_new_colors();
8248 must_redraw = CLEAR;
8249 }
8250# ifdef FEAT_GUI_X11
8251 if (set_group_colors((char_u *)"Menu",
8252 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8253 TRUE, FALSE, FALSE))
8254 {
8255# ifdef FEAT_MENU
8256 gui_mch_new_menu_colors();
8257# endif
8258 must_redraw = CLEAR;
8259 }
8260# ifdef FEAT_BEVAL
8261 if (set_group_colors((char_u *)"Tooltip",
8262 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8263 FALSE, FALSE, TRUE))
8264 {
8265# ifdef FEAT_TOOLBAR
8266 gui_mch_new_tooltip_colors();
8267# endif
8268 must_redraw = CLEAR;
8269 }
8270# endif
8271 if (set_group_colors((char_u *)"Scrollbar",
8272 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8273 FALSE, FALSE, FALSE))
8274 {
8275 gui_new_scrollbar_colors();
8276 must_redraw = CLEAR;
8277 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008278# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008279 }
8280#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008281#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008282# ifdef FEAT_GUI
8283 else
8284# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008285 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008286 int idx;
8287
8288 idx = syn_name2id((char_u *)"Normal") - 1;
8289 if (idx >= 0)
8290 {
8291 gui_do_one_color(idx, FALSE, FALSE);
8292
8293 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8294 {
8295 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8296 must_redraw = CLEAR;
8297 }
8298 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8299 {
8300 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8301 must_redraw = CLEAR;
8302 }
8303 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008304 }
8305#endif
8306}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008307#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008308
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008309#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008310/*
8311 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8312 */
8313 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008314set_group_colors(
8315 char_u *name,
8316 guicolor_T *fgp,
8317 guicolor_T *bgp,
8318 int do_menu,
8319 int use_norm,
8320 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008321{
8322 int idx;
8323
8324 idx = syn_name2id(name) - 1;
8325 if (idx >= 0)
8326 {
8327 gui_do_one_color(idx, do_menu, do_tooltip);
8328
8329 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8330 *fgp = HL_TABLE()[idx].sg_gui_fg;
8331 else if (use_norm)
8332 *fgp = gui.def_norm_pixel;
8333 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8334 *bgp = HL_TABLE()[idx].sg_gui_bg;
8335 else if (use_norm)
8336 *bgp = gui.def_back_pixel;
8337 return TRUE;
8338 }
8339 return FALSE;
8340}
8341
8342/*
8343 * Get the font of the "Normal" group.
8344 * Returns "" when it's not found or not set.
8345 */
8346 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008347hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008348{
8349 int id;
8350 char_u *s;
8351
8352 id = syn_name2id((char_u *)"Normal");
8353 if (id > 0)
8354 {
8355 s = HL_TABLE()[id - 1].sg_font_name;
8356 if (s != NULL)
8357 return s;
8358 }
8359 return (char_u *)"";
8360}
8361
8362/*
8363 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8364 * actually chosen to be used.
8365 */
8366 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008367hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008368{
8369 int id;
8370
8371 id = syn_name2id((char_u *)"Normal");
8372 if (id > 0)
8373 {
8374 vim_free(HL_TABLE()[id - 1].sg_font_name);
8375 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8376 }
8377}
8378
8379/*
8380 * Set background color for "Normal" group. Called by gui_set_bg_color()
8381 * when the color is known.
8382 */
8383 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008384hl_set_bg_color_name(
8385 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386{
8387 int id;
8388
8389 if (name != NULL)
8390 {
8391 id = syn_name2id((char_u *)"Normal");
8392 if (id > 0)
8393 {
8394 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8395 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8396 }
8397 }
8398}
8399
8400/*
8401 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8402 * when the color is known.
8403 */
8404 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008405hl_set_fg_color_name(
8406 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008407{
8408 int id;
8409
8410 if (name != NULL)
8411 {
8412 id = syn_name2id((char_u *)"Normal");
8413 if (id > 0)
8414 {
8415 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8416 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8417 }
8418 }
8419}
8420
8421/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008422 * Return the handle for a font name.
8423 * Returns NOFONT when failed.
8424 */
8425 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008426font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008427{
8428 if (STRCMP(name, "NONE") == 0)
8429 return NOFONT;
8430
8431 return gui_mch_get_font(name, TRUE);
8432}
8433
8434# ifdef FEAT_XFONTSET
8435/*
8436 * Return the handle for a fontset name.
8437 * Returns NOFONTSET when failed.
8438 */
8439 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008440fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008441{
8442 if (STRCMP(name, "NONE") == 0)
8443 return NOFONTSET;
8444
8445 return gui_mch_get_fontset(name, TRUE, fixed_width);
8446}
8447# endif
8448
8449/*
8450 * Get the font or fontset for one highlight group.
8451 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008453hl_do_font(
8454 int idx,
8455 char_u *arg,
8456 int do_normal, /* set normal font */
8457 int do_menu UNUSED, /* set menu font */
8458 int do_tooltip UNUSED, /* set tooltip font */
8459 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008460{
8461# ifdef FEAT_XFONTSET
8462 /* If 'guifontset' is not empty, first try using the name as a
8463 * fontset. If that doesn't work, use it as a font name. */
8464 if (*p_guifontset != NUL
8465# ifdef FONTSET_ALWAYS
8466 || do_menu
8467# endif
8468# ifdef FEAT_BEVAL_TIP
8469 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8470 || do_tooltip
8471# endif
8472 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008473 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008474 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008475 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008476 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8477# ifdef FONTSET_ALWAYS
8478 || do_menu
8479# endif
8480# ifdef FEAT_BEVAL_TIP
8481 || do_tooltip
8482# endif
8483 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008484 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008485 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8486 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008487 /* If it worked and it's the Normal group, use it as the normal
8488 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008489 if (do_normal)
8490 gui_init_font(arg, TRUE);
8491# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8492 if (do_menu)
8493 {
8494# ifdef FONTSET_ALWAYS
8495 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8496# else
8497 /* YIKES! This is a bug waiting to crash the program */
8498 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8499# endif
8500 gui_mch_new_menu_font();
8501 }
8502# ifdef FEAT_BEVAL
8503 if (do_tooltip)
8504 {
8505 /* The Athena widget set cannot currently handle switching between
8506 * displaying a single font and a fontset.
8507 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008508 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008509 * XFontStruct is used.
8510 */
8511 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8512 gui_mch_new_tooltip_font();
8513 }
8514# endif
8515# endif
8516 }
8517 else
8518# endif
8519 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008520 if (free_font)
8521 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008522 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8523 /* If it worked and it's the Normal group, use it as the
8524 * normal font. Same for the Menu group. */
8525 if (HL_TABLE()[idx].sg_font != NOFONT)
8526 {
8527 if (do_normal)
8528 gui_init_font(arg, FALSE);
8529#ifndef FONTSET_ALWAYS
8530# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8531 if (do_menu)
8532 {
8533 gui.menu_font = HL_TABLE()[idx].sg_font;
8534 gui_mch_new_menu_font();
8535 }
8536# endif
8537#endif
8538 }
8539 }
8540}
8541
8542#endif /* FEAT_GUI */
8543
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008544#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008545/*
8546 * Return the handle for a color name.
8547 * Returns INVALCOLOR when failed.
8548 */
8549 static guicolor_T
8550color_name2handle(char_u *name)
8551{
8552 if (STRCMP(name, "NONE") == 0)
8553 return INVALCOLOR;
8554
8555 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8556 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008557#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008558 if (gui.in_use)
8559#endif
8560#ifdef FEAT_GUI
8561 return gui.norm_pixel;
8562#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008563#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008564 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008565 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008566 /* Guess that the foreground is black or white. */
8567 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008568#endif
8569 }
8570 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8571 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008572#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008573 if (gui.in_use)
8574#endif
8575#ifdef FEAT_GUI
8576 return gui.back_pixel;
8577#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008578#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008579 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008580 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008581 /* Guess that the background is white or black. */
8582 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008583#endif
8584 }
8585
8586 return GUI_GET_COLOR(name);
8587}
8588#endif
8589
Bram Moolenaar071d4272004-06-13 20:20:40 +00008590/*
8591 * Table with the specifications for an attribute number.
8592 * Note that this table is used by ALL buffers. This is required because the
8593 * GUI can redraw at any time for any buffer.
8594 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008595static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008596
8597#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8598
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008599static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008600
8601#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8602
8603#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008604static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008605
8606#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8607#endif
8608
8609/*
8610 * Return the attr number for a set of colors and font.
8611 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8612 * if the combination is new.
8613 * Return 0 for error (no more room).
8614 */
8615 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008616get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008617{
8618 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008619 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008620 static int recursive = FALSE;
8621
8622 /*
8623 * Init the table, in case it wasn't done yet.
8624 */
8625 table->ga_itemsize = sizeof(attrentry_T);
8626 table->ga_growsize = 7;
8627
8628 /*
8629 * Try to find an entry with the same specifications.
8630 */
8631 for (i = 0; i < table->ga_len; ++i)
8632 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008633 taep = &(((attrentry_T *)table->ga_data)[i]);
8634 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008635 && (
8636#ifdef FEAT_GUI
8637 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008638 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8639 && aep->ae_u.gui.bg_color
8640 == taep->ae_u.gui.bg_color
8641 && aep->ae_u.gui.sp_color
8642 == taep->ae_u.gui.sp_color
8643 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008644# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008645 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008646# endif
8647 ))
8648 ||
8649#endif
8650 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008651 && (aep->ae_u.term.start == NULL)
8652 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008653 && (aep->ae_u.term.start == NULL
8654 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008655 taep->ae_u.term.start) == 0)
8656 && (aep->ae_u.term.stop == NULL)
8657 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008658 && (aep->ae_u.term.stop == NULL
8659 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008660 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008661 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008662 && aep->ae_u.cterm.fg_color
8663 == taep->ae_u.cterm.fg_color
8664 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008665 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008666#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008667 && aep->ae_u.cterm.fg_rgb
8668 == taep->ae_u.cterm.fg_rgb
8669 && aep->ae_u.cterm.bg_rgb
8670 == taep->ae_u.cterm.bg_rgb
8671#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008672 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008673
8674 return i + ATTR_OFF;
8675 }
8676
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008677 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008678 {
8679 /*
8680 * Running out of attribute entries! remove all attributes, and
8681 * compute new ones for all groups.
8682 * When called recursively, we are really out of numbers.
8683 */
8684 if (recursive)
8685 {
8686 EMSG(_("E424: Too many different highlighting attributes in use"));
8687 return 0;
8688 }
8689 recursive = TRUE;
8690
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008691 clear_hl_tables();
8692
Bram Moolenaar071d4272004-06-13 20:20:40 +00008693 must_redraw = CLEAR;
8694
8695 for (i = 0; i < highlight_ga.ga_len; ++i)
8696 set_hl_attr(i);
8697
8698 recursive = FALSE;
8699 }
8700
8701 /*
8702 * This is a new combination of colors and font, add an entry.
8703 */
8704 if (ga_grow(table, 1) == FAIL)
8705 return 0;
8706
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008707 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8708 vim_memset(taep, 0, sizeof(attrentry_T));
8709 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008710#ifdef FEAT_GUI
8711 if (table == &gui_attr_table)
8712 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008713 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8714 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8715 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8716 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008717# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008718 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008719# endif
8720 }
8721#endif
8722 if (table == &term_attr_table)
8723 {
8724 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008725 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008726 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008727 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008728 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008729 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008730 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008731 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008732 }
8733 else if (table == &cterm_attr_table)
8734 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008735 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8736 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008737#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008738 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8739 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8740#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008741 }
8742 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008743 return (table->ga_len - 1 + ATTR_OFF);
8744}
8745
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008746/*
8747 * Clear all highlight tables.
8748 */
8749 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008750clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008751{
8752 int i;
8753 attrentry_T *taep;
8754
8755#ifdef FEAT_GUI
8756 ga_clear(&gui_attr_table);
8757#endif
8758 for (i = 0; i < term_attr_table.ga_len; ++i)
8759 {
8760 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8761 vim_free(taep->ae_u.term.start);
8762 vim_free(taep->ae_u.term.stop);
8763 }
8764 ga_clear(&term_attr_table);
8765 ga_clear(&cterm_attr_table);
8766}
8767
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008768#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008769/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008770 * Combine special attributes (e.g., for spelling) with other attributes
8771 * (e.g., for syntax highlighting).
8772 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008773 * This creates a new group when required.
8774 * Since we expect there to be few spelling mistakes we don't cache the
8775 * result.
8776 * Return the resulting attributes.
8777 */
8778 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008779hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008780{
8781 attrentry_T *char_aep = NULL;
8782 attrentry_T *spell_aep;
8783 attrentry_T new_en;
8784
8785 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008786 return prim_attr;
8787 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8788 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008789#ifdef FEAT_GUI
8790 if (gui.in_use)
8791 {
8792 if (char_attr > HL_ALL)
8793 char_aep = syn_gui_attr2entry(char_attr);
8794 if (char_aep != NULL)
8795 new_en = *char_aep;
8796 else
8797 {
8798 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008799 new_en.ae_u.gui.fg_color = INVALCOLOR;
8800 new_en.ae_u.gui.bg_color = INVALCOLOR;
8801 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008802 if (char_attr <= HL_ALL)
8803 new_en.ae_attr = char_attr;
8804 }
8805
Bram Moolenaar30abd282005-06-22 22:35:10 +00008806 if (prim_attr <= HL_ALL)
8807 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008808 else
8809 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008810 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008811 if (spell_aep != NULL)
8812 {
8813 new_en.ae_attr |= spell_aep->ae_attr;
8814 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8815 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8816 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8817 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8818 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8819 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8820 if (spell_aep->ae_u.gui.font != NOFONT)
8821 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8822# ifdef FEAT_XFONTSET
8823 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8824 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8825# endif
8826 }
8827 }
8828 return get_attr_entry(&gui_attr_table, &new_en);
8829 }
8830#endif
8831
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008832 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008833 {
8834 if (char_attr > HL_ALL)
8835 char_aep = syn_cterm_attr2entry(char_attr);
8836 if (char_aep != NULL)
8837 new_en = *char_aep;
8838 else
8839 {
8840 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008841#ifdef FEAT_TERMGUICOLORS
8842 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8843 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8844#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008845 if (char_attr <= HL_ALL)
8846 new_en.ae_attr = char_attr;
8847 }
8848
Bram Moolenaar30abd282005-06-22 22:35:10 +00008849 if (prim_attr <= HL_ALL)
8850 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008851 else
8852 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008853 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008854 if (spell_aep != NULL)
8855 {
8856 new_en.ae_attr |= spell_aep->ae_attr;
8857 if (spell_aep->ae_u.cterm.fg_color > 0)
8858 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8859 if (spell_aep->ae_u.cterm.bg_color > 0)
8860 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008861#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008862 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008863 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008864 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008865 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8866#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008867 }
8868 }
8869 return get_attr_entry(&cterm_attr_table, &new_en);
8870 }
8871
8872 if (char_attr > HL_ALL)
8873 char_aep = syn_term_attr2entry(char_attr);
8874 if (char_aep != NULL)
8875 new_en = *char_aep;
8876 else
8877 {
8878 vim_memset(&new_en, 0, sizeof(new_en));
8879 if (char_attr <= HL_ALL)
8880 new_en.ae_attr = char_attr;
8881 }
8882
Bram Moolenaar30abd282005-06-22 22:35:10 +00008883 if (prim_attr <= HL_ALL)
8884 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008885 else
8886 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008887 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008888 if (spell_aep != NULL)
8889 {
8890 new_en.ae_attr |= spell_aep->ae_attr;
8891 if (spell_aep->ae_u.term.start != NULL)
8892 {
8893 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8894 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8895 }
8896 }
8897 }
8898 return get_attr_entry(&term_attr_table, &new_en);
8899}
8900#endif
8901
Bram Moolenaar071d4272004-06-13 20:20:40 +00008902#ifdef FEAT_GUI
8903
8904 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008905syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008906{
8907 attr -= ATTR_OFF;
8908 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8909 return NULL;
8910 return &(GUI_ATTR_ENTRY(attr));
8911}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008912#endif /* FEAT_GUI */
8913
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008914/*
8915 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8916 * Only to be used when "attr" > HL_ALL.
8917 */
8918 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008919syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008920{
8921 attrentry_T *aep;
8922
8923#ifdef FEAT_GUI
8924 if (gui.in_use)
8925 aep = syn_gui_attr2entry(attr);
8926 else
8927#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008928 if (IS_CTERM)
8929 aep = syn_cterm_attr2entry(attr);
8930 else
8931 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008932
8933 if (aep == NULL) /* highlighting not set */
8934 return 0;
8935 return aep->ae_attr;
8936}
8937
8938
Bram Moolenaar071d4272004-06-13 20:20:40 +00008939 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008940syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008941{
8942 attr -= ATTR_OFF;
8943 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8944 return NULL;
8945 return &(TERM_ATTR_ENTRY(attr));
8946}
8947
8948 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008949syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008950{
8951 attr -= ATTR_OFF;
8952 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8953 return NULL;
8954 return &(CTERM_ATTR_ENTRY(attr));
8955}
8956
8957#define LIST_ATTR 1
8958#define LIST_STRING 2
8959#define LIST_INT 3
8960
8961 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008962highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008963{
8964 struct hl_group *sgp;
8965 int didh = FALSE;
8966
8967 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8968
8969 didh = highlight_list_arg(id, didh, LIST_ATTR,
8970 sgp->sg_term, NULL, "term");
8971 didh = highlight_list_arg(id, didh, LIST_STRING,
8972 0, sgp->sg_start, "start");
8973 didh = highlight_list_arg(id, didh, LIST_STRING,
8974 0, sgp->sg_stop, "stop");
8975
8976 didh = highlight_list_arg(id, didh, LIST_ATTR,
8977 sgp->sg_cterm, NULL, "cterm");
8978 didh = highlight_list_arg(id, didh, LIST_INT,
8979 sgp->sg_cterm_fg, NULL, "ctermfg");
8980 didh = highlight_list_arg(id, didh, LIST_INT,
8981 sgp->sg_cterm_bg, NULL, "ctermbg");
8982
Bram Moolenaar61623362010-07-14 22:04:22 +02008983#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008984 didh = highlight_list_arg(id, didh, LIST_ATTR,
8985 sgp->sg_gui, NULL, "gui");
8986 didh = highlight_list_arg(id, didh, LIST_STRING,
8987 0, sgp->sg_gui_fg_name, "guifg");
8988 didh = highlight_list_arg(id, didh, LIST_STRING,
8989 0, sgp->sg_gui_bg_name, "guibg");
8990 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008991 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008992#endif
8993#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008994 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008995 0, sgp->sg_font_name, "font");
8996#endif
8997
Bram Moolenaar661b1822005-07-28 22:36:45 +00008998 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008999 {
9000 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009001 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009002 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009003 msg_putchar(' ');
9004 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9005 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009006
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009007 if (!didh)
9008 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009009#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009010 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009011 last_set_msg(sgp->sg_scriptID);
9012#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009013}
9014
9015 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009016highlight_list_arg(
9017 int id,
9018 int didh,
9019 int type,
9020 int iarg,
9021 char_u *sarg,
9022 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009023{
9024 char_u buf[100];
9025 char_u *ts;
9026 int i;
9027
Bram Moolenaar661b1822005-07-28 22:36:45 +00009028 if (got_int)
9029 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009030 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9031 {
9032 ts = buf;
9033 if (type == LIST_INT)
9034 sprintf((char *)buf, "%d", iarg - 1);
9035 else if (type == LIST_STRING)
9036 ts = sarg;
9037 else /* type == LIST_ATTR */
9038 {
9039 buf[0] = NUL;
9040 for (i = 0; hl_attr_table[i] != 0; ++i)
9041 {
9042 if (iarg & hl_attr_table[i])
9043 {
9044 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009045 vim_strcat(buf, (char_u *)",", 100);
9046 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009047 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9048 }
9049 }
9050 }
9051
9052 (void)syn_list_header(didh,
9053 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9054 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009055 if (!got_int)
9056 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009057 if (*name != NUL)
9058 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009059 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9060 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009061 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009062 msg_outtrans(ts);
9063 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009064 }
9065 return didh;
9066}
9067
9068#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9069/*
9070 * Return "1" if highlight group "id" has attribute "flag".
9071 * Return NULL otherwise.
9072 */
9073 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009074highlight_has_attr(
9075 int id,
9076 int flag,
9077 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009078{
9079 int attr;
9080
9081 if (id <= 0 || id > highlight_ga.ga_len)
9082 return NULL;
9083
Bram Moolenaar61623362010-07-14 22:04:22 +02009084#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009085 if (modec == 'g')
9086 attr = HL_TABLE()[id - 1].sg_gui;
9087 else
9088#endif
9089 if (modec == 'c')
9090 attr = HL_TABLE()[id - 1].sg_cterm;
9091 else
9092 attr = HL_TABLE()[id - 1].sg_term;
9093
9094 if (attr & flag)
9095 return (char_u *)"1";
9096 return NULL;
9097}
9098#endif
9099
9100#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9101/*
9102 * Return color name of highlight group "id".
9103 */
9104 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009105highlight_color(
9106 int id,
9107 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9108 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009109{
9110 static char_u name[20];
9111 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009112 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009113 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009114 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009115
9116 if (id <= 0 || id > highlight_ga.ga_len)
9117 return NULL;
9118
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009119 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009120 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009121 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009122 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009123 font = TRUE;
9124 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009125 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009126 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9127 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009128 if (modec == 'g')
9129 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009130# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009131# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009132 /* return font name */
9133 if (font)
9134 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009135# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009136
Bram Moolenaar071d4272004-06-13 20:20:40 +00009137 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009138 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009139 {
9140 guicolor_T color;
9141 long_u rgb;
9142 static char_u buf[10];
9143
9144 if (fg)
9145 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009146 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009147# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009148 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009149# else
9150 color = INVALCOLOR;
9151# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009152 else
9153 color = HL_TABLE()[id - 1].sg_gui_bg;
9154 if (color == INVALCOLOR)
9155 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009156 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009157 sprintf((char *)buf, "#%02x%02x%02x",
9158 (unsigned)(rgb >> 16),
9159 (unsigned)(rgb >> 8) & 255,
9160 (unsigned)rgb & 255);
9161 return buf;
9162 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009163# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009164 if (fg)
9165 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009166 if (sp)
9167 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009168 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9169 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009170 if (font || sp)
9171 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009172 if (modec == 'c')
9173 {
9174 if (fg)
9175 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9176 else
9177 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009178 if (n < 0)
9179 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009180 sprintf((char *)name, "%d", n);
9181 return name;
9182 }
9183 /* term doesn't have color */
9184 return NULL;
9185}
9186#endif
9187
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009188#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009189 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009190 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009191/*
9192 * Return color name of highlight group "id" as RGB value.
9193 */
9194 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009195highlight_gui_color_rgb(
9196 int id,
9197 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009198{
9199 guicolor_T color;
9200
9201 if (id <= 0 || id > highlight_ga.ga_len)
9202 return 0L;
9203
9204 if (fg)
9205 color = HL_TABLE()[id - 1].sg_gui_fg;
9206 else
9207 color = HL_TABLE()[id - 1].sg_gui_bg;
9208
9209 if (color == INVALCOLOR)
9210 return 0L;
9211
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009212 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009213}
9214#endif
9215
9216/*
9217 * Output the syntax list header.
9218 * Return TRUE when started a new line.
9219 */
9220 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009221syn_list_header(
9222 int did_header, /* did header already */
9223 int outlen, /* length of string that comes */
9224 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009225{
9226 int endcol = 19;
9227 int newline = TRUE;
9228
9229 if (!did_header)
9230 {
9231 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009232 if (got_int)
9233 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009234 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9235 endcol = 15;
9236 }
9237 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009238 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009239 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009240 if (got_int)
9241 return TRUE;
9242 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009243 else
9244 {
9245 if (msg_col >= endcol) /* wrap around is like starting a new line */
9246 newline = FALSE;
9247 }
9248
9249 if (msg_col >= endcol) /* output at least one space */
9250 endcol = msg_col + 1;
9251 if (Columns <= endcol) /* avoid hang for tiny window */
9252 endcol = Columns - 1;
9253
9254 msg_advance(endcol);
9255
9256 /* Show "xxx" with the attributes. */
9257 if (!did_header)
9258 {
9259 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9260 msg_putchar(' ');
9261 }
9262
9263 return newline;
9264}
9265
9266/*
9267 * Set the attribute numbers for a highlight group.
9268 * Called after one of the attributes has changed.
9269 */
9270 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009271set_hl_attr(
9272 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009273{
9274 attrentry_T at_en;
9275 struct hl_group *sgp = HL_TABLE() + idx;
9276
9277 /* The "Normal" group doesn't need an attribute number */
9278 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9279 return;
9280
9281#ifdef FEAT_GUI
9282 /*
9283 * For the GUI mode: If there are other than "normal" highlighting
9284 * attributes, need to allocate an attr number.
9285 */
9286 if (sgp->sg_gui_fg == INVALCOLOR
9287 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009288 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009289 && sgp->sg_font == NOFONT
9290# ifdef FEAT_XFONTSET
9291 && sgp->sg_fontset == NOFONTSET
9292# endif
9293 )
9294 {
9295 sgp->sg_gui_attr = sgp->sg_gui;
9296 }
9297 else
9298 {
9299 at_en.ae_attr = sgp->sg_gui;
9300 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9301 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009302 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009303 at_en.ae_u.gui.font = sgp->sg_font;
9304# ifdef FEAT_XFONTSET
9305 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9306# endif
9307 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9308 }
9309#endif
9310 /*
9311 * For the term mode: If there are other than "normal" highlighting
9312 * attributes, need to allocate an attr number.
9313 */
9314 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9315 sgp->sg_term_attr = sgp->sg_term;
9316 else
9317 {
9318 at_en.ae_attr = sgp->sg_term;
9319 at_en.ae_u.term.start = sgp->sg_start;
9320 at_en.ae_u.term.stop = sgp->sg_stop;
9321 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9322 }
9323
9324 /*
9325 * For the color term mode: If there are other than "normal"
9326 * highlighting attributes, need to allocate an attr number.
9327 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009328 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009329# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009330 && sgp->sg_gui_fg == INVALCOLOR
9331 && sgp->sg_gui_bg == INVALCOLOR
9332# endif
9333 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009334 sgp->sg_cterm_attr = sgp->sg_cterm;
9335 else
9336 {
9337 at_en.ae_attr = sgp->sg_cterm;
9338 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9339 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009340# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009341 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9342 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009343# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009344 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9345 }
9346}
9347
9348/*
9349 * Lookup a highlight group name and return it's ID.
9350 * If it is not found, 0 is returned.
9351 */
9352 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009353syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009354{
9355 int i;
9356 char_u name_u[200];
9357
9358 /* Avoid using stricmp() too much, it's slow on some systems */
9359 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9360 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009361 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009362 vim_strup(name_u);
9363 for (i = highlight_ga.ga_len; --i >= 0; )
9364 if (HL_TABLE()[i].sg_name_u != NULL
9365 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9366 break;
9367 return i + 1;
9368}
9369
9370#if defined(FEAT_EVAL) || defined(PROTO)
9371/*
9372 * Return TRUE if highlight group "name" exists.
9373 */
9374 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009375highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009376{
9377 return (syn_name2id(name) > 0);
9378}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009379
9380# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9381/*
9382 * Return the name of highlight group "id".
9383 * When not a valid ID return an empty string.
9384 */
9385 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009386syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009387{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009388 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009389 return (char_u *)"";
9390 return HL_TABLE()[id - 1].sg_name;
9391}
9392# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009393#endif
9394
9395/*
9396 * Like syn_name2id(), but take a pointer + length argument.
9397 */
9398 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009399syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009400{
9401 char_u *name;
9402 int id = 0;
9403
9404 name = vim_strnsave(linep, len);
9405 if (name != NULL)
9406 {
9407 id = syn_name2id(name);
9408 vim_free(name);
9409 }
9410 return id;
9411}
9412
9413/*
9414 * Find highlight group name in the table and return it's ID.
9415 * The argument is a pointer to the name and the length of the name.
9416 * If it doesn't exist yet, a new entry is created.
9417 * Return 0 for failure.
9418 */
9419 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009420syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009421{
9422 int id;
9423 char_u *name;
9424
9425 name = vim_strnsave(pp, len);
9426 if (name == NULL)
9427 return 0;
9428
9429 id = syn_name2id(name);
9430 if (id == 0) /* doesn't exist yet */
9431 id = syn_add_group(name);
9432 else
9433 vim_free(name);
9434 return id;
9435}
9436
9437/*
9438 * Add new highlight group and return it's ID.
9439 * "name" must be an allocated string, it will be consumed.
9440 * Return 0 for failure.
9441 */
9442 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009443syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009444{
9445 char_u *p;
9446
9447 /* Check that the name is ASCII letters, digits and underscore. */
9448 for (p = name; *p != NUL; ++p)
9449 {
9450 if (!vim_isprintc(*p))
9451 {
9452 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009453 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009454 return 0;
9455 }
9456 else if (!ASCII_ISALNUM(*p) && *p != '_')
9457 {
9458 /* This is an error, but since there previously was no check only
9459 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009460 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009461 MSG(_("W18: Invalid character in group name"));
9462 break;
9463 }
9464 }
9465
9466 /*
9467 * First call for this growarray: init growing array.
9468 */
9469 if (highlight_ga.ga_data == NULL)
9470 {
9471 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9472 highlight_ga.ga_growsize = 10;
9473 }
9474
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009475 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009476 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009477 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009478 vim_free(name);
9479 return 0;
9480 }
9481
Bram Moolenaar071d4272004-06-13 20:20:40 +00009482 /*
9483 * Make room for at least one other syntax_highlight entry.
9484 */
9485 if (ga_grow(&highlight_ga, 1) == FAIL)
9486 {
9487 vim_free(name);
9488 return 0;
9489 }
9490
9491 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9492 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9493 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009494#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009495 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9496 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009497# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009498 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009499# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009500#endif
9501 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009502
9503 return highlight_ga.ga_len; /* ID is index plus one */
9504}
9505
9506/*
9507 * When, just after calling syn_add_group(), an error is discovered, this
9508 * function deletes the new name.
9509 */
9510 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009511syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009512{
9513 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009514 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9515 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9516}
9517
9518/*
9519 * Translate a group ID to highlight attributes.
9520 */
9521 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009522syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009523{
9524 int attr;
9525 struct hl_group *sgp;
9526
9527 hl_id = syn_get_final_id(hl_id);
9528 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9529
9530#ifdef FEAT_GUI
9531 /*
9532 * Only use GUI attr when the GUI is being used.
9533 */
9534 if (gui.in_use)
9535 attr = sgp->sg_gui_attr;
9536 else
9537#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009538 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009539 attr = sgp->sg_cterm_attr;
9540 else
9541 attr = sgp->sg_term_attr;
9542
9543 return attr;
9544}
9545
9546#ifdef FEAT_GUI
9547/*
9548 * Get the GUI colors and attributes for a group ID.
9549 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9550 */
9551 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009552syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009553{
9554 struct hl_group *sgp;
9555
9556 hl_id = syn_get_final_id(hl_id);
9557 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9558
9559 *fgp = sgp->sg_gui_fg;
9560 *bgp = sgp->sg_gui_bg;
9561 return sgp->sg_gui;
9562}
9563#endif
9564
9565/*
9566 * Translate a group ID to the final group ID (following links).
9567 */
9568 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009569syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009570{
9571 int count;
9572 struct hl_group *sgp;
9573
9574 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9575 return 0; /* Can be called from eval!! */
9576
9577 /*
9578 * Follow links until there is no more.
9579 * Look out for loops! Break after 100 links.
9580 */
9581 for (count = 100; --count >= 0; )
9582 {
9583 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9584 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9585 break;
9586 hl_id = sgp->sg_link;
9587 }
9588
9589 return hl_id;
9590}
9591
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009592#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009593/*
9594 * Call this function just after the GUI has started.
9595 * It finds the font and color handles for the highlighting groups.
9596 */
9597 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009598highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009599{
9600 int idx;
9601
9602 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009603# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9604# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009605 if (USE_24BIT)
9606# endif
9607 set_normal_colors();
9608# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009609
9610 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9611 gui_do_one_color(idx, FALSE, FALSE);
9612
9613 highlight_changed();
9614}
9615
9616 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009617gui_do_one_color(
9618 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009619 int do_menu UNUSED, /* TRUE: might set the menu font */
9620 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009621{
9622 int didit = FALSE;
9623
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009624# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009625# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009626 if (gui.in_use)
9627# endif
9628 if (HL_TABLE()[idx].sg_font_name != NULL)
9629 {
9630 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009631 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009632 didit = TRUE;
9633 }
9634# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009635 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9636 {
9637 HL_TABLE()[idx].sg_gui_fg =
9638 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9639 didit = TRUE;
9640 }
9641 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9642 {
9643 HL_TABLE()[idx].sg_gui_bg =
9644 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9645 didit = TRUE;
9646 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009647# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009648 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9649 {
9650 HL_TABLE()[idx].sg_gui_sp =
9651 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9652 didit = TRUE;
9653 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009654# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009655 if (didit) /* need to get a new attr number */
9656 set_hl_attr(idx);
9657}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009658#endif
9659
9660/*
9661 * Translate the 'highlight' option into attributes in highlight_attr[] and
9662 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9663 * corresponding highlights to use on top of HLF_SNC is computed.
9664 * Called only when the 'highlight' option has been changed and upon first
9665 * screen redraw after any :highlight command.
9666 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9667 */
9668 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009669highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009670{
9671 int hlf;
9672 int i;
9673 char_u *p;
9674 int attr;
9675 char_u *end;
9676 int id;
9677#ifdef USER_HIGHLIGHT
9678 char_u userhl[10];
9679# ifdef FEAT_STL_OPT
9680 int id_SNC = -1;
9681 int id_S = -1;
9682 int hlcnt;
9683# endif
9684#endif
9685 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9686
9687 need_highlight_changed = FALSE;
9688
9689 /*
9690 * Clear all attributes.
9691 */
9692 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9693 highlight_attr[hlf] = 0;
9694
9695 /*
9696 * First set all attributes to their default value.
9697 * Then use the attributes from the 'highlight' option.
9698 */
9699 for (i = 0; i < 2; ++i)
9700 {
9701 if (i)
9702 p = p_hl;
9703 else
9704 p = get_highlight_default();
9705 if (p == NULL) /* just in case */
9706 continue;
9707
9708 while (*p)
9709 {
9710 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9711 if (hl_flags[hlf] == *p)
9712 break;
9713 ++p;
9714 if (hlf == (int)HLF_COUNT || *p == NUL)
9715 return FAIL;
9716
9717 /*
9718 * Allow several hl_flags to be combined, like "bu" for
9719 * bold-underlined.
9720 */
9721 attr = 0;
9722 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9723 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01009724 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009725 continue;
9726
9727 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9728 return FAIL;
9729
9730 switch (*p)
9731 {
9732 case 'b': attr |= HL_BOLD;
9733 break;
9734 case 'i': attr |= HL_ITALIC;
9735 break;
9736 case '-':
9737 case 'n': /* no highlighting */
9738 break;
9739 case 'r': attr |= HL_INVERSE;
9740 break;
9741 case 's': attr |= HL_STANDOUT;
9742 break;
9743 case 'u': attr |= HL_UNDERLINE;
9744 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009745 case 'c': attr |= HL_UNDERCURL;
9746 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009747 case ':': ++p; /* highlight group name */
9748 if (attr || *p == NUL) /* no combinations */
9749 return FAIL;
9750 end = vim_strchr(p, ',');
9751 if (end == NULL)
9752 end = p + STRLEN(p);
9753 id = syn_check_group(p, (int)(end - p));
9754 if (id == 0)
9755 return FAIL;
9756 attr = syn_id2attr(id);
9757 p = end - 1;
9758#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9759 if (hlf == (int)HLF_SNC)
9760 id_SNC = syn_get_final_id(id);
9761 else if (hlf == (int)HLF_S)
9762 id_S = syn_get_final_id(id);
9763#endif
9764 break;
9765 default: return FAIL;
9766 }
9767 }
9768 highlight_attr[hlf] = attr;
9769
9770 p = skip_to_option_part(p); /* skip comma and spaces */
9771 }
9772 }
9773
9774#ifdef USER_HIGHLIGHT
9775 /* Setup the user highlights
9776 *
9777 * Temporarily utilize 10 more hl entries. Have to be in there
9778 * simultaneously in case of table overflows in get_attr_entry()
9779 */
9780# ifdef FEAT_STL_OPT
9781 if (ga_grow(&highlight_ga, 10) == FAIL)
9782 return FAIL;
9783 hlcnt = highlight_ga.ga_len;
9784 if (id_S == 0)
9785 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009786 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009787 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9788 id_S = hlcnt + 10;
9789 }
9790# endif
9791 for (i = 0; i < 9; i++)
9792 {
9793 sprintf((char *)userhl, "User%d", i + 1);
9794 id = syn_name2id(userhl);
9795 if (id == 0)
9796 {
9797 highlight_user[i] = 0;
9798# ifdef FEAT_STL_OPT
9799 highlight_stlnc[i] = 0;
9800# endif
9801 }
9802 else
9803 {
9804# ifdef FEAT_STL_OPT
9805 struct hl_group *hlt = HL_TABLE();
9806# endif
9807
9808 highlight_user[i] = syn_id2attr(id);
9809# ifdef FEAT_STL_OPT
9810 if (id_SNC == 0)
9811 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009812 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009813 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9814 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009815# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009816 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9817# endif
9818 }
9819 else
9820 mch_memmove(&hlt[hlcnt + i],
9821 &hlt[id_SNC - 1],
9822 sizeof(struct hl_group));
9823 hlt[hlcnt + i].sg_link = 0;
9824
9825 /* Apply difference between UserX and HLF_S to HLF_SNC */
9826 hlt[hlcnt + i].sg_term ^=
9827 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9828 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9829 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9830 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9831 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9832 hlt[hlcnt + i].sg_cterm ^=
9833 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9834 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9835 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9836 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9837 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009838# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009839 hlt[hlcnt + i].sg_gui ^=
9840 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009841# endif
9842# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009843 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9844 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9845 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9846 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009847 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9848 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009849 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9850 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9851# ifdef FEAT_XFONTSET
9852 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9853 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9854# endif
9855# endif
9856 highlight_ga.ga_len = hlcnt + i + 1;
9857 set_hl_attr(hlcnt + i); /* At long last we can apply */
9858 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9859# endif
9860 }
9861 }
9862# ifdef FEAT_STL_OPT
9863 highlight_ga.ga_len = hlcnt;
9864# endif
9865
9866#endif /* USER_HIGHLIGHT */
9867
9868 return OK;
9869}
9870
Bram Moolenaar4f688582007-07-24 12:34:30 +00009871#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009872
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009873static void highlight_list(void);
9874static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009875
9876/*
9877 * Handle command line completion for :highlight command.
9878 */
9879 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009880set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009881{
9882 char_u *p;
9883
9884 /* Default: expand group names */
9885 xp->xp_context = EXPAND_HIGHLIGHT;
9886 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009887 include_link = 2;
9888 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009889
9890 /* (part of) subcommand already typed */
9891 if (*arg != NUL)
9892 {
9893 p = skiptowhite(arg);
9894 if (*p != NUL) /* past "default" or group name */
9895 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009896 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009897 if (STRNCMP("default", arg, p - arg) == 0)
9898 {
9899 arg = skipwhite(p);
9900 xp->xp_pattern = arg;
9901 p = skiptowhite(arg);
9902 }
9903 if (*p != NUL) /* past group name */
9904 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009905 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009906 if (arg[1] == 'i' && arg[0] == 'N')
9907 highlight_list();
9908 if (STRNCMP("link", arg, p - arg) == 0
9909 || STRNCMP("clear", arg, p - arg) == 0)
9910 {
9911 xp->xp_pattern = skipwhite(p);
9912 p = skiptowhite(xp->xp_pattern);
9913 if (*p != NUL) /* past first group name */
9914 {
9915 xp->xp_pattern = skipwhite(p);
9916 p = skiptowhite(xp->xp_pattern);
9917 }
9918 }
9919 if (*p != NUL) /* past group name(s) */
9920 xp->xp_context = EXPAND_NOTHING;
9921 }
9922 }
9923 }
9924}
9925
9926/*
9927 * List highlighting matches in a nice way.
9928 */
9929 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009930highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009931{
9932 int i;
9933
9934 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +01009935 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009936 for (i = 40; --i >= 0; )
9937 highlight_list_two(99, 0);
9938}
9939
9940 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009941highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009942{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009943 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009944 msg_clr_eos();
9945 out_flush();
9946 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9947}
9948
9949#endif /* FEAT_CMDL_COMPL */
9950
9951#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9952 || defined(FEAT_SIGNS) || defined(PROTO)
9953/*
9954 * Function given to ExpandGeneric() to obtain the list of group names.
9955 * Also used for synIDattr() function.
9956 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009957 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009958get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009959{
Bram Moolenaar15eedf12017-01-22 19:25:33 +01009960 if (idx < 0)
9961 return NULL;
9962 /* Items are never removed from the table, skip the ones that were cleared.
9963 */
9964 while (idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
9965 ++idx;
9966
Bram Moolenaar071d4272004-06-13 20:20:40 +00009967#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009968 if (idx == highlight_ga.ga_len && include_none != 0)
9969 return (char_u *)"none";
9970 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009971 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009972 if (idx == highlight_ga.ga_len + include_none + include_default
9973 && include_link != 0)
9974 return (char_u *)"link";
9975 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9976 && include_link != 0)
9977 return (char_u *)"clear";
9978#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01009979 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009980 return NULL;
9981 return HL_TABLE()[idx].sg_name;
9982}
9983#endif
9984
Bram Moolenaar4f688582007-07-24 12:34:30 +00009985#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009986/*
9987 * Free all the highlight group fonts.
9988 * Used when quitting for systems which need it.
9989 */
9990 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009991free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009992{
9993 int idx;
9994
9995 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9996 {
9997 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9998 HL_TABLE()[idx].sg_font = NOFONT;
9999# ifdef FEAT_XFONTSET
10000 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10001 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10002# endif
10003 }
10004
10005 gui_mch_free_font(gui.norm_font);
10006# ifdef FEAT_XFONTSET
10007 gui_mch_free_fontset(gui.fontset);
10008# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010009# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010010 gui_mch_free_font(gui.bold_font);
10011 gui_mch_free_font(gui.ital_font);
10012 gui_mch_free_font(gui.boldital_font);
10013# endif
10014}
10015#endif
10016
10017/**************************************
10018 * End of Highlighting stuff *
10019 **************************************/