blob: aed7ee6924980cbb487d7141fd611b2611245a40 [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 */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000036/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020037#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020040#endif
41#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000042 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 GuiFont sg_font; /* GUI font handle */
44#ifdef FEAT_XFONTSET
45 GuiFontset sg_fontset; /* GUI fontset handle */
46#endif
47 char_u *sg_font_name; /* GUI font or fontset name */
48 int sg_gui_attr; /* Screen attr for GUI mode */
49#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020050#if defined(FEAT_GUI) || defined(FEAT_EVAL)
51/* Store the sp color name for the GUI or synIDattr() */
52 int sg_gui; /* "gui=" highlighting attributes */
53 char_u *sg_gui_fg_name;/* GUI foreground color name */
54 char_u *sg_gui_bg_name;/* GUI background color name */
55 char_u *sg_gui_sp_name;/* GUI special color name */
56#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000057 int sg_link; /* link to this highlight group ID */
58 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000059#ifdef FEAT_EVAL
60 scid_T sg_scriptID; /* script in which the group was last set */
61#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000062};
63
64#define SG_TERM 1 /* term has been set */
65#define SG_CTERM 2 /* cterm has been set */
66#define SG_GUI 4 /* gui has been set */
67#define SG_LINK 8 /* link has been set */
68
69static garray_T highlight_ga; /* highlight groups for 'highlight' option */
70
71#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
72
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020073#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
74
Bram Moolenaar071d4272004-06-13 20:20:40 +000075#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000076/* Flags to indicate an additional string for highlight name completion. */
77static int include_none = 0; /* when 1 include "None" */
78static int include_default = 0; /* when 1 include "default" */
79static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000080#endif
81
82/*
83 * The "term", "cterm" and "gui" arguments can be any combination of the
84 * following names, separated by commas (but no spaces!).
85 */
86static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000087 {"bold", "standout", "underline", "undercurl",
88 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000089static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000090 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000091
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010092static int get_attr_entry(garray_T *table, attrentry_T *aep);
93static void syn_unadd_group(void);
94static void set_hl_attr(int idx);
95static void highlight_list_one(int id);
96static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
97static int syn_add_group(char_u *name);
98static int syn_list_header(int did_header, int outlen, int id);
99static int hl_has_settings(int idx, int check_link);
100static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200102#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100103static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100104static guicolor_T color_name2handle(char_u *name);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200105#endif
106#ifdef FEAT_GUI
107static 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 +0100108static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100110static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100112static 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 +0000113#endif
114
115/*
116 * An attribute number is the index in attr_table plus ATTR_OFF.
117 */
118#define ATTR_OFF (HL_ALL + 1)
119
120#if defined(FEAT_SYN_HL) || defined(PROTO)
121
122#define SYN_NAMELEN 50 /* maximum length of a syntax name */
123
124/* different types of offsets that are possible */
125#define SPO_MS_OFF 0 /* match start offset */
126#define SPO_ME_OFF 1 /* match end offset */
127#define SPO_HS_OFF 2 /* highl. start offset */
128#define SPO_HE_OFF 3 /* highl. end offset */
129#define SPO_RS_OFF 4 /* region start offset */
130#define SPO_RE_OFF 5 /* region end offset */
131#define SPO_LC_OFF 6 /* leading context offset */
132#define SPO_COUNT 7
133
134static char *(spo_name_tab[SPO_COUNT]) =
135 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
136
137/*
138 * The patterns that are being searched for are stored in a syn_pattern.
139 * A match item consists of one pattern.
140 * A start/end item consists of n start patterns and m end patterns.
141 * A start/skip/end item consists of n start patterns, one skip pattern and m
142 * end patterns.
143 * For the latter two, the patterns are always consecutive: start-skip-end.
144 *
145 * A character offset can be given for the matched text (_m_start and _m_end)
146 * and for the actually highlighted text (_h_start and _h_end).
147 */
148typedef struct syn_pattern
149{
150 char sp_type; /* see SPTYPE_ defines below */
151 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200152 int sp_flags; /* see HL_ defines below */
153#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200154 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200155#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156 struct sp_syn sp_syn; /* struct passed to in_id_list() */
157 short sp_syn_match_id; /* highlight group ID of pattern */
158 char_u *sp_pattern; /* regexp to match, pattern */
159 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200160#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200161 syn_time_T sp_time;
162#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163 int sp_ic; /* ignore-case flag for sp_prog */
164 short sp_off_flags; /* see below */
165 int sp_offsets[SPO_COUNT]; /* offsets */
166 short *sp_cont_list; /* cont. group IDs, if non-zero */
167 short *sp_next_list; /* next group IDs, if non-zero */
168 int sp_sync_idx; /* sync item index (syncing only) */
169 int sp_line_id; /* ID of last line where tried */
170 int sp_startcol; /* next match in sp_line_id line */
171} synpat_T;
172
173/* The sp_off_flags are computed like this:
174 * offset from the start of the matched text: (1 << SPO_XX_OFF)
175 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
176 * When both are present, only one is used.
177 */
178
179#define SPTYPE_MATCH 1 /* match keyword with this group ID */
180#define SPTYPE_START 2 /* match a regexp, start of item */
181#define SPTYPE_END 3 /* match a regexp, end of item */
182#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
183
Bram Moolenaar071d4272004-06-13 20:20:40 +0000184
185#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
186
187#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
188
189/*
190 * Flags for b_syn_sync_flags:
191 */
192#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
193#define SF_MATCH 0x02 /* sync by matching a pattern */
194
195#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
196
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197#define MAXKEYWLEN 80 /* maximum length of a keyword */
198
199/*
200 * The attributes of the syntax item that has been recognized.
201 */
202static int current_attr = 0; /* attr of current syntax word */
203#ifdef FEAT_EVAL
204static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000205static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200207#ifdef FEAT_CONCEAL
208static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200209static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200210static int current_sub_char = 0;
211#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212
Bram Moolenaar217ad922005-03-20 22:37:15 +0000213typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000214{
215 char_u *scl_name; /* syntax cluster name */
216 char_u *scl_name_u; /* uppercase of scl_name */
217 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000218} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * Methods of combining two clusters
222 */
223#define CLUSTER_REPLACE 1 /* replace first list with second */
224#define CLUSTER_ADD 2 /* add second list to first */
225#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
226
Bram Moolenaar217ad922005-03-20 22:37:15 +0000227#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228
229/*
230 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200231 * 0 - 19999 normal syntax groups
232 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
233 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
234 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
235 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200237#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200238#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
239#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
240#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
241
Bram Moolenaar42431a72011-04-01 14:44:59 +0200242#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
243#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244
245/*
246 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
247 * expand_filename(). Most of the other syntax commands don't need it, so
248 * instead of passing it to them, we stow it here.
249 */
250static char_u **syn_cmdlinep;
251
252/*
253 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200254 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 * rules in each ":syn include"'d file.
256 */
257static int current_syn_inc_tag = 0;
258static int running_syn_inc_tag = 0;
259
260/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000261 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
262 * This avoids adding a pointer to the hashtable item.
263 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
264 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
265 * HI2KE() converts a hashitem pointer to a var pointer.
266 */
267static keyentry_T dumkey;
268#define KE2HIKEY(kp) ((kp)->keyword)
269#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
270#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
271
272/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000273 * To reduce the time spent in keepend(), remember at which level in the state
274 * stack the first item with "keepend" is present. When "-1", there is no
275 * "keepend" on the stack.
276 */
277static int keepend_level = -1;
278
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200279static char msg_no_items[] = N_("No Syntax items defined for this buffer");
280
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281/*
282 * For the current state we need to remember more than just the idx.
283 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
284 * (The end positions have the column number of the next char)
285 */
286typedef struct state_item
287{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000288 int si_idx; /* index of syntax pattern or
289 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000291 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000292 int si_m_lnum; /* lnum of the match */
293 int si_m_startcol; /* starting column of the match */
294 lpos_T si_m_endpos; /* just after end posn of the match */
295 lpos_T si_h_startpos; /* start position of the highlighting */
296 lpos_T si_h_endpos; /* end position of the highlighting */
297 lpos_T si_eoe_pos; /* end position of end pattern */
298 int si_end_idx; /* group ID for end pattern or zero */
299 int si_ends; /* if match ends before si_m_endpos */
300 int si_attr; /* attributes in this state */
301 long si_flags; /* HL_HAS_EOL flag in this state, and
302 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200303#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200304 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200305 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200306#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 short *si_cont_list; /* list of contained groups */
308 short *si_next_list; /* nextgroup IDs after this item ends */
309 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
310 * pattern */
311} stateitem_T;
312
313#define KEYWORD_IDX -1 /* value of si_idx for keywords */
314#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
315 but contained groups */
316
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200317#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100318static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200319#endif
320
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000322 * Struct to reduce the number of arguments to get_syn_options(), it's used
323 * very often.
324 */
325typedef struct
326{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000327 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000328 int keyword; /* TRUE for ":syn keyword" */
329 int *sync_idx; /* syntax item for "grouphere" argument, NULL
330 if not allowed */
331 char has_cont_list; /* TRUE if "cont_list" can be used */
332 short *cont_list; /* group IDs for "contains" argument */
333 short *cont_in_list; /* group IDs for "containedin" argument */
334 short *next_list; /* group IDs for "nextgroup" argument */
335} syn_opt_arg_T;
336
337/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000338 * The next possible match in the current line for any pattern is remembered,
339 * to avoid having to try for a match in each column.
340 * If next_match_idx == -1, not tried (in this line) yet.
341 * If next_match_col == MAXCOL, no match found in this line.
342 * (All end positions have the column of the char after the end)
343 */
344static int next_match_col; /* column for start of next match */
345static lpos_T next_match_m_endpos; /* position for end of next match */
346static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
347static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
348static int next_match_idx; /* index of matched item */
349static long next_match_flags; /* flags for next match */
350static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
351static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
352static int next_match_end_idx; /* ID of group for end pattn or zero */
353static reg_extmatch_T *next_match_extmatch = NULL;
354
355/*
356 * A state stack is an array of integers or stateitem_T, stored in a
357 * garray_T. A state stack is invalid if it's itemsize entry is zero.
358 */
359#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
360#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
361
362/*
363 * The current state (within the line) of the recognition engine.
364 * When current_state.ga_itemsize is 0 the current state is invalid.
365 */
366static win_T *syn_win; /* current window for highlighting */
367static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200368static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369static linenr_T current_lnum = 0; /* lnum of current state */
370static colnr_T current_col = 0; /* column of current state */
371static int current_state_stored = 0; /* TRUE if stored current state
372 * after setting current_finished */
373static int current_finished = 0; /* current line has been finished */
374static garray_T current_state /* current stack of state_items */
375 = {0, 0, 0, 0, NULL};
376static short *current_next_list = NULL; /* when non-zero, nextgroup list */
377static int current_next_flags = 0; /* flags for current_next_list */
378static int current_line_id = 0; /* unique number for current line */
379
380#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
381
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100382static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100383static void save_chartab(char_u *chartab);
384static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100385static int syn_match_linecont(linenr_T lnum);
386static void syn_start_line(void);
387static void syn_update_ends(int startofline);
388static void syn_stack_alloc(void);
389static int syn_stack_cleanup(void);
390static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
391static synstate_T *syn_stack_find_entry(linenr_T lnum);
392static synstate_T *store_current_state(void);
393static void load_current_state(synstate_T *from);
394static void invalidate_current_state(void);
395static int syn_stack_equal(synstate_T *sp);
396static void validate_current_state(void);
397static int syn_finish_line(int syncing);
398static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
399static int did_match_already(int idx, garray_T *gap);
400static stateitem_T *push_next_match(stateitem_T *cur_si);
401static void check_state_ends(void);
402static void update_si_attr(int idx);
403static void check_keepend(void);
404static void update_si_end(stateitem_T *sip, int startcol, int force);
405static short *copy_id_list(short *list);
406static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
407static int push_current_state(int idx);
408static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200409#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100410static void syn_clear_time(syn_time_T *tt);
411static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200412#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100413static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200414#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100415static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200416#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100417static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418static int syn_time_on = FALSE;
419# define IF_SYN_TIME(p) (p)
420#else
421# define IF_SYN_TIME(p) NULL
422typedef int syn_time_T;
423#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100425static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
426static 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);
427static void clear_syn_state(synstate_T *p);
428static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100430static void limit_pos(lpos_T *pos, lpos_T *limit);
431static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
432static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
433static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
434static char_u *syn_getcurline(void);
435static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
436static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
437static void syn_cmd_case(exarg_T *eap, int syncing);
438static void syn_cmd_spell(exarg_T *eap, int syncing);
439static void syntax_sync_clear(void);
440static void syn_remove_pattern(synblock_T *block, int idx);
441static void syn_clear_pattern(synblock_T *block, int i);
442static void syn_clear_cluster(synblock_T *block, int i);
443static void syn_cmd_clear(exarg_T *eap, int syncing);
444static void syn_cmd_conceal(exarg_T *eap, int syncing);
445static void syn_clear_one(int id, int syncing);
446static void syn_cmd_on(exarg_T *eap, int syncing);
447static void syn_cmd_enable(exarg_T *eap, int syncing);
448static void syn_cmd_reset(exarg_T *eap, int syncing);
449static void syn_cmd_manual(exarg_T *eap, int syncing);
450static void syn_cmd_off(exarg_T *eap, int syncing);
451static void syn_cmd_onoff(exarg_T *eap, char *name);
452static void syn_cmd_list(exarg_T *eap, int syncing);
453static void syn_lines_msg(void);
454static void syn_match_msg(void);
455static void syn_stack_free_block(synblock_T *block);
456static void syn_list_one(int id, int syncing, int link_only);
457static void syn_list_cluster(int id);
458static void put_id_list(char_u *name, short *list, int attr);
459static void put_pattern(char *s, int c, synpat_T *spp, int attr);
460static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
461static void syn_clear_keyword(int id, hashtab_T *ht);
462static void clear_keywtab(hashtab_T *ht);
463static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
464static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100465static 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 +0100466static void syn_cmd_include(exarg_T *eap, int syncing);
467static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
468static void syn_cmd_keyword(exarg_T *eap, int syncing);
469static void syn_cmd_match(exarg_T *eap, int syncing);
470static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100472static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000473#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100474static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100476static void syn_cmd_cluster(exarg_T *eap, int syncing);
477static int syn_scl_name2id(char_u *name);
478static int syn_scl_namen2id(char_u *linep, int len);
479static int syn_check_cluster(char_u *pp, int len);
480static int syn_add_cluster(char_u *name);
481static void init_syn_patterns(void);
482static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
483static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100484static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100485static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
486static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487
488/*
489 * Start the syntax recognition for a line. This function is normally called
490 * from the screen updating, once for each displayed line.
491 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
492 * it. Careful: curbuf and curwin are likely to point to another buffer and
493 * window.
494 */
495 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100496syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497{
498 synstate_T *p;
499 synstate_T *last_valid = NULL;
500 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000501 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 linenr_T parsed_lnum;
503 linenr_T first_stored;
504 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000505 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200507#ifdef FEAT_CONCEAL
508 current_sub_char = NUL;
509#endif
510
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511 /*
512 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000513 * Also do this when a change was made, the current state may be invalid
514 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200516 if (syn_block != wp->w_s
517 || syn_buf != wp->w_buffer
518 || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 {
520 invalidate_current_state();
521 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200522 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000524 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525 syn_win = wp;
526
527 /*
528 * Allocate syntax stack when needed.
529 */
530 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200531 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000532 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200533 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534
535 /*
536 * If the state of the end of the previous line is useful, store it.
537 */
538 if (VALID_STATE(&current_state)
539 && current_lnum < lnum
540 && current_lnum < syn_buf->b_ml.ml_line_count)
541 {
542 (void)syn_finish_line(FALSE);
543 if (!current_state_stored)
544 {
545 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000546 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547 }
548
549 /*
550 * If the current_lnum is now the same as "lnum", keep the current
551 * state (this happens very often!). Otherwise invalidate
552 * current_state and figure it out below.
553 */
554 if (current_lnum != lnum)
555 invalidate_current_state();
556 }
557 else
558 invalidate_current_state();
559
560 /*
561 * Try to synchronize from a saved state in b_sst_array[].
562 * Only do this if lnum is not before and not to far beyond a saved state.
563 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200564 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565 {
566 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200567 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 {
569 if (p->sst_lnum > lnum)
570 break;
571 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
572 {
573 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 last_min_valid = p;
576 }
577 }
578 if (last_min_valid != NULL)
579 load_current_state(last_min_valid);
580 }
581
582 /*
583 * If "lnum" is before or far beyond a line with a saved state, need to
584 * re-synchronize.
585 */
586 if (INVALID_STATE(&current_state))
587 {
588 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200589 if (current_lnum == 1)
590 /* First line is always valid, no matter "minlines". */
591 first_stored = 1;
592 else
593 /* Need to parse "minlines" lines before state can be considered
594 * valid to store. */
595 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 }
597 else
598 first_stored = current_lnum;
599
600 /*
601 * Advance from the sync point or saved state until the current line.
602 * Save some entries for syncing with later on.
603 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200604 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000605 dist = 999999;
606 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200607 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000608 while (current_lnum < lnum)
609 {
610 syn_start_line();
611 (void)syn_finish_line(FALSE);
612 ++current_lnum;
613
614 /* If we parsed at least "minlines" lines or started at a valid
615 * state, the current state is considered valid. */
616 if (current_lnum >= first_stored)
617 {
618 /* Check if the saved state entry is for the current line and is
619 * equal to the current state. If so, then validate all saved
620 * states that depended on a change before the parsed line. */
621 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000622 prev = syn_stack_find_entry(current_lnum - 1);
623 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200624 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000626 sp = prev;
627 while (sp != NULL && sp->sst_lnum < current_lnum)
628 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629 if (sp != NULL
630 && sp->sst_lnum == current_lnum
631 && syn_stack_equal(sp))
632 {
633 parsed_lnum = current_lnum;
634 prev = sp;
635 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
636 {
637 if (sp->sst_lnum <= lnum)
638 /* valid state before desired line, use this one */
639 prev = sp;
640 else if (sp->sst_change_lnum == 0)
641 /* past saved states depending on change, break here. */
642 break;
643 sp->sst_change_lnum = 0;
644 sp = sp->sst_next;
645 }
646 load_current_state(prev);
647 }
648 /* Store the state at this line when it's the first one, the line
649 * where we start parsing, or some distance from the previously
650 * saved state. But only when parsed at least 'minlines'. */
651 else if (prev == NULL
652 || current_lnum == lnum
653 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000654 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 }
656
657 /* This can take a long time: break when CTRL-C pressed. The current
658 * state will be wrong then. */
659 line_breakcheck();
660 if (got_int)
661 {
662 current_lnum = lnum;
663 break;
664 }
665 }
666
667 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668}
669
670/*
671 * We cannot simply discard growarrays full of state_items or buf_states; we
672 * have to manually release their extmatch pointers first.
673 */
674 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100675clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676{
677 int i;
678 garray_T *gap;
679
680 if (p->sst_stacksize > SST_FIX_STATES)
681 {
682 gap = &(p->sst_union.sst_ga);
683 for (i = 0; i < gap->ga_len; i++)
684 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
685 ga_clear(gap);
686 }
687 else
688 {
689 for (i = 0; i < p->sst_stacksize; i++)
690 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
691 }
692}
693
694/*
695 * Cleanup the current_state stack.
696 */
697 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100698clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699{
700 int i;
701 stateitem_T *sip;
702
703 sip = (stateitem_T *)(current_state.ga_data);
704 for (i = 0; i < current_state.ga_len; i++)
705 unref_extmatch(sip[i].si_extmatch);
706 ga_clear(&current_state);
707}
708
709/*
710 * Try to find a synchronisation point for line "lnum".
711 *
712 * This sets current_lnum and the current state. One of three methods is
713 * used:
714 * 1. Search backwards for the end of a C-comment.
715 * 2. Search backwards for given sync patterns.
716 * 3. Simply start on a given number of lines above "lnum".
717 */
718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100719syn_sync(
720 win_T *wp,
721 linenr_T start_lnum,
722 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723{
724 buf_T *curbuf_save;
725 win_T *curwin_save;
726 pos_T cursor_save;
727 int idx;
728 linenr_T lnum;
729 linenr_T end_lnum;
730 linenr_T break_lnum;
731 int had_sync_point;
732 stateitem_T *cur_si;
733 synpat_T *spp;
734 char_u *line;
735 int found_flags = 0;
736 int found_match_idx = 0;
737 linenr_T found_current_lnum = 0;
738 int found_current_col= 0;
739 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000740 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741
742 /*
743 * Clear any current state that might be hanging around.
744 */
745 invalidate_current_state();
746
747 /*
748 * Start at least "minlines" back. Default starting point for parsing is
749 * there.
750 * Start further back, to avoid that scrolling backwards will result in
751 * resyncing for every line. Now it resyncs only one out of N lines,
752 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
753 * Watch out for overflow when minlines is MAXLNUM.
754 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200755 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756 start_lnum = 1;
757 else
758 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200759 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 else if (syn_block->b_syn_sync_minlines < 10)
762 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200764 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
765 if (syn_block->b_syn_sync_maxlines != 0
766 && lnum > syn_block->b_syn_sync_maxlines)
767 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000768 if (lnum >= start_lnum)
769 start_lnum = 1;
770 else
771 start_lnum -= lnum;
772 }
773 current_lnum = start_lnum;
774
775 /*
776 * 1. Search backwards for the end of a C-style comment.
777 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200778 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 {
780 /* Need to make syn_buf the current buffer for a moment, to be able to
781 * use find_start_comment(). */
782 curwin_save = curwin;
783 curwin = wp;
784 curbuf_save = curbuf;
785 curbuf = syn_buf;
786
787 /*
788 * Skip lines that end in a backslash.
789 */
790 for ( ; start_lnum > 1; --start_lnum)
791 {
792 line = ml_get(start_lnum - 1);
793 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
794 break;
795 }
796 current_lnum = start_lnum;
797
798 /* set cursor to start of search */
799 cursor_save = wp->w_cursor;
800 wp->w_cursor.lnum = start_lnum;
801 wp->w_cursor.col = 0;
802
803 /*
804 * If the line is inside a comment, need to find the syntax item that
805 * defines the comment.
806 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
807 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200808 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200810 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
811 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
812 == syn_block->b_syn_sync_id
813 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000814 {
815 validate_current_state();
816 if (push_current_state(idx) == OK)
817 update_si_attr(current_state.ga_len - 1);
818 break;
819 }
820 }
821
822 /* restore cursor and buffer */
823 wp->w_cursor = cursor_save;
824 curwin = curwin_save;
825 curbuf = curbuf_save;
826 }
827
828 /*
829 * 2. Search backwards for given sync patterns.
830 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200831 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200833 if (syn_block->b_syn_sync_maxlines != 0
834 && start_lnum > syn_block->b_syn_sync_maxlines)
835 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000836 else
837 break_lnum = 0;
838
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000839 found_m_endpos.lnum = 0;
840 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000841 end_lnum = start_lnum;
842 lnum = start_lnum;
843 while (--lnum > break_lnum)
844 {
845 /* This can take a long time: break when CTRL-C pressed. */
846 line_breakcheck();
847 if (got_int)
848 {
849 invalidate_current_state();
850 current_lnum = start_lnum;
851 break;
852 }
853
854 /* Check if we have run into a valid saved state stack now. */
855 if (last_valid != NULL && lnum == last_valid->sst_lnum)
856 {
857 load_current_state(last_valid);
858 break;
859 }
860
861 /*
862 * Check if the previous line has the line-continuation pattern.
863 */
864 if (lnum > 1 && syn_match_linecont(lnum - 1))
865 continue;
866
867 /*
868 * Start with nothing on the state stack
869 */
870 validate_current_state();
871
872 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
873 {
874 syn_start_line();
875 for (;;)
876 {
877 had_sync_point = syn_finish_line(TRUE);
878 /*
879 * When a sync point has been found, remember where, and
880 * continue to look for another one, further on in the line.
881 */
882 if (had_sync_point && current_state.ga_len)
883 {
884 cur_si = &CUR_STATE(current_state.ga_len - 1);
885 if (cur_si->si_m_endpos.lnum > start_lnum)
886 {
887 /* ignore match that goes to after where started */
888 current_lnum = end_lnum;
889 break;
890 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000891 if (cur_si->si_idx < 0)
892 {
893 /* Cannot happen? */
894 found_flags = 0;
895 found_match_idx = KEYWORD_IDX;
896 }
897 else
898 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200899 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000900 found_flags = spp->sp_flags;
901 found_match_idx = spp->sp_sync_idx;
902 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 found_current_lnum = current_lnum;
904 found_current_col = current_col;
905 found_m_endpos = cur_si->si_m_endpos;
906 /*
907 * Continue after the match (be aware of a zero-length
908 * match).
909 */
910 if (found_m_endpos.lnum > current_lnum)
911 {
912 current_lnum = found_m_endpos.lnum;
913 current_col = found_m_endpos.col;
914 if (current_lnum >= end_lnum)
915 break;
916 }
917 else if (found_m_endpos.col > current_col)
918 current_col = found_m_endpos.col;
919 else
920 ++current_col;
921
922 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000923 * an item that ends here, need to do that now. Be
924 * careful not to go past the NUL. */
925 prev_current_col = current_col;
926 if (syn_getcurline()[current_col] != NUL)
927 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000929 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 }
931 else
932 break;
933 }
934 }
935
936 /*
937 * If a sync point was encountered, break here.
938 */
939 if (found_flags)
940 {
941 /*
942 * Put the item that was specified by the sync point on the
943 * state stack. If there was no item specified, make the
944 * state stack empty.
945 */
946 clear_current_state();
947 if (found_match_idx >= 0
948 && push_current_state(found_match_idx) == OK)
949 update_si_attr(current_state.ga_len - 1);
950
951 /*
952 * When using "grouphere", continue from the sync point
953 * match, until the end of the line. Parsing starts at
954 * the next line.
955 * For "groupthere" the parsing starts at start_lnum.
956 */
957 if (found_flags & HL_SYNC_HERE)
958 {
959 if (current_state.ga_len)
960 {
961 cur_si = &CUR_STATE(current_state.ga_len - 1);
962 cur_si->si_h_startpos.lnum = found_current_lnum;
963 cur_si->si_h_startpos.col = found_current_col;
964 update_si_end(cur_si, (int)current_col, TRUE);
965 check_keepend();
966 }
967 current_col = found_m_endpos.col;
968 current_lnum = found_m_endpos.lnum;
969 (void)syn_finish_line(FALSE);
970 ++current_lnum;
971 }
972 else
973 current_lnum = start_lnum;
974
975 break;
976 }
977
978 end_lnum = lnum;
979 invalidate_current_state();
980 }
981
982 /* Ran into start of the file or exceeded maximum number of lines */
983 if (lnum <= break_lnum)
984 {
985 invalidate_current_state();
986 current_lnum = break_lnum + 1;
987 }
988 }
989
990 validate_current_state();
991}
992
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100993 static void
994save_chartab(char_u *chartab)
995{
996 if (syn_block->b_syn_isk != empty_option)
997 {
998 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
999 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1000 (size_t)32);
1001 }
1002}
1003
1004 static void
1005restore_chartab(char_u *chartab)
1006{
1007 if (syn_win->w_s->b_syn_isk != empty_option)
1008 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1009}
1010
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011/*
1012 * Return TRUE if the line-continuation pattern matches in line "lnum".
1013 */
1014 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001015syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016{
1017 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001018 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001019 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020
Bram Moolenaar860cae12010-06-05 23:22:07 +02001021 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001023 /* use syntax iskeyword option */
1024 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001025 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1026 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001027 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001028 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001029 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001030 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001031 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032 }
1033 return FALSE;
1034}
1035
1036/*
1037 * Prepare the current state for the start of a line.
1038 */
1039 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001040syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001041{
1042 current_finished = FALSE;
1043 current_col = 0;
1044
1045 /*
1046 * Need to update the end of a start/skip/end that continues from the
1047 * previous line and regions that have "keepend".
1048 */
1049 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001050 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001051 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001052 check_state_ends();
1053 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054
1055 next_match_idx = -1;
1056 ++current_line_id;
1057}
1058
1059/*
1060 * Check for items in the stack that need their end updated.
1061 * When "startofline" is TRUE the last item is always updated.
1062 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1063 */
1064 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001065syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066{
1067 stateitem_T *cur_si;
1068 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001069 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070
1071 if (startofline)
1072 {
1073 /* Check for a match carried over from a previous line with a
1074 * contained region. The match ends as soon as the region ends. */
1075 for (i = 0; i < current_state.ga_len; ++i)
1076 {
1077 cur_si = &CUR_STATE(i);
1078 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001079 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 == SPTYPE_MATCH
1081 && cur_si->si_m_endpos.lnum < current_lnum)
1082 {
1083 cur_si->si_flags |= HL_MATCHCONT;
1084 cur_si->si_m_endpos.lnum = 0;
1085 cur_si->si_m_endpos.col = 0;
1086 cur_si->si_h_endpos = cur_si->si_m_endpos;
1087 cur_si->si_ends = TRUE;
1088 }
1089 }
1090 }
1091
1092 /*
1093 * Need to update the end of a start/skip/end that continues from the
1094 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001095 * influence contained items. If we've just removed "extend"
1096 * (startofline == 0) then we should update ends of normal regions
1097 * contained inside "keepend" because "extend" could have extended
1098 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 * Then check for items ending in column 0.
1100 */
1101 i = current_state.ga_len - 1;
1102 if (keepend_level >= 0)
1103 for ( ; i > keepend_level; --i)
1104 if (CUR_STATE(i).si_flags & HL_EXTEND)
1105 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001106
1107 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 for ( ; i < current_state.ga_len; ++i)
1109 {
1110 cur_si = &CUR_STATE(i);
1111 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001112 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 || (i == current_state.ga_len - 1 && startofline))
1114 {
1115 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1116 cur_si->si_h_startpos.lnum = current_lnum;
1117
1118 if (!(cur_si->si_flags & HL_MATCHCONT))
1119 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001120
1121 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1122 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123 }
1124 }
1125 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001126}
1127
1128/****************************************
1129 * Handling of the state stack cache.
1130 */
1131
1132/*
1133 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1134 *
1135 * To speed up syntax highlighting, the state stack for the start of some
1136 * lines is cached. These entries can be used to start parsing at that point.
1137 *
1138 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1139 * valid entries. b_sst_first points to the first one, then follow sst_next.
1140 * The entries are sorted on line number. The first entry is often for line 2
1141 * (line 1 always starts with an empty stack).
1142 * There is also a list for free entries. This construction is used to avoid
1143 * having to allocate and free memory blocks too often.
1144 *
1145 * When making changes to the buffer, this is logged in b_mod_*. When calling
1146 * update_screen() to update the display, it will call
1147 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1148 * entries. The entries which are inside the changed area are removed,
1149 * because they must be recomputed. Entries below the changed have their line
1150 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1151 * set to indicate that a check must be made if the changed lines would change
1152 * the cached entry.
1153 *
1154 * When later displaying lines, an entry is stored for each line. Displayed
1155 * lines are likely to be displayed again, in which case the state at the
1156 * start of the line is needed.
1157 * For not displayed lines, an entry is stored for every so many lines. These
1158 * entries will be used e.g., when scrolling backwards. The distance between
1159 * entries depends on the number of lines in the buffer. For small buffers
1160 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1161 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1162 */
1163
Bram Moolenaar860cae12010-06-05 23:22:07 +02001164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001166{
1167 synstate_T *p;
1168
1169 if (block->b_sst_array != NULL)
1170 {
1171 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1172 clear_syn_state(p);
1173 vim_free(block->b_sst_array);
1174 block->b_sst_array = NULL;
1175 block->b_sst_len = 0;
1176 }
1177}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178/*
1179 * Free b_sst_array[] for buffer "buf".
1180 * Used when syntax items changed to force resyncing everywhere.
1181 */
1182 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001183syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 win_T *wp;
1186
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 syn_stack_free_block(block);
1188
1189
Bram Moolenaar071d4272004-06-13 20:20:40 +00001190#ifdef FEAT_FOLDING
1191 /* When using "syntax" fold method, must update all folds. */
1192 FOR_ALL_WINDOWS(wp)
1193 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001194 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 foldUpdateAll(wp);
1196 }
1197#endif
1198}
1199
1200/*
1201 * Allocate the syntax state stack for syn_buf when needed.
1202 * If the number of entries in b_sst_array[] is much too big or a bit too
1203 * small, reallocate it.
1204 * Also used to allocate b_sst_array[] for the first time.
1205 */
1206 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001207syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208{
1209 long len;
1210 synstate_T *to, *from;
1211 synstate_T *sstp;
1212
1213 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1214 if (len < SST_MIN_ENTRIES)
1215 len = SST_MIN_ENTRIES;
1216 else if (len > SST_MAX_ENTRIES)
1217 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001218 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219 {
1220 /* Allocate 50% too much, to avoid reallocating too often. */
1221 len = syn_buf->b_ml.ml_line_count;
1222 len = (len + len / 2) / SST_DIST + Rows * 2;
1223 if (len < SST_MIN_ENTRIES)
1224 len = SST_MIN_ENTRIES;
1225 else if (len > SST_MAX_ENTRIES)
1226 len = SST_MAX_ENTRIES;
1227
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229 {
1230 /* When shrinking the array, cleanup the existing stack.
1231 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001232 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 && syn_stack_cleanup())
1234 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001235 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1236 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 }
1238
1239 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1240 if (sstp == NULL) /* out of memory! */
1241 return;
1242
1243 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001244 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 {
1246 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 from = from->sst_next)
1249 {
1250 ++to;
1251 *to = *from;
1252 to->sst_next = to + 1;
1253 }
1254 }
1255 if (to != sstp - 1)
1256 {
1257 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 syn_block->b_sst_first = sstp;
1259 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260 }
1261 else
1262 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001263 syn_block->b_sst_first = NULL;
1264 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 }
1266
1267 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 while (++to < sstp + len)
1270 to->sst_next = to + 1;
1271 (sstp + len - 1)->sst_next = NULL;
1272
Bram Moolenaar860cae12010-06-05 23:22:07 +02001273 vim_free(syn_block->b_sst_array);
1274 syn_block->b_sst_array = sstp;
1275 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001276 }
1277}
1278
1279/*
1280 * Check for changes in a buffer to affect stored syntax states. Uses the
1281 * b_mod_* fields.
1282 * Called from update_screen(), before screen is being updated, once for each
1283 * displayed buffer.
1284 */
1285 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001286syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001288 win_T *wp;
1289
1290 syn_stack_apply_changes_block(&buf->b_s, buf);
1291
1292 FOR_ALL_WINDOWS(wp)
1293 {
1294 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1295 syn_stack_apply_changes_block(wp->w_s, buf);
1296 }
1297}
1298
1299 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001300syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001301{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302 synstate_T *p, *prev, *np;
1303 linenr_T n;
1304
Bram Moolenaar860cae12010-06-05 23:22:07 +02001305 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 return;
1307
1308 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312 {
1313 n = p->sst_lnum + buf->b_mod_xlines;
1314 if (n <= buf->b_mod_bot)
1315 {
1316 /* this state is inside the changed area, remove it */
1317 np = p->sst_next;
1318 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001319 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 else
1321 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 p = np;
1324 continue;
1325 }
1326 /* This state is below the changed area. Remember the line
1327 * that needs to be parsed before this entry can be made valid
1328 * again. */
1329 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1330 {
1331 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1332 p->sst_change_lnum += buf->b_mod_xlines;
1333 else
1334 p->sst_change_lnum = buf->b_mod_top;
1335 }
1336 if (p->sst_change_lnum == 0
1337 || p->sst_change_lnum < buf->b_mod_bot)
1338 p->sst_change_lnum = buf->b_mod_bot;
1339
1340 p->sst_lnum = n;
1341 }
1342 prev = p;
1343 p = p->sst_next;
1344 }
1345}
1346
1347/*
1348 * Reduce the number of entries in the state stack for syn_buf.
1349 * Returns TRUE if at least one entry was freed.
1350 */
1351 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001352syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353{
1354 synstate_T *p, *prev;
1355 disptick_T tick;
1356 int above;
1357 int dist;
1358 int retval = FALSE;
1359
Bram Moolenaar860cae12010-06-05 23:22:07 +02001360 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 return retval;
1362
1363 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001364 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001365 dist = 999999;
1366 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001367 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001368
1369 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001370 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371 * be removed. Set "above" when the "tick" for the oldest entry is above
1372 * "b_sst_lasttick" (the display tick wraps around).
1373 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001374 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1378 {
1379 if (prev->sst_lnum + dist > p->sst_lnum)
1380 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001381 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 {
1383 if (!above || p->sst_tick < tick)
1384 tick = p->sst_tick;
1385 above = TRUE;
1386 }
1387 else if (!above && p->sst_tick < tick)
1388 tick = p->sst_tick;
1389 }
1390 }
1391
1392 /*
1393 * Go through the list to make the entries for the oldest tick at an
1394 * interval of several lines.
1395 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001396 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1398 {
1399 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1400 {
1401 /* Move this entry from used list to free list */
1402 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001403 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001404 p = prev;
1405 retval = TRUE;
1406 }
1407 }
1408 return retval;
1409}
1410
1411/*
1412 * Free the allocated memory for a syn_state item.
1413 * Move the entry into the free list.
1414 */
1415 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001416syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001417{
1418 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001419 p->sst_next = block->b_sst_firstfree;
1420 block->b_sst_firstfree = p;
1421 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422}
1423
1424/*
1425 * Find an entry in the list of state stacks at or before "lnum".
1426 * Returns NULL when there is no entry or the first entry is after "lnum".
1427 */
1428 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001429syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430{
1431 synstate_T *p, *prev;
1432
1433 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001434 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435 {
1436 if (p->sst_lnum == lnum)
1437 return p;
1438 if (p->sst_lnum > lnum)
1439 break;
1440 }
1441 return prev;
1442}
1443
1444/*
1445 * Try saving the current state in b_sst_array[].
1446 * The current state must be valid for the start of the current_lnum line!
1447 */
1448 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001449store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450{
1451 int i;
1452 synstate_T *p;
1453 bufstate_T *bp;
1454 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001455 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456
1457 /*
1458 * If the current state contains a start or end pattern that continues
1459 * from the previous line, we can't use it. Don't store it then.
1460 */
1461 for (i = current_state.ga_len - 1; i >= 0; --i)
1462 {
1463 cur_si = &CUR_STATE(i);
1464 if (cur_si->si_h_startpos.lnum >= current_lnum
1465 || cur_si->si_m_endpos.lnum >= current_lnum
1466 || cur_si->si_h_endpos.lnum >= current_lnum
1467 || (cur_si->si_end_idx
1468 && cur_si->si_eoe_pos.lnum >= current_lnum))
1469 break;
1470 }
1471 if (i >= 0)
1472 {
1473 if (sp != NULL)
1474 {
1475 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001476 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001478 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 else
1480 {
1481 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001482 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 if (p->sst_next == sp)
1484 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001485 if (p != NULL) /* just in case */
1486 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001488 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 sp = NULL;
1490 }
1491 }
1492 else if (sp == NULL || sp->sst_lnum != current_lnum)
1493 {
1494 /*
1495 * Add a new entry
1496 */
1497 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001498 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 {
1500 (void)syn_stack_cleanup();
1501 /* "sp" may have been moved to the freelist now */
1502 sp = syn_stack_find_entry(current_lnum);
1503 }
1504 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001505 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 sp = NULL;
1507 else
1508 {
1509 /* Take the first item from the free list and put it in the used
1510 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001511 p = syn_block->b_sst_firstfree;
1512 syn_block->b_sst_firstfree = p->sst_next;
1513 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514 if (sp == NULL)
1515 {
1516 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001517 p->sst_next = syn_block->b_sst_first;
1518 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 }
1520 else
1521 {
1522 /* insert in list after *sp */
1523 p->sst_next = sp->sst_next;
1524 sp->sst_next = p;
1525 }
1526 sp = p;
1527 sp->sst_stacksize = 0;
1528 sp->sst_lnum = current_lnum;
1529 }
1530 }
1531 if (sp != NULL)
1532 {
1533 /* When overwriting an existing state stack, clear it first */
1534 clear_syn_state(sp);
1535 sp->sst_stacksize = current_state.ga_len;
1536 if (current_state.ga_len > SST_FIX_STATES)
1537 {
1538 /* Need to clear it, might be something remaining from when the
1539 * length was less than SST_FIX_STATES. */
1540 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1541 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1542 sp->sst_stacksize = 0;
1543 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001544 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1546 }
1547 else
1548 bp = sp->sst_union.sst_stack;
1549 for (i = 0; i < sp->sst_stacksize; ++i)
1550 {
1551 bp[i].bs_idx = CUR_STATE(i).si_idx;
1552 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001553#ifdef FEAT_CONCEAL
1554 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1555 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1556#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1558 }
1559 sp->sst_next_flags = current_next_flags;
1560 sp->sst_next_list = current_next_list;
1561 sp->sst_tick = display_tick;
1562 sp->sst_change_lnum = 0;
1563 }
1564 current_state_stored = TRUE;
1565 return sp;
1566}
1567
1568/*
1569 * Copy a state stack from "from" in b_sst_array[] to current_state;
1570 */
1571 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001572load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573{
1574 int i;
1575 bufstate_T *bp;
1576
1577 clear_current_state();
1578 validate_current_state();
1579 keepend_level = -1;
1580 if (from->sst_stacksize
1581 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1582 {
1583 if (from->sst_stacksize > SST_FIX_STATES)
1584 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1585 else
1586 bp = from->sst_union.sst_stack;
1587 for (i = 0; i < from->sst_stacksize; ++i)
1588 {
1589 CUR_STATE(i).si_idx = bp[i].bs_idx;
1590 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001591#ifdef FEAT_CONCEAL
1592 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1593 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1594#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1596 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1597 keepend_level = i;
1598 CUR_STATE(i).si_ends = FALSE;
1599 CUR_STATE(i).si_m_lnum = 0;
1600 if (CUR_STATE(i).si_idx >= 0)
1601 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001602 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 else
1604 CUR_STATE(i).si_next_list = NULL;
1605 update_si_attr(i);
1606 }
1607 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608 }
1609 current_next_list = from->sst_next_list;
1610 current_next_flags = from->sst_next_flags;
1611 current_lnum = from->sst_lnum;
1612}
1613
1614/*
1615 * Compare saved state stack "*sp" with the current state.
1616 * Return TRUE when they are equal.
1617 */
1618 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001619syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620{
1621 int i, j;
1622 bufstate_T *bp;
1623 reg_extmatch_T *six, *bsx;
1624
1625 /* First a quick check if the stacks have the same size end nextlist. */
1626 if (sp->sst_stacksize == current_state.ga_len
1627 && sp->sst_next_list == current_next_list)
1628 {
1629 /* Need to compare all states on both stacks. */
1630 if (sp->sst_stacksize > SST_FIX_STATES)
1631 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1632 else
1633 bp = sp->sst_union.sst_stack;
1634
1635 for (i = current_state.ga_len; --i >= 0; )
1636 {
1637 /* If the item has another index the state is different. */
1638 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1639 break;
1640 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1641 {
1642 /* When the extmatch pointers are different, the strings in
1643 * them can still be the same. Check if the extmatch
1644 * references are equal. */
1645 bsx = bp[i].bs_extmatch;
1646 six = CUR_STATE(i).si_extmatch;
1647 /* If one of the extmatch pointers is NULL the states are
1648 * different. */
1649 if (bsx == NULL || six == NULL)
1650 break;
1651 for (j = 0; j < NSUBEXP; ++j)
1652 {
1653 /* Check each referenced match string. They must all be
1654 * equal. */
1655 if (bsx->matches[j] != six->matches[j])
1656 {
1657 /* If the pointer is different it can still be the
1658 * same text. Compare the strings, ignore case when
1659 * the start item has the sp_ic flag set. */
1660 if (bsx->matches[j] == NULL
1661 || six->matches[j] == NULL)
1662 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001663 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 ? MB_STRICMP(bsx->matches[j],
1665 six->matches[j]) != 0
1666 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1667 break;
1668 }
1669 }
1670 if (j != NSUBEXP)
1671 break;
1672 }
1673 }
1674 if (i < 0)
1675 return TRUE;
1676 }
1677 return FALSE;
1678}
1679
1680/*
1681 * We stop parsing syntax above line "lnum". If the stored state at or below
1682 * this line depended on a change before it, it now depends on the line below
1683 * the last parsed line.
1684 * The window looks like this:
1685 * line which changed
1686 * displayed line
1687 * displayed line
1688 * lnum -> line below window
1689 */
1690 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001691syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692{
1693 synstate_T *sp;
1694
1695 sp = syn_stack_find_entry(lnum);
1696 if (sp != NULL && sp->sst_lnum < lnum)
1697 sp = sp->sst_next;
1698
1699 if (sp != NULL && sp->sst_change_lnum != 0)
1700 sp->sst_change_lnum = lnum;
1701}
1702
1703/*
1704 * End of handling of the state stack.
1705 ****************************************/
1706
1707 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001708invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001709{
1710 clear_current_state();
1711 current_state.ga_itemsize = 0; /* mark current_state invalid */
1712 current_next_list = NULL;
1713 keepend_level = -1;
1714}
1715
1716 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001717validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718{
1719 current_state.ga_itemsize = sizeof(stateitem_T);
1720 current_state.ga_growsize = 3;
1721}
1722
1723/*
1724 * Return TRUE if the syntax at start of lnum changed since last time.
1725 * This will only be called just after get_syntax_attr() for the previous
1726 * line, to check if the next line needs to be redrawn too.
1727 */
1728 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731 int retval = TRUE;
1732 synstate_T *sp;
1733
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734 /*
1735 * Check the state stack when:
1736 * - lnum is just below the previously syntaxed line.
1737 * - lnum is not before the lines with saved states.
1738 * - lnum is not past the lines with saved states.
1739 * - lnum is at or before the last changed line.
1740 */
1741 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1742 {
1743 sp = syn_stack_find_entry(lnum);
1744 if (sp != NULL && sp->sst_lnum == lnum)
1745 {
1746 /*
1747 * finish the previous line (needed when not all of the line was
1748 * drawn)
1749 */
1750 (void)syn_finish_line(FALSE);
1751
1752 /*
1753 * Compare the current state with the previously saved state of
1754 * the line.
1755 */
1756 if (syn_stack_equal(sp))
1757 retval = FALSE;
1758
1759 /*
1760 * Store the current state in b_sst_array[] for later use.
1761 */
1762 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001763 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 }
1765 }
1766
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 return retval;
1768}
1769
1770/*
1771 * Finish the current line.
1772 * This doesn't return any attributes, it only gets the state at the end of
1773 * the line. It can start anywhere in the line, as long as the current state
1774 * is valid.
1775 */
1776 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001777syn_finish_line(
1778 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779{
1780 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001781 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782
1783 if (!current_finished)
1784 {
1785 while (!current_finished)
1786 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001787 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 /*
1789 * When syncing, and found some item, need to check the item.
1790 */
1791 if (syncing && current_state.ga_len)
1792 {
1793 /*
1794 * Check for match with sync item.
1795 */
1796 cur_si = &CUR_STATE(current_state.ga_len - 1);
1797 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001798 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1800 return TRUE;
1801
1802 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001803 * that ends here, need to do that now. Be careful not to go
1804 * past the NUL. */
1805 prev_current_col = current_col;
1806 if (syn_getcurline()[current_col] != NUL)
1807 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001809 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 }
1811 ++current_col;
1812 }
1813 }
1814 return FALSE;
1815}
1816
1817/*
1818 * Return highlight attributes for next character.
1819 * Must first call syntax_start() once for the line.
1820 * "col" is normally 0 for the first use in a line, and increments by one each
1821 * time. It's allowed to skip characters and to stop before the end of the
1822 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001823 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1824 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825 */
1826 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001827get_syntax_attr(
1828 colnr_T col,
1829 int *can_spell,
1830 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831{
1832 int attr = 0;
1833
Bram Moolenaar349955a2007-08-14 21:07:36 +00001834 if (can_spell != NULL)
1835 /* Default: Only do spelling when there is no @Spell cluster or when
1836 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001837 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1838 ? (syn_block->b_spell_cluster_id == 0)
1839 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001840
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001842 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 return 0;
1844
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001845 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001846 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001847 {
1848 clear_current_state();
1849#ifdef FEAT_EVAL
1850 current_id = 0;
1851 current_trans_id = 0;
1852#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001853#ifdef FEAT_CONCEAL
1854 current_flags = 0;
1855#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001856 return 0;
1857 }
1858
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 /* Make sure current_state is valid */
1860 if (INVALID_STATE(&current_state))
1861 validate_current_state();
1862
1863 /*
1864 * Skip from the current column to "col", get the attributes for "col".
1865 */
1866 while (current_col <= col)
1867 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001868 attr = syn_current_attr(FALSE, TRUE, can_spell,
1869 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 ++current_col;
1871 }
1872
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873 return attr;
1874}
1875
1876/*
1877 * Get syntax attributes for current_lnum, current_col.
1878 */
1879 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001880syn_current_attr(
1881 int syncing, /* When 1: called for syncing */
1882 int displaying, /* result will be displayed */
1883 int *can_spell, /* return: do spell checking */
1884 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885{
1886 int syn_id;
1887 lpos_T endpos; /* was: char_u *endp; */
1888 lpos_T hl_startpos; /* was: int hl_startcol; */
1889 lpos_T hl_endpos;
1890 lpos_T eos_pos; /* end-of-start match (start region) */
1891 lpos_T eoe_pos; /* end-of-end pattern */
1892 int end_idx; /* group ID for end pattern */
1893 int idx;
1894 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001895 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 int startcol;
1897 int endcol;
1898 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001899 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001900 short *next_list;
1901 int found_match; /* found usable match */
1902 static int try_next_column = FALSE; /* must try in next col */
1903 int do_keywords;
1904 regmmatch_T regmatch;
1905 lpos_T pos;
1906 int lc_col;
1907 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001908 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909 char_u *line; /* current line. NOTE: becomes invalid after
1910 looking for a pattern match! */
1911
1912 /* variables for zero-width matches that have a "nextgroup" argument */
1913 int keep_next_list;
1914 int zero_width_next_list = FALSE;
1915 garray_T zero_width_next_ga;
1916
1917 /*
1918 * No character, no attributes! Past end of line?
1919 * Do try matching with an empty line (could be the start of a region).
1920 */
1921 line = syn_getcurline();
1922 if (line[current_col] == NUL && current_col != 0)
1923 {
1924 /*
1925 * If we found a match after the last column, use it.
1926 */
1927 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1928 && next_match_col != MAXCOL)
1929 (void)push_next_match(NULL);
1930
1931 current_finished = TRUE;
1932 current_state_stored = FALSE;
1933 return 0;
1934 }
1935
1936 /* if the current or next character is NUL, we will finish the line now */
1937 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1938 {
1939 current_finished = TRUE;
1940 current_state_stored = FALSE;
1941 }
1942
1943 /*
1944 * When in the previous column there was a match but it could not be used
1945 * (empty match or already matched in this column) need to try again in
1946 * the next column.
1947 */
1948 if (try_next_column)
1949 {
1950 next_match_idx = -1;
1951 try_next_column = FALSE;
1952 }
1953
1954 /* Only check for keywords when not syncing and there are some. */
1955 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001956 && (syn_block->b_keywtab.ht_used > 0
1957 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958
1959 /* Init the list of zero-width matches with a nextlist. This is used to
1960 * avoid matching the same item in the same position twice. */
1961 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1962
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001963 /* use syntax iskeyword option */
1964 save_chartab(buf_chartab);
1965
Bram Moolenaar071d4272004-06-13 20:20:40 +00001966 /*
1967 * Repeat matching keywords and patterns, to find contained items at the
1968 * same column. This stops when there are no extra matches at the current
1969 * column.
1970 */
1971 do
1972 {
1973 found_match = FALSE;
1974 keep_next_list = FALSE;
1975 syn_id = 0;
1976
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001977
Bram Moolenaar071d4272004-06-13 20:20:40 +00001978 /*
1979 * 1. Check for a current state.
1980 * Only when there is no current state, or if the current state may
1981 * contain other things, we need to check for keywords and patterns.
1982 * Always need to check for contained items if some item has the
1983 * "containedin" argument (takes extra time!).
1984 */
1985 if (current_state.ga_len)
1986 cur_si = &CUR_STATE(current_state.ga_len - 1);
1987 else
1988 cur_si = NULL;
1989
Bram Moolenaar860cae12010-06-05 23:22:07 +02001990 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 || cur_si->si_cont_list != NULL)
1992 {
1993 /*
1994 * 2. Check for keywords, if on a keyword char after a non-keyword
1995 * char. Don't do this when syncing.
1996 */
1997 if (do_keywords)
1998 {
1999 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002000 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002002 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003#ifdef FEAT_MBYTE
2004 - (has_mbyte
2005 ? (*mb_head_off)(line, line + current_col - 1)
2006 : 0)
2007#endif
2008 , syn_buf)))
2009 {
2010 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002011 &endcol, &flags, &next_list, cur_si,
2012 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002013 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 {
2015 if (push_current_state(KEYWORD_IDX) == OK)
2016 {
2017 cur_si = &CUR_STATE(current_state.ga_len - 1);
2018 cur_si->si_m_startcol = current_col;
2019 cur_si->si_h_startpos.lnum = current_lnum;
2020 cur_si->si_h_startpos.col = 0; /* starts right away */
2021 cur_si->si_m_endpos.lnum = current_lnum;
2022 cur_si->si_m_endpos.col = endcol;
2023 cur_si->si_h_endpos.lnum = current_lnum;
2024 cur_si->si_h_endpos.col = endcol;
2025 cur_si->si_ends = TRUE;
2026 cur_si->si_end_idx = 0;
2027 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002028#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002029 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002030 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002031 if (current_state.ga_len > 1)
2032 cur_si->si_flags |=
2033 CUR_STATE(current_state.ga_len - 2).si_flags
2034 & HL_CONCEAL;
2035#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 cur_si->si_id = syn_id;
2037 cur_si->si_trans_id = syn_id;
2038 if (flags & HL_TRANSP)
2039 {
2040 if (current_state.ga_len < 2)
2041 {
2042 cur_si->si_attr = 0;
2043 cur_si->si_trans_id = 0;
2044 }
2045 else
2046 {
2047 cur_si->si_attr = CUR_STATE(
2048 current_state.ga_len - 2).si_attr;
2049 cur_si->si_trans_id = CUR_STATE(
2050 current_state.ga_len - 2).si_trans_id;
2051 }
2052 }
2053 else
2054 cur_si->si_attr = syn_id2attr(syn_id);
2055 cur_si->si_cont_list = NULL;
2056 cur_si->si_next_list = next_list;
2057 check_keepend();
2058 }
2059 else
2060 vim_free(next_list);
2061 }
2062 }
2063 }
2064
2065 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002066 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002067 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002068 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 {
2070 /*
2071 * If we didn't check for a match yet, or we are past it, check
2072 * for any match with a pattern.
2073 */
2074 if (next_match_idx < 0 || next_match_col < (int)current_col)
2075 {
2076 /*
2077 * Check all relevant patterns for a match at this
2078 * position. This is complicated, because matching with a
2079 * pattern takes quite a bit of time, thus we want to
2080 * avoid doing it when it's not needed.
2081 */
2082 next_match_idx = 0; /* no match in this line yet */
2083 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002084 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002086 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 if ( spp->sp_syncing == syncing
2088 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2089 && (spp->sp_type == SPTYPE_MATCH
2090 || spp->sp_type == SPTYPE_START)
2091 && (current_next_list != NULL
2092 ? in_id_list(NULL, current_next_list,
2093 &spp->sp_syn, 0)
2094 : (cur_si == NULL
2095 ? !(spp->sp_flags & HL_CONTAINED)
2096 : in_id_list(cur_si,
2097 cur_si->si_cont_list, &spp->sp_syn,
2098 spp->sp_flags & HL_CONTAINED))))
2099 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002100 int r;
2101
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102 /* If we already tried matching in this line, and
2103 * there isn't a match before next_match_col, skip
2104 * this item. */
2105 if (spp->sp_line_id == current_line_id
2106 && spp->sp_startcol >= next_match_col)
2107 continue;
2108 spp->sp_line_id = current_line_id;
2109
2110 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2111 if (lc_col < 0)
2112 lc_col = 0;
2113
2114 regmatch.rmm_ic = spp->sp_ic;
2115 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002116 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002117 current_lnum,
2118 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002119 IF_SYN_TIME(&spp->sp_time));
2120 spp->sp_prog = regmatch.regprog;
2121 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002122 {
2123 /* no match in this line, try another one */
2124 spp->sp_startcol = MAXCOL;
2125 continue;
2126 }
2127
2128 /*
2129 * Compute the first column of the match.
2130 */
2131 syn_add_start_off(&pos, &regmatch,
2132 spp, SPO_MS_OFF, -1);
2133 if (pos.lnum > current_lnum)
2134 {
2135 /* must have used end of match in a next line,
2136 * we can't handle that */
2137 spp->sp_startcol = MAXCOL;
2138 continue;
2139 }
2140 startcol = pos.col;
2141
2142 /* remember the next column where this pattern
2143 * matches in the current line */
2144 spp->sp_startcol = startcol;
2145
2146 /*
2147 * If a previously found match starts at a lower
2148 * column number, don't use this one.
2149 */
2150 if (startcol >= next_match_col)
2151 continue;
2152
2153 /*
2154 * If we matched this pattern at this position
2155 * before, skip it. Must retry in the next
2156 * column, because it may match from there.
2157 */
2158 if (did_match_already(idx, &zero_width_next_ga))
2159 {
2160 try_next_column = TRUE;
2161 continue;
2162 }
2163
2164 endpos.lnum = regmatch.endpos[0].lnum;
2165 endpos.col = regmatch.endpos[0].col;
2166
2167 /* Compute the highlight start. */
2168 syn_add_start_off(&hl_startpos, &regmatch,
2169 spp, SPO_HS_OFF, -1);
2170
2171 /* Compute the region start. */
2172 /* Default is to use the end of the match. */
2173 syn_add_end_off(&eos_pos, &regmatch,
2174 spp, SPO_RS_OFF, 0);
2175
2176 /*
2177 * Grab the external submatches before they get
2178 * overwritten. Reference count doesn't change.
2179 */
2180 unref_extmatch(cur_extmatch);
2181 cur_extmatch = re_extmatch_out;
2182 re_extmatch_out = NULL;
2183
2184 flags = 0;
2185 eoe_pos.lnum = 0; /* avoid warning */
2186 eoe_pos.col = 0;
2187 end_idx = 0;
2188 hl_endpos.lnum = 0;
2189
2190 /*
2191 * For a "oneline" the end must be found in the
2192 * same line too. Search for it after the end of
2193 * the match with the start pattern. Set the
2194 * resulting end positions at the same time.
2195 */
2196 if (spp->sp_type == SPTYPE_START
2197 && (spp->sp_flags & HL_ONELINE))
2198 {
2199 lpos_T startpos;
2200
2201 startpos = endpos;
2202 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2203 &flags, &eoe_pos, &end_idx, cur_extmatch);
2204 if (endpos.lnum == 0)
2205 continue; /* not found */
2206 }
2207
2208 /*
2209 * For a "match" the size must be > 0 after the
2210 * end offset needs has been added. Except when
2211 * syncing.
2212 */
2213 else if (spp->sp_type == SPTYPE_MATCH)
2214 {
2215 syn_add_end_off(&hl_endpos, &regmatch, spp,
2216 SPO_HE_OFF, 0);
2217 syn_add_end_off(&endpos, &regmatch, spp,
2218 SPO_ME_OFF, 0);
2219 if (endpos.lnum == current_lnum
2220 && (int)endpos.col + syncing < startcol)
2221 {
2222 /*
2223 * If an empty string is matched, may need
2224 * to try matching again at next column.
2225 */
2226 if (regmatch.startpos[0].col
2227 == regmatch.endpos[0].col)
2228 try_next_column = TRUE;
2229 continue;
2230 }
2231 }
2232
2233 /*
2234 * keep the best match so far in next_match_*
2235 */
2236 /* Highlighting must start after startpos and end
2237 * before endpos. */
2238 if (hl_startpos.lnum == current_lnum
2239 && (int)hl_startpos.col < startcol)
2240 hl_startpos.col = startcol;
2241 limit_pos_zero(&hl_endpos, &endpos);
2242
2243 next_match_idx = idx;
2244 next_match_col = startcol;
2245 next_match_m_endpos = endpos;
2246 next_match_h_endpos = hl_endpos;
2247 next_match_h_startpos = hl_startpos;
2248 next_match_flags = flags;
2249 next_match_eos_pos = eos_pos;
2250 next_match_eoe_pos = eoe_pos;
2251 next_match_end_idx = end_idx;
2252 unref_extmatch(next_match_extmatch);
2253 next_match_extmatch = cur_extmatch;
2254 cur_extmatch = NULL;
2255 }
2256 }
2257 }
2258
2259 /*
2260 * If we found a match at the current column, use it.
2261 */
2262 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2263 {
2264 synpat_T *lspp;
2265
2266 /* When a zero-width item matched which has a nextgroup,
2267 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002268 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269 if (next_match_m_endpos.lnum == current_lnum
2270 && next_match_m_endpos.col == current_col
2271 && lspp->sp_next_list != NULL)
2272 {
2273 current_next_list = lspp->sp_next_list;
2274 current_next_flags = lspp->sp_flags;
2275 keep_next_list = TRUE;
2276 zero_width_next_list = TRUE;
2277
2278 /* Add the index to a list, so that we can check
2279 * later that we don't match it again (and cause an
2280 * endless loop). */
2281 if (ga_grow(&zero_width_next_ga, 1) == OK)
2282 {
2283 ((int *)(zero_width_next_ga.ga_data))
2284 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 }
2286 next_match_idx = -1;
2287 }
2288 else
2289 cur_si = push_next_match(cur_si);
2290 found_match = TRUE;
2291 }
2292 }
2293 }
2294
2295 /*
2296 * Handle searching for nextgroup match.
2297 */
2298 if (current_next_list != NULL && !keep_next_list)
2299 {
2300 /*
2301 * If a nextgroup was not found, continue looking for one if:
2302 * - this is an empty line and the "skipempty" option was given
2303 * - we are on white space and the "skipwhite" option was given
2304 */
2305 if (!found_match)
2306 {
2307 line = syn_getcurline();
2308 if (((current_next_flags & HL_SKIPWHITE)
2309 && vim_iswhite(line[current_col]))
2310 || ((current_next_flags & HL_SKIPEMPTY)
2311 && *line == NUL))
2312 break;
2313 }
2314
2315 /*
2316 * If a nextgroup was found: Use it, and continue looking for
2317 * contained matches.
2318 * If a nextgroup was not found: Continue looking for a normal
2319 * match.
2320 * When did set current_next_list for a zero-width item and no
2321 * match was found don't loop (would get stuck).
2322 */
2323 current_next_list = NULL;
2324 next_match_idx = -1;
2325 if (!zero_width_next_list)
2326 found_match = TRUE;
2327 }
2328
2329 } while (found_match);
2330
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002331 restore_chartab(buf_chartab);
2332
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333 /*
2334 * Use attributes from the current state, if within its highlighting.
2335 * If not, use attributes from the current-but-one state, etc.
2336 */
2337 current_attr = 0;
2338#ifdef FEAT_EVAL
2339 current_id = 0;
2340 current_trans_id = 0;
2341#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002342#ifdef FEAT_CONCEAL
2343 current_flags = 0;
2344#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345 if (cur_si != NULL)
2346 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002347#ifndef FEAT_EVAL
2348 int current_trans_id = 0;
2349#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2351 {
2352 sip = &CUR_STATE(idx);
2353 if ((current_lnum > sip->si_h_startpos.lnum
2354 || (current_lnum == sip->si_h_startpos.lnum
2355 && current_col >= sip->si_h_startpos.col))
2356 && (sip->si_h_endpos.lnum == 0
2357 || current_lnum < sip->si_h_endpos.lnum
2358 || (current_lnum == sip->si_h_endpos.lnum
2359 && current_col < sip->si_h_endpos.col)))
2360 {
2361 current_attr = sip->si_attr;
2362#ifdef FEAT_EVAL
2363 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002365 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002366#ifdef FEAT_CONCEAL
2367 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002368 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002369 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002370#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371 break;
2372 }
2373 }
2374
Bram Moolenaar217ad922005-03-20 22:37:15 +00002375 if (can_spell != NULL)
2376 {
2377 struct sp_syn sps;
2378
2379 /*
2380 * set "can_spell" to TRUE if spell checking is supposed to be
2381 * done in the current item.
2382 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002383 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002384 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002385 /* There is no @Spell cluster: Do spelling for items without
2386 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002387 if (syn_block->b_nospell_cluster_id == 0
2388 || current_trans_id == 0)
2389 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002390 else
2391 {
2392 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002393 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002394 sps.cont_in_list = NULL;
2395 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2396 }
2397 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002398 else
2399 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002400 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002401 * the @Spell cluster. But not when @NoSpell is also there.
2402 * At the toplevel only spell check when ":syn spell toplevel"
2403 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002404 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002405 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002406 else
2407 {
2408 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002409 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002410 sps.cont_in_list = NULL;
2411 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2412
Bram Moolenaar860cae12010-06-05 23:22:07 +02002413 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002414 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002415 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002416 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2417 *can_spell = FALSE;
2418 }
2419 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002420 }
2421 }
2422
2423
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 /*
2425 * Check for end of current state (and the states before it) at the
2426 * next column. Don't do this for syncing, because we would miss a
2427 * single character match.
2428 * First check if the current state ends at the current column. It
2429 * may be for an empty match and a containing item might end in the
2430 * current column.
2431 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002432 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 {
2434 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002435 if (current_state.ga_len > 0
2436 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002437 {
2438 ++current_col;
2439 check_state_ends();
2440 --current_col;
2441 }
2442 }
2443 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002444 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002445 /* Default: Only do spelling when there is no @Spell cluster or when
2446 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002447 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2448 ? (syn_block->b_spell_cluster_id == 0)
2449 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002451 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 if (current_next_list != NULL
2453 && syn_getcurline()[current_col + 1] == NUL
2454 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2455 current_next_list = NULL;
2456
2457 if (zero_width_next_ga.ga_len > 0)
2458 ga_clear(&zero_width_next_ga);
2459
2460 /* No longer need external matches. But keep next_match_extmatch. */
2461 unref_extmatch(re_extmatch_out);
2462 re_extmatch_out = NULL;
2463 unref_extmatch(cur_extmatch);
2464
2465 return current_attr;
2466}
2467
2468
2469/*
2470 * Check if we already matched pattern "idx" at the current column.
2471 */
2472 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002473did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002474{
2475 int i;
2476
2477 for (i = current_state.ga_len; --i >= 0; )
2478 if (CUR_STATE(i).si_m_startcol == (int)current_col
2479 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2480 && CUR_STATE(i).si_idx == idx)
2481 return TRUE;
2482
2483 /* Zero-width matches with a nextgroup argument are not put on the syntax
2484 * stack, and can only be matched once anyway. */
2485 for (i = gap->ga_len; --i >= 0; )
2486 if (((int *)(gap->ga_data))[i] == idx)
2487 return TRUE;
2488
2489 return FALSE;
2490}
2491
2492/*
2493 * Push the next match onto the stack.
2494 */
2495 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002496push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497{
2498 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002499#ifdef FEAT_CONCEAL
2500 int save_flags;
2501#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502
Bram Moolenaar860cae12010-06-05 23:22:07 +02002503 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504
2505 /*
2506 * Push the item in current_state stack;
2507 */
2508 if (push_current_state(next_match_idx) == OK)
2509 {
2510 /*
2511 * If it's a start-skip-end type that crosses lines, figure out how
2512 * much it continues in this line. Otherwise just fill in the length.
2513 */
2514 cur_si = &CUR_STATE(current_state.ga_len - 1);
2515 cur_si->si_h_startpos = next_match_h_startpos;
2516 cur_si->si_m_startcol = current_col;
2517 cur_si->si_m_lnum = current_lnum;
2518 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002519#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002520 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002521 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002522 if (current_state.ga_len > 1)
2523 cur_si->si_flags |=
2524 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2525#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 cur_si->si_next_list = spp->sp_next_list;
2527 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2528 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2529 {
2530 /* Try to find the end pattern in the current line */
2531 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2532 check_keepend();
2533 }
2534 else
2535 {
2536 cur_si->si_m_endpos = next_match_m_endpos;
2537 cur_si->si_h_endpos = next_match_h_endpos;
2538 cur_si->si_ends = TRUE;
2539 cur_si->si_flags |= next_match_flags;
2540 cur_si->si_eoe_pos = next_match_eoe_pos;
2541 cur_si->si_end_idx = next_match_end_idx;
2542 }
2543 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2544 keepend_level = current_state.ga_len - 1;
2545 check_keepend();
2546 update_si_attr(current_state.ga_len - 1);
2547
Bram Moolenaar860cae12010-06-05 23:22:07 +02002548#ifdef FEAT_CONCEAL
2549 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2550#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002551 /*
2552 * If the start pattern has another highlight group, push another item
2553 * on the stack for the start pattern.
2554 */
2555 if ( spp->sp_type == SPTYPE_START
2556 && spp->sp_syn_match_id != 0
2557 && push_current_state(next_match_idx) == OK)
2558 {
2559 cur_si = &CUR_STATE(current_state.ga_len - 1);
2560 cur_si->si_h_startpos = next_match_h_startpos;
2561 cur_si->si_m_startcol = current_col;
2562 cur_si->si_m_lnum = current_lnum;
2563 cur_si->si_m_endpos = next_match_eos_pos;
2564 cur_si->si_h_endpos = next_match_eos_pos;
2565 cur_si->si_ends = TRUE;
2566 cur_si->si_end_idx = 0;
2567 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002568#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002569 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002570 cur_si->si_flags |= save_flags;
2571 if (cur_si->si_flags & HL_CONCEALENDS)
2572 cur_si->si_flags |= HL_CONCEAL;
2573#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574 cur_si->si_next_list = NULL;
2575 check_keepend();
2576 update_si_attr(current_state.ga_len - 1);
2577 }
2578 }
2579
2580 next_match_idx = -1; /* try other match next time */
2581
2582 return cur_si;
2583}
2584
2585/*
2586 * Check for end of current state (and the states before it).
2587 */
2588 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002589check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590{
2591 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002592 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593
2594 cur_si = &CUR_STATE(current_state.ga_len - 1);
2595 for (;;)
2596 {
2597 if (cur_si->si_ends
2598 && (cur_si->si_m_endpos.lnum < current_lnum
2599 || (cur_si->si_m_endpos.lnum == current_lnum
2600 && cur_si->si_m_endpos.col <= current_col)))
2601 {
2602 /*
2603 * If there is an end pattern group ID, highlight the end pattern
2604 * now. No need to pop the current item from the stack.
2605 * Only do this if the end pattern continues beyond the current
2606 * position.
2607 */
2608 if (cur_si->si_end_idx
2609 && (cur_si->si_eoe_pos.lnum > current_lnum
2610 || (cur_si->si_eoe_pos.lnum == current_lnum
2611 && cur_si->si_eoe_pos.col > current_col)))
2612 {
2613 cur_si->si_idx = cur_si->si_end_idx;
2614 cur_si->si_end_idx = 0;
2615 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2616 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2617 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002618#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002619 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002620 if (cur_si->si_flags & HL_CONCEALENDS)
2621 cur_si->si_flags |= HL_CONCEAL;
2622#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002624
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002625 /* nextgroup= should not match in the end pattern */
2626 current_next_list = NULL;
2627
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002628 /* what matches next may be different now, clear it */
2629 next_match_idx = 0;
2630 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002631 break;
2632 }
2633 else
2634 {
2635 /* handle next_list, unless at end of line and no "skipnl" or
2636 * "skipempty" */
2637 current_next_list = cur_si->si_next_list;
2638 current_next_flags = cur_si->si_flags;
2639 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2640 && syn_getcurline()[current_col] == NUL)
2641 current_next_list = NULL;
2642
2643 /* When the ended item has "extend", another item with
2644 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002645 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646
2647 pop_current_state();
2648
2649 if (current_state.ga_len == 0)
2650 break;
2651
Bram Moolenaar81993f42008-01-11 20:27:45 +00002652 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 {
2654 syn_update_ends(FALSE);
2655 if (current_state.ga_len == 0)
2656 break;
2657 }
2658
2659 cur_si = &CUR_STATE(current_state.ga_len - 1);
2660
2661 /*
2662 * Only for a region the search for the end continues after
2663 * the end of the contained item. If the contained match
2664 * included the end-of-line, break here, the region continues.
2665 * Don't do this when:
2666 * - "keepend" is used for the contained item
2667 * - not at the end of the line (could be end="x$"me=e-1).
2668 * - "excludenl" is used (HL_HAS_EOL won't be set)
2669 */
2670 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002671 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 == SPTYPE_START
2673 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2674 {
2675 update_si_end(cur_si, (int)current_col, TRUE);
2676 check_keepend();
2677 if ((current_next_flags & HL_HAS_EOL)
2678 && keepend_level < 0
2679 && syn_getcurline()[current_col] == NUL)
2680 break;
2681 }
2682 }
2683 }
2684 else
2685 break;
2686 }
2687}
2688
2689/*
2690 * Update an entry in the current_state stack for a match or region. This
2691 * fills in si_attr, si_next_list and si_cont_list.
2692 */
2693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002694update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695{
2696 stateitem_T *sip = &CUR_STATE(idx);
2697 synpat_T *spp;
2698
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002699 /* This should not happen... */
2700 if (sip->si_idx < 0)
2701 return;
2702
Bram Moolenaar860cae12010-06-05 23:22:07 +02002703 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 if (sip->si_flags & HL_MATCH)
2705 sip->si_id = spp->sp_syn_match_id;
2706 else
2707 sip->si_id = spp->sp_syn.id;
2708 sip->si_attr = syn_id2attr(sip->si_id);
2709 sip->si_trans_id = sip->si_id;
2710 if (sip->si_flags & HL_MATCH)
2711 sip->si_cont_list = NULL;
2712 else
2713 sip->si_cont_list = spp->sp_cont_list;
2714
2715 /*
2716 * For transparent items, take attr from outer item.
2717 * Also take cont_list, if there is none.
2718 * Don't do this for the matchgroup of a start or end pattern.
2719 */
2720 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2721 {
2722 if (idx == 0)
2723 {
2724 sip->si_attr = 0;
2725 sip->si_trans_id = 0;
2726 if (sip->si_cont_list == NULL)
2727 sip->si_cont_list = ID_LIST_ALL;
2728 }
2729 else
2730 {
2731 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2732 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002733 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2734 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 if (sip->si_cont_list == NULL)
2736 {
2737 sip->si_flags |= HL_TRANS_CONT;
2738 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2739 }
2740 }
2741 }
2742}
2743
2744/*
2745 * Check the current stack for patterns with "keepend" flag.
2746 * Propagate the match-end to contained items, until a "skipend" item is found.
2747 */
2748 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002749check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750{
2751 int i;
2752 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002753 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754 stateitem_T *sip;
2755
2756 /*
2757 * This check can consume a lot of time; only do it from the level where
2758 * there really is a keepend.
2759 */
2760 if (keepend_level < 0)
2761 return;
2762
2763 /*
2764 * Find the last index of an "extend" item. "keepend" items before that
2765 * won't do anything. If there is no "extend" item "i" will be
2766 * "keepend_level" and all "keepend" items will work normally.
2767 */
2768 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2769 if (CUR_STATE(i).si_flags & HL_EXTEND)
2770 break;
2771
2772 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002773 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002774 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002775 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002776 for ( ; i < current_state.ga_len; ++i)
2777 {
2778 sip = &CUR_STATE(i);
2779 if (maxpos.lnum != 0)
2780 {
2781 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002782 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2784 sip->si_ends = TRUE;
2785 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002786 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2787 {
2788 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789 || maxpos.lnum > sip->si_m_endpos.lnum
2790 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002791 && maxpos.col > sip->si_m_endpos.col))
2792 maxpos = sip->si_m_endpos;
2793 if (maxpos_h.lnum == 0
2794 || maxpos_h.lnum > sip->si_h_endpos.lnum
2795 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2796 && maxpos_h.col > sip->si_h_endpos.col))
2797 maxpos_h = sip->si_h_endpos;
2798 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799 }
2800}
2801
2802/*
2803 * Update an entry in the current_state stack for a start-skip-end pattern.
2804 * This finds the end of the current item, if it's in the current line.
2805 *
2806 * Return the flags for the matched END.
2807 */
2808 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002809update_si_end(
2810 stateitem_T *sip,
2811 int startcol, /* where to start searching for the end */
2812 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813{
2814 lpos_T startpos;
2815 lpos_T endpos;
2816 lpos_T hl_endpos;
2817 lpos_T end_endpos;
2818 int end_idx;
2819
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002820 /* return quickly for a keyword */
2821 if (sip->si_idx < 0)
2822 return;
2823
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824 /* Don't update when it's already done. Can be a match of an end pattern
2825 * that started in a previous line. Watch out: can also be a "keepend"
2826 * from a containing item. */
2827 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2828 return;
2829
2830 /*
2831 * We need to find the end of the region. It may continue in the next
2832 * line.
2833 */
2834 end_idx = 0;
2835 startpos.lnum = current_lnum;
2836 startpos.col = startcol;
2837 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2838 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2839
2840 if (endpos.lnum == 0)
2841 {
2842 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002843 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 {
2845 /* a "oneline" never continues in the next line */
2846 sip->si_ends = TRUE;
2847 sip->si_m_endpos.lnum = current_lnum;
2848 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2849 }
2850 else
2851 {
2852 /* continues in the next line */
2853 sip->si_ends = FALSE;
2854 sip->si_m_endpos.lnum = 0;
2855 }
2856 sip->si_h_endpos = sip->si_m_endpos;
2857 }
2858 else
2859 {
2860 /* match within this line */
2861 sip->si_m_endpos = endpos;
2862 sip->si_h_endpos = hl_endpos;
2863 sip->si_eoe_pos = end_endpos;
2864 sip->si_ends = TRUE;
2865 sip->si_end_idx = end_idx;
2866 }
2867}
2868
2869/*
2870 * Add a new state to the current state stack.
2871 * It is cleared and the index set to "idx".
2872 * Return FAIL if it's not possible (out of memory).
2873 */
2874 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002875push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876{
2877 if (ga_grow(&current_state, 1) == FAIL)
2878 return FAIL;
2879 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2880 CUR_STATE(current_state.ga_len).si_idx = idx;
2881 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002882 return OK;
2883}
2884
2885/*
2886 * Remove a state from the current_state stack.
2887 */
2888 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002889pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890{
2891 if (current_state.ga_len)
2892 {
2893 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2894 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 }
2896 /* after the end of a pattern, try matching a keyword or pattern */
2897 next_match_idx = -1;
2898
2899 /* if first state with "keepend" is popped, reset keepend_level */
2900 if (keepend_level >= current_state.ga_len)
2901 keepend_level = -1;
2902}
2903
2904/*
2905 * Find the end of a start/skip/end syntax region after "startpos".
2906 * Only checks one line.
2907 * Also handles a match item that continued from a previous line.
2908 * If not found, the syntax item continues in the next line. m_endpos->lnum
2909 * will be 0.
2910 * If found, the end of the region and the end of the highlighting is
2911 * computed.
2912 */
2913 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002914find_endpos(
2915 int idx, /* index of the pattern */
2916 lpos_T *startpos, /* where to start looking for an END match */
2917 lpos_T *m_endpos, /* return: end of match */
2918 lpos_T *hl_endpos, /* return: end of highlighting */
2919 long *flagsp, /* return: flags of matching END */
2920 lpos_T *end_endpos, /* return: end of end pattern match */
2921 int *end_idx, /* return: group ID for end pat. match, or 0 */
2922 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923{
2924 colnr_T matchcol;
2925 synpat_T *spp, *spp_skip;
2926 int start_idx;
2927 int best_idx;
2928 regmmatch_T regmatch;
2929 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2930 lpos_T pos;
2931 char_u *line;
2932 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002933 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002935 /* just in case we are invoked for a keyword */
2936 if (idx < 0)
2937 return;
2938
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 /*
2940 * Check for being called with a START pattern.
2941 * Can happen with a match that continues to the next line, because it
2942 * contained a region.
2943 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002944 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945 if (spp->sp_type != SPTYPE_START)
2946 {
2947 *hl_endpos = *startpos;
2948 return;
2949 }
2950
2951 /*
2952 * Find the SKIP or first END pattern after the last START pattern.
2953 */
2954 for (;;)
2955 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002956 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 if (spp->sp_type != SPTYPE_START)
2958 break;
2959 ++idx;
2960 }
2961
2962 /*
2963 * Lookup the SKIP pattern (if present)
2964 */
2965 if (spp->sp_type == SPTYPE_SKIP)
2966 {
2967 spp_skip = spp;
2968 ++idx;
2969 }
2970 else
2971 spp_skip = NULL;
2972
2973 /* Setup external matches for syn_regexec(). */
2974 unref_extmatch(re_extmatch_in);
2975 re_extmatch_in = ref_extmatch(start_ext);
2976
2977 matchcol = startpos->col; /* start looking for a match at sstart */
2978 start_idx = idx; /* remember the first END pattern. */
2979 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002980
2981 /* use syntax iskeyword option */
2982 save_chartab(buf_chartab);
2983
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 for (;;)
2985 {
2986 /*
2987 * Find end pattern that matches first after "matchcol".
2988 */
2989 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002990 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991 {
2992 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002993 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994
Bram Moolenaar860cae12010-06-05 23:22:07 +02002995 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2997 break;
2998 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2999 if (lc_col < 0)
3000 lc_col = 0;
3001
3002 regmatch.rmm_ic = spp->sp_ic;
3003 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003004 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3005 IF_SYN_TIME(&spp->sp_time));
3006 spp->sp_prog = regmatch.regprog;
3007 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 {
3009 if (best_idx == -1 || regmatch.startpos[0].col
3010 < best_regmatch.startpos[0].col)
3011 {
3012 best_idx = idx;
3013 best_regmatch.startpos[0] = regmatch.startpos[0];
3014 best_regmatch.endpos[0] = regmatch.endpos[0];
3015 }
3016 }
3017 }
3018
3019 /*
3020 * If all end patterns have been tried, and there is no match, the
3021 * item continues until end-of-line.
3022 */
3023 if (best_idx == -1)
3024 break;
3025
3026 /*
3027 * If the skip pattern matches before the end pattern,
3028 * continue searching after the skip pattern.
3029 */
3030 if (spp_skip != NULL)
3031 {
3032 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003033 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003034
3035 if (lc_col < 0)
3036 lc_col = 0;
3037 regmatch.rmm_ic = spp_skip->sp_ic;
3038 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003039 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3040 IF_SYN_TIME(&spp_skip->sp_time));
3041 spp_skip->sp_prog = regmatch.regprog;
3042 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 <= best_regmatch.startpos[0].col)
3044 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003045 int line_len;
3046
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047 /* Add offset to skip pattern match */
3048 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3049
3050 /* If the skip pattern goes on to the next line, there is no
3051 * match with an end pattern in this line. */
3052 if (pos.lnum > startpos->lnum)
3053 break;
3054
3055 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003056 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003057
3058 /* take care of an empty match or negative offset */
3059 if (pos.col <= matchcol)
3060 ++matchcol;
3061 else if (pos.col <= regmatch.endpos[0].col)
3062 matchcol = pos.col;
3063 else
3064 /* Be careful not to jump over the NUL at the end-of-line */
3065 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003066 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 ++matchcol)
3068 ;
3069
3070 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003071 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 break;
3073
3074 continue; /* start with first end pattern again */
3075 }
3076 }
3077
3078 /*
3079 * Match from start pattern to end pattern.
3080 * Correct for match and highlight offset of end pattern.
3081 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003082 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003083 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3084 /* can't end before the start */
3085 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3086 m_endpos->col = startpos->col;
3087
3088 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3089 /* can't end before the start */
3090 if (end_endpos->lnum == startpos->lnum
3091 && end_endpos->col < startpos->col)
3092 end_endpos->col = startpos->col;
3093 /* can't end after the match */
3094 limit_pos(end_endpos, m_endpos);
3095
3096 /*
3097 * If the end group is highlighted differently, adjust the pointers.
3098 */
3099 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3100 {
3101 *end_idx = best_idx;
3102 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3103 {
3104 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3105 hl_endpos->col = best_regmatch.endpos[0].col;
3106 }
3107 else
3108 {
3109 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3110 hl_endpos->col = best_regmatch.startpos[0].col;
3111 }
3112 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3113
3114 /* can't end before the start */
3115 if (hl_endpos->lnum == startpos->lnum
3116 && hl_endpos->col < startpos->col)
3117 hl_endpos->col = startpos->col;
3118 limit_pos(hl_endpos, m_endpos);
3119
3120 /* now the match ends where the highlighting ends, it is turned
3121 * into the matchgroup for the end */
3122 *m_endpos = *hl_endpos;
3123 }
3124 else
3125 {
3126 *end_idx = 0;
3127 *hl_endpos = *end_endpos;
3128 }
3129
3130 *flagsp = spp->sp_flags;
3131
3132 had_match = TRUE;
3133 break;
3134 }
3135
3136 /* no match for an END pattern in this line */
3137 if (!had_match)
3138 m_endpos->lnum = 0;
3139
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003140 restore_chartab(buf_chartab);
3141
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142 /* Remove external matches. */
3143 unref_extmatch(re_extmatch_in);
3144 re_extmatch_in = NULL;
3145}
3146
3147/*
3148 * Limit "pos" not to be after "limit".
3149 */
3150 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003151limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152{
3153 if (pos->lnum > limit->lnum)
3154 *pos = *limit;
3155 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3156 pos->col = limit->col;
3157}
3158
3159/*
3160 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3161 */
3162 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003163limit_pos_zero(
3164 lpos_T *pos,
3165 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003166{
3167 if (pos->lnum == 0)
3168 *pos = *limit;
3169 else
3170 limit_pos(pos, limit);
3171}
3172
3173/*
3174 * Add offset to matched text for end of match or highlight.
3175 */
3176 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003177syn_add_end_off(
3178 lpos_T *result, /* returned position */
3179 regmmatch_T *regmatch, /* start/end of match */
3180 synpat_T *spp, /* matched pattern */
3181 int idx, /* index of offset */
3182 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003183{
3184 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003185 int off;
3186 char_u *base;
3187 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188
3189 if (spp->sp_off_flags & (1 << idx))
3190 {
3191 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003192 col = regmatch->startpos[0].col;
3193 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 }
3195 else
3196 {
3197 result->lnum = regmatch->endpos[0].lnum;
3198 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003199 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003200 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003201 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3202 * is a matchgroup. Watch out for match with last NL in the buffer. */
3203 if (result->lnum > syn_buf->b_ml.ml_line_count)
3204 col = 0;
3205 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003206 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003207 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3208 p = base + col;
3209 if (off > 0)
3210 {
3211 while (off-- > 0 && *p != NUL)
3212 mb_ptr_adv(p);
3213 }
3214 else if (off < 0)
3215 {
3216 while (off++ < 0 && base < p)
3217 mb_ptr_back(base, p);
3218 }
3219 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003220 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003221 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222}
3223
3224/*
3225 * Add offset to matched text for start of match or highlight.
3226 * Avoid resulting column to become negative.
3227 */
3228 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003229syn_add_start_off(
3230 lpos_T *result, /* returned position */
3231 regmmatch_T *regmatch, /* start/end of match */
3232 synpat_T *spp,
3233 int idx,
3234 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235{
3236 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003237 int off;
3238 char_u *base;
3239 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240
3241 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3242 {
3243 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003244 col = regmatch->endpos[0].col;
3245 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 }
3247 else
3248 {
3249 result->lnum = regmatch->startpos[0].lnum;
3250 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003251 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003253 if (result->lnum > syn_buf->b_ml.ml_line_count)
3254 {
3255 /* a "\n" at the end of the pattern may take us below the last line */
3256 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003257 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003258 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003259 if (off != 0)
3260 {
3261 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3262 p = base + col;
3263 if (off > 0)
3264 {
3265 while (off-- && *p != NUL)
3266 mb_ptr_adv(p);
3267 }
3268 else if (off < 0)
3269 {
3270 while (off++ && base < p)
3271 mb_ptr_back(base, p);
3272 }
3273 col = (int)(p - base);
3274 }
3275 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276}
3277
3278/*
3279 * Get current line in syntax buffer.
3280 */
3281 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003282syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283{
3284 return ml_get_buf(syn_buf, current_lnum, FALSE);
3285}
3286
3287/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003288 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289 * Returns TRUE when there is a match.
3290 */
3291 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003292syn_regexec(
3293 regmmatch_T *rmp,
3294 linenr_T lnum,
3295 colnr_T col,
3296 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003298 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003299#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003300 proftime_T pt;
3301
3302 if (syn_time_on)
3303 profile_start(&pt);
3304#endif
3305
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003306 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003307 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3308
Bram Moolenaarf7512552013-06-06 14:55:19 +02003309#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003310 if (syn_time_on)
3311 {
3312 profile_end(&pt);
3313 profile_add(&st->total, &pt);
3314 if (profile_cmp(&pt, &st->slowest) < 0)
3315 st->slowest = pt;
3316 ++st->count;
3317 if (r > 0)
3318 ++st->match;
3319 }
3320#endif
3321
3322 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323 {
3324 rmp->startpos[0].lnum += lnum;
3325 rmp->endpos[0].lnum += lnum;
3326 return TRUE;
3327 }
3328 return FALSE;
3329}
3330
3331/*
3332 * Check one position in a line for a matching keyword.
3333 * The caller must check if a keyword can start at startcol.
3334 * Return it's ID if found, 0 otherwise.
3335 */
3336 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003337check_keyword_id(
3338 char_u *line,
3339 int startcol, /* position in line to check for keyword */
3340 int *endcolp, /* return: character after found keyword */
3341 long *flagsp, /* return: flags of matching keyword */
3342 short **next_listp, /* return: next_list of matching keyword */
3343 stateitem_T *cur_si, /* item at the top of the stack */
3344 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003346 keyentry_T *kp;
3347 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003349 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003351 hashtab_T *ht;
3352 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353
3354 /* Find first character after the keyword. First character was already
3355 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003356 kwp = line + startcol;
3357 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 do
3359 {
3360#ifdef FEAT_MBYTE
3361 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003362 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 else
3364#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003365 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003367 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368
Bram Moolenaardad6b692005-01-25 22:14:34 +00003369 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 return 0;
3371
3372 /*
3373 * Must make a copy of the keyword, so we can add a NUL and make it
3374 * lowercase.
3375 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003376 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003377
3378 /*
3379 * Try twice:
3380 * 1. matching case
3381 * 2. ignoring case
3382 */
3383 for (round = 1; round <= 2; ++round)
3384 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003385 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003386 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 if (round == 2) /* ignore case */
3389 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390
3391 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 * Find keywords that match. There can be several with different
3393 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394 * When current_next_list is non-zero accept only that group, otherwise:
3395 * Accept a not-contained keyword at toplevel.
3396 * Accept a keyword at other levels only if it is in the contains list.
3397 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003398 hi = hash_find(ht, keyword);
3399 if (!HASHITEM_EMPTY(hi))
3400 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003402 if (current_next_list != 0
3403 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3404 : (cur_si == NULL
3405 ? !(kp->flags & HL_CONTAINED)
3406 : in_id_list(cur_si, cur_si->si_cont_list,
3407 &kp->k_syn, kp->flags & HL_CONTAINED)))
3408 {
3409 *endcolp = startcol + kwlen;
3410 *flagsp = kp->flags;
3411 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003412#ifdef FEAT_CONCEAL
3413 *ccharp = kp->k_char;
3414#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003415 return kp->k_syn.id;
3416 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417 }
3418 }
3419 return 0;
3420}
3421
3422/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423 * Handle ":syntax conceal" command.
3424 */
3425 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003426syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003427{
3428#ifdef FEAT_CONCEAL
3429 char_u *arg = eap->arg;
3430 char_u *next;
3431
3432 eap->nextcmd = find_nextcmd(arg);
3433 if (eap->skip)
3434 return;
3435
3436 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003437 if (*arg == NUL)
3438 {
3439 if (curwin->w_s->b_syn_conceal)
3440 MSG(_("syn conceal on"));
3441 else
3442 MSG(_("syn conceal off"));
3443 }
3444 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003445 curwin->w_s->b_syn_conceal = TRUE;
3446 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3447 curwin->w_s->b_syn_conceal = FALSE;
3448 else
3449 EMSG2(_("E390: Illegal argument: %s"), arg);
3450#endif
3451}
3452
3453/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003454 * Handle ":syntax case" command.
3455 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003457syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458{
3459 char_u *arg = eap->arg;
3460 char_u *next;
3461
3462 eap->nextcmd = find_nextcmd(arg);
3463 if (eap->skip)
3464 return;
3465
3466 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003467 if (*arg == NUL)
3468 {
3469 if (curwin->w_s->b_syn_ic)
3470 MSG(_("syntax case ignore"));
3471 else
3472 MSG(_("syntax case match"));
3473 }
3474 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 else
3479 EMSG2(_("E390: Illegal argument: %s"), arg);
3480}
3481
3482/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003483 * Handle ":syntax spell" command.
3484 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003485 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003486syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003487{
3488 char_u *arg = eap->arg;
3489 char_u *next;
3490
3491 eap->nextcmd = find_nextcmd(arg);
3492 if (eap->skip)
3493 return;
3494
3495 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003496 if (*arg == NUL)
3497 {
3498 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3499 MSG(_("syntax spell toplevel"));
3500 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3501 MSG(_("syntax spell notoplevel"));
3502 else
3503 MSG(_("syntax spell default"));
3504 }
3505 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003507 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003509 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003511 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003512 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003513 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003514 return;
3515 }
3516
3517 /* assume spell checking changed, force a redraw */
3518 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003519}
3520
3521/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003522 * Handle ":syntax iskeyword" command.
3523 */
3524 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003525syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003526{
3527 char_u *arg = eap->arg;
3528 char_u save_chartab[32];
3529 char_u *save_isk;
3530
3531 if (eap->skip)
3532 return;
3533
3534 arg = skipwhite(arg);
3535 if (*arg == NUL)
3536 {
3537 MSG_PUTS("\n");
3538 MSG_PUTS(_("syntax iskeyword "));
3539 if (curwin->w_s->b_syn_isk != empty_option)
3540 msg_outtrans(curwin->w_s->b_syn_isk);
3541 else
3542 msg_outtrans((char_u *)"not set");
3543 }
3544 else
3545 {
3546 if (STRNICMP(arg, "clear", 5) == 0)
3547 {
3548 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3549 (size_t)32);
3550 clear_string_option(&curwin->w_s->b_syn_isk);
3551 }
3552 else
3553 {
3554 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3555 save_isk = curbuf->b_p_isk;
3556 curbuf->b_p_isk = vim_strsave(arg);
3557
3558 buf_init_chartab(curbuf, FALSE);
3559 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3560 (size_t)32);
3561 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3562 clear_string_option(&curwin->w_s->b_syn_isk);
3563 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3564 curbuf->b_p_isk = save_isk;
3565 }
3566 }
3567 redraw_win_later(curwin, NOT_VALID);
3568}
3569
3570/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571 * Clear all syntax info for one buffer.
3572 */
3573 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003574syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575{
3576 int i;
3577
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578 block->b_syn_error = FALSE; /* clear previous error */
3579 block->b_syn_ic = FALSE; /* Use case, by default */
3580 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3581 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003582#ifdef FEAT_CONCEAL
3583 block->b_syn_conceal = FALSE;
3584#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003585
3586 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003587 clear_keywtab(&block->b_keywtab);
3588 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589
3590 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003591 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3592 syn_clear_pattern(block, i);
3593 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003594
3595 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003596 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3597 syn_clear_cluster(block, i);
3598 ga_clear(&block->b_syn_clusters);
3599 block->b_spell_cluster_id = 0;
3600 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003601
Bram Moolenaar860cae12010-06-05 23:22:07 +02003602 block->b_syn_sync_flags = 0;
3603 block->b_syn_sync_minlines = 0;
3604 block->b_syn_sync_maxlines = 0;
3605 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003606
Bram Moolenaar473de612013-06-08 18:19:48 +02003607 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003608 block->b_syn_linecont_prog = NULL;
3609 vim_free(block->b_syn_linecont_pat);
3610 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003612 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003614 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615
3616 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003619
3620 /* Reset the counter for ":syn include" */
3621 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003622}
3623
3624/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003625 * Get rid of ownsyntax for window "wp".
3626 */
3627 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003628reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003629{
3630 if (wp->w_s != &wp->w_buffer->b_s)
3631 {
3632 syntax_clear(wp->w_s);
3633 vim_free(wp->w_s);
3634 wp->w_s = &wp->w_buffer->b_s;
3635 }
3636}
3637
3638/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639 * Clear syncing info for one buffer.
3640 */
3641 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003642syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643{
3644 int i;
3645
3646 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003647 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3648 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3649 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650
Bram Moolenaar860cae12010-06-05 23:22:07 +02003651 curwin->w_s->b_syn_sync_flags = 0;
3652 curwin->w_s->b_syn_sync_minlines = 0;
3653 curwin->w_s->b_syn_sync_maxlines = 0;
3654 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003655
Bram Moolenaar473de612013-06-08 18:19:48 +02003656 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003657 curwin->w_s->b_syn_linecont_prog = NULL;
3658 vim_free(curwin->w_s->b_syn_linecont_pat);
3659 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003660 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003662 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663}
3664
3665/*
3666 * Remove one pattern from the buffer's pattern list.
3667 */
3668 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003669syn_remove_pattern(
3670 synblock_T *block,
3671 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672{
3673 synpat_T *spp;
3674
Bram Moolenaar860cae12010-06-05 23:22:07 +02003675 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676#ifdef FEAT_FOLDING
3677 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003678 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003680 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003682 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3683 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684}
3685
3686/*
3687 * Clear and free one syntax pattern. When clearing all, must be called from
3688 * last to first!
3689 */
3690 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003691syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003692{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003693 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003694 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003696 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003698 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3699 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3700 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701 }
3702}
3703
3704/*
3705 * Clear and free one syntax cluster.
3706 */
3707 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003708syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003710 vim_free(SYN_CLSTR(block)[i].scl_name);
3711 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3712 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713}
3714
3715/*
3716 * Handle ":syntax clear" command.
3717 */
3718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003719syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720{
3721 char_u *arg = eap->arg;
3722 char_u *arg_end;
3723 int id;
3724
3725 eap->nextcmd = find_nextcmd(arg);
3726 if (eap->skip)
3727 return;
3728
3729 /*
3730 * We have to disable this within ":syn include @group filename",
3731 * because otherwise @group would get deleted.
3732 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3733 * clear".
3734 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003735 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 return;
3737
3738 if (ends_excmd(*arg))
3739 {
3740 /*
3741 * No argument: Clear all syntax items.
3742 */
3743 if (syncing)
3744 syntax_sync_clear();
3745 else
3746 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003747 syntax_clear(curwin->w_s);
3748 if (curwin->w_s == &curwin->w_buffer->b_s)
3749 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003750 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 }
3752 }
3753 else
3754 {
3755 /*
3756 * Clear the group IDs that are in the argument.
3757 */
3758 while (!ends_excmd(*arg))
3759 {
3760 arg_end = skiptowhite(arg);
3761 if (*arg == '@')
3762 {
3763 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3764 if (id == 0)
3765 {
3766 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3767 break;
3768 }
3769 else
3770 {
3771 /*
3772 * We can't physically delete a cluster without changing
3773 * the IDs of other clusters, so we do the next best thing
3774 * and make it empty.
3775 */
3776 short scl_id = id - SYNID_CLUSTER;
3777
Bram Moolenaar860cae12010-06-05 23:22:07 +02003778 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3779 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 }
3781 }
3782 else
3783 {
3784 id = syn_namen2id(arg, (int)(arg_end - arg));
3785 if (id == 0)
3786 {
3787 EMSG2(_(e_nogroup), arg);
3788 break;
3789 }
3790 else
3791 syn_clear_one(id, syncing);
3792 }
3793 arg = skipwhite(arg_end);
3794 }
3795 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003796 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003797 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003798}
3799
3800/*
3801 * Clear one syntax group for the current buffer.
3802 */
3803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003804syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805{
3806 synpat_T *spp;
3807 int idx;
3808
3809 /* Clear keywords only when not ":syn sync clear group-name" */
3810 if (!syncing)
3811 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003812 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3813 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 }
3815
3816 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003817 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003819 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3821 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003822 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 }
3824}
3825
3826/*
3827 * Handle ":syntax on" command.
3828 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003830syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831{
3832 syn_cmd_onoff(eap, "syntax");
3833}
3834
3835/*
3836 * Handle ":syntax enable" command.
3837 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003839syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840{
3841 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3842 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003843 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844}
3845
3846/*
3847 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003848 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003851syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852{
3853 eap->nextcmd = check_nextcmd(eap->arg);
3854 if (!eap->skip)
3855 {
3856 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3857 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003858 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 }
3860}
3861
3862/*
3863 * Handle ":syntax manual" command.
3864 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003866syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867{
3868 syn_cmd_onoff(eap, "manual");
3869}
3870
3871/*
3872 * Handle ":syntax off" command.
3873 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003875syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876{
3877 syn_cmd_onoff(eap, "nosyntax");
3878}
3879
3880 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003881syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882{
3883 char_u buf[100];
3884
3885 eap->nextcmd = check_nextcmd(eap->arg);
3886 if (!eap->skip)
3887 {
3888 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003889 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003890 do_cmdline_cmd(buf);
3891 }
3892}
3893
3894/*
3895 * Handle ":syntax [list]" command: list current syntax words.
3896 */
3897 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003898syn_cmd_list(
3899 exarg_T *eap,
3900 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901{
3902 char_u *arg = eap->arg;
3903 int id;
3904 char_u *arg_end;
3905
3906 eap->nextcmd = find_nextcmd(arg);
3907 if (eap->skip)
3908 return;
3909
Bram Moolenaar860cae12010-06-05 23:22:07 +02003910 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003912 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913 return;
3914 }
3915
3916 if (syncing)
3917 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003918 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919 {
3920 MSG_PUTS(_("syncing on C-style comments"));
3921 syn_lines_msg();
3922 syn_match_msg();
3923 return;
3924 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003925 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 MSG_PUTS(_("no syncing"));
3929 else
3930 {
3931 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003932 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 MSG_PUTS(_(" lines before top line"));
3934 syn_match_msg();
3935 }
3936 return;
3937 }
3938 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003939 if (curwin->w_s->b_syn_sync_minlines > 0
3940 || curwin->w_s->b_syn_sync_maxlines > 0
3941 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942 {
3943 MSG_PUTS(_("\nsyncing on items"));
3944 syn_lines_msg();
3945 syn_match_msg();
3946 }
3947 }
3948 else
3949 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3950 if (ends_excmd(*arg))
3951 {
3952 /*
3953 * No argument: List all group IDs and all syntax clusters.
3954 */
3955 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3956 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003957 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958 syn_list_cluster(id);
3959 }
3960 else
3961 {
3962 /*
3963 * List the group IDs and syntax clusters that are in the argument.
3964 */
3965 while (!ends_excmd(*arg) && !got_int)
3966 {
3967 arg_end = skiptowhite(arg);
3968 if (*arg == '@')
3969 {
3970 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3971 if (id == 0)
3972 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3973 else
3974 syn_list_cluster(id - SYNID_CLUSTER);
3975 }
3976 else
3977 {
3978 id = syn_namen2id(arg, (int)(arg_end - arg));
3979 if (id == 0)
3980 EMSG2(_(e_nogroup), arg);
3981 else
3982 syn_list_one(id, syncing, TRUE);
3983 }
3984 arg = skipwhite(arg_end);
3985 }
3986 }
3987 eap->nextcmd = check_nextcmd(arg);
3988}
3989
3990 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003991syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003993 if (curwin->w_s->b_syn_sync_maxlines > 0
3994 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003995 {
3996 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003997 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003998 {
3999 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004000 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4001 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 MSG_PUTS(", ");
4003 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004004 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 {
4006 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004007 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004008 }
4009 MSG_PUTS(_(" lines before top line"));
4010 }
4011}
4012
4013 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004014syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004015{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004016 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017 {
4018 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004019 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004020 MSG_PUTS(_(" line breaks"));
4021 }
4022}
4023
4024static int last_matchgroup;
4025
4026struct name_list
4027{
4028 int flag;
4029 char *name;
4030};
4031
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004032static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033
4034/*
4035 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4036 */
4037 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004038syn_list_one(
4039 int id,
4040 int syncing, /* when TRUE: list syncing items */
4041 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042{
4043 int attr;
4044 int idx;
4045 int did_header = FALSE;
4046 synpat_T *spp;
4047 static struct name_list namelist1[] =
4048 {
4049 {HL_DISPLAY, "display"},
4050 {HL_CONTAINED, "contained"},
4051 {HL_ONELINE, "oneline"},
4052 {HL_KEEPEND, "keepend"},
4053 {HL_EXTEND, "extend"},
4054 {HL_EXCLUDENL, "excludenl"},
4055 {HL_TRANSP, "transparent"},
4056 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004057#ifdef FEAT_CONCEAL
4058 {HL_CONCEAL, "conceal"},
4059 {HL_CONCEALENDS, "concealends"},
4060#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 {0, NULL}
4062 };
4063 static struct name_list namelist2[] =
4064 {
4065 {HL_SKIPWHITE, "skipwhite"},
4066 {HL_SKIPNL, "skipnl"},
4067 {HL_SKIPEMPTY, "skipempty"},
4068 {0, NULL}
4069 };
4070
4071 attr = hl_attr(HLF_D); /* highlight like directories */
4072
4073 /* list the keywords for "id" */
4074 if (!syncing)
4075 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004076 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4077 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 did_header, attr);
4079 }
4080
4081 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004082 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004084 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4086 continue;
4087
4088 (void)syn_list_header(did_header, 999, id);
4089 did_header = TRUE;
4090 last_matchgroup = 0;
4091 if (spp->sp_type == SPTYPE_MATCH)
4092 {
4093 put_pattern("match", ' ', spp, attr);
4094 msg_putchar(' ');
4095 }
4096 else if (spp->sp_type == SPTYPE_START)
4097 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004098 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4099 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4100 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4101 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4102 while (idx < curwin->w_s->b_syn_patterns.ga_len
4103 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4104 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105 --idx;
4106 msg_putchar(' ');
4107 }
4108 syn_list_flags(namelist1, spp->sp_flags, attr);
4109
4110 if (spp->sp_cont_list != NULL)
4111 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4112
4113 if (spp->sp_syn.cont_in_list != NULL)
4114 put_id_list((char_u *)"containedin",
4115 spp->sp_syn.cont_in_list, attr);
4116
4117 if (spp->sp_next_list != NULL)
4118 {
4119 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4120 syn_list_flags(namelist2, spp->sp_flags, attr);
4121 }
4122 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4123 {
4124 if (spp->sp_flags & HL_SYNC_HERE)
4125 msg_puts_attr((char_u *)"grouphere", attr);
4126 else
4127 msg_puts_attr((char_u *)"groupthere", attr);
4128 msg_putchar(' ');
4129 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004130 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4132 else
4133 MSG_PUTS("NONE");
4134 msg_putchar(' ');
4135 }
4136 }
4137
4138 /* list the link, if there is one */
4139 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4140 {
4141 (void)syn_list_header(did_header, 999, id);
4142 msg_puts_attr((char_u *)"links to", attr);
4143 msg_putchar(' ');
4144 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4145 }
4146}
4147
4148 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004149syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150{
4151 int i;
4152
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004153 for (i = 0; nlist[i].flag != 0; ++i)
4154 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004156 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 msg_putchar(' ');
4158 }
4159}
4160
4161/*
4162 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4163 */
4164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004165syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166{
4167 int endcol = 15;
4168
4169 /* slight hack: roughly duplicate the guts of syn_list_header() */
4170 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004171 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172
4173 if (msg_col >= endcol) /* output at least one space */
4174 endcol = msg_col + 1;
4175 if (Columns <= endcol) /* avoid hang for tiny window */
4176 endcol = Columns - 1;
4177
4178 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004179 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004180 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004181 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 hl_attr(HLF_D));
4183 }
4184 else
4185 {
4186 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4187 msg_puts((char_u *)"=NONE");
4188 }
4189}
4190
4191 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004192put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193{
4194 short *p;
4195
4196 msg_puts_attr(name, attr);
4197 msg_putchar('=');
4198 for (p = list; *p; ++p)
4199 {
4200 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4201 {
4202 if (p[1])
4203 MSG_PUTS("ALLBUT");
4204 else
4205 MSG_PUTS("ALL");
4206 }
4207 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4208 {
4209 MSG_PUTS("TOP");
4210 }
4211 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4212 {
4213 MSG_PUTS("CONTAINED");
4214 }
4215 else if (*p >= SYNID_CLUSTER)
4216 {
4217 short scl_id = *p - SYNID_CLUSTER;
4218
4219 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004220 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 }
4222 else
4223 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4224 if (p[1])
4225 msg_putchar(',');
4226 }
4227 msg_putchar(' ');
4228}
4229
4230 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004231put_pattern(
4232 char *s,
4233 int c,
4234 synpat_T *spp,
4235 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236{
4237 long n;
4238 int mask;
4239 int first;
4240 static char *sepchars = "/+=-#@\"|'^&";
4241 int i;
4242
4243 /* May have to write "matchgroup=group" */
4244 if (last_matchgroup != spp->sp_syn_match_id)
4245 {
4246 last_matchgroup = spp->sp_syn_match_id;
4247 msg_puts_attr((char_u *)"matchgroup", attr);
4248 msg_putchar('=');
4249 if (last_matchgroup == 0)
4250 msg_outtrans((char_u *)"NONE");
4251 else
4252 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4253 msg_putchar(' ');
4254 }
4255
4256 /* output the name of the pattern and an '=' or ' ' */
4257 msg_puts_attr((char_u *)s, attr);
4258 msg_putchar(c);
4259
4260 /* output the pattern, in between a char that is not in the pattern */
4261 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4262 if (sepchars[++i] == NUL)
4263 {
4264 i = 0; /* no good char found, just use the first one */
4265 break;
4266 }
4267 msg_putchar(sepchars[i]);
4268 msg_outtrans(spp->sp_pattern);
4269 msg_putchar(sepchars[i]);
4270
4271 /* output any pattern options */
4272 first = TRUE;
4273 for (i = 0; i < SPO_COUNT; ++i)
4274 {
4275 mask = (1 << i);
4276 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4277 {
4278 if (!first)
4279 msg_putchar(','); /* separate with commas */
4280 msg_puts((char_u *)spo_name_tab[i]);
4281 n = spp->sp_offsets[i];
4282 if (i != SPO_LC_OFF)
4283 {
4284 if (spp->sp_off_flags & mask)
4285 msg_putchar('s');
4286 else
4287 msg_putchar('e');
4288 if (n > 0)
4289 msg_putchar('+');
4290 }
4291 if (n || i == SPO_LC_OFF)
4292 msg_outnum(n);
4293 first = FALSE;
4294 }
4295 }
4296 msg_putchar(' ');
4297}
4298
4299/*
4300 * List or clear the keywords for one syntax group.
4301 * Return TRUE if the header has been printed.
4302 */
4303 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004304syn_list_keywords(
4305 int id,
4306 hashtab_T *ht,
4307 int did_header, /* header has already been printed */
4308 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311 hashitem_T *hi;
4312 keyentry_T *kp;
4313 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 int prev_contained = 0;
4315 short *prev_next_list = NULL;
4316 short *prev_cont_in_list = NULL;
4317 int prev_skipnl = 0;
4318 int prev_skipwhite = 0;
4319 int prev_skipempty = 0;
4320
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321 /*
4322 * Unfortunately, this list of keywords is not sorted on alphabet but on
4323 * hash value...
4324 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004325 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 --todo;
4331 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 if (prev_contained != (kp->flags & HL_CONTAINED)
4336 || prev_skipnl != (kp->flags & HL_SKIPNL)
4337 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4338 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4339 || prev_cont_in_list != kp->k_syn.cont_in_list
4340 || prev_next_list != kp->next_list)
4341 outlen = 9999;
4342 else
4343 outlen = (int)STRLEN(kp->keyword);
4344 /* output "contained" and "nextgroup" on each line */
4345 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004347 prev_contained = 0;
4348 prev_next_list = NULL;
4349 prev_cont_in_list = NULL;
4350 prev_skipnl = 0;
4351 prev_skipwhite = 0;
4352 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 did_header = TRUE;
4355 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 put_id_list((char_u *)"containedin",
4364 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 if (kp->next_list != prev_next_list)
4369 {
4370 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4371 msg_putchar(' ');
4372 prev_next_list = kp->next_list;
4373 if (kp->flags & HL_SKIPNL)
4374 {
4375 msg_puts_attr((char_u *)"skipnl", attr);
4376 msg_putchar(' ');
4377 prev_skipnl = (kp->flags & HL_SKIPNL);
4378 }
4379 if (kp->flags & HL_SKIPWHITE)
4380 {
4381 msg_puts_attr((char_u *)"skipwhite", attr);
4382 msg_putchar(' ');
4383 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4384 }
4385 if (kp->flags & HL_SKIPEMPTY)
4386 {
4387 msg_puts_attr((char_u *)"skipempty", attr);
4388 msg_putchar(' ');
4389 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4390 }
4391 }
4392 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 }
4395 }
4396 }
4397
4398 return did_header;
4399}
4400
4401 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004402syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 hashitem_T *hi;
4405 keyentry_T *kp;
4406 keyentry_T *kp_prev;
4407 keyentry_T *kp_next;
4408 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004409
Bram Moolenaardad6b692005-01-25 22:14:34 +00004410 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004411 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004412 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004414 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004416 --todo;
4417 kp_prev = NULL;
4418 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004420 if (kp->k_syn.id == id)
4421 {
4422 kp_next = kp->ke_next;
4423 if (kp_prev == NULL)
4424 {
4425 if (kp_next == NULL)
4426 hash_remove(ht, hi);
4427 else
4428 hi->hi_key = KE2HIKEY(kp_next);
4429 }
4430 else
4431 kp_prev->ke_next = kp_next;
4432 vim_free(kp->next_list);
4433 vim_free(kp->k_syn.cont_in_list);
4434 vim_free(kp);
4435 kp = kp_next;
4436 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004438 {
4439 kp_prev = kp;
4440 kp = kp->ke_next;
4441 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 }
4443 }
4444 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446}
4447
4448/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 */
4451 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004452clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004454 hashitem_T *hi;
4455 int todo;
4456 keyentry_T *kp;
4457 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004459 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004460 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004462 if (!HASHITEM_EMPTY(hi))
4463 {
4464 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004465 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004467 kp_next = kp->ke_next;
4468 vim_free(kp->next_list);
4469 vim_free(kp->k_syn.cont_in_list);
4470 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004472 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004474 hash_clear(ht);
4475 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476}
4477
4478/*
4479 * Add a keyword to the list of keywords.
4480 */
4481 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004482add_keyword(
4483 char_u *name, /* name of keyword */
4484 int id, /* group ID for this keyword */
4485 int flags, /* flags for this keyword */
4486 short *cont_in_list, /* containedin for this keyword */
4487 short *next_list, /* nextgroup for this keyword */
4488 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004490 keyentry_T *kp;
4491 hashtab_T *ht;
4492 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004493 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004495 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496
Bram Moolenaar860cae12010-06-05 23:22:07 +02004497 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004498 name_ic = str_foldcase(name, (int)STRLEN(name),
4499 name_folded, MAXKEYWLEN + 1);
4500 else
4501 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004502 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4503 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004506 kp->k_syn.id = id;
4507 kp->k_syn.inc_tag = current_syn_inc_tag;
4508 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004509 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004510 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004512 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004513 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514
Bram Moolenaar860cae12010-06-05 23:22:07 +02004515 if (curwin->w_s->b_syn_ic)
4516 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004518 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519
Bram Moolenaardad6b692005-01-25 22:14:34 +00004520 hash = hash_hash(kp->keyword);
4521 hi = hash_lookup(ht, kp->keyword, hash);
4522 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004524 /* new keyword, add to hashtable */
4525 kp->ke_next = NULL;
4526 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004528 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004530 /* keyword already exists, prepend to list */
4531 kp->ke_next = HI2KE(hi);
4532 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004533 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534}
4535
4536/*
4537 * Get the start and end of the group name argument.
4538 * Return a pointer to the first argument.
4539 * Return NULL if the end of the command was found instead of further args.
4540 */
4541 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004542get_group_name(
4543 char_u *arg, /* start of the argument */
4544 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545{
4546 char_u *rest;
4547
4548 *name_end = skiptowhite(arg);
4549 rest = skipwhite(*name_end);
4550
4551 /*
4552 * Check if there are enough arguments. The first argument may be a
4553 * pattern, where '|' is allowed, so only check for NUL.
4554 */
4555 if (ends_excmd(*arg) || *rest == NUL)
4556 return NULL;
4557 return rest;
4558}
4559
4560/*
4561 * Check for syntax command option arguments.
4562 * This can be called at any place in the list of arguments, and just picks
4563 * out the arguments that are known. Can be called several times in a row to
4564 * collect all options in between other arguments.
4565 * Return a pointer to the next argument (which isn't an option).
4566 * Return NULL for any error;
4567 */
4568 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004569get_syn_options(
4570 char_u *arg, /* next argument to be checked */
4571 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004572 int *conceal_char UNUSED,
4573 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 char_u *gname_start, *gname;
4576 int syn_id;
4577 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004578 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 int i;
4580 int fidx;
4581 static struct flag
4582 {
4583 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004584 int argtype;
4585 int flags;
4586 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4587 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4588 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4589 {"eExXtTeEnNdD", 0, HL_EXTEND},
4590 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4591 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4592 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4593 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4594 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4595 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4596 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4597 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4598 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004599 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4600 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4601 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004602 {"cCoOnNtTaAiInNsS", 1, 0},
4603 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4604 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004606 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607
4608 if (arg == NULL) /* already detected error */
4609 return NULL;
4610
Bram Moolenaar860cae12010-06-05 23:22:07 +02004611#ifdef FEAT_CONCEAL
4612 if (curwin->w_s->b_syn_conceal)
4613 opt->flags |= HL_CONCEAL;
4614#endif
4615
Bram Moolenaar071d4272004-06-13 20:20:40 +00004616 for (;;)
4617 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004618 /*
4619 * This is used very often when a large number of keywords is defined.
4620 * Need to skip quickly when no option name is found.
4621 * Also avoid tolower(), it's slow.
4622 */
4623 if (strchr(first_letters, *arg) == NULL)
4624 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625
4626 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4627 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 p = flagtab[fidx].name;
4629 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4630 if (arg[len] != p[i] && arg[len] != p[i + 1])
4631 break;
4632 if (p[i] == NUL && (vim_iswhite(arg[len])
4633 || (flagtab[fidx].argtype > 0
4634 ? arg[len] == '='
4635 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004637 if (opt->keyword
4638 && (flagtab[fidx].flags == HL_DISPLAY
4639 || flagtab[fidx].flags == HL_FOLD
4640 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641 /* treat "display", "fold" and "extend" as a keyword */
4642 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 break;
4644 }
4645 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004646 if (fidx < 0) /* no match found */
4647 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004649 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004651 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652 {
4653 EMSG(_("E395: contains argument not accepted here"));
4654 return NULL;
4655 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004656 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657 return NULL;
4658 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004659 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004660 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004661 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 return NULL;
4663 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004664 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004666 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 return NULL;
4668 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004669 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4670 {
4671#ifdef FEAT_MBYTE
4672 /* cchar=? */
4673 if (has_mbyte)
4674 {
4675# ifdef FEAT_CONCEAL
4676 *conceal_char = mb_ptr2char(arg + 6);
4677# endif
4678 arg += mb_ptr2len(arg + 6) - 1;
4679 }
4680 else
4681#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004682 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004683#ifdef FEAT_CONCEAL
4684 *conceal_char = arg[6];
4685#else
4686 ;
4687#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004688 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004689#ifdef FEAT_CONCEAL
4690 if (!vim_isprintc_strict(*conceal_char))
4691 {
4692 EMSG(_("E844: invalid cchar value"));
4693 return NULL;
4694 }
4695#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004696 arg = skipwhite(arg + 7);
4697 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004699 {
4700 opt->flags |= flagtab[fidx].flags;
4701 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004703 if (flagtab[fidx].flags == HL_SYNC_HERE
4704 || flagtab[fidx].flags == HL_SYNC_THERE)
4705 {
4706 if (opt->sync_idx == NULL)
4707 {
4708 EMSG(_("E393: group[t]here not accepted here"));
4709 return NULL;
4710 }
4711 gname_start = arg;
4712 arg = skiptowhite(arg);
4713 if (gname_start == arg)
4714 return NULL;
4715 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4716 if (gname == NULL)
4717 return NULL;
4718 if (STRCMP(gname, "NONE") == 0)
4719 *opt->sync_idx = NONE_IDX;
4720 else
4721 {
4722 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004723 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4724 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4725 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004726 {
4727 *opt->sync_idx = i;
4728 break;
4729 }
4730 if (i < 0)
4731 {
4732 EMSG2(_("E394: Didn't find region item for %s"), gname);
4733 vim_free(gname);
4734 return NULL;
4735 }
4736 }
4737
4738 vim_free(gname);
4739 arg = skipwhite(arg);
4740 }
4741#ifdef FEAT_FOLDING
4742 else if (flagtab[fidx].flags == HL_FOLD
4743 && foldmethodIsSyntax(curwin))
4744 /* Need to update folds later. */
4745 foldUpdateAll(curwin);
4746#endif
4747 }
4748 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004749
4750 return arg;
4751}
4752
4753/*
4754 * Adjustments to syntax item when declared in a ":syn include"'d file.
4755 * Set the contained flag, and if the item is not already contained, add it
4756 * to the specified top-level group, if any.
4757 */
4758 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004759syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004760{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004761 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 return;
4763 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004764 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765 {
4766 /* We have to alloc this, because syn_combine_list() will free it. */
4767 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004768 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004769
4770 if (grp_list != NULL)
4771 {
4772 grp_list[0] = id;
4773 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004774 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775 CLUSTER_ADD);
4776 }
4777 }
4778}
4779
4780/*
4781 * Handle ":syntax include [@{group-name}] filename" command.
4782 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004783 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004784syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785{
4786 char_u *arg = eap->arg;
4787 int sgl_id = 1;
4788 char_u *group_name_end;
4789 char_u *rest;
4790 char_u *errormsg = NULL;
4791 int prev_toplvl_grp;
4792 int prev_syn_inc_tag;
4793 int source = FALSE;
4794
4795 eap->nextcmd = find_nextcmd(arg);
4796 if (eap->skip)
4797 return;
4798
4799 if (arg[0] == '@')
4800 {
4801 ++arg;
4802 rest = get_group_name(arg, &group_name_end);
4803 if (rest == NULL)
4804 {
4805 EMSG((char_u *)_("E397: Filename required"));
4806 return;
4807 }
4808 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004809 if (sgl_id == 0)
4810 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811 /* separate_nextcmd() and expand_filename() depend on this */
4812 eap->arg = rest;
4813 }
4814
4815 /*
4816 * Everything that's left, up to the next command, should be the
4817 * filename to include.
4818 */
4819 eap->argt |= (XFILE | NOSPC);
4820 separate_nextcmd(eap);
4821 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4822 {
4823 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4824 * file. Need to expand the file name first. In other cases
4825 * ":runtime!" is used. */
4826 source = TRUE;
4827 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4828 {
4829 if (errormsg != NULL)
4830 EMSG(errormsg);
4831 return;
4832 }
4833 }
4834
4835 /*
4836 * Save and restore the existing top-level grouplist id and ":syn
4837 * include" tag around the actual inclusion.
4838 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004839 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4840 {
4841 EMSG((char_u *)_("E847: Too many syntax includes"));
4842 return;
4843 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004844 prev_syn_inc_tag = current_syn_inc_tag;
4845 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004846 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4847 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004848 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004849 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004851 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004852 current_syn_inc_tag = prev_syn_inc_tag;
4853}
4854
4855/*
4856 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4857 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004858 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004859syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860{
4861 char_u *arg = eap->arg;
4862 char_u *group_name_end;
4863 int syn_id;
4864 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004865 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004866 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004867 char_u *kw;
4868 syn_opt_arg_T syn_opt_arg;
4869 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004870 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871
4872 rest = get_group_name(arg, &group_name_end);
4873
4874 if (rest != NULL)
4875 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004876 if (eap->skip)
4877 syn_id = -1;
4878 else
4879 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004880 if (syn_id != 0)
4881 /* allocate a buffer, for removing backslashes in the keyword */
4882 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 if (keyword_copy != NULL)
4884 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004885 syn_opt_arg.flags = 0;
4886 syn_opt_arg.keyword = TRUE;
4887 syn_opt_arg.sync_idx = NULL;
4888 syn_opt_arg.has_cont_list = FALSE;
4889 syn_opt_arg.cont_in_list = NULL;
4890 syn_opt_arg.next_list = NULL;
4891
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 /*
4893 * The options given apply to ALL keywords, so all options must be
4894 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004895 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 cnt = 0;
4898 p = keyword_copy;
4899 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004901 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4902 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004903 if (rest == NULL || ends_excmd(*rest))
4904 break;
4905 /* Copy the keyword, removing backslashes, and add a NUL. */
4906 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004908 if (*rest == '\\' && rest[1] != NUL)
4909 ++rest;
4910 *p++ = *rest++;
4911 }
4912 *p++ = NUL;
4913 ++cnt;
4914 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004916 if (!eap->skip)
4917 {
4918 /* Adjust flags for use of ":syn include". */
4919 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4920
4921 /*
4922 * 2: Add an entry for each keyword.
4923 */
4924 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4925 {
4926 for (p = vim_strchr(kw, '['); ; )
4927 {
4928 if (p != NULL)
4929 *p = NUL;
4930 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004931 syn_opt_arg.cont_in_list,
4932 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004933 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004934 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004935 if (p[1] == NUL)
4936 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004937 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004938 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004939 }
4940 if (p[1] == ']')
4941 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004942 if (p[2] != NUL)
4943 {
4944 EMSG3(_("E890: trailing char after ']': %s]%s"),
4945 kw, &p[2]);
4946 goto error;
4947 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004948 kw = p + 1; /* skip over the "]" */
4949 break;
4950 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004951#ifdef FEAT_MBYTE
4952 if (has_mbyte)
4953 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004954 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004955
4956 mch_memmove(p, p + 1, l);
4957 p += l;
4958 }
4959 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004961 {
4962 p[0] = p[1];
4963 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964 }
4965 }
4966 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004968error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004970 vim_free(syn_opt_arg.cont_in_list);
4971 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972 }
4973 }
4974
4975 if (rest != NULL)
4976 eap->nextcmd = check_nextcmd(rest);
4977 else
4978 EMSG2(_(e_invarg2), arg);
4979
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004980 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004981 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004982}
4983
4984/*
4985 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4986 *
4987 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4988 */
4989 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004990syn_cmd_match(
4991 exarg_T *eap,
4992 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993{
4994 char_u *arg = eap->arg;
4995 char_u *group_name_end;
4996 char_u *rest;
4997 synpat_T item; /* the item found in the line */
4998 int syn_id;
4999 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005000 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005002 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003
5004 /* Isolate the group name, check for validity */
5005 rest = get_group_name(arg, &group_name_end);
5006
5007 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005008 syn_opt_arg.flags = 0;
5009 syn_opt_arg.keyword = FALSE;
5010 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5011 syn_opt_arg.has_cont_list = TRUE;
5012 syn_opt_arg.cont_list = NULL;
5013 syn_opt_arg.cont_in_list = NULL;
5014 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005015 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016
5017 /* get the pattern. */
5018 init_syn_patterns();
5019 vim_memset(&item, 0, sizeof(item));
5020 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005021 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5022 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005023
5024 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005025 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026
5027 if (rest != NULL) /* all arguments are valid */
5028 {
5029 /*
5030 * Check for trailing command and illegal trailing arguments.
5031 */
5032 eap->nextcmd = check_nextcmd(rest);
5033 if (!ends_excmd(*rest) || eap->skip)
5034 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005035 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036 && (syn_id = syn_check_group(arg,
5037 (int)(group_name_end - arg))) != 0)
5038 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005039 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040 /*
5041 * Store the pattern in the syn_items list
5042 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005043 idx = curwin->w_s->b_syn_patterns.ga_len;
5044 SYN_ITEMS(curwin->w_s)[idx] = item;
5045 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5046 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5047 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5048 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5049 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5050 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5051 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5052 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005053 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005054#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005055 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005056#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005057 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005058 curwin->w_s->b_syn_containedin = TRUE;
5059 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5060 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005061
5062 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005063 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005064 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005065#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005067 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068#endif
5069
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005070 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005071 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005072 return; /* don't free the progs and patterns now */
5073 }
5074 }
5075
5076 /*
5077 * Something failed, free the allocated memory.
5078 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005079 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005080 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005081 vim_free(syn_opt_arg.cont_list);
5082 vim_free(syn_opt_arg.cont_in_list);
5083 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005084
5085 if (rest == NULL)
5086 EMSG2(_(e_invarg2), arg);
5087}
5088
5089/*
5090 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5091 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5092 */
5093 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005094syn_cmd_region(
5095 exarg_T *eap,
5096 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005097{
5098 char_u *arg = eap->arg;
5099 char_u *group_name_end;
5100 char_u *rest; /* next arg, NULL on error */
5101 char_u *key_end;
5102 char_u *key = NULL;
5103 char_u *p;
5104 int item;
5105#define ITEM_START 0
5106#define ITEM_SKIP 1
5107#define ITEM_END 2
5108#define ITEM_MATCHGROUP 3
5109 struct pat_ptr
5110 {
5111 synpat_T *pp_synp; /* pointer to syn_pattern */
5112 int pp_matchgroup_id; /* matchgroup ID */
5113 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5114 } *(pat_ptrs[3]);
5115 /* patterns found in the line */
5116 struct pat_ptr *ppp;
5117 struct pat_ptr *ppp_next;
5118 int pat_count = 0; /* nr of syn_patterns found */
5119 int syn_id;
5120 int matchgroup_id = 0;
5121 int not_enough = FALSE; /* not enough arguments */
5122 int illegal = FALSE; /* illegal arguments */
5123 int success = FALSE;
5124 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005125 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005126 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005127
5128 /* Isolate the group name, check for validity */
5129 rest = get_group_name(arg, &group_name_end);
5130
5131 pat_ptrs[0] = NULL;
5132 pat_ptrs[1] = NULL;
5133 pat_ptrs[2] = NULL;
5134
5135 init_syn_patterns();
5136
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005137 syn_opt_arg.flags = 0;
5138 syn_opt_arg.keyword = FALSE;
5139 syn_opt_arg.sync_idx = NULL;
5140 syn_opt_arg.has_cont_list = TRUE;
5141 syn_opt_arg.cont_list = NULL;
5142 syn_opt_arg.cont_in_list = NULL;
5143 syn_opt_arg.next_list = NULL;
5144
Bram Moolenaar071d4272004-06-13 20:20:40 +00005145 /*
5146 * get the options, patterns and matchgroup.
5147 */
5148 while (rest != NULL && !ends_excmd(*rest))
5149 {
5150 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005151 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 if (rest == NULL || ends_excmd(*rest))
5153 break;
5154
5155 /* must be a pattern or matchgroup then */
5156 key_end = rest;
5157 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5158 ++key_end;
5159 vim_free(key);
5160 key = vim_strnsave_up(rest, (int)(key_end - rest));
5161 if (key == NULL) /* out of memory */
5162 {
5163 rest = NULL;
5164 break;
5165 }
5166 if (STRCMP(key, "MATCHGROUP") == 0)
5167 item = ITEM_MATCHGROUP;
5168 else if (STRCMP(key, "START") == 0)
5169 item = ITEM_START;
5170 else if (STRCMP(key, "END") == 0)
5171 item = ITEM_END;
5172 else if (STRCMP(key, "SKIP") == 0)
5173 {
5174 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5175 {
5176 illegal = TRUE;
5177 break;
5178 }
5179 item = ITEM_SKIP;
5180 }
5181 else
5182 break;
5183 rest = skipwhite(key_end);
5184 if (*rest != '=')
5185 {
5186 rest = NULL;
5187 EMSG2(_("E398: Missing '=': %s"), arg);
5188 break;
5189 }
5190 rest = skipwhite(rest + 1);
5191 if (*rest == NUL)
5192 {
5193 not_enough = TRUE;
5194 break;
5195 }
5196
5197 if (item == ITEM_MATCHGROUP)
5198 {
5199 p = skiptowhite(rest);
5200 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5201 matchgroup_id = 0;
5202 else
5203 {
5204 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5205 if (matchgroup_id == 0)
5206 {
5207 illegal = TRUE;
5208 break;
5209 }
5210 }
5211 rest = skipwhite(p);
5212 }
5213 else
5214 {
5215 /*
5216 * Allocate room for a syn_pattern, and link it in the list of
5217 * syn_patterns for this item, at the start (because the list is
5218 * used from end to start).
5219 */
5220 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5221 if (ppp == NULL)
5222 {
5223 rest = NULL;
5224 break;
5225 }
5226 ppp->pp_next = pat_ptrs[item];
5227 pat_ptrs[item] = ppp;
5228 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5229 if (ppp->pp_synp == NULL)
5230 {
5231 rest = NULL;
5232 break;
5233 }
5234
5235 /*
5236 * Get the syntax pattern and the following offset(s).
5237 */
5238 /* Enable the appropriate \z specials. */
5239 if (item == ITEM_START)
5240 reg_do_extmatch = REX_SET;
5241 else if (item == ITEM_SKIP || item == ITEM_END)
5242 reg_do_extmatch = REX_USE;
5243 rest = get_syn_pattern(rest, ppp->pp_synp);
5244 reg_do_extmatch = 0;
5245 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005246 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5248 ppp->pp_matchgroup_id = matchgroup_id;
5249 ++pat_count;
5250 }
5251 }
5252 vim_free(key);
5253 if (illegal || not_enough)
5254 rest = NULL;
5255
5256 /*
5257 * Must have a "start" and "end" pattern.
5258 */
5259 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5260 pat_ptrs[ITEM_END] == NULL))
5261 {
5262 not_enough = TRUE;
5263 rest = NULL;
5264 }
5265
5266 if (rest != NULL)
5267 {
5268 /*
5269 * Check for trailing garbage or command.
5270 * If OK, add the item.
5271 */
5272 eap->nextcmd = check_nextcmd(rest);
5273 if (!ends_excmd(*rest) || eap->skip)
5274 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005275 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005276 && (syn_id = syn_check_group(arg,
5277 (int)(group_name_end - arg))) != 0)
5278 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005279 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 /*
5281 * Store the start/skip/end in the syn_items list
5282 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005283 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005284 for (item = ITEM_START; item <= ITEM_END; ++item)
5285 {
5286 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5287 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005288 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5289 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5290 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005291 (item == ITEM_START) ? SPTYPE_START :
5292 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005293 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5294 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005295 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5296 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005297 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005298 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005299#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005300 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005301#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 if (item == ITEM_START)
5303 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005304 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005305 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005306 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005307 syn_opt_arg.cont_in_list;
5308 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 curwin->w_s->b_syn_containedin = TRUE;
5310 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005311 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005312 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005313 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 ++idx;
5315#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005316 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005317 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318#endif
5319 }
5320 }
5321
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005322 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005323 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005324 success = TRUE; /* don't free the progs and patterns now */
5325 }
5326 }
5327
5328 /*
5329 * Free the allocated memory.
5330 */
5331 for (item = ITEM_START; item <= ITEM_END; ++item)
5332 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5333 {
5334 if (!success)
5335 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005336 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005337 vim_free(ppp->pp_synp->sp_pattern);
5338 }
5339 vim_free(ppp->pp_synp);
5340 ppp_next = ppp->pp_next;
5341 vim_free(ppp);
5342 }
5343
5344 if (!success)
5345 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005346 vim_free(syn_opt_arg.cont_list);
5347 vim_free(syn_opt_arg.cont_in_list);
5348 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005349 if (not_enough)
5350 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5351 else if (illegal || rest == NULL)
5352 EMSG2(_(e_invarg2), arg);
5353 }
5354}
5355
5356/*
5357 * A simple syntax group ID comparison function suitable for use in qsort()
5358 */
5359 static int
5360#ifdef __BORLANDC__
5361_RTLENTRYF
5362#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005363syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005364{
5365 const short *s1 = v1;
5366 const short *s2 = v2;
5367
5368 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5369}
5370
5371/*
5372 * Combines lists of syntax clusters.
5373 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5374 */
5375 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005376syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005377{
5378 int count1 = 0;
5379 int count2 = 0;
5380 short *g1;
5381 short *g2;
5382 short *clstr = NULL;
5383 int count;
5384 int round;
5385
5386 /*
5387 * Handle degenerate cases.
5388 */
5389 if (*clstr2 == NULL)
5390 return;
5391 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5392 {
5393 if (list_op == CLUSTER_REPLACE)
5394 vim_free(*clstr1);
5395 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5396 *clstr1 = *clstr2;
5397 else
5398 vim_free(*clstr2);
5399 return;
5400 }
5401
5402 for (g1 = *clstr1; *g1; g1++)
5403 ++count1;
5404 for (g2 = *clstr2; *g2; g2++)
5405 ++count2;
5406
5407 /*
5408 * For speed purposes, sort both lists.
5409 */
5410 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5411 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5412
5413 /*
5414 * We proceed in two passes; in round 1, we count the elements to place
5415 * in the new list, and in round 2, we allocate and populate the new
5416 * list. For speed, we use a mergesort-like method, adding the smaller
5417 * of the current elements in each list to the new list.
5418 */
5419 for (round = 1; round <= 2; round++)
5420 {
5421 g1 = *clstr1;
5422 g2 = *clstr2;
5423 count = 0;
5424
5425 /*
5426 * First, loop through the lists until one of them is empty.
5427 */
5428 while (*g1 && *g2)
5429 {
5430 /*
5431 * We always want to add from the first list.
5432 */
5433 if (*g1 < *g2)
5434 {
5435 if (round == 2)
5436 clstr[count] = *g1;
5437 count++;
5438 g1++;
5439 continue;
5440 }
5441 /*
5442 * We only want to add from the second list if we're adding the
5443 * lists.
5444 */
5445 if (list_op == CLUSTER_ADD)
5446 {
5447 if (round == 2)
5448 clstr[count] = *g2;
5449 count++;
5450 }
5451 if (*g1 == *g2)
5452 g1++;
5453 g2++;
5454 }
5455
5456 /*
5457 * Now add the leftovers from whichever list didn't get finished
5458 * first. As before, we only want to add from the second list if
5459 * we're adding the lists.
5460 */
5461 for (; *g1; g1++, count++)
5462 if (round == 2)
5463 clstr[count] = *g1;
5464 if (list_op == CLUSTER_ADD)
5465 for (; *g2; g2++, count++)
5466 if (round == 2)
5467 clstr[count] = *g2;
5468
5469 if (round == 1)
5470 {
5471 /*
5472 * If the group ended up empty, we don't need to allocate any
5473 * space for it.
5474 */
5475 if (count == 0)
5476 {
5477 clstr = NULL;
5478 break;
5479 }
5480 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5481 if (clstr == NULL)
5482 break;
5483 clstr[count] = 0;
5484 }
5485 }
5486
5487 /*
5488 * Finally, put the new list in place.
5489 */
5490 vim_free(*clstr1);
5491 vim_free(*clstr2);
5492 *clstr1 = clstr;
5493}
5494
5495/*
5496 * Lookup a syntax cluster name and return it's ID.
5497 * If it is not found, 0 is returned.
5498 */
5499 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005500syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005501{
5502 int i;
5503 char_u *name_u;
5504
5505 /* Avoid using stricmp() too much, it's slow on some systems */
5506 name_u = vim_strsave_up(name);
5507 if (name_u == NULL)
5508 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005509 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5510 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5511 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 break;
5513 vim_free(name_u);
5514 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5515}
5516
5517/*
5518 * Like syn_scl_name2id(), but take a pointer + length argument.
5519 */
5520 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005521syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522{
5523 char_u *name;
5524 int id = 0;
5525
5526 name = vim_strnsave(linep, len);
5527 if (name != NULL)
5528 {
5529 id = syn_scl_name2id(name);
5530 vim_free(name);
5531 }
5532 return id;
5533}
5534
5535/*
5536 * Find syntax cluster name in the table and return it's ID.
5537 * The argument is a pointer to the name and the length of the name.
5538 * If it doesn't exist yet, a new entry is created.
5539 * Return 0 for failure.
5540 */
5541 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005542syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543{
5544 int id;
5545 char_u *name;
5546
5547 name = vim_strnsave(pp, len);
5548 if (name == NULL)
5549 return 0;
5550
5551 id = syn_scl_name2id(name);
5552 if (id == 0) /* doesn't exist yet */
5553 id = syn_add_cluster(name);
5554 else
5555 vim_free(name);
5556 return id;
5557}
5558
5559/*
5560 * Add new syntax cluster and return it's ID.
5561 * "name" must be an allocated string, it will be consumed.
5562 * Return 0 for failure.
5563 */
5564 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005565syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005566{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005567 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005568
5569 /*
5570 * First call for this growarray: init growing array.
5571 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005572 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005573 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005574 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5575 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 }
5577
Bram Moolenaar42431a72011-04-01 14:44:59 +02005578 len = curwin->w_s->b_syn_clusters.ga_len;
5579 if (len >= MAX_CLUSTER_ID)
5580 {
5581 EMSG((char_u *)_("E848: Too many syntax clusters"));
5582 vim_free(name);
5583 return 0;
5584 }
5585
Bram Moolenaar071d4272004-06-13 20:20:40 +00005586 /*
5587 * Make room for at least one other cluster entry.
5588 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005589 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005590 {
5591 vim_free(name);
5592 return 0;
5593 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594
Bram Moolenaar860cae12010-06-05 23:22:07 +02005595 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5596 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5597 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5598 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5599 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600
Bram Moolenaar217ad922005-03-20 22:37:15 +00005601 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005602 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005603 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005604 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005605
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 return len + SYNID_CLUSTER;
5607}
5608
5609/*
5610 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5611 * [add={groupname},..] [remove={groupname},..]".
5612 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005614syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005615{
5616 char_u *arg = eap->arg;
5617 char_u *group_name_end;
5618 char_u *rest;
5619 int scl_id;
5620 short *clstr_list;
5621 int got_clstr = FALSE;
5622 int opt_len;
5623 int list_op;
5624
5625 eap->nextcmd = find_nextcmd(arg);
5626 if (eap->skip)
5627 return;
5628
5629 rest = get_group_name(arg, &group_name_end);
5630
5631 if (rest != NULL)
5632 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005633 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5634 if (scl_id == 0)
5635 return;
5636 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637
5638 for (;;)
5639 {
5640 if (STRNICMP(rest, "add", 3) == 0
5641 && (vim_iswhite(rest[3]) || rest[3] == '='))
5642 {
5643 opt_len = 3;
5644 list_op = CLUSTER_ADD;
5645 }
5646 else if (STRNICMP(rest, "remove", 6) == 0
5647 && (vim_iswhite(rest[6]) || rest[6] == '='))
5648 {
5649 opt_len = 6;
5650 list_op = CLUSTER_SUBTRACT;
5651 }
5652 else if (STRNICMP(rest, "contains", 8) == 0
5653 && (vim_iswhite(rest[8]) || rest[8] == '='))
5654 {
5655 opt_len = 8;
5656 list_op = CLUSTER_REPLACE;
5657 }
5658 else
5659 break;
5660
5661 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005662 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663 {
5664 EMSG2(_(e_invarg2), rest);
5665 break;
5666 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005667 if (scl_id >= 0)
5668 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005669 &clstr_list, list_op);
5670 got_clstr = TRUE;
5671 }
5672
5673 if (got_clstr)
5674 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005675 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005676 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005677 }
5678 }
5679
5680 if (!got_clstr)
5681 EMSG(_("E400: No cluster specified"));
5682 if (rest == NULL || !ends_excmd(*rest))
5683 EMSG2(_(e_invarg2), arg);
5684}
5685
5686/*
5687 * On first call for current buffer: Init growing array.
5688 */
5689 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005690init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005691{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005692 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5693 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005694}
5695
5696/*
5697 * Get one pattern for a ":syntax match" or ":syntax region" command.
5698 * Stores the pattern and program in a synpat_T.
5699 * Returns a pointer to the next argument, or NULL in case of an error.
5700 */
5701 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005702get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703{
5704 char_u *end;
5705 int *p;
5706 int idx;
5707 char_u *cpo_save;
5708
5709 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005710 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711 return NULL;
5712
5713 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5714 if (*end != *arg) /* end delimiter not found */
5715 {
5716 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5717 return NULL;
5718 }
5719 /* store the pattern and compiled regexp program */
5720 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5721 return NULL;
5722
5723 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5724 cpo_save = p_cpo;
5725 p_cpo = (char_u *)"";
5726 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5727 p_cpo = cpo_save;
5728
5729 if (ci->sp_prog == NULL)
5730 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005731 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005732#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005733 syn_clear_time(&ci->sp_time);
5734#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005735
5736 /*
5737 * Check for a match, highlight or region offset.
5738 */
5739 ++end;
5740 do
5741 {
5742 for (idx = SPO_COUNT; --idx >= 0; )
5743 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5744 break;
5745 if (idx >= 0)
5746 {
5747 p = &(ci->sp_offsets[idx]);
5748 if (idx != SPO_LC_OFF)
5749 switch (end[3])
5750 {
5751 case 's': break;
5752 case 'b': break;
5753 case 'e': idx += SPO_COUNT; break;
5754 default: idx = -1; break;
5755 }
5756 if (idx >= 0)
5757 {
5758 ci->sp_off_flags |= (1 << idx);
5759 if (idx == SPO_LC_OFF) /* lc=99 */
5760 {
5761 end += 3;
5762 *p = getdigits(&end);
5763
5764 /* "lc=" offset automatically sets "ms=" offset */
5765 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5766 {
5767 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5768 ci->sp_offsets[SPO_MS_OFF] = *p;
5769 }
5770 }
5771 else /* yy=x+99 */
5772 {
5773 end += 4;
5774 if (*end == '+')
5775 {
5776 ++end;
5777 *p = getdigits(&end); /* positive offset */
5778 }
5779 else if (*end == '-')
5780 {
5781 ++end;
5782 *p = -getdigits(&end); /* negative offset */
5783 }
5784 }
5785 if (*end != ',')
5786 break;
5787 ++end;
5788 }
5789 }
5790 } while (idx >= 0);
5791
5792 if (!ends_excmd(*end) && !vim_iswhite(*end))
5793 {
5794 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5795 return NULL;
5796 }
5797 return skipwhite(end);
5798}
5799
5800/*
5801 * Handle ":syntax sync .." command.
5802 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005804syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005805{
5806 char_u *arg_start = eap->arg;
5807 char_u *arg_end;
5808 char_u *key = NULL;
5809 char_u *next_arg;
5810 int illegal = FALSE;
5811 int finished = FALSE;
5812 long n;
5813 char_u *cpo_save;
5814
5815 if (ends_excmd(*arg_start))
5816 {
5817 syn_cmd_list(eap, TRUE);
5818 return;
5819 }
5820
5821 while (!ends_excmd(*arg_start))
5822 {
5823 arg_end = skiptowhite(arg_start);
5824 next_arg = skipwhite(arg_end);
5825 vim_free(key);
5826 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5827 if (STRCMP(key, "CCOMMENT") == 0)
5828 {
5829 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005830 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 if (!ends_excmd(*next_arg))
5832 {
5833 arg_end = skiptowhite(next_arg);
5834 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005835 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005836 (int)(arg_end - next_arg));
5837 next_arg = skipwhite(arg_end);
5838 }
5839 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005840 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005841 }
5842 else if ( STRNCMP(key, "LINES", 5) == 0
5843 || STRNCMP(key, "MINLINES", 8) == 0
5844 || STRNCMP(key, "MAXLINES", 8) == 0
5845 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5846 {
5847 if (key[4] == 'S')
5848 arg_end = key + 6;
5849 else if (key[0] == 'L')
5850 arg_end = key + 11;
5851 else
5852 arg_end = key + 9;
5853 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5854 {
5855 illegal = TRUE;
5856 break;
5857 }
5858 n = getdigits(&arg_end);
5859 if (!eap->skip)
5860 {
5861 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005862 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005864 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005866 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867 }
5868 }
5869 else if (STRCMP(key, "FROMSTART") == 0)
5870 {
5871 if (!eap->skip)
5872 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005873 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5874 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005875 }
5876 }
5877 else if (STRCMP(key, "LINECONT") == 0)
5878 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005879 if (*next_arg == NUL) /* missing pattern */
5880 {
5881 illegal = TRUE;
5882 break;
5883 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005884 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005885 {
5886 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5887 finished = TRUE;
5888 break;
5889 }
5890 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5891 if (*arg_end != *next_arg) /* end delimiter not found */
5892 {
5893 illegal = TRUE;
5894 break;
5895 }
5896
5897 if (!eap->skip)
5898 {
5899 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005900 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005901 (int)(arg_end - next_arg - 1))) == NULL)
5902 {
5903 finished = TRUE;
5904 break;
5905 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005906 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005907
5908 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5909 cpo_save = p_cpo;
5910 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005911 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005912 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005914#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005915 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5916#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917
Bram Moolenaar860cae12010-06-05 23:22:07 +02005918 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005919 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005920 vim_free(curwin->w_s->b_syn_linecont_pat);
5921 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005922 finished = TRUE;
5923 break;
5924 }
5925 }
5926 next_arg = skipwhite(arg_end + 1);
5927 }
5928 else
5929 {
5930 eap->arg = next_arg;
5931 if (STRCMP(key, "MATCH") == 0)
5932 syn_cmd_match(eap, TRUE);
5933 else if (STRCMP(key, "REGION") == 0)
5934 syn_cmd_region(eap, TRUE);
5935 else if (STRCMP(key, "CLEAR") == 0)
5936 syn_cmd_clear(eap, TRUE);
5937 else
5938 illegal = TRUE;
5939 finished = TRUE;
5940 break;
5941 }
5942 arg_start = next_arg;
5943 }
5944 vim_free(key);
5945 if (illegal)
5946 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5947 else if (!finished)
5948 {
5949 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005950 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005951 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952 }
5953}
5954
5955/*
5956 * Convert a line of highlight group names into a list of group ID numbers.
5957 * "arg" should point to the "contains" or "nextgroup" keyword.
5958 * "arg" is advanced to after the last group name.
5959 * Careful: the argument is modified (NULs added).
5960 * returns FAIL for some error, OK for success.
5961 */
5962 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005963get_id_list(
5964 char_u **arg,
5965 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005966 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005967 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005968 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005969{
5970 char_u *p = NULL;
5971 char_u *end;
5972 int round;
5973 int count;
5974 int total_count = 0;
5975 short *retval = NULL;
5976 char_u *name;
5977 regmatch_T regmatch;
5978 int id;
5979 int i;
5980 int failed = FALSE;
5981
5982 /*
5983 * We parse the list twice:
5984 * round == 1: count the number of items, allocate the array.
5985 * round == 2: fill the array with the items.
5986 * In round 1 new groups may be added, causing the number of items to
5987 * grow when a regexp is used. In that case round 1 is done once again.
5988 */
5989 for (round = 1; round <= 2; ++round)
5990 {
5991 /*
5992 * skip "contains"
5993 */
5994 p = skipwhite(*arg + keylen);
5995 if (*p != '=')
5996 {
5997 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5998 break;
5999 }
6000 p = skipwhite(p + 1);
6001 if (ends_excmd(*p))
6002 {
6003 EMSG2(_("E406: Empty argument: %s"), *arg);
6004 break;
6005 }
6006
6007 /*
6008 * parse the arguments after "contains"
6009 */
6010 count = 0;
6011 while (!ends_excmd(*p))
6012 {
6013 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
6014 ;
6015 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6016 if (name == NULL)
6017 {
6018 failed = TRUE;
6019 break;
6020 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006021 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006022 if ( STRCMP(name + 1, "ALLBUT") == 0
6023 || STRCMP(name + 1, "ALL") == 0
6024 || STRCMP(name + 1, "TOP") == 0
6025 || STRCMP(name + 1, "CONTAINED") == 0)
6026 {
6027 if (TOUPPER_ASC(**arg) != 'C')
6028 {
6029 EMSG2(_("E407: %s not allowed here"), name + 1);
6030 failed = TRUE;
6031 vim_free(name);
6032 break;
6033 }
6034 if (count != 0)
6035 {
6036 EMSG2(_("E408: %s must be first in contains list"), name + 1);
6037 failed = TRUE;
6038 vim_free(name);
6039 break;
6040 }
6041 if (name[1] == 'A')
6042 id = SYNID_ALLBUT;
6043 else if (name[1] == 'T')
6044 id = SYNID_TOP;
6045 else
6046 id = SYNID_CONTAINED;
6047 id += current_syn_inc_tag;
6048 }
6049 else if (name[1] == '@')
6050 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01006051 if (!skip)
6052 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006053 }
6054 else
6055 {
6056 /*
6057 * Handle full group name.
6058 */
6059 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6060 id = syn_check_group(name + 1, (int)(end - p));
6061 else
6062 {
6063 /*
6064 * Handle match of regexp with group names.
6065 */
6066 *name = '^';
6067 STRCAT(name, "$");
6068 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6069 if (regmatch.regprog == NULL)
6070 {
6071 failed = TRUE;
6072 vim_free(name);
6073 break;
6074 }
6075
6076 regmatch.rm_ic = TRUE;
6077 id = 0;
6078 for (i = highlight_ga.ga_len; --i >= 0; )
6079 {
6080 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6081 (colnr_T)0))
6082 {
6083 if (round == 2)
6084 {
6085 /* Got more items than expected; can happen
6086 * when adding items that match:
6087 * "contains=a.*b,axb".
6088 * Go back to first round */
6089 if (count >= total_count)
6090 {
6091 vim_free(retval);
6092 round = 1;
6093 }
6094 else
6095 retval[count] = i + 1;
6096 }
6097 ++count;
6098 id = -1; /* remember that we found one */
6099 }
6100 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006101 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006102 }
6103 }
6104 vim_free(name);
6105 if (id == 0)
6106 {
6107 EMSG2(_("E409: Unknown group name: %s"), p);
6108 failed = TRUE;
6109 break;
6110 }
6111 if (id > 0)
6112 {
6113 if (round == 2)
6114 {
6115 /* Got more items than expected, go back to first round */
6116 if (count >= total_count)
6117 {
6118 vim_free(retval);
6119 round = 1;
6120 }
6121 else
6122 retval[count] = id;
6123 }
6124 ++count;
6125 }
6126 p = skipwhite(end);
6127 if (*p != ',')
6128 break;
6129 p = skipwhite(p + 1); /* skip comma in between arguments */
6130 }
6131 if (failed)
6132 break;
6133 if (round == 1)
6134 {
6135 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6136 if (retval == NULL)
6137 break;
6138 retval[count] = 0; /* zero means end of the list */
6139 total_count = count;
6140 }
6141 }
6142
6143 *arg = p;
6144 if (failed || retval == NULL)
6145 {
6146 vim_free(retval);
6147 return FAIL;
6148 }
6149
6150 if (*list == NULL)
6151 *list = retval;
6152 else
6153 vim_free(retval); /* list already found, don't overwrite it */
6154
6155 return OK;
6156}
6157
6158/*
6159 * Make a copy of an ID list.
6160 */
6161 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006162copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006163{
6164 int len;
6165 int count;
6166 short *retval;
6167
6168 if (list == NULL)
6169 return NULL;
6170
6171 for (count = 0; list[count]; ++count)
6172 ;
6173 len = (count + 1) * sizeof(short);
6174 retval = (short *)alloc((unsigned)len);
6175 if (retval != NULL)
6176 mch_memmove(retval, list, (size_t)len);
6177
6178 return retval;
6179}
6180
6181/*
6182 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6183 * "cur_si" can be NULL if not checking the "containedin" list.
6184 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6185 * the current item.
6186 * This function is called very often, keep it fast!!
6187 */
6188 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006189in_id_list(
6190 stateitem_T *cur_si, /* current item or NULL */
6191 short *list, /* id list */
6192 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6193 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006194{
6195 int retval;
6196 short *scl_list;
6197 short item;
6198 short id = ssp->id;
6199 static int depth = 0;
6200 int r;
6201
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006202 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006203 if (cur_si != NULL && ssp->cont_in_list != NULL
6204 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006205 {
6206 /* Ignore transparent items without a contains argument. Double check
6207 * that we don't go back past the first one. */
6208 while ((cur_si->si_flags & HL_TRANS_CONT)
6209 && cur_si > (stateitem_T *)(current_state.ga_data))
6210 --cur_si;
6211 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6212 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006213 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6214 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006215 return TRUE;
6216 }
6217
6218 if (list == NULL)
6219 return FALSE;
6220
6221 /*
6222 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6223 * inside anything. Only allow not-contained groups.
6224 */
6225 if (list == ID_LIST_ALL)
6226 return !contained;
6227
6228 /*
6229 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6230 * contains list. We also require that "id" is at the same ":syn include"
6231 * level as the list.
6232 */
6233 item = *list;
6234 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6235 {
6236 if (item < SYNID_TOP)
6237 {
6238 /* ALL or ALLBUT: accept all groups in the same file */
6239 if (item - SYNID_ALLBUT != ssp->inc_tag)
6240 return FALSE;
6241 }
6242 else if (item < SYNID_CONTAINED)
6243 {
6244 /* TOP: accept all not-contained groups in the same file */
6245 if (item - SYNID_TOP != ssp->inc_tag || contained)
6246 return FALSE;
6247 }
6248 else
6249 {
6250 /* CONTAINED: accept all contained groups in the same file */
6251 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6252 return FALSE;
6253 }
6254 item = *++list;
6255 retval = FALSE;
6256 }
6257 else
6258 retval = TRUE;
6259
6260 /*
6261 * Return "retval" if id is in the contains list.
6262 */
6263 while (item != 0)
6264 {
6265 if (item == id)
6266 return retval;
6267 if (item >= SYNID_CLUSTER)
6268 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006269 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006270 /* restrict recursiveness to 30 to avoid an endless loop for a
6271 * cluster that includes itself (indirectly) */
6272 if (scl_list != NULL && depth < 30)
6273 {
6274 ++depth;
6275 r = in_id_list(NULL, scl_list, ssp, contained);
6276 --depth;
6277 if (r)
6278 return retval;
6279 }
6280 }
6281 item = *++list;
6282 }
6283 return !retval;
6284}
6285
6286struct subcommand
6287{
Bram Moolenaard99df422016-01-29 23:20:40 +01006288 char *name; /* subcommand name */
6289 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006290};
6291
6292static struct subcommand subcommands[] =
6293{
6294 {"case", syn_cmd_case},
6295 {"clear", syn_cmd_clear},
6296 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006297 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006298 {"enable", syn_cmd_enable},
6299 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006300 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006301 {"keyword", syn_cmd_keyword},
6302 {"list", syn_cmd_list},
6303 {"manual", syn_cmd_manual},
6304 {"match", syn_cmd_match},
6305 {"on", syn_cmd_on},
6306 {"off", syn_cmd_off},
6307 {"region", syn_cmd_region},
6308 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006309 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006310 {"sync", syn_cmd_sync},
6311 {"", syn_cmd_list},
6312 {NULL, NULL}
6313};
6314
6315/*
6316 * ":syntax".
6317 * This searches the subcommands[] table for the subcommand name, and calls a
6318 * syntax_subcommand() function to do the rest.
6319 */
6320 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006321ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006322{
6323 char_u *arg = eap->arg;
6324 char_u *subcmd_end;
6325 char_u *subcmd_name;
6326 int i;
6327
6328 syn_cmdlinep = eap->cmdlinep;
6329
6330 /* isolate subcommand name */
6331 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6332 ;
6333 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6334 if (subcmd_name != NULL)
6335 {
6336 if (eap->skip) /* skip error messages for all subcommands */
6337 ++emsg_skip;
6338 for (i = 0; ; ++i)
6339 {
6340 if (subcommands[i].name == NULL)
6341 {
6342 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6343 break;
6344 }
6345 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6346 {
6347 eap->arg = skipwhite(subcmd_end);
6348 (subcommands[i].func)(eap, FALSE);
6349 break;
6350 }
6351 }
6352 vim_free(subcmd_name);
6353 if (eap->skip)
6354 --emsg_skip;
6355 }
6356}
6357
Bram Moolenaar860cae12010-06-05 23:22:07 +02006358 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006359ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006360{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006361 char_u *old_value;
6362 char_u *new_value;
6363
Bram Moolenaar860cae12010-06-05 23:22:07 +02006364 if (curwin->w_s == &curwin->w_buffer->b_s)
6365 {
6366 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6367 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006368 hash_init(&curwin->w_s->b_keywtab);
6369 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006370#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006371 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006372 curwin->w_p_spell = FALSE; /* No spell checking */
6373 clear_string_option(&curwin->w_s->b_p_spc);
6374 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006375 clear_string_option(&curwin->w_s->b_p_spl);
6376#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006377 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006378 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006379
6380 /* save value of b:current_syntax */
6381 old_value = get_var_value((char_u *)"b:current_syntax");
6382 if (old_value != NULL)
6383 old_value = vim_strsave(old_value);
6384
Bram Moolenaard1413d92016-03-02 21:51:56 +01006385#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006386 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6387 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006388 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006389#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006390
6391 /* move value of b:current_syntax to w:current_syntax */
6392 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006393 if (new_value != NULL)
6394 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006395
6396 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006397 if (old_value == NULL)
6398 do_unlet((char_u *)"b:current_syntax", TRUE);
6399 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006400 {
6401 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6402 vim_free(old_value);
6403 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006404}
6405
6406 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006407syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006408{
6409 return (win->w_s->b_syn_patterns.ga_len != 0
6410 || win->w_s->b_syn_clusters.ga_len != 0
6411 || win->w_s->b_keywtab.ht_used > 0
6412 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413}
6414
6415#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6416
6417static enum
6418{
6419 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006420 EXP_CASE, /* expand ":syn case" arguments */
6421 EXP_SPELL, /* expand ":syn spell" arguments */
6422 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006423} expand_what;
6424
Bram Moolenaar4f688582007-07-24 12:34:30 +00006425/*
6426 * Reset include_link, include_default, include_none to 0.
6427 * Called when we are done expanding.
6428 */
6429 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006430reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006431{
6432 include_link = include_default = include_none = 0;
6433}
6434
6435/*
6436 * Handle command line completion for :match and :echohl command: Add "None"
6437 * as highlight group.
6438 */
6439 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006440set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006441{
6442 xp->xp_context = EXPAND_HIGHLIGHT;
6443 xp->xp_pattern = arg;
6444 include_none = 1;
6445}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006446
6447/*
6448 * Handle command line completion for :syntax command.
6449 */
6450 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006451set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006452{
6453 char_u *p;
6454
6455 /* Default: expand subcommands */
6456 xp->xp_context = EXPAND_SYNTAX;
6457 expand_what = EXP_SUBCMD;
6458 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006459 include_link = 0;
6460 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006461
6462 /* (part of) subcommand already typed */
6463 if (*arg != NUL)
6464 {
6465 p = skiptowhite(arg);
6466 if (*p != NUL) /* past first word */
6467 {
6468 xp->xp_pattern = skipwhite(p);
6469 if (*skiptowhite(xp->xp_pattern) != NUL)
6470 xp->xp_context = EXPAND_NOTHING;
6471 else if (STRNICMP(arg, "case", p - arg) == 0)
6472 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006473 else if (STRNICMP(arg, "spell", p - arg) == 0)
6474 expand_what = EXP_SPELL;
6475 else if (STRNICMP(arg, "sync", p - arg) == 0)
6476 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006477 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6478 || STRNICMP(arg, "region", p - arg) == 0
6479 || STRNICMP(arg, "match", p - arg) == 0
6480 || STRNICMP(arg, "list", p - arg) == 0)
6481 xp->xp_context = EXPAND_HIGHLIGHT;
6482 else
6483 xp->xp_context = EXPAND_NOTHING;
6484 }
6485 }
6486}
6487
Bram Moolenaar071d4272004-06-13 20:20:40 +00006488/*
6489 * Function given to ExpandGeneric() to obtain the list syntax names for
6490 * expansion.
6491 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006493get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006494{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006495 switch (expand_what)
6496 {
6497 case EXP_SUBCMD:
6498 return (char_u *)subcommands[idx].name;
6499 case EXP_CASE:
6500 {
6501 static char *case_args[] = {"match", "ignore", NULL};
6502 return (char_u *)case_args[idx];
6503 }
6504 case EXP_SPELL:
6505 {
6506 static char *spell_args[] =
6507 {"toplevel", "notoplevel", "default", NULL};
6508 return (char_u *)spell_args[idx];
6509 }
6510 case EXP_SYNC:
6511 {
6512 static char *sync_args[] =
6513 {"ccomment", "clear", "fromstart",
6514 "linebreaks=", "linecont", "lines=", "match",
6515 "maxlines=", "minlines=", "region", NULL};
6516 return (char_u *)sync_args[idx];
6517 }
6518 }
6519 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006520}
6521
6522#endif /* FEAT_CMDL_COMPL */
6523
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524/*
6525 * Function called for expression evaluation: get syntax ID at file position.
6526 */
6527 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006528syn_get_id(
6529 win_T *wp,
6530 long lnum,
6531 colnr_T col,
6532 int trans, /* remove transparency */
6533 int *spellp, /* return: can do spell checking */
6534 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535{
6536 /* When the position is not after the current position and in the same
6537 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006538 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006540 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006541 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006542 else if (wp->w_buffer == syn_buf
6543 && lnum == current_lnum
6544 && col > current_col)
6545 /* next_match may not be correct when moving around, e.g. with the
6546 * "skip" expression in searchpair() */
6547 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006548
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006549 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006550
6551 return (trans ? current_trans_id : current_id);
6552}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006553
Bram Moolenaar860cae12010-06-05 23:22:07 +02006554#if defined(FEAT_CONCEAL) || defined(PROTO)
6555/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006556 * Get extra information about the syntax item. Must be called right after
6557 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006558 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006559 * Returns the current flags.
6560 */
6561 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006562get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006563{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006564 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006565 return current_flags;
6566}
6567
6568/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006569 * Return conceal substitution character
6570 */
6571 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006572syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006573{
6574 return current_sub_char;
6575}
6576#endif
6577
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006578#if defined(FEAT_EVAL) || defined(PROTO)
6579/*
6580 * Return the syntax ID at position "i" in the current stack.
6581 * The caller must have called syn_get_id() before to fill the stack.
6582 * Returns -1 when "i" is out of range.
6583 */
6584 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006585syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006586{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006587 if (i >= current_state.ga_len)
6588 {
6589 /* Need to invalidate the state, because we didn't properly finish it
6590 * for the last character, "keep_state" was TRUE. */
6591 invalidate_current_state();
6592 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006593 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006594 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006595 return CUR_STATE(i).si_id;
6596}
6597#endif
6598
Bram Moolenaar071d4272004-06-13 20:20:40 +00006599#if defined(FEAT_FOLDING) || defined(PROTO)
6600/*
6601 * Function called to get folding level for line "lnum" in window "wp".
6602 */
6603 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006604syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006605{
6606 int level = 0;
6607 int i;
6608
6609 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006610 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611 {
6612 syntax_start(wp, lnum);
6613
6614 for (i = 0; i < current_state.ga_len; ++i)
6615 if (CUR_STATE(i).si_flags & HL_FOLD)
6616 ++level;
6617 }
6618 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006619 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006620 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006621 if (level < 0)
6622 level = 0;
6623 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006624 return level;
6625}
6626#endif
6627
Bram Moolenaar01615492015-02-03 13:00:38 +01006628#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006629/*
6630 * ":syntime".
6631 */
6632 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006633ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006634{
6635 if (STRCMP(eap->arg, "on") == 0)
6636 syn_time_on = TRUE;
6637 else if (STRCMP(eap->arg, "off") == 0)
6638 syn_time_on = FALSE;
6639 else if (STRCMP(eap->arg, "clear") == 0)
6640 syntime_clear();
6641 else if (STRCMP(eap->arg, "report") == 0)
6642 syntime_report();
6643 else
6644 EMSG2(_(e_invarg2), eap->arg);
6645}
6646
6647 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006648syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006649{
6650 profile_zero(&st->total);
6651 profile_zero(&st->slowest);
6652 st->count = 0;
6653 st->match = 0;
6654}
6655
6656/*
6657 * Clear the syntax timing for the current buffer.
6658 */
6659 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006660syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006661{
6662 int idx;
6663 synpat_T *spp;
6664
6665 if (!syntax_present(curwin))
6666 {
6667 MSG(_(msg_no_items));
6668 return;
6669 }
6670 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6671 {
6672 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6673 syn_clear_time(&spp->sp_time);
6674 }
6675}
6676
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006677#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6678/*
6679 * Function given to ExpandGeneric() to obtain the possible arguments of the
6680 * ":syntime {on,off,clear,report}" command.
6681 */
6682 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006683get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006684{
6685 switch (idx)
6686 {
6687 case 0: return (char_u *)"on";
6688 case 1: return (char_u *)"off";
6689 case 2: return (char_u *)"clear";
6690 case 3: return (char_u *)"report";
6691 }
6692 return NULL;
6693}
6694#endif
6695
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006696typedef struct
6697{
6698 proftime_T total;
6699 int count;
6700 int match;
6701 proftime_T slowest;
6702 proftime_T average;
6703 int id;
6704 char_u *pattern;
6705} time_entry_T;
6706
6707 static int
6708#ifdef __BORLANDC__
6709_RTLENTRYF
6710#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006711syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006712{
6713 const time_entry_T *s1 = v1;
6714 const time_entry_T *s2 = v2;
6715
6716 return profile_cmp(&s1->total, &s2->total);
6717}
6718
6719/*
6720 * Clear the syntax timing for the current buffer.
6721 */
6722 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006723syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006724{
6725 int idx;
6726 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006727# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006728 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006729# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006730 int len;
6731 proftime_T total_total;
6732 int total_count = 0;
6733 garray_T ga;
6734 time_entry_T *p;
6735
6736 if (!syntax_present(curwin))
6737 {
6738 MSG(_(msg_no_items));
6739 return;
6740 }
6741
6742 ga_init2(&ga, sizeof(time_entry_T), 50);
6743 profile_zero(&total_total);
6744 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6745 {
6746 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6747 if (spp->sp_time.count > 0)
6748 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006749 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006750 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6751 p->total = spp->sp_time.total;
6752 profile_add(&total_total, &spp->sp_time.total);
6753 p->count = spp->sp_time.count;
6754 p->match = spp->sp_time.match;
6755 total_count += spp->sp_time.count;
6756 p->slowest = spp->sp_time.slowest;
6757# ifdef FEAT_FLOAT
6758 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6759 p->average = tm;
6760# endif
6761 p->id = spp->sp_syn.id;
6762 p->pattern = spp->sp_pattern;
6763 ++ga.ga_len;
6764 }
6765 }
6766
Bram Moolenaara2162552017-01-08 17:46:20 +01006767 /* Sort on total time. Skip if there are no items to avoid passing NULL
6768 * pointer to qsort(). */
6769 if (ga.ga_len > 1)
6770 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006771 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006772
6773 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6774 MSG_PUTS("\n");
6775 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6776 {
6777 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6778 p = ((time_entry_T *)ga.ga_data) + idx;
6779
6780 MSG_PUTS(profile_msg(&p->total));
6781 MSG_PUTS(" "); /* make sure there is always a separating space */
6782 msg_advance(13);
6783 msg_outnum(p->count);
6784 MSG_PUTS(" ");
6785 msg_advance(20);
6786 msg_outnum(p->match);
6787 MSG_PUTS(" ");
6788 msg_advance(26);
6789 MSG_PUTS(profile_msg(&p->slowest));
6790 MSG_PUTS(" ");
6791 msg_advance(38);
6792# ifdef FEAT_FLOAT
6793 MSG_PUTS(profile_msg(&p->average));
6794 MSG_PUTS(" ");
6795# endif
6796 msg_advance(50);
6797 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6798 MSG_PUTS(" ");
6799
6800 msg_advance(69);
6801 if (Columns < 80)
6802 len = 20; /* will wrap anyway */
6803 else
6804 len = Columns - 70;
6805 if (len > (int)STRLEN(p->pattern))
6806 len = (int)STRLEN(p->pattern);
6807 msg_outtrans_len(p->pattern, len);
6808 MSG_PUTS("\n");
6809 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006810 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006811 if (!got_int)
6812 {
6813 MSG_PUTS("\n");
6814 MSG_PUTS(profile_msg(&total_total));
6815 msg_advance(13);
6816 msg_outnum(total_count);
6817 MSG_PUTS("\n");
6818 }
6819}
6820#endif
6821
Bram Moolenaar071d4272004-06-13 20:20:40 +00006822#endif /* FEAT_SYN_HL */
6823
Bram Moolenaar071d4272004-06-13 20:20:40 +00006824/**************************************
6825 * Highlighting stuff *
6826 **************************************/
6827
6828/*
6829 * The default highlight groups. These are compiled-in for fast startup and
6830 * they still work when the runtime files can't be found.
6831 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006832 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6833 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006834 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006835#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006836# define CENT(a, b) b
6837#else
6838# define CENT(a, b) a
6839#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006840static char *(highlight_init_both[]) =
6841 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006842 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6843 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6844 CENT("IncSearch term=reverse cterm=reverse",
6845 "IncSearch term=reverse cterm=reverse gui=reverse"),
6846 CENT("ModeMsg term=bold cterm=bold",
6847 "ModeMsg term=bold cterm=bold gui=bold"),
6848 CENT("NonText term=bold ctermfg=Blue",
6849 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6850 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6851 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6852 CENT("StatusLineNC term=reverse cterm=reverse",
6853 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006854 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006855#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006856 CENT("VertSplit term=reverse cterm=reverse",
6857 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006858#endif
6859#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006860 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6861 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006862#endif
6863#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006864 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6865 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006866#endif
6867#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006868 CENT("PmenuSbar ctermbg=Grey",
6869 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006870#endif
6871#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006872 CENT("TabLineSel term=bold cterm=bold",
6873 "TabLineSel term=bold cterm=bold gui=bold"),
6874 CENT("TabLineFill term=reverse cterm=reverse",
6875 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006876#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006877#ifdef FEAT_GUI
6878 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006879 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006880#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006881 NULL
6882 };
6883
6884static char *(highlight_init_light[]) =
6885 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006886 CENT("Directory term=bold ctermfg=DarkBlue",
6887 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6888 CENT("LineNr term=underline ctermfg=Brown",
6889 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006890 CENT("CursorLineNr term=bold ctermfg=Brown",
6891 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006892 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6893 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6894 CENT("Question term=standout ctermfg=DarkGreen",
6895 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6896 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6897 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006898#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006899 CENT("SpellBad term=reverse ctermbg=LightRed",
6900 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6901 CENT("SpellCap term=reverse ctermbg=LightBlue",
6902 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6903 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6904 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6905 CENT("SpellLocal term=underline ctermbg=Cyan",
6906 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006907#endif
6908#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006909 CENT("PmenuThumb ctermbg=Black",
6910 "PmenuThumb ctermbg=Black guibg=Black"),
6911 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6912 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6913 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6914 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006915#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006916 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6917 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6918 CENT("Title term=bold ctermfg=DarkMagenta",
6919 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6920 CENT("WarningMsg term=standout ctermfg=DarkRed",
6921 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006922#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006923 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6924 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006925#endif
6926#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006927 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6928 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6929 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6930 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006931#endif
6932#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006933 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6934 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006935#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006936 CENT("Visual term=reverse",
6937 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006938#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006939 CENT("DiffAdd term=bold ctermbg=LightBlue",
6940 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6941 CENT("DiffChange term=bold ctermbg=LightMagenta",
6942 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6943 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6944 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006945#endif
6946#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006947 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6948 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006949#endif
6950#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006951 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006952 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006953 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006954 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006955 CENT("ColorColumn term=reverse ctermbg=LightRed",
6956 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006957#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006958#ifdef FEAT_CONCEAL
6959 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6960 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6961#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006962#ifdef FEAT_AUTOCMD
6963 CENT("MatchParen term=reverse ctermbg=Cyan",
6964 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6965#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006967 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006968#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006969 NULL
6970 };
6971
6972static char *(highlight_init_dark[]) =
6973 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006974 CENT("Directory term=bold ctermfg=LightCyan",
6975 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6976 CENT("LineNr term=underline ctermfg=Yellow",
6977 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006978 CENT("CursorLineNr term=bold ctermfg=Yellow",
6979 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006980 CENT("MoreMsg term=bold ctermfg=LightGreen",
6981 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6982 CENT("Question term=standout ctermfg=LightGreen",
6983 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6984 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6985 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6986 CENT("SpecialKey term=bold ctermfg=LightBlue",
6987 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006988#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006989 CENT("SpellBad term=reverse ctermbg=Red",
6990 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6991 CENT("SpellCap term=reverse ctermbg=Blue",
6992 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6993 CENT("SpellRare term=reverse ctermbg=Magenta",
6994 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6995 CENT("SpellLocal term=underline ctermbg=Cyan",
6996 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006997#endif
6998#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006999 CENT("PmenuThumb ctermbg=White",
7000 "PmenuThumb ctermbg=White guibg=White"),
7001 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7002 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007003 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7004 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007005#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007006 CENT("Title term=bold ctermfg=LightMagenta",
7007 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7008 CENT("WarningMsg term=standout ctermfg=LightRed",
7009 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007010#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007011 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7012 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007013#endif
7014#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007015 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7016 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7017 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7018 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007019#endif
7020#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007021 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7022 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007023#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007024 CENT("Visual term=reverse",
7025 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007026#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007027 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7028 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7029 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7030 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7031 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7032 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007033#endif
7034#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007035 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7036 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007037#endif
7038#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007039 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007040 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007041 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007042 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007043 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7044 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007045#endif
7046#ifdef FEAT_AUTOCMD
7047 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7048 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007049#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007050#ifdef FEAT_CONCEAL
7051 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7052 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7053#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007054#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007055 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007056#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007057 NULL
7058 };
7059
7060 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007061init_highlight(
7062 int both, /* include groups where 'bg' doesn't matter */
7063 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064{
7065 int i;
7066 char **pp;
7067 static int had_both = FALSE;
7068#ifdef FEAT_EVAL
7069 char_u *p;
7070
7071 /*
7072 * Try finding the color scheme file. Used when a color file was loaded
7073 * and 'background' or 't_Co' is changed.
7074 */
7075 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007076 if (p != NULL)
7077 {
7078 /* The value of g:colors_name could be freed when sourcing the script,
7079 * making "p" invalid, so copy it. */
7080 char_u *copy_p = vim_strsave(p);
7081 int r;
7082
7083 if (copy_p != NULL)
7084 {
7085 r = load_colors(copy_p);
7086 vim_free(copy_p);
7087 if (r == OK)
7088 return;
7089 }
7090 }
7091
Bram Moolenaar071d4272004-06-13 20:20:40 +00007092#endif
7093
7094 /*
7095 * Didn't use a color file, use the compiled-in colors.
7096 */
7097 if (both)
7098 {
7099 had_both = TRUE;
7100 pp = highlight_init_both;
7101 for (i = 0; pp[i] != NULL; ++i)
7102 do_highlight((char_u *)pp[i], reset, TRUE);
7103 }
7104 else if (!had_both)
7105 /* Don't do anything before the call with both == TRUE from main().
7106 * Not everything has been setup then, and that call will overrule
7107 * everything anyway. */
7108 return;
7109
7110 if (*p_bg == 'l')
7111 pp = highlight_init_light;
7112 else
7113 pp = highlight_init_dark;
7114 for (i = 0; pp[i] != NULL; ++i)
7115 do_highlight((char_u *)pp[i], reset, TRUE);
7116
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007117 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007118 * depend on the number of colors available.
7119 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007120 * to avoid Statement highlighted text disappears.
7121 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007122 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007123 do_highlight((char_u *)(*p_bg == 'l'
7124 ? "Visual cterm=NONE ctermbg=LightGrey"
7125 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007126 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007127 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007128 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7129 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007130 if (*p_bg == 'l')
7131 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7132 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007133
Bram Moolenaar071d4272004-06-13 20:20:40 +00007134#ifdef FEAT_SYN_HL
7135 /*
7136 * If syntax highlighting is enabled load the highlighting for it.
7137 */
7138 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007139 {
7140 static int recursive = 0;
7141
7142 if (recursive >= 5)
7143 EMSG(_("E679: recursive loop loading syncolor.vim"));
7144 else
7145 {
7146 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007147 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007148 --recursive;
7149 }
7150 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007151#endif
7152}
7153
7154/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007155 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007156 * Return OK for success, FAIL for failure.
7157 */
7158 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007159load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007160{
7161 char_u *buf;
7162 int retval = FAIL;
7163 static int recursive = FALSE;
7164
7165 /* When being called recursively, this is probably because setting
7166 * 'background' caused the highlighting to be reloaded. This means it is
7167 * working, thus we should return OK. */
7168 if (recursive)
7169 return OK;
7170
7171 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007172 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007173 if (buf != NULL)
7174 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007175 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007176 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007178#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007179 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007180#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007181 }
7182 recursive = FALSE;
7183
7184 return retval;
7185}
7186
7187/*
7188 * Handle the ":highlight .." command.
7189 * When using ":hi clear" this is called recursively for each group with
7190 * "forceit" and "init" both TRUE.
7191 */
7192 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007193do_highlight(
7194 char_u *line,
7195 int forceit,
7196 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007197{
7198 char_u *name_end;
7199 char_u *p;
7200 char_u *linep;
7201 char_u *key_start;
7202 char_u *arg_start;
7203 char_u *key = NULL, *arg = NULL;
7204 long i;
7205 int off;
7206 int len;
7207 int attr;
7208 int id;
7209 int idx;
7210 int dodefault = FALSE;
7211 int doclear = FALSE;
7212 int dolink = FALSE;
7213 int error = FALSE;
7214 int color;
7215 int is_normal_group = FALSE; /* "Normal" group */
7216#ifdef FEAT_GUI_X11
7217 int is_menu_group = FALSE; /* "Menu" group */
7218 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7219 int is_tooltip_group = FALSE; /* "Tooltip" group */
7220 int do_colors = FALSE; /* need to update colors? */
7221#else
7222# define is_menu_group 0
7223# define is_tooltip_group 0
7224#endif
7225
7226 /*
7227 * If no argument, list current highlighting.
7228 */
7229 if (ends_excmd(*line))
7230 {
7231 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7232 /* TODO: only call when the group has attributes set */
7233 highlight_list_one((int)i);
7234 return;
7235 }
7236
7237 /*
7238 * Isolate the name.
7239 */
7240 name_end = skiptowhite(line);
7241 linep = skipwhite(name_end);
7242
7243 /*
7244 * Check for "default" argument.
7245 */
7246 if (STRNCMP(line, "default", name_end - line) == 0)
7247 {
7248 dodefault = TRUE;
7249 line = linep;
7250 name_end = skiptowhite(line);
7251 linep = skipwhite(name_end);
7252 }
7253
7254 /*
7255 * Check for "clear" or "link" argument.
7256 */
7257 if (STRNCMP(line, "clear", name_end - line) == 0)
7258 doclear = TRUE;
7259 if (STRNCMP(line, "link", name_end - line) == 0)
7260 dolink = TRUE;
7261
7262 /*
7263 * ":highlight {group-name}": list highlighting for one group.
7264 */
7265 if (!doclear && !dolink && ends_excmd(*linep))
7266 {
7267 id = syn_namen2id(line, (int)(name_end - line));
7268 if (id == 0)
7269 EMSG2(_("E411: highlight group not found: %s"), line);
7270 else
7271 highlight_list_one(id);
7272 return;
7273 }
7274
7275 /*
7276 * Handle ":highlight link {from} {to}" command.
7277 */
7278 if (dolink)
7279 {
7280 char_u *from_start = linep;
7281 char_u *from_end;
7282 char_u *to_start;
7283 char_u *to_end;
7284 int from_id;
7285 int to_id;
7286
7287 from_end = skiptowhite(from_start);
7288 to_start = skipwhite(from_end);
7289 to_end = skiptowhite(to_start);
7290
7291 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7292 {
7293 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7294 from_start);
7295 return;
7296 }
7297
7298 if (!ends_excmd(*skipwhite(to_end)))
7299 {
7300 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7301 return;
7302 }
7303
7304 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7305 if (STRNCMP(to_start, "NONE", 4) == 0)
7306 to_id = 0;
7307 else
7308 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7309
7310 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7311 {
7312 /*
7313 * Don't allow a link when there already is some highlighting
7314 * for the group, unless '!' is used
7315 */
7316 if (to_id > 0 && !forceit && !init
7317 && hl_has_settings(from_id - 1, dodefault))
7318 {
7319 if (sourcing_name == NULL && !dodefault)
7320 EMSG(_("E414: group has settings, highlight link ignored"));
7321 }
7322 else
7323 {
7324 if (!init)
7325 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7326 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007327#ifdef FEAT_EVAL
7328 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7329#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007330 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007331 }
7332 }
7333
7334 /* Only call highlight_changed() once, after sourcing a syntax file */
7335 need_highlight_changed = TRUE;
7336
7337 return;
7338 }
7339
7340 if (doclear)
7341 {
7342 /*
7343 * ":highlight clear [group]" command.
7344 */
7345 line = linep;
7346 if (ends_excmd(*line))
7347 {
7348#ifdef FEAT_GUI
7349 /* First, we do not destroy the old values, but allocate the new
7350 * ones and update the display. THEN we destroy the old values.
7351 * If we destroy the old values first, then the old values
7352 * (such as GuiFont's or GuiFontset's) will still be displayed but
7353 * invalid because they were free'd.
7354 */
7355 if (gui.in_use)
7356 {
7357# ifdef FEAT_BEVAL_TIP
7358 gui_init_tooltip_font();
7359# endif
7360# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7361 gui_init_menu_font();
7362# endif
7363 }
7364# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7365 gui_mch_def_colors();
7366# endif
7367# ifdef FEAT_GUI_X11
7368# ifdef FEAT_MENU
7369
7370 /* This only needs to be done when there is no Menu highlight
7371 * group defined by default, which IS currently the case.
7372 */
7373 gui_mch_new_menu_colors();
7374# endif
7375 if (gui.in_use)
7376 {
7377 gui_new_scrollbar_colors();
7378# ifdef FEAT_BEVAL
7379 gui_mch_new_tooltip_colors();
7380# endif
7381# ifdef FEAT_MENU
7382 gui_mch_new_menu_font();
7383# endif
7384 }
7385# endif
7386
7387 /* Ok, we're done allocating the new default graphics items.
7388 * The screen should already be refreshed at this point.
7389 * It is now Ok to clear out the old data.
7390 */
7391#endif
7392#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007393 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007394#endif
7395 restore_cterm_colors();
7396
7397 /*
7398 * Clear all default highlight groups and load the defaults.
7399 */
7400 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7401 highlight_clear(idx);
7402 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007403#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007404 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007405 highlight_gui_started();
7406#endif
7407 highlight_changed();
7408 redraw_later_clear();
7409 return;
7410 }
7411 name_end = skiptowhite(line);
7412 linep = skipwhite(name_end);
7413 }
7414
7415 /*
7416 * Find the group name in the table. If it does not exist yet, add it.
7417 */
7418 id = syn_check_group(line, (int)(name_end - line));
7419 if (id == 0) /* failed (out of memory) */
7420 return;
7421 idx = id - 1; /* index is ID minus one */
7422
7423 /* Return if "default" was used and the group already has settings. */
7424 if (dodefault && hl_has_settings(idx, TRUE))
7425 return;
7426
7427 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7428 is_normal_group = TRUE;
7429#ifdef FEAT_GUI_X11
7430 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7431 is_menu_group = TRUE;
7432 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7433 is_scrollbar_group = TRUE;
7434 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7435 is_tooltip_group = TRUE;
7436#endif
7437
7438 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7439 if (doclear || (forceit && init))
7440 {
7441 highlight_clear(idx);
7442 if (!doclear)
7443 HL_TABLE()[idx].sg_set = 0;
7444 }
7445
7446 if (!doclear)
7447 while (!ends_excmd(*linep))
7448 {
7449 key_start = linep;
7450 if (*linep == '=')
7451 {
7452 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7453 error = TRUE;
7454 break;
7455 }
7456
7457 /*
7458 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7459 * "guibg").
7460 */
7461 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7462 ++linep;
7463 vim_free(key);
7464 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7465 if (key == NULL)
7466 {
7467 error = TRUE;
7468 break;
7469 }
7470 linep = skipwhite(linep);
7471
7472 if (STRCMP(key, "NONE") == 0)
7473 {
7474 if (!init || HL_TABLE()[idx].sg_set == 0)
7475 {
7476 if (!init)
7477 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7478 highlight_clear(idx);
7479 }
7480 continue;
7481 }
7482
7483 /*
7484 * Check for the equal sign.
7485 */
7486 if (*linep != '=')
7487 {
7488 EMSG2(_("E416: missing equal sign: %s"), key_start);
7489 error = TRUE;
7490 break;
7491 }
7492 ++linep;
7493
7494 /*
7495 * Isolate the argument.
7496 */
7497 linep = skipwhite(linep);
7498 if (*linep == '\'') /* guifg='color name' */
7499 {
7500 arg_start = ++linep;
7501 linep = vim_strchr(linep, '\'');
7502 if (linep == NULL)
7503 {
7504 EMSG2(_(e_invarg2), key_start);
7505 error = TRUE;
7506 break;
7507 }
7508 }
7509 else
7510 {
7511 arg_start = linep;
7512 linep = skiptowhite(linep);
7513 }
7514 if (linep == arg_start)
7515 {
7516 EMSG2(_("E417: missing argument: %s"), key_start);
7517 error = TRUE;
7518 break;
7519 }
7520 vim_free(arg);
7521 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7522 if (arg == NULL)
7523 {
7524 error = TRUE;
7525 break;
7526 }
7527 if (*linep == '\'')
7528 ++linep;
7529
7530 /*
7531 * Store the argument.
7532 */
7533 if ( STRCMP(key, "TERM") == 0
7534 || STRCMP(key, "CTERM") == 0
7535 || STRCMP(key, "GUI") == 0)
7536 {
7537 attr = 0;
7538 off = 0;
7539 while (arg[off] != NUL)
7540 {
7541 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7542 {
7543 len = (int)STRLEN(hl_name_table[i]);
7544 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7545 {
7546 attr |= hl_attr_table[i];
7547 off += len;
7548 break;
7549 }
7550 }
7551 if (i < 0)
7552 {
7553 EMSG2(_("E418: Illegal value: %s"), arg);
7554 error = TRUE;
7555 break;
7556 }
7557 if (arg[off] == ',') /* another one follows */
7558 ++off;
7559 }
7560 if (error)
7561 break;
7562 if (*key == 'T')
7563 {
7564 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7565 {
7566 if (!init)
7567 HL_TABLE()[idx].sg_set |= SG_TERM;
7568 HL_TABLE()[idx].sg_term = attr;
7569 }
7570 }
7571 else if (*key == 'C')
7572 {
7573 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7574 {
7575 if (!init)
7576 HL_TABLE()[idx].sg_set |= SG_CTERM;
7577 HL_TABLE()[idx].sg_cterm = attr;
7578 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7579 }
7580 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007581#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007582 else
7583 {
7584 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7585 {
7586 if (!init)
7587 HL_TABLE()[idx].sg_set |= SG_GUI;
7588 HL_TABLE()[idx].sg_gui = attr;
7589 }
7590 }
7591#endif
7592 }
7593 else if (STRCMP(key, "FONT") == 0)
7594 {
7595 /* in non-GUI fonts are simply ignored */
7596#ifdef FEAT_GUI
7597 if (!gui.shell_created)
7598 {
7599 /* GUI not started yet, always accept the name. */
7600 vim_free(HL_TABLE()[idx].sg_font_name);
7601 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7602 }
7603 else
7604 {
7605 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7606# ifdef FEAT_XFONTSET
7607 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7608# endif
7609 /* First, save the current font/fontset.
7610 * Then try to allocate the font/fontset.
7611 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7612 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7613 */
7614
7615 HL_TABLE()[idx].sg_font = NOFONT;
7616# ifdef FEAT_XFONTSET
7617 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7618# endif
7619 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007620 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007621
7622# ifdef FEAT_XFONTSET
7623 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7624 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007625 /* New fontset was accepted. Free the old one, if there
7626 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007627 gui_mch_free_fontset(temp_sg_fontset);
7628 vim_free(HL_TABLE()[idx].sg_font_name);
7629 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7630 }
7631 else
7632 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7633# endif
7634 if (HL_TABLE()[idx].sg_font != NOFONT)
7635 {
7636 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007637 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007638 gui_mch_free_font(temp_sg_font);
7639 vim_free(HL_TABLE()[idx].sg_font_name);
7640 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7641 }
7642 else
7643 HL_TABLE()[idx].sg_font = temp_sg_font;
7644 }
7645#endif
7646 }
7647 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7648 {
7649 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7650 {
7651 if (!init)
7652 HL_TABLE()[idx].sg_set |= SG_CTERM;
7653
7654 /* When setting the foreground color, and previously the "bold"
7655 * flag was set for a light color, reset it now */
7656 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7657 {
7658 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7659 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7660 }
7661
7662 if (VIM_ISDIGIT(*arg))
7663 color = atoi((char *)arg);
7664 else if (STRICMP(arg, "fg") == 0)
7665 {
7666 if (cterm_normal_fg_color)
7667 color = cterm_normal_fg_color - 1;
7668 else
7669 {
7670 EMSG(_("E419: FG color unknown"));
7671 error = TRUE;
7672 break;
7673 }
7674 }
7675 else if (STRICMP(arg, "bg") == 0)
7676 {
7677 if (cterm_normal_bg_color > 0)
7678 color = cterm_normal_bg_color - 1;
7679 else
7680 {
7681 EMSG(_("E420: BG color unknown"));
7682 error = TRUE;
7683 break;
7684 }
7685 }
7686 else
7687 {
7688 static char *(color_names[28]) = {
7689 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7690 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7691 "Gray", "Grey",
7692 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7693 "Blue", "LightBlue", "Green", "LightGreen",
7694 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7695 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7696 static int color_numbers_16[28] = {0, 1, 2, 3,
7697 4, 5, 6, 6,
7698 7, 7,
7699 7, 7, 8, 8,
7700 9, 9, 10, 10,
7701 11, 11, 12, 12, 13,
7702 13, 14, 14, 15, -1};
7703 /* for xterm with 88 colors... */
7704 static int color_numbers_88[28] = {0, 4, 2, 6,
7705 1, 5, 32, 72,
7706 84, 84,
7707 7, 7, 82, 82,
7708 12, 43, 10, 61,
7709 14, 63, 9, 74, 13,
7710 75, 11, 78, 15, -1};
7711 /* for xterm with 256 colors... */
7712 static int color_numbers_256[28] = {0, 4, 2, 6,
7713 1, 5, 130, 130,
7714 248, 248,
7715 7, 7, 242, 242,
7716 12, 81, 10, 121,
7717 14, 159, 9, 224, 13,
7718 225, 11, 229, 15, -1};
7719 /* for terminals with less than 16 colors... */
7720 static int color_numbers_8[28] = {0, 4, 2, 6,
7721 1, 5, 3, 3,
7722 7, 7,
7723 7, 7, 0+8, 0+8,
7724 4+8, 4+8, 2+8, 2+8,
7725 6+8, 6+8, 1+8, 1+8, 5+8,
7726 5+8, 3+8, 3+8, 7+8, -1};
7727#if defined(__QNXNTO__)
7728 static int *color_numbers_8_qansi = color_numbers_8;
7729 /* On qnx, the 8 & 16 color arrays are the same */
7730 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7731 color_numbers_8_qansi = color_numbers_16;
7732#endif
7733
7734 /* reduce calls to STRICMP a bit, it can be slow */
7735 off = TOUPPER_ASC(*arg);
7736 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7737 if (off == color_names[i][0]
7738 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7739 break;
7740 if (i < 0)
7741 {
7742 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7743 error = TRUE;
7744 break;
7745 }
7746
7747 /* Use the _16 table to check if its a valid color name. */
7748 color = color_numbers_16[i];
7749 if (color >= 0)
7750 {
7751 if (t_colors == 8)
7752 {
7753 /* t_Co is 8: use the 8 colors table */
7754#if defined(__QNXNTO__)
7755 color = color_numbers_8_qansi[i];
7756#else
7757 color = color_numbers_8[i];
7758#endif
7759 if (key[5] == 'F')
7760 {
7761 /* set/reset bold attribute to get light foreground
7762 * colors (on some terminals, e.g. "linux") */
7763 if (color & 8)
7764 {
7765 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7766 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7767 }
7768 else
7769 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7770 }
7771 color &= 7; /* truncate to 8 colors */
7772 }
7773 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007774 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007775 {
7776 /*
7777 * Guess: if the termcap entry ends in 'm', it is
7778 * probably an xterm-like terminal. Use the changed
7779 * order for colors.
7780 */
7781 if (*T_CAF != NUL)
7782 p = T_CAF;
7783 else
7784 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007785 if (*p != NUL && (t_colors > 256
7786 || *(p + STRLEN(p) - 1) == 'm'))
7787 {
7788 if (t_colors == 88)
7789 color = color_numbers_88[i];
7790 else if (t_colors >= 256)
7791 color = color_numbers_256[i];
7792 else
7793 color = color_numbers_8[i];
7794 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007795 }
7796 }
7797 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007798 /* Add one to the argument, to avoid zero. Zero is used for
7799 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800 if (key[5] == 'F')
7801 {
7802 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7803 if (is_normal_group)
7804 {
7805 cterm_normal_fg_color = color + 1;
7806 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7807#ifdef FEAT_GUI
7808 /* Don't do this if the GUI is used. */
7809 if (!gui.in_use && !gui.starting)
7810#endif
7811 {
7812 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007813 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007814 term_fg_color(color);
7815 }
7816 }
7817 }
7818 else
7819 {
7820 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7821 if (is_normal_group)
7822 {
7823 cterm_normal_bg_color = color + 1;
7824#ifdef FEAT_GUI
7825 /* Don't mess with 'background' if the GUI is used. */
7826 if (!gui.in_use && !gui.starting)
7827#endif
7828 {
7829 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007830 if (color >= 0)
7831 {
7832 if (termcap_active)
7833 term_bg_color(color);
7834 if (t_colors < 16)
7835 i = (color == 0 || color == 4);
7836 else
7837 i = (color < 7 || color == 8);
7838 /* Set the 'background' option if the value is
7839 * wrong. */
7840 if (i != (*p_bg == 'd'))
7841 set_option_value((char_u *)"bg", 0L,
7842 i ? (char_u *)"dark"
7843 : (char_u *)"light", 0);
7844 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 }
7846 }
7847 }
7848 }
7849 }
7850 else if (STRCMP(key, "GUIFG") == 0)
7851 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007852#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007853 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007855 if (!init)
7856 HL_TABLE()[idx].sg_set |= SG_GUI;
7857
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007858# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007859 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007860 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007861 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007862 {
7863 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007864# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007865 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7866 if (STRCMP(arg, "NONE"))
7867 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7868 else
7869 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007870# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007871# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007872 if (is_menu_group)
7873 gui.menu_fg_pixel = i;
7874 if (is_scrollbar_group)
7875 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007876# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007877 if (is_tooltip_group)
7878 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007879# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007880 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007881# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007882 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007883# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007884 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007885#endif
7886 }
7887 else if (STRCMP(key, "GUIBG") == 0)
7888 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007889#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007890 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007891 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007892 if (!init)
7893 HL_TABLE()[idx].sg_set |= SG_GUI;
7894
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007895# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007896 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007897 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007898 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007899 {
7900 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007901# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007902 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7903 if (STRCMP(arg, "NONE") != 0)
7904 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7905 else
7906 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007907# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007908# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007909 if (is_menu_group)
7910 gui.menu_bg_pixel = i;
7911 if (is_scrollbar_group)
7912 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007913# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007914 if (is_tooltip_group)
7915 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007916# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007917 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007918# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007919 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007920# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007921 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007922#endif
7923 }
7924 else if (STRCMP(key, "GUISP") == 0)
7925 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007926#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007927 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7928 {
7929 if (!init)
7930 HL_TABLE()[idx].sg_set |= SG_GUI;
7931
Bram Moolenaar61623362010-07-14 22:04:22 +02007932# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007933 i = color_name2handle(arg);
7934 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7935 {
7936 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007937# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007938 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7939 if (STRCMP(arg, "NONE") != 0)
7940 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7941 else
7942 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007943# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007944 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007945# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007946 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007947#endif
7948 }
7949 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7950 {
7951 char_u buf[100];
7952 char_u *tname;
7953
7954 if (!init)
7955 HL_TABLE()[idx].sg_set |= SG_TERM;
7956
7957 /*
7958 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007959 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007960 */
7961 if (STRNCMP(arg, "t_", 2) == 0)
7962 {
7963 off = 0;
7964 buf[0] = 0;
7965 while (arg[off] != NUL)
7966 {
7967 /* Isolate one termcap name */
7968 for (len = 0; arg[off + len] &&
7969 arg[off + len] != ','; ++len)
7970 ;
7971 tname = vim_strnsave(arg + off, len);
7972 if (tname == NULL) /* out of memory */
7973 {
7974 error = TRUE;
7975 break;
7976 }
7977 /* lookup the escape sequence for the item */
7978 p = get_term_code(tname);
7979 vim_free(tname);
7980 if (p == NULL) /* ignore non-existing things */
7981 p = (char_u *)"";
7982
7983 /* Append it to the already found stuff */
7984 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7985 {
7986 EMSG2(_("E422: terminal code too long: %s"), arg);
7987 error = TRUE;
7988 break;
7989 }
7990 STRCAT(buf, p);
7991
7992 /* Advance to the next item */
7993 off += len;
7994 if (arg[off] == ',') /* another one follows */
7995 ++off;
7996 }
7997 }
7998 else
7999 {
8000 /*
8001 * Copy characters from arg[] to buf[], translating <> codes.
8002 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008003 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008004 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008005 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008006 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008007 off += len;
8008 else /* copy as normal char */
8009 buf[off++] = *p++;
8010 }
8011 buf[off] = NUL;
8012 }
8013 if (error)
8014 break;
8015
8016 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8017 p = NULL;
8018 else
8019 p = vim_strsave(buf);
8020 if (key[2] == 'A')
8021 {
8022 vim_free(HL_TABLE()[idx].sg_start);
8023 HL_TABLE()[idx].sg_start = p;
8024 }
8025 else
8026 {
8027 vim_free(HL_TABLE()[idx].sg_stop);
8028 HL_TABLE()[idx].sg_stop = p;
8029 }
8030 }
8031 else
8032 {
8033 EMSG2(_("E423: Illegal argument: %s"), key_start);
8034 error = TRUE;
8035 break;
8036 }
8037
8038 /*
8039 * When highlighting has been given for a group, don't link it.
8040 */
8041 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8042 HL_TABLE()[idx].sg_link = 0;
8043
8044 /*
8045 * Continue with next argument.
8046 */
8047 linep = skipwhite(linep);
8048 }
8049
8050 /*
8051 * If there is an error, and it's a new entry, remove it from the table.
8052 */
8053 if (error && idx == highlight_ga.ga_len)
8054 syn_unadd_group();
8055 else
8056 {
8057 if (is_normal_group)
8058 {
8059 HL_TABLE()[idx].sg_term_attr = 0;
8060 HL_TABLE()[idx].sg_cterm_attr = 0;
8061#ifdef FEAT_GUI
8062 HL_TABLE()[idx].sg_gui_attr = 0;
8063 /*
8064 * Need to update all groups, because they might be using "bg"
8065 * and/or "fg", which have been changed now.
8066 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008067#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008068#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008069 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008070 highlight_gui_started();
8071#endif
8072 }
8073#ifdef FEAT_GUI_X11
8074# ifdef FEAT_MENU
8075 else if (is_menu_group)
8076 {
8077 if (gui.in_use && do_colors)
8078 gui_mch_new_menu_colors();
8079 }
8080# endif
8081 else if (is_scrollbar_group)
8082 {
8083 if (gui.in_use && do_colors)
8084 gui_new_scrollbar_colors();
8085 }
8086# ifdef FEAT_BEVAL
8087 else if (is_tooltip_group)
8088 {
8089 if (gui.in_use && do_colors)
8090 gui_mch_new_tooltip_colors();
8091 }
8092# endif
8093#endif
8094 else
8095 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008096#ifdef FEAT_EVAL
8097 HL_TABLE()[idx].sg_scriptID = current_SID;
8098#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008099 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008100 }
8101 vim_free(key);
8102 vim_free(arg);
8103
8104 /* Only call highlight_changed() once, after sourcing a syntax file */
8105 need_highlight_changed = TRUE;
8106}
8107
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008108#if defined(EXITFREE) || defined(PROTO)
8109 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008110free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008111{
8112 int i;
8113
8114 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008115 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008116 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008117 vim_free(HL_TABLE()[i].sg_name);
8118 vim_free(HL_TABLE()[i].sg_name_u);
8119 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008120 ga_clear(&highlight_ga);
8121}
8122#endif
8123
Bram Moolenaar071d4272004-06-13 20:20:40 +00008124/*
8125 * Reset the cterm colors to what they were before Vim was started, if
8126 * possible. Otherwise reset them to zero.
8127 */
8128 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008129restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008130{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008131#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008132 /* Since t_me has been set, this probably means that the user
8133 * wants to use this as default colors. Need to reset default
8134 * background/foreground colors. */
8135 mch_set_normal_colors();
8136#else
8137 cterm_normal_fg_color = 0;
8138 cterm_normal_fg_bold = 0;
8139 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008140# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008141 cterm_normal_fg_gui_color = INVALCOLOR;
8142 cterm_normal_bg_gui_color = INVALCOLOR;
8143# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144#endif
8145}
8146
8147/*
8148 * Return TRUE if highlight group "idx" has any settings.
8149 * When "check_link" is TRUE also check for an existing link.
8150 */
8151 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008152hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008153{
8154 return ( HL_TABLE()[idx].sg_term_attr != 0
8155 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008156 || HL_TABLE()[idx].sg_cterm_fg != 0
8157 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008158#ifdef FEAT_GUI
8159 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008160 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8161 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8162 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8163 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008164#endif
8165 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8166}
8167
8168/*
8169 * Clear highlighting for one group.
8170 */
8171 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008172highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008173{
8174 HL_TABLE()[idx].sg_term = 0;
8175 vim_free(HL_TABLE()[idx].sg_start);
8176 HL_TABLE()[idx].sg_start = NULL;
8177 vim_free(HL_TABLE()[idx].sg_stop);
8178 HL_TABLE()[idx].sg_stop = NULL;
8179 HL_TABLE()[idx].sg_term_attr = 0;
8180 HL_TABLE()[idx].sg_cterm = 0;
8181 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8182 HL_TABLE()[idx].sg_cterm_fg = 0;
8183 HL_TABLE()[idx].sg_cterm_bg = 0;
8184 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008185#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008186 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008187 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8188 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008189 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8190 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008191 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8192 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008193#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008194#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008195 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8196 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008197#endif
8198#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008199 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8201 HL_TABLE()[idx].sg_font = NOFONT;
8202# ifdef FEAT_XFONTSET
8203 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8204 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8205# endif
8206 vim_free(HL_TABLE()[idx].sg_font_name);
8207 HL_TABLE()[idx].sg_font_name = NULL;
8208 HL_TABLE()[idx].sg_gui_attr = 0;
8209#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008210#ifdef FEAT_EVAL
8211 /* Clear the script ID only when there is no link, since that is not
8212 * cleared. */
8213 if (HL_TABLE()[idx].sg_link == 0)
8214 HL_TABLE()[idx].sg_scriptID = 0;
8215#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008216}
8217
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008218#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219/*
8220 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008221 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008222 * "Tooltip" colors.
8223 */
8224 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008225set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008227#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008228# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008229 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008230# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008231 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008232 if (set_group_colors((char_u *)"Normal",
8233 &gui.norm_pixel, &gui.back_pixel,
8234 FALSE, TRUE, FALSE))
8235 {
8236 gui_mch_new_colors();
8237 must_redraw = CLEAR;
8238 }
8239# ifdef FEAT_GUI_X11
8240 if (set_group_colors((char_u *)"Menu",
8241 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8242 TRUE, FALSE, FALSE))
8243 {
8244# ifdef FEAT_MENU
8245 gui_mch_new_menu_colors();
8246# endif
8247 must_redraw = CLEAR;
8248 }
8249# ifdef FEAT_BEVAL
8250 if (set_group_colors((char_u *)"Tooltip",
8251 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8252 FALSE, FALSE, TRUE))
8253 {
8254# ifdef FEAT_TOOLBAR
8255 gui_mch_new_tooltip_colors();
8256# endif
8257 must_redraw = CLEAR;
8258 }
8259# endif
8260 if (set_group_colors((char_u *)"Scrollbar",
8261 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8262 FALSE, FALSE, FALSE))
8263 {
8264 gui_new_scrollbar_colors();
8265 must_redraw = CLEAR;
8266 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008267# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008268 }
8269#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008270#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008271# ifdef FEAT_GUI
8272 else
8273# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008275 int idx;
8276
8277 idx = syn_name2id((char_u *)"Normal") - 1;
8278 if (idx >= 0)
8279 {
8280 gui_do_one_color(idx, FALSE, FALSE);
8281
8282 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8283 {
8284 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8285 must_redraw = CLEAR;
8286 }
8287 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8288 {
8289 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8290 must_redraw = CLEAR;
8291 }
8292 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008293 }
8294#endif
8295}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008296#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008298#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008299/*
8300 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8301 */
8302 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008303set_group_colors(
8304 char_u *name,
8305 guicolor_T *fgp,
8306 guicolor_T *bgp,
8307 int do_menu,
8308 int use_norm,
8309 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008310{
8311 int idx;
8312
8313 idx = syn_name2id(name) - 1;
8314 if (idx >= 0)
8315 {
8316 gui_do_one_color(idx, do_menu, do_tooltip);
8317
8318 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8319 *fgp = HL_TABLE()[idx].sg_gui_fg;
8320 else if (use_norm)
8321 *fgp = gui.def_norm_pixel;
8322 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8323 *bgp = HL_TABLE()[idx].sg_gui_bg;
8324 else if (use_norm)
8325 *bgp = gui.def_back_pixel;
8326 return TRUE;
8327 }
8328 return FALSE;
8329}
8330
8331/*
8332 * Get the font of the "Normal" group.
8333 * Returns "" when it's not found or not set.
8334 */
8335 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008336hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008337{
8338 int id;
8339 char_u *s;
8340
8341 id = syn_name2id((char_u *)"Normal");
8342 if (id > 0)
8343 {
8344 s = HL_TABLE()[id - 1].sg_font_name;
8345 if (s != NULL)
8346 return s;
8347 }
8348 return (char_u *)"";
8349}
8350
8351/*
8352 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8353 * actually chosen to be used.
8354 */
8355 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008356hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008357{
8358 int id;
8359
8360 id = syn_name2id((char_u *)"Normal");
8361 if (id > 0)
8362 {
8363 vim_free(HL_TABLE()[id - 1].sg_font_name);
8364 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8365 }
8366}
8367
8368/*
8369 * Set background color for "Normal" group. Called by gui_set_bg_color()
8370 * when the color is known.
8371 */
8372 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008373hl_set_bg_color_name(
8374 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008375{
8376 int id;
8377
8378 if (name != NULL)
8379 {
8380 id = syn_name2id((char_u *)"Normal");
8381 if (id > 0)
8382 {
8383 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8384 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8385 }
8386 }
8387}
8388
8389/*
8390 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8391 * when the color is known.
8392 */
8393 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008394hl_set_fg_color_name(
8395 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008396{
8397 int id;
8398
8399 if (name != NULL)
8400 {
8401 id = syn_name2id((char_u *)"Normal");
8402 if (id > 0)
8403 {
8404 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8405 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8406 }
8407 }
8408}
8409
8410/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411 * Return the handle for a font name.
8412 * Returns NOFONT when failed.
8413 */
8414 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008415font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008416{
8417 if (STRCMP(name, "NONE") == 0)
8418 return NOFONT;
8419
8420 return gui_mch_get_font(name, TRUE);
8421}
8422
8423# ifdef FEAT_XFONTSET
8424/*
8425 * Return the handle for a fontset name.
8426 * Returns NOFONTSET when failed.
8427 */
8428 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008429fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008430{
8431 if (STRCMP(name, "NONE") == 0)
8432 return NOFONTSET;
8433
8434 return gui_mch_get_fontset(name, TRUE, fixed_width);
8435}
8436# endif
8437
8438/*
8439 * Get the font or fontset for one highlight group.
8440 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008441 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008442hl_do_font(
8443 int idx,
8444 char_u *arg,
8445 int do_normal, /* set normal font */
8446 int do_menu UNUSED, /* set menu font */
8447 int do_tooltip UNUSED, /* set tooltip font */
8448 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008449{
8450# ifdef FEAT_XFONTSET
8451 /* If 'guifontset' is not empty, first try using the name as a
8452 * fontset. If that doesn't work, use it as a font name. */
8453 if (*p_guifontset != NUL
8454# ifdef FONTSET_ALWAYS
8455 || do_menu
8456# endif
8457# ifdef FEAT_BEVAL_TIP
8458 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8459 || do_tooltip
8460# endif
8461 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008462 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008463 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008464 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008465 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8466# ifdef FONTSET_ALWAYS
8467 || do_menu
8468# endif
8469# ifdef FEAT_BEVAL_TIP
8470 || do_tooltip
8471# endif
8472 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008473 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008474 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8475 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008476 /* If it worked and it's the Normal group, use it as the normal
8477 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008478 if (do_normal)
8479 gui_init_font(arg, TRUE);
8480# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8481 if (do_menu)
8482 {
8483# ifdef FONTSET_ALWAYS
8484 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8485# else
8486 /* YIKES! This is a bug waiting to crash the program */
8487 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8488# endif
8489 gui_mch_new_menu_font();
8490 }
8491# ifdef FEAT_BEVAL
8492 if (do_tooltip)
8493 {
8494 /* The Athena widget set cannot currently handle switching between
8495 * displaying a single font and a fontset.
8496 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008497 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008498 * XFontStruct is used.
8499 */
8500 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8501 gui_mch_new_tooltip_font();
8502 }
8503# endif
8504# endif
8505 }
8506 else
8507# endif
8508 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008509 if (free_font)
8510 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008511 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8512 /* If it worked and it's the Normal group, use it as the
8513 * normal font. Same for the Menu group. */
8514 if (HL_TABLE()[idx].sg_font != NOFONT)
8515 {
8516 if (do_normal)
8517 gui_init_font(arg, FALSE);
8518#ifndef FONTSET_ALWAYS
8519# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8520 if (do_menu)
8521 {
8522 gui.menu_font = HL_TABLE()[idx].sg_font;
8523 gui_mch_new_menu_font();
8524 }
8525# endif
8526#endif
8527 }
8528 }
8529}
8530
8531#endif /* FEAT_GUI */
8532
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008533#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008534/*
8535 * Return the handle for a color name.
8536 * Returns INVALCOLOR when failed.
8537 */
8538 static guicolor_T
8539color_name2handle(char_u *name)
8540{
8541 if (STRCMP(name, "NONE") == 0)
8542 return INVALCOLOR;
8543
8544 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8545 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008546#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008547 if (gui.in_use)
8548#endif
8549#ifdef FEAT_GUI
8550 return gui.norm_pixel;
8551#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008552#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008553 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008554 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008555 /* Guess that the foreground is black or white. */
8556 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008557#endif
8558 }
8559 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8560 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008561#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008562 if (gui.in_use)
8563#endif
8564#ifdef FEAT_GUI
8565 return gui.back_pixel;
8566#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008567#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008568 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008569 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008570 /* Guess that the background is white or black. */
8571 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008572#endif
8573 }
8574
8575 return GUI_GET_COLOR(name);
8576}
8577#endif
8578
Bram Moolenaar071d4272004-06-13 20:20:40 +00008579/*
8580 * Table with the specifications for an attribute number.
8581 * Note that this table is used by ALL buffers. This is required because the
8582 * GUI can redraw at any time for any buffer.
8583 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008584static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585
8586#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8587
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008588static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008589
8590#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8591
8592#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008593static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008594
8595#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8596#endif
8597
8598/*
8599 * Return the attr number for a set of colors and font.
8600 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8601 * if the combination is new.
8602 * Return 0 for error (no more room).
8603 */
8604 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008605get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008606{
8607 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008608 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008609 static int recursive = FALSE;
8610
8611 /*
8612 * Init the table, in case it wasn't done yet.
8613 */
8614 table->ga_itemsize = sizeof(attrentry_T);
8615 table->ga_growsize = 7;
8616
8617 /*
8618 * Try to find an entry with the same specifications.
8619 */
8620 for (i = 0; i < table->ga_len; ++i)
8621 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008622 taep = &(((attrentry_T *)table->ga_data)[i]);
8623 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008624 && (
8625#ifdef FEAT_GUI
8626 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008627 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8628 && aep->ae_u.gui.bg_color
8629 == taep->ae_u.gui.bg_color
8630 && aep->ae_u.gui.sp_color
8631 == taep->ae_u.gui.sp_color
8632 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008633# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008634 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008635# endif
8636 ))
8637 ||
8638#endif
8639 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008640 && (aep->ae_u.term.start == NULL)
8641 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008642 && (aep->ae_u.term.start == NULL
8643 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008644 taep->ae_u.term.start) == 0)
8645 && (aep->ae_u.term.stop == NULL)
8646 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008647 && (aep->ae_u.term.stop == NULL
8648 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008649 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008650 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008651 && aep->ae_u.cterm.fg_color
8652 == taep->ae_u.cterm.fg_color
8653 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008654 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008655#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008656 && aep->ae_u.cterm.fg_rgb
8657 == taep->ae_u.cterm.fg_rgb
8658 && aep->ae_u.cterm.bg_rgb
8659 == taep->ae_u.cterm.bg_rgb
8660#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008661 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662
8663 return i + ATTR_OFF;
8664 }
8665
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008666 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008667 {
8668 /*
8669 * Running out of attribute entries! remove all attributes, and
8670 * compute new ones for all groups.
8671 * When called recursively, we are really out of numbers.
8672 */
8673 if (recursive)
8674 {
8675 EMSG(_("E424: Too many different highlighting attributes in use"));
8676 return 0;
8677 }
8678 recursive = TRUE;
8679
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008680 clear_hl_tables();
8681
Bram Moolenaar071d4272004-06-13 20:20:40 +00008682 must_redraw = CLEAR;
8683
8684 for (i = 0; i < highlight_ga.ga_len; ++i)
8685 set_hl_attr(i);
8686
8687 recursive = FALSE;
8688 }
8689
8690 /*
8691 * This is a new combination of colors and font, add an entry.
8692 */
8693 if (ga_grow(table, 1) == FAIL)
8694 return 0;
8695
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008696 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8697 vim_memset(taep, 0, sizeof(attrentry_T));
8698 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008699#ifdef FEAT_GUI
8700 if (table == &gui_attr_table)
8701 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008702 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8703 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8704 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8705 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008706# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008707 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008708# endif
8709 }
8710#endif
8711 if (table == &term_attr_table)
8712 {
8713 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008714 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008715 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008716 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008717 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008718 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008719 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008720 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008721 }
8722 else if (table == &cterm_attr_table)
8723 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008724 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8725 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008726#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008727 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8728 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8729#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008730 }
8731 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008732 return (table->ga_len - 1 + ATTR_OFF);
8733}
8734
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008735/*
8736 * Clear all highlight tables.
8737 */
8738 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008739clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008740{
8741 int i;
8742 attrentry_T *taep;
8743
8744#ifdef FEAT_GUI
8745 ga_clear(&gui_attr_table);
8746#endif
8747 for (i = 0; i < term_attr_table.ga_len; ++i)
8748 {
8749 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8750 vim_free(taep->ae_u.term.start);
8751 vim_free(taep->ae_u.term.stop);
8752 }
8753 ga_clear(&term_attr_table);
8754 ga_clear(&cterm_attr_table);
8755}
8756
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008757#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008758/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008759 * Combine special attributes (e.g., for spelling) with other attributes
8760 * (e.g., for syntax highlighting).
8761 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008762 * This creates a new group when required.
8763 * Since we expect there to be few spelling mistakes we don't cache the
8764 * result.
8765 * Return the resulting attributes.
8766 */
8767 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008768hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008769{
8770 attrentry_T *char_aep = NULL;
8771 attrentry_T *spell_aep;
8772 attrentry_T new_en;
8773
8774 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008775 return prim_attr;
8776 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8777 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008778#ifdef FEAT_GUI
8779 if (gui.in_use)
8780 {
8781 if (char_attr > HL_ALL)
8782 char_aep = syn_gui_attr2entry(char_attr);
8783 if (char_aep != NULL)
8784 new_en = *char_aep;
8785 else
8786 {
8787 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008788 new_en.ae_u.gui.fg_color = INVALCOLOR;
8789 new_en.ae_u.gui.bg_color = INVALCOLOR;
8790 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008791 if (char_attr <= HL_ALL)
8792 new_en.ae_attr = char_attr;
8793 }
8794
Bram Moolenaar30abd282005-06-22 22:35:10 +00008795 if (prim_attr <= HL_ALL)
8796 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008797 else
8798 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008799 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008800 if (spell_aep != NULL)
8801 {
8802 new_en.ae_attr |= spell_aep->ae_attr;
8803 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8804 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8805 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8806 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8807 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8808 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8809 if (spell_aep->ae_u.gui.font != NOFONT)
8810 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8811# ifdef FEAT_XFONTSET
8812 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8813 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8814# endif
8815 }
8816 }
8817 return get_attr_entry(&gui_attr_table, &new_en);
8818 }
8819#endif
8820
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008821 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008822 {
8823 if (char_attr > HL_ALL)
8824 char_aep = syn_cterm_attr2entry(char_attr);
8825 if (char_aep != NULL)
8826 new_en = *char_aep;
8827 else
8828 {
8829 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008830#ifdef FEAT_TERMGUICOLORS
8831 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8832 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8833#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008834 if (char_attr <= HL_ALL)
8835 new_en.ae_attr = char_attr;
8836 }
8837
Bram Moolenaar30abd282005-06-22 22:35:10 +00008838 if (prim_attr <= HL_ALL)
8839 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008840 else
8841 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008842 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008843 if (spell_aep != NULL)
8844 {
8845 new_en.ae_attr |= spell_aep->ae_attr;
8846 if (spell_aep->ae_u.cterm.fg_color > 0)
8847 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8848 if (spell_aep->ae_u.cterm.bg_color > 0)
8849 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008850#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008851 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008852 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008853 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008854 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8855#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008856 }
8857 }
8858 return get_attr_entry(&cterm_attr_table, &new_en);
8859 }
8860
8861 if (char_attr > HL_ALL)
8862 char_aep = syn_term_attr2entry(char_attr);
8863 if (char_aep != NULL)
8864 new_en = *char_aep;
8865 else
8866 {
8867 vim_memset(&new_en, 0, sizeof(new_en));
8868 if (char_attr <= HL_ALL)
8869 new_en.ae_attr = char_attr;
8870 }
8871
Bram Moolenaar30abd282005-06-22 22:35:10 +00008872 if (prim_attr <= HL_ALL)
8873 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008874 else
8875 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008876 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008877 if (spell_aep != NULL)
8878 {
8879 new_en.ae_attr |= spell_aep->ae_attr;
8880 if (spell_aep->ae_u.term.start != NULL)
8881 {
8882 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8883 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8884 }
8885 }
8886 }
8887 return get_attr_entry(&term_attr_table, &new_en);
8888}
8889#endif
8890
Bram Moolenaar071d4272004-06-13 20:20:40 +00008891#ifdef FEAT_GUI
8892
8893 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008894syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008895{
8896 attr -= ATTR_OFF;
8897 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8898 return NULL;
8899 return &(GUI_ATTR_ENTRY(attr));
8900}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008901#endif /* FEAT_GUI */
8902
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008903/*
8904 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8905 * Only to be used when "attr" > HL_ALL.
8906 */
8907 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008908syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008909{
8910 attrentry_T *aep;
8911
8912#ifdef FEAT_GUI
8913 if (gui.in_use)
8914 aep = syn_gui_attr2entry(attr);
8915 else
8916#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008917 if (IS_CTERM)
8918 aep = syn_cterm_attr2entry(attr);
8919 else
8920 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008921
8922 if (aep == NULL) /* highlighting not set */
8923 return 0;
8924 return aep->ae_attr;
8925}
8926
8927
Bram Moolenaar071d4272004-06-13 20:20:40 +00008928 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008929syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008930{
8931 attr -= ATTR_OFF;
8932 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8933 return NULL;
8934 return &(TERM_ATTR_ENTRY(attr));
8935}
8936
8937 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008938syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008939{
8940 attr -= ATTR_OFF;
8941 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8942 return NULL;
8943 return &(CTERM_ATTR_ENTRY(attr));
8944}
8945
8946#define LIST_ATTR 1
8947#define LIST_STRING 2
8948#define LIST_INT 3
8949
8950 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008951highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008952{
8953 struct hl_group *sgp;
8954 int didh = FALSE;
8955
8956 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8957
8958 didh = highlight_list_arg(id, didh, LIST_ATTR,
8959 sgp->sg_term, NULL, "term");
8960 didh = highlight_list_arg(id, didh, LIST_STRING,
8961 0, sgp->sg_start, "start");
8962 didh = highlight_list_arg(id, didh, LIST_STRING,
8963 0, sgp->sg_stop, "stop");
8964
8965 didh = highlight_list_arg(id, didh, LIST_ATTR,
8966 sgp->sg_cterm, NULL, "cterm");
8967 didh = highlight_list_arg(id, didh, LIST_INT,
8968 sgp->sg_cterm_fg, NULL, "ctermfg");
8969 didh = highlight_list_arg(id, didh, LIST_INT,
8970 sgp->sg_cterm_bg, NULL, "ctermbg");
8971
Bram Moolenaar61623362010-07-14 22:04:22 +02008972#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008973 didh = highlight_list_arg(id, didh, LIST_ATTR,
8974 sgp->sg_gui, NULL, "gui");
8975 didh = highlight_list_arg(id, didh, LIST_STRING,
8976 0, sgp->sg_gui_fg_name, "guifg");
8977 didh = highlight_list_arg(id, didh, LIST_STRING,
8978 0, sgp->sg_gui_bg_name, "guibg");
8979 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008980 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008981#endif
8982#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008983 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008984 0, sgp->sg_font_name, "font");
8985#endif
8986
Bram Moolenaar661b1822005-07-28 22:36:45 +00008987 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008988 {
8989 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008990 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008991 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8992 msg_putchar(' ');
8993 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8994 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008995
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008996 if (!didh)
8997 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008998#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008999 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009000 last_set_msg(sgp->sg_scriptID);
9001#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009002}
9003
9004 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009005highlight_list_arg(
9006 int id,
9007 int didh,
9008 int type,
9009 int iarg,
9010 char_u *sarg,
9011 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009012{
9013 char_u buf[100];
9014 char_u *ts;
9015 int i;
9016
Bram Moolenaar661b1822005-07-28 22:36:45 +00009017 if (got_int)
9018 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009019 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9020 {
9021 ts = buf;
9022 if (type == LIST_INT)
9023 sprintf((char *)buf, "%d", iarg - 1);
9024 else if (type == LIST_STRING)
9025 ts = sarg;
9026 else /* type == LIST_ATTR */
9027 {
9028 buf[0] = NUL;
9029 for (i = 0; hl_attr_table[i] != 0; ++i)
9030 {
9031 if (iarg & hl_attr_table[i])
9032 {
9033 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009034 vim_strcat(buf, (char_u *)",", 100);
9035 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009036 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9037 }
9038 }
9039 }
9040
9041 (void)syn_list_header(didh,
9042 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9043 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009044 if (!got_int)
9045 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009046 if (*name != NUL)
9047 {
9048 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
9049 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
9050 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009051 msg_outtrans(ts);
9052 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009053 }
9054 return didh;
9055}
9056
9057#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9058/*
9059 * Return "1" if highlight group "id" has attribute "flag".
9060 * Return NULL otherwise.
9061 */
9062 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009063highlight_has_attr(
9064 int id,
9065 int flag,
9066 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009067{
9068 int attr;
9069
9070 if (id <= 0 || id > highlight_ga.ga_len)
9071 return NULL;
9072
Bram Moolenaar61623362010-07-14 22:04:22 +02009073#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009074 if (modec == 'g')
9075 attr = HL_TABLE()[id - 1].sg_gui;
9076 else
9077#endif
9078 if (modec == 'c')
9079 attr = HL_TABLE()[id - 1].sg_cterm;
9080 else
9081 attr = HL_TABLE()[id - 1].sg_term;
9082
9083 if (attr & flag)
9084 return (char_u *)"1";
9085 return NULL;
9086}
9087#endif
9088
9089#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9090/*
9091 * Return color name of highlight group "id".
9092 */
9093 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009094highlight_color(
9095 int id,
9096 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9097 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009098{
9099 static char_u name[20];
9100 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009101 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009102 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009103 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009104
9105 if (id <= 0 || id > highlight_ga.ga_len)
9106 return NULL;
9107
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009108 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009109 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009110 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009111 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009112 font = TRUE;
9113 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009114 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009115 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9116 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009117 if (modec == 'g')
9118 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009119# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009120# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009121 /* return font name */
9122 if (font)
9123 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009124# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009125
Bram Moolenaar071d4272004-06-13 20:20:40 +00009126 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009127 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009128 {
9129 guicolor_T color;
9130 long_u rgb;
9131 static char_u buf[10];
9132
9133 if (fg)
9134 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009135 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009136# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009137 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009138# else
9139 color = INVALCOLOR;
9140# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009141 else
9142 color = HL_TABLE()[id - 1].sg_gui_bg;
9143 if (color == INVALCOLOR)
9144 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009145 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009146 sprintf((char *)buf, "#%02x%02x%02x",
9147 (unsigned)(rgb >> 16),
9148 (unsigned)(rgb >> 8) & 255,
9149 (unsigned)rgb & 255);
9150 return buf;
9151 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009152# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009153 if (fg)
9154 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009155 if (sp)
9156 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009157 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9158 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009159 if (font || sp)
9160 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009161 if (modec == 'c')
9162 {
9163 if (fg)
9164 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9165 else
9166 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009167 if (n < 0)
9168 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009169 sprintf((char *)name, "%d", n);
9170 return name;
9171 }
9172 /* term doesn't have color */
9173 return NULL;
9174}
9175#endif
9176
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009177#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009178 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009179 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009180/*
9181 * Return color name of highlight group "id" as RGB value.
9182 */
9183 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009184highlight_gui_color_rgb(
9185 int id,
9186 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009187{
9188 guicolor_T color;
9189
9190 if (id <= 0 || id > highlight_ga.ga_len)
9191 return 0L;
9192
9193 if (fg)
9194 color = HL_TABLE()[id - 1].sg_gui_fg;
9195 else
9196 color = HL_TABLE()[id - 1].sg_gui_bg;
9197
9198 if (color == INVALCOLOR)
9199 return 0L;
9200
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009201 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009202}
9203#endif
9204
9205/*
9206 * Output the syntax list header.
9207 * Return TRUE when started a new line.
9208 */
9209 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009210syn_list_header(
9211 int did_header, /* did header already */
9212 int outlen, /* length of string that comes */
9213 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009214{
9215 int endcol = 19;
9216 int newline = TRUE;
9217
9218 if (!did_header)
9219 {
9220 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009221 if (got_int)
9222 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009223 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9224 endcol = 15;
9225 }
9226 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009227 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009228 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009229 if (got_int)
9230 return TRUE;
9231 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009232 else
9233 {
9234 if (msg_col >= endcol) /* wrap around is like starting a new line */
9235 newline = FALSE;
9236 }
9237
9238 if (msg_col >= endcol) /* output at least one space */
9239 endcol = msg_col + 1;
9240 if (Columns <= endcol) /* avoid hang for tiny window */
9241 endcol = Columns - 1;
9242
9243 msg_advance(endcol);
9244
9245 /* Show "xxx" with the attributes. */
9246 if (!did_header)
9247 {
9248 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9249 msg_putchar(' ');
9250 }
9251
9252 return newline;
9253}
9254
9255/*
9256 * Set the attribute numbers for a highlight group.
9257 * Called after one of the attributes has changed.
9258 */
9259 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009260set_hl_attr(
9261 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009262{
9263 attrentry_T at_en;
9264 struct hl_group *sgp = HL_TABLE() + idx;
9265
9266 /* The "Normal" group doesn't need an attribute number */
9267 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9268 return;
9269
9270#ifdef FEAT_GUI
9271 /*
9272 * For the GUI mode: If there are other than "normal" highlighting
9273 * attributes, need to allocate an attr number.
9274 */
9275 if (sgp->sg_gui_fg == INVALCOLOR
9276 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009277 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009278 && sgp->sg_font == NOFONT
9279# ifdef FEAT_XFONTSET
9280 && sgp->sg_fontset == NOFONTSET
9281# endif
9282 )
9283 {
9284 sgp->sg_gui_attr = sgp->sg_gui;
9285 }
9286 else
9287 {
9288 at_en.ae_attr = sgp->sg_gui;
9289 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9290 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009291 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009292 at_en.ae_u.gui.font = sgp->sg_font;
9293# ifdef FEAT_XFONTSET
9294 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9295# endif
9296 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9297 }
9298#endif
9299 /*
9300 * For the term mode: If there are other than "normal" highlighting
9301 * attributes, need to allocate an attr number.
9302 */
9303 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9304 sgp->sg_term_attr = sgp->sg_term;
9305 else
9306 {
9307 at_en.ae_attr = sgp->sg_term;
9308 at_en.ae_u.term.start = sgp->sg_start;
9309 at_en.ae_u.term.stop = sgp->sg_stop;
9310 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9311 }
9312
9313 /*
9314 * For the color term mode: If there are other than "normal"
9315 * highlighting attributes, need to allocate an attr number.
9316 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009317 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009318# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009319 && sgp->sg_gui_fg == INVALCOLOR
9320 && sgp->sg_gui_bg == INVALCOLOR
9321# endif
9322 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009323 sgp->sg_cterm_attr = sgp->sg_cterm;
9324 else
9325 {
9326 at_en.ae_attr = sgp->sg_cterm;
9327 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9328 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009329# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009330 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9331 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009332# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009333 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9334 }
9335}
9336
9337/*
9338 * Lookup a highlight group name and return it's ID.
9339 * If it is not found, 0 is returned.
9340 */
9341 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009342syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009343{
9344 int i;
9345 char_u name_u[200];
9346
9347 /* Avoid using stricmp() too much, it's slow on some systems */
9348 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9349 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009350 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009351 vim_strup(name_u);
9352 for (i = highlight_ga.ga_len; --i >= 0; )
9353 if (HL_TABLE()[i].sg_name_u != NULL
9354 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9355 break;
9356 return i + 1;
9357}
9358
9359#if defined(FEAT_EVAL) || defined(PROTO)
9360/*
9361 * Return TRUE if highlight group "name" exists.
9362 */
9363 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009364highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009365{
9366 return (syn_name2id(name) > 0);
9367}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009368
9369# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9370/*
9371 * Return the name of highlight group "id".
9372 * When not a valid ID return an empty string.
9373 */
9374 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009375syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009376{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009377 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009378 return (char_u *)"";
9379 return HL_TABLE()[id - 1].sg_name;
9380}
9381# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009382#endif
9383
9384/*
9385 * Like syn_name2id(), but take a pointer + length argument.
9386 */
9387 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009388syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009389{
9390 char_u *name;
9391 int id = 0;
9392
9393 name = vim_strnsave(linep, len);
9394 if (name != NULL)
9395 {
9396 id = syn_name2id(name);
9397 vim_free(name);
9398 }
9399 return id;
9400}
9401
9402/*
9403 * Find highlight group name in the table and return it's ID.
9404 * The argument is a pointer to the name and the length of the name.
9405 * If it doesn't exist yet, a new entry is created.
9406 * Return 0 for failure.
9407 */
9408 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009409syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009410{
9411 int id;
9412 char_u *name;
9413
9414 name = vim_strnsave(pp, len);
9415 if (name == NULL)
9416 return 0;
9417
9418 id = syn_name2id(name);
9419 if (id == 0) /* doesn't exist yet */
9420 id = syn_add_group(name);
9421 else
9422 vim_free(name);
9423 return id;
9424}
9425
9426/*
9427 * Add new highlight group and return it's ID.
9428 * "name" must be an allocated string, it will be consumed.
9429 * Return 0 for failure.
9430 */
9431 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009432syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009433{
9434 char_u *p;
9435
9436 /* Check that the name is ASCII letters, digits and underscore. */
9437 for (p = name; *p != NUL; ++p)
9438 {
9439 if (!vim_isprintc(*p))
9440 {
9441 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009442 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009443 return 0;
9444 }
9445 else if (!ASCII_ISALNUM(*p) && *p != '_')
9446 {
9447 /* This is an error, but since there previously was no check only
9448 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009449 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009450 MSG(_("W18: Invalid character in group name"));
9451 break;
9452 }
9453 }
9454
9455 /*
9456 * First call for this growarray: init growing array.
9457 */
9458 if (highlight_ga.ga_data == NULL)
9459 {
9460 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9461 highlight_ga.ga_growsize = 10;
9462 }
9463
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009464 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009465 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009466 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009467 vim_free(name);
9468 return 0;
9469 }
9470
Bram Moolenaar071d4272004-06-13 20:20:40 +00009471 /*
9472 * Make room for at least one other syntax_highlight entry.
9473 */
9474 if (ga_grow(&highlight_ga, 1) == FAIL)
9475 {
9476 vim_free(name);
9477 return 0;
9478 }
9479
9480 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9481 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9482 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009483#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009484 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9485 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009486# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009487 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009488# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009489#endif
9490 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009491
9492 return highlight_ga.ga_len; /* ID is index plus one */
9493}
9494
9495/*
9496 * When, just after calling syn_add_group(), an error is discovered, this
9497 * function deletes the new name.
9498 */
9499 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009500syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009501{
9502 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009503 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9504 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9505}
9506
9507/*
9508 * Translate a group ID to highlight attributes.
9509 */
9510 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009511syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009512{
9513 int attr;
9514 struct hl_group *sgp;
9515
9516 hl_id = syn_get_final_id(hl_id);
9517 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9518
9519#ifdef FEAT_GUI
9520 /*
9521 * Only use GUI attr when the GUI is being used.
9522 */
9523 if (gui.in_use)
9524 attr = sgp->sg_gui_attr;
9525 else
9526#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009527 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009528 attr = sgp->sg_cterm_attr;
9529 else
9530 attr = sgp->sg_term_attr;
9531
9532 return attr;
9533}
9534
9535#ifdef FEAT_GUI
9536/*
9537 * Get the GUI colors and attributes for a group ID.
9538 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9539 */
9540 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009541syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009542{
9543 struct hl_group *sgp;
9544
9545 hl_id = syn_get_final_id(hl_id);
9546 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9547
9548 *fgp = sgp->sg_gui_fg;
9549 *bgp = sgp->sg_gui_bg;
9550 return sgp->sg_gui;
9551}
9552#endif
9553
9554/*
9555 * Translate a group ID to the final group ID (following links).
9556 */
9557 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009558syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009559{
9560 int count;
9561 struct hl_group *sgp;
9562
9563 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9564 return 0; /* Can be called from eval!! */
9565
9566 /*
9567 * Follow links until there is no more.
9568 * Look out for loops! Break after 100 links.
9569 */
9570 for (count = 100; --count >= 0; )
9571 {
9572 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9573 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9574 break;
9575 hl_id = sgp->sg_link;
9576 }
9577
9578 return hl_id;
9579}
9580
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009581#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009582/*
9583 * Call this function just after the GUI has started.
9584 * It finds the font and color handles for the highlighting groups.
9585 */
9586 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009587highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009588{
9589 int idx;
9590
9591 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009592# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9593# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009594 if (USE_24BIT)
9595# endif
9596 set_normal_colors();
9597# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009598
9599 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9600 gui_do_one_color(idx, FALSE, FALSE);
9601
9602 highlight_changed();
9603}
9604
9605 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009606gui_do_one_color(
9607 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009608 int do_menu UNUSED, /* TRUE: might set the menu font */
9609 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009610{
9611 int didit = FALSE;
9612
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009613# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009614# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009615 if (gui.in_use)
9616# endif
9617 if (HL_TABLE()[idx].sg_font_name != NULL)
9618 {
9619 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009620 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009621 didit = TRUE;
9622 }
9623# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009624 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9625 {
9626 HL_TABLE()[idx].sg_gui_fg =
9627 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9628 didit = TRUE;
9629 }
9630 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9631 {
9632 HL_TABLE()[idx].sg_gui_bg =
9633 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9634 didit = TRUE;
9635 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009636# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009637 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9638 {
9639 HL_TABLE()[idx].sg_gui_sp =
9640 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9641 didit = TRUE;
9642 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009643# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009644 if (didit) /* need to get a new attr number */
9645 set_hl_attr(idx);
9646}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009647#endif
9648
9649/*
9650 * Translate the 'highlight' option into attributes in highlight_attr[] and
9651 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9652 * corresponding highlights to use on top of HLF_SNC is computed.
9653 * Called only when the 'highlight' option has been changed and upon first
9654 * screen redraw after any :highlight command.
9655 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9656 */
9657 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009658highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009659{
9660 int hlf;
9661 int i;
9662 char_u *p;
9663 int attr;
9664 char_u *end;
9665 int id;
9666#ifdef USER_HIGHLIGHT
9667 char_u userhl[10];
9668# ifdef FEAT_STL_OPT
9669 int id_SNC = -1;
9670 int id_S = -1;
9671 int hlcnt;
9672# endif
9673#endif
9674 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9675
9676 need_highlight_changed = FALSE;
9677
9678 /*
9679 * Clear all attributes.
9680 */
9681 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9682 highlight_attr[hlf] = 0;
9683
9684 /*
9685 * First set all attributes to their default value.
9686 * Then use the attributes from the 'highlight' option.
9687 */
9688 for (i = 0; i < 2; ++i)
9689 {
9690 if (i)
9691 p = p_hl;
9692 else
9693 p = get_highlight_default();
9694 if (p == NULL) /* just in case */
9695 continue;
9696
9697 while (*p)
9698 {
9699 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9700 if (hl_flags[hlf] == *p)
9701 break;
9702 ++p;
9703 if (hlf == (int)HLF_COUNT || *p == NUL)
9704 return FAIL;
9705
9706 /*
9707 * Allow several hl_flags to be combined, like "bu" for
9708 * bold-underlined.
9709 */
9710 attr = 0;
9711 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9712 {
9713 if (vim_iswhite(*p)) /* ignore white space */
9714 continue;
9715
9716 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9717 return FAIL;
9718
9719 switch (*p)
9720 {
9721 case 'b': attr |= HL_BOLD;
9722 break;
9723 case 'i': attr |= HL_ITALIC;
9724 break;
9725 case '-':
9726 case 'n': /* no highlighting */
9727 break;
9728 case 'r': attr |= HL_INVERSE;
9729 break;
9730 case 's': attr |= HL_STANDOUT;
9731 break;
9732 case 'u': attr |= HL_UNDERLINE;
9733 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009734 case 'c': attr |= HL_UNDERCURL;
9735 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009736 case ':': ++p; /* highlight group name */
9737 if (attr || *p == NUL) /* no combinations */
9738 return FAIL;
9739 end = vim_strchr(p, ',');
9740 if (end == NULL)
9741 end = p + STRLEN(p);
9742 id = syn_check_group(p, (int)(end - p));
9743 if (id == 0)
9744 return FAIL;
9745 attr = syn_id2attr(id);
9746 p = end - 1;
9747#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9748 if (hlf == (int)HLF_SNC)
9749 id_SNC = syn_get_final_id(id);
9750 else if (hlf == (int)HLF_S)
9751 id_S = syn_get_final_id(id);
9752#endif
9753 break;
9754 default: return FAIL;
9755 }
9756 }
9757 highlight_attr[hlf] = attr;
9758
9759 p = skip_to_option_part(p); /* skip comma and spaces */
9760 }
9761 }
9762
9763#ifdef USER_HIGHLIGHT
9764 /* Setup the user highlights
9765 *
9766 * Temporarily utilize 10 more hl entries. Have to be in there
9767 * simultaneously in case of table overflows in get_attr_entry()
9768 */
9769# ifdef FEAT_STL_OPT
9770 if (ga_grow(&highlight_ga, 10) == FAIL)
9771 return FAIL;
9772 hlcnt = highlight_ga.ga_len;
9773 if (id_S == 0)
9774 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009775 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009776 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9777 id_S = hlcnt + 10;
9778 }
9779# endif
9780 for (i = 0; i < 9; i++)
9781 {
9782 sprintf((char *)userhl, "User%d", i + 1);
9783 id = syn_name2id(userhl);
9784 if (id == 0)
9785 {
9786 highlight_user[i] = 0;
9787# ifdef FEAT_STL_OPT
9788 highlight_stlnc[i] = 0;
9789# endif
9790 }
9791 else
9792 {
9793# ifdef FEAT_STL_OPT
9794 struct hl_group *hlt = HL_TABLE();
9795# endif
9796
9797 highlight_user[i] = syn_id2attr(id);
9798# ifdef FEAT_STL_OPT
9799 if (id_SNC == 0)
9800 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009801 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009802 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9803 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009804# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009805 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9806# endif
9807 }
9808 else
9809 mch_memmove(&hlt[hlcnt + i],
9810 &hlt[id_SNC - 1],
9811 sizeof(struct hl_group));
9812 hlt[hlcnt + i].sg_link = 0;
9813
9814 /* Apply difference between UserX and HLF_S to HLF_SNC */
9815 hlt[hlcnt + i].sg_term ^=
9816 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9817 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9818 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9819 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9820 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9821 hlt[hlcnt + i].sg_cterm ^=
9822 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9823 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9824 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9825 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9826 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009827# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009828 hlt[hlcnt + i].sg_gui ^=
9829 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009830# endif
9831# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009832 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9833 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9834 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9835 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009836 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9837 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009838 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9839 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9840# ifdef FEAT_XFONTSET
9841 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9842 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9843# endif
9844# endif
9845 highlight_ga.ga_len = hlcnt + i + 1;
9846 set_hl_attr(hlcnt + i); /* At long last we can apply */
9847 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9848# endif
9849 }
9850 }
9851# ifdef FEAT_STL_OPT
9852 highlight_ga.ga_len = hlcnt;
9853# endif
9854
9855#endif /* USER_HIGHLIGHT */
9856
9857 return OK;
9858}
9859
Bram Moolenaar4f688582007-07-24 12:34:30 +00009860#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009861
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009862static void highlight_list(void);
9863static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009864
9865/*
9866 * Handle command line completion for :highlight command.
9867 */
9868 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009869set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009870{
9871 char_u *p;
9872
9873 /* Default: expand group names */
9874 xp->xp_context = EXPAND_HIGHLIGHT;
9875 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009876 include_link = 2;
9877 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009878
9879 /* (part of) subcommand already typed */
9880 if (*arg != NUL)
9881 {
9882 p = skiptowhite(arg);
9883 if (*p != NUL) /* past "default" or group name */
9884 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009885 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009886 if (STRNCMP("default", arg, p - arg) == 0)
9887 {
9888 arg = skipwhite(p);
9889 xp->xp_pattern = arg;
9890 p = skiptowhite(arg);
9891 }
9892 if (*p != NUL) /* past group name */
9893 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009894 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009895 if (arg[1] == 'i' && arg[0] == 'N')
9896 highlight_list();
9897 if (STRNCMP("link", arg, p - arg) == 0
9898 || STRNCMP("clear", arg, p - arg) == 0)
9899 {
9900 xp->xp_pattern = skipwhite(p);
9901 p = skiptowhite(xp->xp_pattern);
9902 if (*p != NUL) /* past first group name */
9903 {
9904 xp->xp_pattern = skipwhite(p);
9905 p = skiptowhite(xp->xp_pattern);
9906 }
9907 }
9908 if (*p != NUL) /* past group name(s) */
9909 xp->xp_context = EXPAND_NOTHING;
9910 }
9911 }
9912 }
9913}
9914
9915/*
9916 * List highlighting matches in a nice way.
9917 */
9918 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009919highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009920{
9921 int i;
9922
9923 for (i = 10; --i >= 0; )
9924 highlight_list_two(i, hl_attr(HLF_D));
9925 for (i = 40; --i >= 0; )
9926 highlight_list_two(99, 0);
9927}
9928
9929 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009930highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009931{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009932 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009933 msg_clr_eos();
9934 out_flush();
9935 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9936}
9937
9938#endif /* FEAT_CMDL_COMPL */
9939
9940#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9941 || defined(FEAT_SIGNS) || defined(PROTO)
9942/*
9943 * Function given to ExpandGeneric() to obtain the list of group names.
9944 * Also used for synIDattr() function.
9945 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009946 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009947get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009948{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009949#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009950 if (idx == highlight_ga.ga_len && include_none != 0)
9951 return (char_u *)"none";
9952 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009953 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009954 if (idx == highlight_ga.ga_len + include_none + include_default
9955 && include_link != 0)
9956 return (char_u *)"link";
9957 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9958 && include_link != 0)
9959 return (char_u *)"clear";
9960#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009961 if (idx < 0 || idx >= highlight_ga.ga_len)
9962 return NULL;
9963 return HL_TABLE()[idx].sg_name;
9964}
9965#endif
9966
Bram Moolenaar4f688582007-07-24 12:34:30 +00009967#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009968/*
9969 * Free all the highlight group fonts.
9970 * Used when quitting for systems which need it.
9971 */
9972 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009973free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009974{
9975 int idx;
9976
9977 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9978 {
9979 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9980 HL_TABLE()[idx].sg_font = NOFONT;
9981# ifdef FEAT_XFONTSET
9982 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9983 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9984# endif
9985 }
9986
9987 gui_mch_free_font(gui.norm_font);
9988# ifdef FEAT_XFONTSET
9989 gui_mch_free_fontset(gui.fontset);
9990# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009991# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009992 gui_mch_free_font(gui.bold_font);
9993 gui_mch_free_font(gui.ital_font);
9994 gui_mch_free_font(gui.boldital_font);
9995# endif
9996}
9997#endif
9998
9999/**************************************
10000 * End of Highlighting stuff *
10001 **************************************/