blob: 2b06b464bf6f9625784a5fd2d5e3a5636cb538fb [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010025 int sg_cleared; /* "hi clear" was used */
Bram Moolenaar071d4272004-06-13 20:20:40 +000026/* for normal terminals */
27 int sg_term; /* "term=" highlighting attributes */
28 char_u *sg_start; /* terminal string for start highl */
29 char_u *sg_stop; /* terminal string for stop highl */
30 int sg_term_attr; /* Screen attr for term mode */
31/* for color terminals */
32 int sg_cterm; /* "cterm=" highlighting attr */
33 int sg_cterm_bold; /* bold attr was set for light color */
34 int sg_cterm_fg; /* terminal fg color number + 1 */
35 int sg_cterm_bg; /* terminal bg color number + 1 */
36 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000037/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000040 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020041#endif
42#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000044 GuiFont sg_font; /* GUI font handle */
45#ifdef FEAT_XFONTSET
46 GuiFontset sg_fontset; /* GUI fontset handle */
47#endif
48 char_u *sg_font_name; /* GUI font or fontset name */
49 int sg_gui_attr; /* Screen attr for GUI mode */
50#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020051#if defined(FEAT_GUI) || defined(FEAT_EVAL)
52/* Store the sp color name for the GUI or synIDattr() */
53 int sg_gui; /* "gui=" highlighting attributes */
54 char_u *sg_gui_fg_name;/* GUI foreground color name */
55 char_u *sg_gui_bg_name;/* GUI background color name */
56 char_u *sg_gui_sp_name;/* GUI special color name */
57#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 int sg_link; /* link to this highlight group ID */
59 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000060#ifdef FEAT_EVAL
61 scid_T sg_scriptID; /* script in which the group was last set */
62#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000063};
64
65#define SG_TERM 1 /* term has been set */
66#define SG_CTERM 2 /* cterm has been set */
67#define SG_GUI 4 /* gui has been set */
68#define SG_LINK 8 /* link has been set */
69
70static garray_T highlight_ga; /* highlight groups for 'highlight' option */
71
72#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
73
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020074#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
75
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000077/* Flags to indicate an additional string for highlight name completion. */
78static int include_none = 0; /* when 1 include "None" */
79static int include_default = 0; /* when 1 include "default" */
80static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000081#endif
82
83/*
84 * The "term", "cterm" and "gui" arguments can be any combination of the
85 * following names, separated by commas (but no spaces!).
86 */
87static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {"bold", "standout", "underline", "undercurl",
Bram Moolenaar0cd2a942017-08-12 15:12:30 +020089 "italic", "reverse", "inverse", "nocombine", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaar0cd2a942017-08-12 15:12:30 +020091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, 0};
92#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
Bram Moolenaar071d4272004-06-13 20:20:40 +000093
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010094static int get_attr_entry(garray_T *table, attrentry_T *aep);
95static void syn_unadd_group(void);
96static void set_hl_attr(int idx);
97static void highlight_list_one(int id);
98static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
99static int syn_add_group(char_u *name);
100static int syn_list_header(int did_header, int outlen, int id);
101static int hl_has_settings(int idx, int check_link);
102static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200104#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100105static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100106static guicolor_T color_name2handle(char_u *name);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200107#endif
108#ifdef FEAT_GUI
109static 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 +0100110static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100112static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000113# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100114static 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 +0000115#endif
116
117/*
118 * An attribute number is the index in attr_table plus ATTR_OFF.
119 */
120#define ATTR_OFF (HL_ALL + 1)
121
122#if defined(FEAT_SYN_HL) || defined(PROTO)
123
124#define SYN_NAMELEN 50 /* maximum length of a syntax name */
125
126/* different types of offsets that are possible */
127#define SPO_MS_OFF 0 /* match start offset */
128#define SPO_ME_OFF 1 /* match end offset */
129#define SPO_HS_OFF 2 /* highl. start offset */
130#define SPO_HE_OFF 3 /* highl. end offset */
131#define SPO_RS_OFF 4 /* region start offset */
132#define SPO_RE_OFF 5 /* region end offset */
133#define SPO_LC_OFF 6 /* leading context offset */
134#define SPO_COUNT 7
135
136static char *(spo_name_tab[SPO_COUNT]) =
137 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
138
139/*
140 * The patterns that are being searched for are stored in a syn_pattern.
141 * A match item consists of one pattern.
142 * A start/end item consists of n start patterns and m end patterns.
143 * A start/skip/end item consists of n start patterns, one skip pattern and m
144 * end patterns.
145 * For the latter two, the patterns are always consecutive: start-skip-end.
146 *
147 * A character offset can be given for the matched text (_m_start and _m_end)
148 * and for the actually highlighted text (_h_start and _h_end).
149 */
150typedef struct syn_pattern
151{
152 char sp_type; /* see SPTYPE_ defines below */
153 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200154 int sp_flags; /* see HL_ defines below */
155#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200156 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200157#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158 struct sp_syn sp_syn; /* struct passed to in_id_list() */
159 short sp_syn_match_id; /* highlight group ID of pattern */
160 char_u *sp_pattern; /* regexp to match, pattern */
161 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200162#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200163 syn_time_T sp_time;
164#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165 int sp_ic; /* ignore-case flag for sp_prog */
166 short sp_off_flags; /* see below */
167 int sp_offsets[SPO_COUNT]; /* offsets */
168 short *sp_cont_list; /* cont. group IDs, if non-zero */
169 short *sp_next_list; /* next group IDs, if non-zero */
170 int sp_sync_idx; /* sync item index (syncing only) */
171 int sp_line_id; /* ID of last line where tried */
172 int sp_startcol; /* next match in sp_line_id line */
173} synpat_T;
174
175/* The sp_off_flags are computed like this:
176 * offset from the start of the matched text: (1 << SPO_XX_OFF)
177 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
178 * When both are present, only one is used.
179 */
180
181#define SPTYPE_MATCH 1 /* match keyword with this group ID */
182#define SPTYPE_START 2 /* match a regexp, start of item */
183#define SPTYPE_END 3 /* match a regexp, end of item */
184#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
185
Bram Moolenaar071d4272004-06-13 20:20:40 +0000186
187#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
188
189#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
190
191/*
192 * Flags for b_syn_sync_flags:
193 */
194#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
195#define SF_MATCH 0x02 /* sync by matching a pattern */
196
197#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
198
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199#define MAXKEYWLEN 80 /* maximum length of a keyword */
200
201/*
202 * The attributes of the syntax item that has been recognized.
203 */
204static int current_attr = 0; /* attr of current syntax word */
205#ifdef FEAT_EVAL
206static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000207static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200209#ifdef FEAT_CONCEAL
210static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200211static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200212static int current_sub_char = 0;
213#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000214
Bram Moolenaar217ad922005-03-20 22:37:15 +0000215typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216{
217 char_u *scl_name; /* syntax cluster name */
218 char_u *scl_name_u; /* uppercase of scl_name */
219 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000220} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221
222/*
223 * Methods of combining two clusters
224 */
225#define CLUSTER_REPLACE 1 /* replace first list with second */
226#define CLUSTER_ADD 2 /* add second list to first */
227#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
228
Bram Moolenaar217ad922005-03-20 22:37:15 +0000229#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000230
231/*
232 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200233 * 0 - 19999 normal syntax groups
234 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
235 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
236 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
237 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200239#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200240#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
241#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
242#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
243
Bram Moolenaar42431a72011-04-01 14:44:59 +0200244#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
245#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000246
247/*
248 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
249 * expand_filename(). Most of the other syntax commands don't need it, so
250 * instead of passing it to them, we stow it here.
251 */
252static char_u **syn_cmdlinep;
253
254/*
255 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200256 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000257 * rules in each ":syn include"'d file.
258 */
259static int current_syn_inc_tag = 0;
260static int running_syn_inc_tag = 0;
261
262/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000263 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
264 * This avoids adding a pointer to the hashtable item.
265 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
266 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
267 * HI2KE() converts a hashitem pointer to a var pointer.
268 */
269static keyentry_T dumkey;
270#define KE2HIKEY(kp) ((kp)->keyword)
271#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
272#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
273
274/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 * To reduce the time spent in keepend(), remember at which level in the state
276 * stack the first item with "keepend" is present. When "-1", there is no
277 * "keepend" on the stack.
278 */
279static int keepend_level = -1;
280
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200281static char msg_no_items[] = N_("No Syntax items defined for this buffer");
282
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283/*
284 * For the current state we need to remember more than just the idx.
285 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
286 * (The end positions have the column number of the next char)
287 */
288typedef struct state_item
289{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000290 int si_idx; /* index of syntax pattern or
291 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000292 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000293 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000294 int si_m_lnum; /* lnum of the match */
295 int si_m_startcol; /* starting column of the match */
296 lpos_T si_m_endpos; /* just after end posn of the match */
297 lpos_T si_h_startpos; /* start position of the highlighting */
298 lpos_T si_h_endpos; /* end position of the highlighting */
299 lpos_T si_eoe_pos; /* end position of end pattern */
300 int si_end_idx; /* group ID for end pattern or zero */
301 int si_ends; /* if match ends before si_m_endpos */
302 int si_attr; /* attributes in this state */
303 long si_flags; /* HL_HAS_EOL flag in this state, and
304 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200305#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200306 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200307 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200308#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 short *si_cont_list; /* list of contained groups */
310 short *si_next_list; /* nextgroup IDs after this item ends */
311 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
312 * pattern */
313} stateitem_T;
314
315#define KEYWORD_IDX -1 /* value of si_idx for keywords */
316#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
317 but contained groups */
318
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200319#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100320static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200321#endif
322
Bram Moolenaar071d4272004-06-13 20:20:40 +0000323/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000324 * Struct to reduce the number of arguments to get_syn_options(), it's used
325 * very often.
326 */
327typedef struct
328{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000329 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000330 int keyword; /* TRUE for ":syn keyword" */
331 int *sync_idx; /* syntax item for "grouphere" argument, NULL
332 if not allowed */
333 char has_cont_list; /* TRUE if "cont_list" can be used */
334 short *cont_list; /* group IDs for "contains" argument */
335 short *cont_in_list; /* group IDs for "containedin" argument */
336 short *next_list; /* group IDs for "nextgroup" argument */
337} syn_opt_arg_T;
338
339/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340 * The next possible match in the current line for any pattern is remembered,
341 * to avoid having to try for a match in each column.
342 * If next_match_idx == -1, not tried (in this line) yet.
343 * If next_match_col == MAXCOL, no match found in this line.
344 * (All end positions have the column of the char after the end)
345 */
346static int next_match_col; /* column for start of next match */
347static lpos_T next_match_m_endpos; /* position for end of next match */
348static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
349static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
350static int next_match_idx; /* index of matched item */
351static long next_match_flags; /* flags for next match */
352static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
353static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
354static int next_match_end_idx; /* ID of group for end pattn or zero */
355static reg_extmatch_T *next_match_extmatch = NULL;
356
357/*
358 * A state stack is an array of integers or stateitem_T, stored in a
359 * garray_T. A state stack is invalid if it's itemsize entry is zero.
360 */
361#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
362#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
363
364/*
365 * The current state (within the line) of the recognition engine.
366 * When current_state.ga_itemsize is 0 the current state is invalid.
367 */
368static win_T *syn_win; /* current window for highlighting */
369static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200370static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200371#ifdef FEAT_RELTIME
372static proftime_T *syn_tm;
373#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000374static linenr_T current_lnum = 0; /* lnum of current state */
375static colnr_T current_col = 0; /* column of current state */
376static int current_state_stored = 0; /* TRUE if stored current state
377 * after setting current_finished */
378static int current_finished = 0; /* current line has been finished */
379static garray_T current_state /* current stack of state_items */
380 = {0, 0, 0, 0, NULL};
381static short *current_next_list = NULL; /* when non-zero, nextgroup list */
382static int current_next_flags = 0; /* flags for current_next_list */
383static int current_line_id = 0; /* unique number for current line */
384
385#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
386
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100387static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100388static void save_chartab(char_u *chartab);
389static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100390static int syn_match_linecont(linenr_T lnum);
391static void syn_start_line(void);
392static void syn_update_ends(int startofline);
393static void syn_stack_alloc(void);
394static int syn_stack_cleanup(void);
395static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
396static synstate_T *syn_stack_find_entry(linenr_T lnum);
397static synstate_T *store_current_state(void);
398static void load_current_state(synstate_T *from);
399static void invalidate_current_state(void);
400static int syn_stack_equal(synstate_T *sp);
401static void validate_current_state(void);
402static int syn_finish_line(int syncing);
403static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
404static int did_match_already(int idx, garray_T *gap);
405static stateitem_T *push_next_match(stateitem_T *cur_si);
406static void check_state_ends(void);
407static void update_si_attr(int idx);
408static void check_keepend(void);
409static void update_si_end(stateitem_T *sip, int startcol, int force);
410static short *copy_id_list(short *list);
411static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
412static int push_current_state(int idx);
413static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200414#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100415static void syn_clear_time(syn_time_T *tt);
416static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200417#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100418static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200419#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100420static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200421#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100422static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200423static int syn_time_on = FALSE;
424# define IF_SYN_TIME(p) (p)
425#else
426# define IF_SYN_TIME(p) NULL
427typedef int syn_time_T;
428#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100430static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
431static 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);
432static void clear_syn_state(synstate_T *p);
433static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100435static void limit_pos(lpos_T *pos, lpos_T *limit);
436static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
437static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
438static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
439static char_u *syn_getcurline(void);
440static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
441static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
442static void syn_cmd_case(exarg_T *eap, int syncing);
443static void syn_cmd_spell(exarg_T *eap, int syncing);
444static void syntax_sync_clear(void);
445static void syn_remove_pattern(synblock_T *block, int idx);
446static void syn_clear_pattern(synblock_T *block, int i);
447static void syn_clear_cluster(synblock_T *block, int i);
448static void syn_cmd_clear(exarg_T *eap, int syncing);
449static void syn_cmd_conceal(exarg_T *eap, int syncing);
450static void syn_clear_one(int id, int syncing);
451static void syn_cmd_on(exarg_T *eap, int syncing);
452static void syn_cmd_enable(exarg_T *eap, int syncing);
453static void syn_cmd_reset(exarg_T *eap, int syncing);
454static void syn_cmd_manual(exarg_T *eap, int syncing);
455static void syn_cmd_off(exarg_T *eap, int syncing);
456static void syn_cmd_onoff(exarg_T *eap, char *name);
457static void syn_cmd_list(exarg_T *eap, int syncing);
458static void syn_lines_msg(void);
459static void syn_match_msg(void);
460static void syn_stack_free_block(synblock_T *block);
461static void syn_list_one(int id, int syncing, int link_only);
462static void syn_list_cluster(int id);
463static void put_id_list(char_u *name, short *list, int attr);
464static void put_pattern(char *s, int c, synpat_T *spp, int attr);
465static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
466static void syn_clear_keyword(int id, hashtab_T *ht);
467static void clear_keywtab(hashtab_T *ht);
468static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
469static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100470static 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 +0100471static void syn_cmd_include(exarg_T *eap, int syncing);
472static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
473static void syn_cmd_keyword(exarg_T *eap, int syncing);
474static void syn_cmd_match(exarg_T *eap, int syncing);
475static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100477static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100479static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100481static void syn_cmd_cluster(exarg_T *eap, int syncing);
482static int syn_scl_name2id(char_u *name);
483static int syn_scl_namen2id(char_u *linep, int len);
484static int syn_check_cluster(char_u *pp, int len);
485static int syn_add_cluster(char_u *name);
486static void init_syn_patterns(void);
487static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
488static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100489static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100490static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
491static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492
493/*
494 * Start the syntax recognition for a line. This function is normally called
495 * from the screen updating, once for each displayed line.
496 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
497 * it. Careful: curbuf and curwin are likely to point to another buffer and
498 * window.
499 */
500 void
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200501syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502{
503 synstate_T *p;
504 synstate_T *last_valid = NULL;
505 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000506 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507 linenr_T parsed_lnum;
508 linenr_T first_stored;
509 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100510 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200512#ifdef FEAT_CONCEAL
513 current_sub_char = NUL;
514#endif
515
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 /*
517 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000518 * Also do this when a change was made, the current state may be invalid
519 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200521 if (syn_block != wp->w_s
522 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100523 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524 {
525 invalidate_current_state();
526 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200527 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000528 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100529 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 syn_win = wp;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200531#ifdef FEAT_RELTIME
532 syn_tm = syntax_tm;
533#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534
535 /*
536 * Allocate syntax stack when needed.
537 */
538 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200539 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000540 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200541 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000542
543 /*
544 * If the state of the end of the previous line is useful, store it.
545 */
546 if (VALID_STATE(&current_state)
547 && current_lnum < lnum
548 && current_lnum < syn_buf->b_ml.ml_line_count)
549 {
550 (void)syn_finish_line(FALSE);
551 if (!current_state_stored)
552 {
553 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000554 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555 }
556
557 /*
558 * If the current_lnum is now the same as "lnum", keep the current
559 * state (this happens very often!). Otherwise invalidate
560 * current_state and figure it out below.
561 */
562 if (current_lnum != lnum)
563 invalidate_current_state();
564 }
565 else
566 invalidate_current_state();
567
568 /*
569 * Try to synchronize from a saved state in b_sst_array[].
570 * Only do this if lnum is not before and not to far beyond a saved state.
571 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200572 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000573 {
574 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200575 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576 {
577 if (p->sst_lnum > lnum)
578 break;
579 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
580 {
581 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200582 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 last_min_valid = p;
584 }
585 }
586 if (last_min_valid != NULL)
587 load_current_state(last_min_valid);
588 }
589
590 /*
591 * If "lnum" is before or far beyond a line with a saved state, need to
592 * re-synchronize.
593 */
594 if (INVALID_STATE(&current_state))
595 {
596 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200597 if (current_lnum == 1)
598 /* First line is always valid, no matter "minlines". */
599 first_stored = 1;
600 else
601 /* Need to parse "minlines" lines before state can be considered
602 * valid to store. */
603 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604 }
605 else
606 first_stored = current_lnum;
607
608 /*
609 * Advance from the sync point or saved state until the current line.
610 * Save some entries for syncing with later on.
611 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200612 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000613 dist = 999999;
614 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200615 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616 while (current_lnum < lnum)
617 {
618 syn_start_line();
619 (void)syn_finish_line(FALSE);
620 ++current_lnum;
621
622 /* If we parsed at least "minlines" lines or started at a valid
623 * state, the current state is considered valid. */
624 if (current_lnum >= first_stored)
625 {
626 /* Check if the saved state entry is for the current line and is
627 * equal to the current state. If so, then validate all saved
628 * states that depended on a change before the parsed line. */
629 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000630 prev = syn_stack_find_entry(current_lnum - 1);
631 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200632 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000633 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000634 sp = prev;
635 while (sp != NULL && sp->sst_lnum < current_lnum)
636 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 if (sp != NULL
638 && sp->sst_lnum == current_lnum
639 && syn_stack_equal(sp))
640 {
641 parsed_lnum = current_lnum;
642 prev = sp;
643 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
644 {
645 if (sp->sst_lnum <= lnum)
646 /* valid state before desired line, use this one */
647 prev = sp;
648 else if (sp->sst_change_lnum == 0)
649 /* past saved states depending on change, break here. */
650 break;
651 sp->sst_change_lnum = 0;
652 sp = sp->sst_next;
653 }
654 load_current_state(prev);
655 }
656 /* Store the state at this line when it's the first one, the line
657 * where we start parsing, or some distance from the previously
658 * saved state. But only when parsed at least 'minlines'. */
659 else if (prev == NULL
660 || current_lnum == lnum
661 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000662 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000663 }
664
665 /* This can take a long time: break when CTRL-C pressed. The current
666 * state will be wrong then. */
667 line_breakcheck();
668 if (got_int)
669 {
670 current_lnum = lnum;
671 break;
672 }
673 }
674
675 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676}
677
678/*
679 * We cannot simply discard growarrays full of state_items or buf_states; we
680 * have to manually release their extmatch pointers first.
681 */
682 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100683clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684{
685 int i;
686 garray_T *gap;
687
688 if (p->sst_stacksize > SST_FIX_STATES)
689 {
690 gap = &(p->sst_union.sst_ga);
691 for (i = 0; i < gap->ga_len; i++)
692 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
693 ga_clear(gap);
694 }
695 else
696 {
697 for (i = 0; i < p->sst_stacksize; i++)
698 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
699 }
700}
701
702/*
703 * Cleanup the current_state stack.
704 */
705 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100706clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707{
708 int i;
709 stateitem_T *sip;
710
711 sip = (stateitem_T *)(current_state.ga_data);
712 for (i = 0; i < current_state.ga_len; i++)
713 unref_extmatch(sip[i].si_extmatch);
714 ga_clear(&current_state);
715}
716
717/*
718 * Try to find a synchronisation point for line "lnum".
719 *
720 * This sets current_lnum and the current state. One of three methods is
721 * used:
722 * 1. Search backwards for the end of a C-comment.
723 * 2. Search backwards for given sync patterns.
724 * 3. Simply start on a given number of lines above "lnum".
725 */
726 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100727syn_sync(
728 win_T *wp,
729 linenr_T start_lnum,
730 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731{
732 buf_T *curbuf_save;
733 win_T *curwin_save;
734 pos_T cursor_save;
735 int idx;
736 linenr_T lnum;
737 linenr_T end_lnum;
738 linenr_T break_lnum;
739 int had_sync_point;
740 stateitem_T *cur_si;
741 synpat_T *spp;
742 char_u *line;
743 int found_flags = 0;
744 int found_match_idx = 0;
745 linenr_T found_current_lnum = 0;
746 int found_current_col= 0;
747 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000748 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749
750 /*
751 * Clear any current state that might be hanging around.
752 */
753 invalidate_current_state();
754
755 /*
756 * Start at least "minlines" back. Default starting point for parsing is
757 * there.
758 * Start further back, to avoid that scrolling backwards will result in
759 * resyncing for every line. Now it resyncs only one out of N lines,
760 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
761 * Watch out for overflow when minlines is MAXLNUM.
762 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200763 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 start_lnum = 1;
765 else
766 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200767 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000768 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200769 else if (syn_block->b_syn_sync_minlines < 10)
770 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000771 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200772 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
773 if (syn_block->b_syn_sync_maxlines != 0
774 && lnum > syn_block->b_syn_sync_maxlines)
775 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 if (lnum >= start_lnum)
777 start_lnum = 1;
778 else
779 start_lnum -= lnum;
780 }
781 current_lnum = start_lnum;
782
783 /*
784 * 1. Search backwards for the end of a C-style comment.
785 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200786 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000787 {
788 /* Need to make syn_buf the current buffer for a moment, to be able to
789 * use find_start_comment(). */
790 curwin_save = curwin;
791 curwin = wp;
792 curbuf_save = curbuf;
793 curbuf = syn_buf;
794
795 /*
796 * Skip lines that end in a backslash.
797 */
798 for ( ; start_lnum > 1; --start_lnum)
799 {
800 line = ml_get(start_lnum - 1);
801 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
802 break;
803 }
804 current_lnum = start_lnum;
805
806 /* set cursor to start of search */
807 cursor_save = wp->w_cursor;
808 wp->w_cursor.lnum = start_lnum;
809 wp->w_cursor.col = 0;
810
811 /*
812 * If the line is inside a comment, need to find the syntax item that
813 * defines the comment.
814 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
815 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200816 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000817 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200818 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
819 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
820 == syn_block->b_syn_sync_id
821 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000822 {
823 validate_current_state();
824 if (push_current_state(idx) == OK)
825 update_si_attr(current_state.ga_len - 1);
826 break;
827 }
828 }
829
830 /* restore cursor and buffer */
831 wp->w_cursor = cursor_save;
832 curwin = curwin_save;
833 curbuf = curbuf_save;
834 }
835
836 /*
837 * 2. Search backwards for given sync patterns.
838 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200839 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000840 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200841 if (syn_block->b_syn_sync_maxlines != 0
842 && start_lnum > syn_block->b_syn_sync_maxlines)
843 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 else
845 break_lnum = 0;
846
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000847 found_m_endpos.lnum = 0;
848 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849 end_lnum = start_lnum;
850 lnum = start_lnum;
851 while (--lnum > break_lnum)
852 {
853 /* This can take a long time: break when CTRL-C pressed. */
854 line_breakcheck();
855 if (got_int)
856 {
857 invalidate_current_state();
858 current_lnum = start_lnum;
859 break;
860 }
861
862 /* Check if we have run into a valid saved state stack now. */
863 if (last_valid != NULL && lnum == last_valid->sst_lnum)
864 {
865 load_current_state(last_valid);
866 break;
867 }
868
869 /*
870 * Check if the previous line has the line-continuation pattern.
871 */
872 if (lnum > 1 && syn_match_linecont(lnum - 1))
873 continue;
874
875 /*
876 * Start with nothing on the state stack
877 */
878 validate_current_state();
879
880 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
881 {
882 syn_start_line();
883 for (;;)
884 {
885 had_sync_point = syn_finish_line(TRUE);
886 /*
887 * When a sync point has been found, remember where, and
888 * continue to look for another one, further on in the line.
889 */
890 if (had_sync_point && current_state.ga_len)
891 {
892 cur_si = &CUR_STATE(current_state.ga_len - 1);
893 if (cur_si->si_m_endpos.lnum > start_lnum)
894 {
895 /* ignore match that goes to after where started */
896 current_lnum = end_lnum;
897 break;
898 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000899 if (cur_si->si_idx < 0)
900 {
901 /* Cannot happen? */
902 found_flags = 0;
903 found_match_idx = KEYWORD_IDX;
904 }
905 else
906 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200907 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000908 found_flags = spp->sp_flags;
909 found_match_idx = spp->sp_sync_idx;
910 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000911 found_current_lnum = current_lnum;
912 found_current_col = current_col;
913 found_m_endpos = cur_si->si_m_endpos;
914 /*
915 * Continue after the match (be aware of a zero-length
916 * match).
917 */
918 if (found_m_endpos.lnum > current_lnum)
919 {
920 current_lnum = found_m_endpos.lnum;
921 current_col = found_m_endpos.col;
922 if (current_lnum >= end_lnum)
923 break;
924 }
925 else if (found_m_endpos.col > current_col)
926 current_col = found_m_endpos.col;
927 else
928 ++current_col;
929
930 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000931 * an item that ends here, need to do that now. Be
932 * careful not to go past the NUL. */
933 prev_current_col = current_col;
934 if (syn_getcurline()[current_col] != NUL)
935 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000936 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000937 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000938 }
939 else
940 break;
941 }
942 }
943
944 /*
945 * If a sync point was encountered, break here.
946 */
947 if (found_flags)
948 {
949 /*
950 * Put the item that was specified by the sync point on the
951 * state stack. If there was no item specified, make the
952 * state stack empty.
953 */
954 clear_current_state();
955 if (found_match_idx >= 0
956 && push_current_state(found_match_idx) == OK)
957 update_si_attr(current_state.ga_len - 1);
958
959 /*
960 * When using "grouphere", continue from the sync point
961 * match, until the end of the line. Parsing starts at
962 * the next line.
963 * For "groupthere" the parsing starts at start_lnum.
964 */
965 if (found_flags & HL_SYNC_HERE)
966 {
967 if (current_state.ga_len)
968 {
969 cur_si = &CUR_STATE(current_state.ga_len - 1);
970 cur_si->si_h_startpos.lnum = found_current_lnum;
971 cur_si->si_h_startpos.col = found_current_col;
972 update_si_end(cur_si, (int)current_col, TRUE);
973 check_keepend();
974 }
975 current_col = found_m_endpos.col;
976 current_lnum = found_m_endpos.lnum;
977 (void)syn_finish_line(FALSE);
978 ++current_lnum;
979 }
980 else
981 current_lnum = start_lnum;
982
983 break;
984 }
985
986 end_lnum = lnum;
987 invalidate_current_state();
988 }
989
990 /* Ran into start of the file or exceeded maximum number of lines */
991 if (lnum <= break_lnum)
992 {
993 invalidate_current_state();
994 current_lnum = break_lnum + 1;
995 }
996 }
997
998 validate_current_state();
999}
1000
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001001 static void
1002save_chartab(char_u *chartab)
1003{
1004 if (syn_block->b_syn_isk != empty_option)
1005 {
1006 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1007 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1008 (size_t)32);
1009 }
1010}
1011
1012 static void
1013restore_chartab(char_u *chartab)
1014{
1015 if (syn_win->w_s->b_syn_isk != empty_option)
1016 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1017}
1018
Bram Moolenaar071d4272004-06-13 20:20:40 +00001019/*
1020 * Return TRUE if the line-continuation pattern matches in line "lnum".
1021 */
1022 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001023syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024{
1025 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001026 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001027 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001028
Bram Moolenaar860cae12010-06-05 23:22:07 +02001029 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001030 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001031 /* use syntax iskeyword option */
1032 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001033 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1034 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001035 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001036 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001037 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001038 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001039 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 }
1041 return FALSE;
1042}
1043
1044/*
1045 * Prepare the current state for the start of a line.
1046 */
1047 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001048syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049{
1050 current_finished = FALSE;
1051 current_col = 0;
1052
1053 /*
1054 * Need to update the end of a start/skip/end that continues from the
1055 * previous line and regions that have "keepend".
1056 */
1057 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001058 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001060 check_state_ends();
1061 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001062
1063 next_match_idx = -1;
1064 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001065#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001066 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001067#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001068}
1069
1070/*
1071 * Check for items in the stack that need their end updated.
1072 * When "startofline" is TRUE the last item is always updated.
1073 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1074 */
1075 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001076syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001077{
1078 stateitem_T *cur_si;
1079 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001080 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081
1082 if (startofline)
1083 {
1084 /* Check for a match carried over from a previous line with a
1085 * contained region. The match ends as soon as the region ends. */
1086 for (i = 0; i < current_state.ga_len; ++i)
1087 {
1088 cur_si = &CUR_STATE(i);
1089 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001090 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091 == SPTYPE_MATCH
1092 && cur_si->si_m_endpos.lnum < current_lnum)
1093 {
1094 cur_si->si_flags |= HL_MATCHCONT;
1095 cur_si->si_m_endpos.lnum = 0;
1096 cur_si->si_m_endpos.col = 0;
1097 cur_si->si_h_endpos = cur_si->si_m_endpos;
1098 cur_si->si_ends = TRUE;
1099 }
1100 }
1101 }
1102
1103 /*
1104 * Need to update the end of a start/skip/end that continues from the
1105 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001106 * influence contained items. If we've just removed "extend"
1107 * (startofline == 0) then we should update ends of normal regions
1108 * contained inside "keepend" because "extend" could have extended
1109 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 * Then check for items ending in column 0.
1111 */
1112 i = current_state.ga_len - 1;
1113 if (keepend_level >= 0)
1114 for ( ; i > keepend_level; --i)
1115 if (CUR_STATE(i).si_flags & HL_EXTEND)
1116 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001117
1118 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119 for ( ; i < current_state.ga_len; ++i)
1120 {
1121 cur_si = &CUR_STATE(i);
1122 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001123 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 || (i == current_state.ga_len - 1 && startofline))
1125 {
1126 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1127 cur_si->si_h_startpos.lnum = current_lnum;
1128
1129 if (!(cur_si->si_flags & HL_MATCHCONT))
1130 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001131
1132 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1133 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 }
1135 }
1136 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137}
1138
1139/****************************************
1140 * Handling of the state stack cache.
1141 */
1142
1143/*
1144 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1145 *
1146 * To speed up syntax highlighting, the state stack for the start of some
1147 * lines is cached. These entries can be used to start parsing at that point.
1148 *
1149 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1150 * valid entries. b_sst_first points to the first one, then follow sst_next.
1151 * The entries are sorted on line number. The first entry is often for line 2
1152 * (line 1 always starts with an empty stack).
1153 * There is also a list for free entries. This construction is used to avoid
1154 * having to allocate and free memory blocks too often.
1155 *
1156 * When making changes to the buffer, this is logged in b_mod_*. When calling
1157 * update_screen() to update the display, it will call
1158 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1159 * entries. The entries which are inside the changed area are removed,
1160 * because they must be recomputed. Entries below the changed have their line
1161 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1162 * set to indicate that a check must be made if the changed lines would change
1163 * the cached entry.
1164 *
1165 * When later displaying lines, an entry is stored for each line. Displayed
1166 * lines are likely to be displayed again, in which case the state at the
1167 * start of the line is needed.
1168 * For not displayed lines, an entry is stored for every so many lines. These
1169 * entries will be used e.g., when scrolling backwards. The distance between
1170 * entries depends on the number of lines in the buffer. For small buffers
1171 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1172 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1173 */
1174
Bram Moolenaar860cae12010-06-05 23:22:07 +02001175 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001176syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001177{
1178 synstate_T *p;
1179
1180 if (block->b_sst_array != NULL)
1181 {
1182 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1183 clear_syn_state(p);
1184 vim_free(block->b_sst_array);
1185 block->b_sst_array = NULL;
1186 block->b_sst_len = 0;
1187 }
1188}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189/*
1190 * Free b_sst_array[] for buffer "buf".
1191 * Used when syntax items changed to force resyncing everywhere.
1192 */
1193 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001194syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001196#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001198#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199
Bram Moolenaar860cae12010-06-05 23:22:07 +02001200 syn_stack_free_block(block);
1201
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202#ifdef FEAT_FOLDING
1203 /* When using "syntax" fold method, must update all folds. */
1204 FOR_ALL_WINDOWS(wp)
1205 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001206 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001207 foldUpdateAll(wp);
1208 }
1209#endif
1210}
1211
1212/*
1213 * Allocate the syntax state stack for syn_buf when needed.
1214 * If the number of entries in b_sst_array[] is much too big or a bit too
1215 * small, reallocate it.
1216 * Also used to allocate b_sst_array[] for the first time.
1217 */
1218 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001219syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220{
1221 long len;
1222 synstate_T *to, *from;
1223 synstate_T *sstp;
1224
1225 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1226 if (len < SST_MIN_ENTRIES)
1227 len = SST_MIN_ENTRIES;
1228 else if (len > SST_MAX_ENTRIES)
1229 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001230 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 {
1232 /* Allocate 50% too much, to avoid reallocating too often. */
1233 len = syn_buf->b_ml.ml_line_count;
1234 len = (len + len / 2) / SST_DIST + Rows * 2;
1235 if (len < SST_MIN_ENTRIES)
1236 len = SST_MIN_ENTRIES;
1237 else if (len > SST_MAX_ENTRIES)
1238 len = SST_MAX_ENTRIES;
1239
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 {
1242 /* When shrinking the array, cleanup the existing stack.
1243 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001244 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 && syn_stack_cleanup())
1246 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1248 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 }
1250
1251 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1252 if (sstp == NULL) /* out of memory! */
1253 return;
1254
1255 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 {
1258 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001259 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260 from = from->sst_next)
1261 {
1262 ++to;
1263 *to = *from;
1264 to->sst_next = to + 1;
1265 }
1266 }
1267 if (to != sstp - 1)
1268 {
1269 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001270 syn_block->b_sst_first = sstp;
1271 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272 }
1273 else
1274 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001275 syn_block->b_sst_first = NULL;
1276 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 }
1278
1279 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001280 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 while (++to < sstp + len)
1282 to->sst_next = to + 1;
1283 (sstp + len - 1)->sst_next = NULL;
1284
Bram Moolenaar860cae12010-06-05 23:22:07 +02001285 vim_free(syn_block->b_sst_array);
1286 syn_block->b_sst_array = sstp;
1287 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288 }
1289}
1290
1291/*
1292 * Check for changes in a buffer to affect stored syntax states. Uses the
1293 * b_mod_* fields.
1294 * Called from update_screen(), before screen is being updated, once for each
1295 * displayed buffer.
1296 */
1297 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001298syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001300 win_T *wp;
1301
1302 syn_stack_apply_changes_block(&buf->b_s, buf);
1303
1304 FOR_ALL_WINDOWS(wp)
1305 {
1306 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1307 syn_stack_apply_changes_block(wp->w_s, buf);
1308 }
1309}
1310
1311 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001312syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 synstate_T *p, *prev, *np;
1315 linenr_T n;
1316
Bram Moolenaar860cae12010-06-05 23:22:07 +02001317 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 return;
1319
1320 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001321 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 {
1325 n = p->sst_lnum + buf->b_mod_xlines;
1326 if (n <= buf->b_mod_bot)
1327 {
1328 /* this state is inside the changed area, remove it */
1329 np = p->sst_next;
1330 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001331 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 else
1333 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001334 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001335 p = np;
1336 continue;
1337 }
1338 /* This state is below the changed area. Remember the line
1339 * that needs to be parsed before this entry can be made valid
1340 * again. */
1341 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1342 {
1343 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1344 p->sst_change_lnum += buf->b_mod_xlines;
1345 else
1346 p->sst_change_lnum = buf->b_mod_top;
1347 }
1348 if (p->sst_change_lnum == 0
1349 || p->sst_change_lnum < buf->b_mod_bot)
1350 p->sst_change_lnum = buf->b_mod_bot;
1351
1352 p->sst_lnum = n;
1353 }
1354 prev = p;
1355 p = p->sst_next;
1356 }
1357}
1358
1359/*
1360 * Reduce the number of entries in the state stack for syn_buf.
1361 * Returns TRUE if at least one entry was freed.
1362 */
1363 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001364syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365{
1366 synstate_T *p, *prev;
1367 disptick_T tick;
1368 int above;
1369 int dist;
1370 int retval = FALSE;
1371
Bram Moolenaar860cae12010-06-05 23:22:07 +02001372 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 return retval;
1374
1375 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001377 dist = 999999;
1378 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380
1381 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001382 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 * be removed. Set "above" when the "tick" for the oldest entry is above
1384 * "b_sst_lasttick" (the display tick wraps around).
1385 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001386 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001388 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1390 {
1391 if (prev->sst_lnum + dist > p->sst_lnum)
1392 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001393 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 {
1395 if (!above || p->sst_tick < tick)
1396 tick = p->sst_tick;
1397 above = TRUE;
1398 }
1399 else if (!above && p->sst_tick < tick)
1400 tick = p->sst_tick;
1401 }
1402 }
1403
1404 /*
1405 * Go through the list to make the entries for the oldest tick at an
1406 * interval of several lines.
1407 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001408 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001409 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1410 {
1411 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1412 {
1413 /* Move this entry from used list to free list */
1414 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001415 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416 p = prev;
1417 retval = TRUE;
1418 }
1419 }
1420 return retval;
1421}
1422
1423/*
1424 * Free the allocated memory for a syn_state item.
1425 * Move the entry into the free list.
1426 */
1427 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001428syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429{
1430 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001431 p->sst_next = block->b_sst_firstfree;
1432 block->b_sst_firstfree = p;
1433 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434}
1435
1436/*
1437 * Find an entry in the list of state stacks at or before "lnum".
1438 * Returns NULL when there is no entry or the first entry is after "lnum".
1439 */
1440 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001441syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442{
1443 synstate_T *p, *prev;
1444
1445 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001446 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447 {
1448 if (p->sst_lnum == lnum)
1449 return p;
1450 if (p->sst_lnum > lnum)
1451 break;
1452 }
1453 return prev;
1454}
1455
1456/*
1457 * Try saving the current state in b_sst_array[].
1458 * The current state must be valid for the start of the current_lnum line!
1459 */
1460 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001461store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462{
1463 int i;
1464 synstate_T *p;
1465 bufstate_T *bp;
1466 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001467 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468
1469 /*
1470 * If the current state contains a start or end pattern that continues
1471 * from the previous line, we can't use it. Don't store it then.
1472 */
1473 for (i = current_state.ga_len - 1; i >= 0; --i)
1474 {
1475 cur_si = &CUR_STATE(i);
1476 if (cur_si->si_h_startpos.lnum >= current_lnum
1477 || cur_si->si_m_endpos.lnum >= current_lnum
1478 || cur_si->si_h_endpos.lnum >= current_lnum
1479 || (cur_si->si_end_idx
1480 && cur_si->si_eoe_pos.lnum >= current_lnum))
1481 break;
1482 }
1483 if (i >= 0)
1484 {
1485 if (sp != NULL)
1486 {
1487 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001488 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001490 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001491 else
1492 {
1493 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001494 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 if (p->sst_next == sp)
1496 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001497 if (p != NULL) /* just in case */
1498 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001500 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 sp = NULL;
1502 }
1503 }
1504 else if (sp == NULL || sp->sst_lnum != current_lnum)
1505 {
1506 /*
1507 * Add a new entry
1508 */
1509 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001510 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001511 {
1512 (void)syn_stack_cleanup();
1513 /* "sp" may have been moved to the freelist now */
1514 sp = syn_stack_find_entry(current_lnum);
1515 }
1516 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001517 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001518 sp = NULL;
1519 else
1520 {
1521 /* Take the first item from the free list and put it in the used
1522 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001523 p = syn_block->b_sst_firstfree;
1524 syn_block->b_sst_firstfree = p->sst_next;
1525 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 if (sp == NULL)
1527 {
1528 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001529 p->sst_next = syn_block->b_sst_first;
1530 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 }
1532 else
1533 {
1534 /* insert in list after *sp */
1535 p->sst_next = sp->sst_next;
1536 sp->sst_next = p;
1537 }
1538 sp = p;
1539 sp->sst_stacksize = 0;
1540 sp->sst_lnum = current_lnum;
1541 }
1542 }
1543 if (sp != NULL)
1544 {
1545 /* When overwriting an existing state stack, clear it first */
1546 clear_syn_state(sp);
1547 sp->sst_stacksize = current_state.ga_len;
1548 if (current_state.ga_len > SST_FIX_STATES)
1549 {
1550 /* Need to clear it, might be something remaining from when the
1551 * length was less than SST_FIX_STATES. */
1552 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1553 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1554 sp->sst_stacksize = 0;
1555 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1558 }
1559 else
1560 bp = sp->sst_union.sst_stack;
1561 for (i = 0; i < sp->sst_stacksize; ++i)
1562 {
1563 bp[i].bs_idx = CUR_STATE(i).si_idx;
1564 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001565#ifdef FEAT_CONCEAL
1566 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1567 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1568#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1570 }
1571 sp->sst_next_flags = current_next_flags;
1572 sp->sst_next_list = current_next_list;
1573 sp->sst_tick = display_tick;
1574 sp->sst_change_lnum = 0;
1575 }
1576 current_state_stored = TRUE;
1577 return sp;
1578}
1579
1580/*
1581 * Copy a state stack from "from" in b_sst_array[] to current_state;
1582 */
1583 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001584load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001585{
1586 int i;
1587 bufstate_T *bp;
1588
1589 clear_current_state();
1590 validate_current_state();
1591 keepend_level = -1;
1592 if (from->sst_stacksize
1593 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1594 {
1595 if (from->sst_stacksize > SST_FIX_STATES)
1596 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1597 else
1598 bp = from->sst_union.sst_stack;
1599 for (i = 0; i < from->sst_stacksize; ++i)
1600 {
1601 CUR_STATE(i).si_idx = bp[i].bs_idx;
1602 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001603#ifdef FEAT_CONCEAL
1604 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1605 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1606#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001607 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1608 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1609 keepend_level = i;
1610 CUR_STATE(i).si_ends = FALSE;
1611 CUR_STATE(i).si_m_lnum = 0;
1612 if (CUR_STATE(i).si_idx >= 0)
1613 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001614 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 else
1616 CUR_STATE(i).si_next_list = NULL;
1617 update_si_attr(i);
1618 }
1619 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620 }
1621 current_next_list = from->sst_next_list;
1622 current_next_flags = from->sst_next_flags;
1623 current_lnum = from->sst_lnum;
1624}
1625
1626/*
1627 * Compare saved state stack "*sp" with the current state.
1628 * Return TRUE when they are equal.
1629 */
1630 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001631syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001632{
1633 int i, j;
1634 bufstate_T *bp;
1635 reg_extmatch_T *six, *bsx;
1636
1637 /* First a quick check if the stacks have the same size end nextlist. */
1638 if (sp->sst_stacksize == current_state.ga_len
1639 && sp->sst_next_list == current_next_list)
1640 {
1641 /* Need to compare all states on both stacks. */
1642 if (sp->sst_stacksize > SST_FIX_STATES)
1643 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1644 else
1645 bp = sp->sst_union.sst_stack;
1646
1647 for (i = current_state.ga_len; --i >= 0; )
1648 {
1649 /* If the item has another index the state is different. */
1650 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1651 break;
1652 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1653 {
1654 /* When the extmatch pointers are different, the strings in
1655 * them can still be the same. Check if the extmatch
1656 * references are equal. */
1657 bsx = bp[i].bs_extmatch;
1658 six = CUR_STATE(i).si_extmatch;
1659 /* If one of the extmatch pointers is NULL the states are
1660 * different. */
1661 if (bsx == NULL || six == NULL)
1662 break;
1663 for (j = 0; j < NSUBEXP; ++j)
1664 {
1665 /* Check each referenced match string. They must all be
1666 * equal. */
1667 if (bsx->matches[j] != six->matches[j])
1668 {
1669 /* If the pointer is different it can still be the
1670 * same text. Compare the strings, ignore case when
1671 * the start item has the sp_ic flag set. */
1672 if (bsx->matches[j] == NULL
1673 || six->matches[j] == NULL)
1674 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001675 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001676 ? MB_STRICMP(bsx->matches[j],
1677 six->matches[j]) != 0
1678 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1679 break;
1680 }
1681 }
1682 if (j != NSUBEXP)
1683 break;
1684 }
1685 }
1686 if (i < 0)
1687 return TRUE;
1688 }
1689 return FALSE;
1690}
1691
1692/*
1693 * We stop parsing syntax above line "lnum". If the stored state at or below
1694 * this line depended on a change before it, it now depends on the line below
1695 * the last parsed line.
1696 * The window looks like this:
1697 * line which changed
1698 * displayed line
1699 * displayed line
1700 * lnum -> line below window
1701 */
1702 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001703syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704{
1705 synstate_T *sp;
1706
1707 sp = syn_stack_find_entry(lnum);
1708 if (sp != NULL && sp->sst_lnum < lnum)
1709 sp = sp->sst_next;
1710
1711 if (sp != NULL && sp->sst_change_lnum != 0)
1712 sp->sst_change_lnum = lnum;
1713}
1714
1715/*
1716 * End of handling of the state stack.
1717 ****************************************/
1718
1719 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001720invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721{
1722 clear_current_state();
1723 current_state.ga_itemsize = 0; /* mark current_state invalid */
1724 current_next_list = NULL;
1725 keepend_level = -1;
1726}
1727
1728 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731 current_state.ga_itemsize = sizeof(stateitem_T);
1732 current_state.ga_growsize = 3;
1733}
1734
1735/*
1736 * Return TRUE if the syntax at start of lnum changed since last time.
1737 * This will only be called just after get_syntax_attr() for the previous
1738 * line, to check if the next line needs to be redrawn too.
1739 */
1740 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001741syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742{
1743 int retval = TRUE;
1744 synstate_T *sp;
1745
Bram Moolenaar071d4272004-06-13 20:20:40 +00001746 /*
1747 * Check the state stack when:
1748 * - lnum is just below the previously syntaxed line.
1749 * - lnum is not before the lines with saved states.
1750 * - lnum is not past the lines with saved states.
1751 * - lnum is at or before the last changed line.
1752 */
1753 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1754 {
1755 sp = syn_stack_find_entry(lnum);
1756 if (sp != NULL && sp->sst_lnum == lnum)
1757 {
1758 /*
1759 * finish the previous line (needed when not all of the line was
1760 * drawn)
1761 */
1762 (void)syn_finish_line(FALSE);
1763
1764 /*
1765 * Compare the current state with the previously saved state of
1766 * the line.
1767 */
1768 if (syn_stack_equal(sp))
1769 retval = FALSE;
1770
1771 /*
1772 * Store the current state in b_sst_array[] for later use.
1773 */
1774 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001775 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776 }
1777 }
1778
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779 return retval;
1780}
1781
1782/*
1783 * Finish the current line.
1784 * This doesn't return any attributes, it only gets the state at the end of
1785 * the line. It can start anywhere in the line, as long as the current state
1786 * is valid.
1787 */
1788 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001789syn_finish_line(
1790 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791{
1792 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001793 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001795 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001797 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1798 /*
1799 * When syncing, and found some item, need to check the item.
1800 */
1801 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001804 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001806 cur_si = &CUR_STATE(current_state.ga_len - 1);
1807 if (cur_si->si_idx >= 0
1808 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1809 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1810 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001812 /* syn_current_attr() will have skipped the check for an item
1813 * that ends here, need to do that now. Be careful not to go
1814 * past the NUL. */
1815 prev_current_col = current_col;
1816 if (syn_getcurline()[current_col] != NUL)
1817 ++current_col;
1818 check_state_ends();
1819 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001821 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 }
1823 return FALSE;
1824}
1825
1826/*
1827 * Return highlight attributes for next character.
1828 * Must first call syntax_start() once for the line.
1829 * "col" is normally 0 for the first use in a line, and increments by one each
1830 * time. It's allowed to skip characters and to stop before the end of the
1831 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001832 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1833 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834 */
1835 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001836get_syntax_attr(
1837 colnr_T col,
1838 int *can_spell,
1839 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840{
1841 int attr = 0;
1842
Bram Moolenaar349955a2007-08-14 21:07:36 +00001843 if (can_spell != NULL)
1844 /* Default: Only do spelling when there is no @Spell cluster or when
1845 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001846 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1847 ? (syn_block->b_spell_cluster_id == 0)
1848 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001849
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001851 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 return 0;
1853
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001854 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001855 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001856 {
1857 clear_current_state();
1858#ifdef FEAT_EVAL
1859 current_id = 0;
1860 current_trans_id = 0;
1861#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001862#ifdef FEAT_CONCEAL
1863 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001864 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001865#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001866 return 0;
1867 }
1868
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 /* Make sure current_state is valid */
1870 if (INVALID_STATE(&current_state))
1871 validate_current_state();
1872
1873 /*
1874 * Skip from the current column to "col", get the attributes for "col".
1875 */
1876 while (current_col <= col)
1877 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001878 attr = syn_current_attr(FALSE, TRUE, can_spell,
1879 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001880 ++current_col;
1881 }
1882
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883 return attr;
1884}
1885
1886/*
1887 * Get syntax attributes for current_lnum, current_col.
1888 */
1889 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001890syn_current_attr(
1891 int syncing, /* When 1: called for syncing */
1892 int displaying, /* result will be displayed */
1893 int *can_spell, /* return: do spell checking */
1894 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895{
1896 int syn_id;
1897 lpos_T endpos; /* was: char_u *endp; */
1898 lpos_T hl_startpos; /* was: int hl_startcol; */
1899 lpos_T hl_endpos;
1900 lpos_T eos_pos; /* end-of-start match (start region) */
1901 lpos_T eoe_pos; /* end-of-end pattern */
1902 int end_idx; /* group ID for end pattern */
1903 int idx;
1904 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001905 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001906 int startcol;
1907 int endcol;
1908 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001909 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910 short *next_list;
1911 int found_match; /* found usable match */
1912 static int try_next_column = FALSE; /* must try in next col */
1913 int do_keywords;
1914 regmmatch_T regmatch;
1915 lpos_T pos;
1916 int lc_col;
1917 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001918 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 char_u *line; /* current line. NOTE: becomes invalid after
1920 looking for a pattern match! */
1921
1922 /* variables for zero-width matches that have a "nextgroup" argument */
1923 int keep_next_list;
1924 int zero_width_next_list = FALSE;
1925 garray_T zero_width_next_ga;
1926
1927 /*
1928 * No character, no attributes! Past end of line?
1929 * Do try matching with an empty line (could be the start of a region).
1930 */
1931 line = syn_getcurline();
1932 if (line[current_col] == NUL && current_col != 0)
1933 {
1934 /*
1935 * If we found a match after the last column, use it.
1936 */
1937 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1938 && next_match_col != MAXCOL)
1939 (void)push_next_match(NULL);
1940
1941 current_finished = TRUE;
1942 current_state_stored = FALSE;
1943 return 0;
1944 }
1945
1946 /* if the current or next character is NUL, we will finish the line now */
1947 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1948 {
1949 current_finished = TRUE;
1950 current_state_stored = FALSE;
1951 }
1952
1953 /*
1954 * When in the previous column there was a match but it could not be used
1955 * (empty match or already matched in this column) need to try again in
1956 * the next column.
1957 */
1958 if (try_next_column)
1959 {
1960 next_match_idx = -1;
1961 try_next_column = FALSE;
1962 }
1963
1964 /* Only check for keywords when not syncing and there are some. */
1965 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001966 && (syn_block->b_keywtab.ht_used > 0
1967 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968
1969 /* Init the list of zero-width matches with a nextlist. This is used to
1970 * avoid matching the same item in the same position twice. */
1971 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1972
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001973 /* use syntax iskeyword option */
1974 save_chartab(buf_chartab);
1975
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 /*
1977 * Repeat matching keywords and patterns, to find contained items at the
1978 * same column. This stops when there are no extra matches at the current
1979 * column.
1980 */
1981 do
1982 {
1983 found_match = FALSE;
1984 keep_next_list = FALSE;
1985 syn_id = 0;
1986
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001987
Bram Moolenaar071d4272004-06-13 20:20:40 +00001988 /*
1989 * 1. Check for a current state.
1990 * Only when there is no current state, or if the current state may
1991 * contain other things, we need to check for keywords and patterns.
1992 * Always need to check for contained items if some item has the
1993 * "containedin" argument (takes extra time!).
1994 */
1995 if (current_state.ga_len)
1996 cur_si = &CUR_STATE(current_state.ga_len - 1);
1997 else
1998 cur_si = NULL;
1999
Bram Moolenaar860cae12010-06-05 23:22:07 +02002000 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 || cur_si->si_cont_list != NULL)
2002 {
2003 /*
2004 * 2. Check for keywords, if on a keyword char after a non-keyword
2005 * char. Don't do this when syncing.
2006 */
2007 if (do_keywords)
2008 {
2009 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002010 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002012 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013#ifdef FEAT_MBYTE
2014 - (has_mbyte
2015 ? (*mb_head_off)(line, line + current_col - 1)
2016 : 0)
2017#endif
2018 , syn_buf)))
2019 {
2020 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002021 &endcol, &flags, &next_list, cur_si,
2022 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002023 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 {
2025 if (push_current_state(KEYWORD_IDX) == OK)
2026 {
2027 cur_si = &CUR_STATE(current_state.ga_len - 1);
2028 cur_si->si_m_startcol = current_col;
2029 cur_si->si_h_startpos.lnum = current_lnum;
2030 cur_si->si_h_startpos.col = 0; /* starts right away */
2031 cur_si->si_m_endpos.lnum = current_lnum;
2032 cur_si->si_m_endpos.col = endcol;
2033 cur_si->si_h_endpos.lnum = current_lnum;
2034 cur_si->si_h_endpos.col = endcol;
2035 cur_si->si_ends = TRUE;
2036 cur_si->si_end_idx = 0;
2037 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002038#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002039 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002040 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002041 if (current_state.ga_len > 1)
2042 cur_si->si_flags |=
2043 CUR_STATE(current_state.ga_len - 2).si_flags
2044 & HL_CONCEAL;
2045#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046 cur_si->si_id = syn_id;
2047 cur_si->si_trans_id = syn_id;
2048 if (flags & HL_TRANSP)
2049 {
2050 if (current_state.ga_len < 2)
2051 {
2052 cur_si->si_attr = 0;
2053 cur_si->si_trans_id = 0;
2054 }
2055 else
2056 {
2057 cur_si->si_attr = CUR_STATE(
2058 current_state.ga_len - 2).si_attr;
2059 cur_si->si_trans_id = CUR_STATE(
2060 current_state.ga_len - 2).si_trans_id;
2061 }
2062 }
2063 else
2064 cur_si->si_attr = syn_id2attr(syn_id);
2065 cur_si->si_cont_list = NULL;
2066 cur_si->si_next_list = next_list;
2067 check_keepend();
2068 }
2069 else
2070 vim_free(next_list);
2071 }
2072 }
2073 }
2074
2075 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002076 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002077 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002078 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002079 {
2080 /*
2081 * If we didn't check for a match yet, or we are past it, check
2082 * for any match with a pattern.
2083 */
2084 if (next_match_idx < 0 || next_match_col < (int)current_col)
2085 {
2086 /*
2087 * Check all relevant patterns for a match at this
2088 * position. This is complicated, because matching with a
2089 * pattern takes quite a bit of time, thus we want to
2090 * avoid doing it when it's not needed.
2091 */
2092 next_match_idx = 0; /* no match in this line yet */
2093 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002094 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002095 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002096 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 if ( spp->sp_syncing == syncing
2098 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2099 && (spp->sp_type == SPTYPE_MATCH
2100 || spp->sp_type == SPTYPE_START)
2101 && (current_next_list != NULL
2102 ? in_id_list(NULL, current_next_list,
2103 &spp->sp_syn, 0)
2104 : (cur_si == NULL
2105 ? !(spp->sp_flags & HL_CONTAINED)
2106 : in_id_list(cur_si,
2107 cur_si->si_cont_list, &spp->sp_syn,
2108 spp->sp_flags & HL_CONTAINED))))
2109 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002110 int r;
2111
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 /* If we already tried matching in this line, and
2113 * there isn't a match before next_match_col, skip
2114 * this item. */
2115 if (spp->sp_line_id == current_line_id
2116 && spp->sp_startcol >= next_match_col)
2117 continue;
2118 spp->sp_line_id = current_line_id;
2119
2120 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2121 if (lc_col < 0)
2122 lc_col = 0;
2123
2124 regmatch.rmm_ic = spp->sp_ic;
2125 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002126 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002127 current_lnum,
2128 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002129 IF_SYN_TIME(&spp->sp_time));
2130 spp->sp_prog = regmatch.regprog;
2131 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 {
2133 /* no match in this line, try another one */
2134 spp->sp_startcol = MAXCOL;
2135 continue;
2136 }
2137
2138 /*
2139 * Compute the first column of the match.
2140 */
2141 syn_add_start_off(&pos, &regmatch,
2142 spp, SPO_MS_OFF, -1);
2143 if (pos.lnum > current_lnum)
2144 {
2145 /* must have used end of match in a next line,
2146 * we can't handle that */
2147 spp->sp_startcol = MAXCOL;
2148 continue;
2149 }
2150 startcol = pos.col;
2151
2152 /* remember the next column where this pattern
2153 * matches in the current line */
2154 spp->sp_startcol = startcol;
2155
2156 /*
2157 * If a previously found match starts at a lower
2158 * column number, don't use this one.
2159 */
2160 if (startcol >= next_match_col)
2161 continue;
2162
2163 /*
2164 * If we matched this pattern at this position
2165 * before, skip it. Must retry in the next
2166 * column, because it may match from there.
2167 */
2168 if (did_match_already(idx, &zero_width_next_ga))
2169 {
2170 try_next_column = TRUE;
2171 continue;
2172 }
2173
2174 endpos.lnum = regmatch.endpos[0].lnum;
2175 endpos.col = regmatch.endpos[0].col;
2176
2177 /* Compute the highlight start. */
2178 syn_add_start_off(&hl_startpos, &regmatch,
2179 spp, SPO_HS_OFF, -1);
2180
2181 /* Compute the region start. */
2182 /* Default is to use the end of the match. */
2183 syn_add_end_off(&eos_pos, &regmatch,
2184 spp, SPO_RS_OFF, 0);
2185
2186 /*
2187 * Grab the external submatches before they get
2188 * overwritten. Reference count doesn't change.
2189 */
2190 unref_extmatch(cur_extmatch);
2191 cur_extmatch = re_extmatch_out;
2192 re_extmatch_out = NULL;
2193
2194 flags = 0;
2195 eoe_pos.lnum = 0; /* avoid warning */
2196 eoe_pos.col = 0;
2197 end_idx = 0;
2198 hl_endpos.lnum = 0;
2199
2200 /*
2201 * For a "oneline" the end must be found in the
2202 * same line too. Search for it after the end of
2203 * the match with the start pattern. Set the
2204 * resulting end positions at the same time.
2205 */
2206 if (spp->sp_type == SPTYPE_START
2207 && (spp->sp_flags & HL_ONELINE))
2208 {
2209 lpos_T startpos;
2210
2211 startpos = endpos;
2212 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2213 &flags, &eoe_pos, &end_idx, cur_extmatch);
2214 if (endpos.lnum == 0)
2215 continue; /* not found */
2216 }
2217
2218 /*
2219 * For a "match" the size must be > 0 after the
2220 * end offset needs has been added. Except when
2221 * syncing.
2222 */
2223 else if (spp->sp_type == SPTYPE_MATCH)
2224 {
2225 syn_add_end_off(&hl_endpos, &regmatch, spp,
2226 SPO_HE_OFF, 0);
2227 syn_add_end_off(&endpos, &regmatch, spp,
2228 SPO_ME_OFF, 0);
2229 if (endpos.lnum == current_lnum
2230 && (int)endpos.col + syncing < startcol)
2231 {
2232 /*
2233 * If an empty string is matched, may need
2234 * to try matching again at next column.
2235 */
2236 if (regmatch.startpos[0].col
2237 == regmatch.endpos[0].col)
2238 try_next_column = TRUE;
2239 continue;
2240 }
2241 }
2242
2243 /*
2244 * keep the best match so far in next_match_*
2245 */
2246 /* Highlighting must start after startpos and end
2247 * before endpos. */
2248 if (hl_startpos.lnum == current_lnum
2249 && (int)hl_startpos.col < startcol)
2250 hl_startpos.col = startcol;
2251 limit_pos_zero(&hl_endpos, &endpos);
2252
2253 next_match_idx = idx;
2254 next_match_col = startcol;
2255 next_match_m_endpos = endpos;
2256 next_match_h_endpos = hl_endpos;
2257 next_match_h_startpos = hl_startpos;
2258 next_match_flags = flags;
2259 next_match_eos_pos = eos_pos;
2260 next_match_eoe_pos = eoe_pos;
2261 next_match_end_idx = end_idx;
2262 unref_extmatch(next_match_extmatch);
2263 next_match_extmatch = cur_extmatch;
2264 cur_extmatch = NULL;
2265 }
2266 }
2267 }
2268
2269 /*
2270 * If we found a match at the current column, use it.
2271 */
2272 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2273 {
2274 synpat_T *lspp;
2275
2276 /* When a zero-width item matched which has a nextgroup,
2277 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002278 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279 if (next_match_m_endpos.lnum == current_lnum
2280 && next_match_m_endpos.col == current_col
2281 && lspp->sp_next_list != NULL)
2282 {
2283 current_next_list = lspp->sp_next_list;
2284 current_next_flags = lspp->sp_flags;
2285 keep_next_list = TRUE;
2286 zero_width_next_list = TRUE;
2287
2288 /* Add the index to a list, so that we can check
2289 * later that we don't match it again (and cause an
2290 * endless loop). */
2291 if (ga_grow(&zero_width_next_ga, 1) == OK)
2292 {
2293 ((int *)(zero_width_next_ga.ga_data))
2294 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002295 }
2296 next_match_idx = -1;
2297 }
2298 else
2299 cur_si = push_next_match(cur_si);
2300 found_match = TRUE;
2301 }
2302 }
2303 }
2304
2305 /*
2306 * Handle searching for nextgroup match.
2307 */
2308 if (current_next_list != NULL && !keep_next_list)
2309 {
2310 /*
2311 * If a nextgroup was not found, continue looking for one if:
2312 * - this is an empty line and the "skipempty" option was given
2313 * - we are on white space and the "skipwhite" option was given
2314 */
2315 if (!found_match)
2316 {
2317 line = syn_getcurline();
2318 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002319 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002320 || ((current_next_flags & HL_SKIPEMPTY)
2321 && *line == NUL))
2322 break;
2323 }
2324
2325 /*
2326 * If a nextgroup was found: Use it, and continue looking for
2327 * contained matches.
2328 * If a nextgroup was not found: Continue looking for a normal
2329 * match.
2330 * When did set current_next_list for a zero-width item and no
2331 * match was found don't loop (would get stuck).
2332 */
2333 current_next_list = NULL;
2334 next_match_idx = -1;
2335 if (!zero_width_next_list)
2336 found_match = TRUE;
2337 }
2338
2339 } while (found_match);
2340
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002341 restore_chartab(buf_chartab);
2342
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 /*
2344 * Use attributes from the current state, if within its highlighting.
2345 * If not, use attributes from the current-but-one state, etc.
2346 */
2347 current_attr = 0;
2348#ifdef FEAT_EVAL
2349 current_id = 0;
2350 current_trans_id = 0;
2351#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002352#ifdef FEAT_CONCEAL
2353 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002354 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002355#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002356 if (cur_si != NULL)
2357 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002358#ifndef FEAT_EVAL
2359 int current_trans_id = 0;
2360#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002361 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2362 {
2363 sip = &CUR_STATE(idx);
2364 if ((current_lnum > sip->si_h_startpos.lnum
2365 || (current_lnum == sip->si_h_startpos.lnum
2366 && current_col >= sip->si_h_startpos.col))
2367 && (sip->si_h_endpos.lnum == 0
2368 || current_lnum < sip->si_h_endpos.lnum
2369 || (current_lnum == sip->si_h_endpos.lnum
2370 && current_col < sip->si_h_endpos.col)))
2371 {
2372 current_attr = sip->si_attr;
2373#ifdef FEAT_EVAL
2374 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002375#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002376 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002377#ifdef FEAT_CONCEAL
2378 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002379 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002380 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002381#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 break;
2383 }
2384 }
2385
Bram Moolenaar217ad922005-03-20 22:37:15 +00002386 if (can_spell != NULL)
2387 {
2388 struct sp_syn sps;
2389
2390 /*
2391 * set "can_spell" to TRUE if spell checking is supposed to be
2392 * done in the current item.
2393 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002394 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002395 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002396 /* There is no @Spell cluster: Do spelling for items without
2397 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002398 if (syn_block->b_nospell_cluster_id == 0
2399 || current_trans_id == 0)
2400 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002401 else
2402 {
2403 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002404 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002405 sps.cont_in_list = NULL;
2406 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2407 }
2408 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002409 else
2410 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002411 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002412 * the @Spell cluster. But not when @NoSpell is also there.
2413 * At the toplevel only spell check when ":syn spell toplevel"
2414 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002415 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002416 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002417 else
2418 {
2419 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002420 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002421 sps.cont_in_list = NULL;
2422 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2423
Bram Moolenaar860cae12010-06-05 23:22:07 +02002424 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002425 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002426 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002427 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2428 *can_spell = FALSE;
2429 }
2430 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002431 }
2432 }
2433
2434
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435 /*
2436 * Check for end of current state (and the states before it) at the
2437 * next column. Don't do this for syncing, because we would miss a
2438 * single character match.
2439 * First check if the current state ends at the current column. It
2440 * may be for an empty match and a containing item might end in the
2441 * current column.
2442 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002443 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 {
2445 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002446 if (current_state.ga_len > 0
2447 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 {
2449 ++current_col;
2450 check_state_ends();
2451 --current_col;
2452 }
2453 }
2454 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002455 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002456 /* Default: Only do spelling when there is no @Spell cluster or when
2457 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002458 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2459 ? (syn_block->b_spell_cluster_id == 0)
2460 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002462 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 if (current_next_list != NULL
2464 && syn_getcurline()[current_col + 1] == NUL
2465 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2466 current_next_list = NULL;
2467
2468 if (zero_width_next_ga.ga_len > 0)
2469 ga_clear(&zero_width_next_ga);
2470
2471 /* No longer need external matches. But keep next_match_extmatch. */
2472 unref_extmatch(re_extmatch_out);
2473 re_extmatch_out = NULL;
2474 unref_extmatch(cur_extmatch);
2475
2476 return current_attr;
2477}
2478
2479
2480/*
2481 * Check if we already matched pattern "idx" at the current column.
2482 */
2483 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002484did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002485{
2486 int i;
2487
2488 for (i = current_state.ga_len; --i >= 0; )
2489 if (CUR_STATE(i).si_m_startcol == (int)current_col
2490 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2491 && CUR_STATE(i).si_idx == idx)
2492 return TRUE;
2493
2494 /* Zero-width matches with a nextgroup argument are not put on the syntax
2495 * stack, and can only be matched once anyway. */
2496 for (i = gap->ga_len; --i >= 0; )
2497 if (((int *)(gap->ga_data))[i] == idx)
2498 return TRUE;
2499
2500 return FALSE;
2501}
2502
2503/*
2504 * Push the next match onto the stack.
2505 */
2506 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002507push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508{
2509 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002510#ifdef FEAT_CONCEAL
2511 int save_flags;
2512#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513
Bram Moolenaar860cae12010-06-05 23:22:07 +02002514 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002515
2516 /*
2517 * Push the item in current_state stack;
2518 */
2519 if (push_current_state(next_match_idx) == OK)
2520 {
2521 /*
2522 * If it's a start-skip-end type that crosses lines, figure out how
2523 * much it continues in this line. Otherwise just fill in the length.
2524 */
2525 cur_si = &CUR_STATE(current_state.ga_len - 1);
2526 cur_si->si_h_startpos = next_match_h_startpos;
2527 cur_si->si_m_startcol = current_col;
2528 cur_si->si_m_lnum = current_lnum;
2529 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002530#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002531 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002532 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002533 if (current_state.ga_len > 1)
2534 cur_si->si_flags |=
2535 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2536#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002537 cur_si->si_next_list = spp->sp_next_list;
2538 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2539 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2540 {
2541 /* Try to find the end pattern in the current line */
2542 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2543 check_keepend();
2544 }
2545 else
2546 {
2547 cur_si->si_m_endpos = next_match_m_endpos;
2548 cur_si->si_h_endpos = next_match_h_endpos;
2549 cur_si->si_ends = TRUE;
2550 cur_si->si_flags |= next_match_flags;
2551 cur_si->si_eoe_pos = next_match_eoe_pos;
2552 cur_si->si_end_idx = next_match_end_idx;
2553 }
2554 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2555 keepend_level = current_state.ga_len - 1;
2556 check_keepend();
2557 update_si_attr(current_state.ga_len - 1);
2558
Bram Moolenaar860cae12010-06-05 23:22:07 +02002559#ifdef FEAT_CONCEAL
2560 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2561#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002562 /*
2563 * If the start pattern has another highlight group, push another item
2564 * on the stack for the start pattern.
2565 */
2566 if ( spp->sp_type == SPTYPE_START
2567 && spp->sp_syn_match_id != 0
2568 && push_current_state(next_match_idx) == OK)
2569 {
2570 cur_si = &CUR_STATE(current_state.ga_len - 1);
2571 cur_si->si_h_startpos = next_match_h_startpos;
2572 cur_si->si_m_startcol = current_col;
2573 cur_si->si_m_lnum = current_lnum;
2574 cur_si->si_m_endpos = next_match_eos_pos;
2575 cur_si->si_h_endpos = next_match_eos_pos;
2576 cur_si->si_ends = TRUE;
2577 cur_si->si_end_idx = 0;
2578 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002579#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002580 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002581 cur_si->si_flags |= save_flags;
2582 if (cur_si->si_flags & HL_CONCEALENDS)
2583 cur_si->si_flags |= HL_CONCEAL;
2584#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 cur_si->si_next_list = NULL;
2586 check_keepend();
2587 update_si_attr(current_state.ga_len - 1);
2588 }
2589 }
2590
2591 next_match_idx = -1; /* try other match next time */
2592
2593 return cur_si;
2594}
2595
2596/*
2597 * Check for end of current state (and the states before it).
2598 */
2599 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002600check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601{
2602 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002603 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002604
2605 cur_si = &CUR_STATE(current_state.ga_len - 1);
2606 for (;;)
2607 {
2608 if (cur_si->si_ends
2609 && (cur_si->si_m_endpos.lnum < current_lnum
2610 || (cur_si->si_m_endpos.lnum == current_lnum
2611 && cur_si->si_m_endpos.col <= current_col)))
2612 {
2613 /*
2614 * If there is an end pattern group ID, highlight the end pattern
2615 * now. No need to pop the current item from the stack.
2616 * Only do this if the end pattern continues beyond the current
2617 * position.
2618 */
2619 if (cur_si->si_end_idx
2620 && (cur_si->si_eoe_pos.lnum > current_lnum
2621 || (cur_si->si_eoe_pos.lnum == current_lnum
2622 && cur_si->si_eoe_pos.col > current_col)))
2623 {
2624 cur_si->si_idx = cur_si->si_end_idx;
2625 cur_si->si_end_idx = 0;
2626 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2627 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2628 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002629#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002630 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002631 if (cur_si->si_flags & HL_CONCEALENDS)
2632 cur_si->si_flags |= HL_CONCEAL;
2633#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002634 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002635
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002636 /* nextgroup= should not match in the end pattern */
2637 current_next_list = NULL;
2638
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002639 /* what matches next may be different now, clear it */
2640 next_match_idx = 0;
2641 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 break;
2643 }
2644 else
2645 {
2646 /* handle next_list, unless at end of line and no "skipnl" or
2647 * "skipempty" */
2648 current_next_list = cur_si->si_next_list;
2649 current_next_flags = cur_si->si_flags;
2650 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2651 && syn_getcurline()[current_col] == NUL)
2652 current_next_list = NULL;
2653
2654 /* When the ended item has "extend", another item with
2655 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002656 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002657
2658 pop_current_state();
2659
2660 if (current_state.ga_len == 0)
2661 break;
2662
Bram Moolenaar81993f42008-01-11 20:27:45 +00002663 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002664 {
2665 syn_update_ends(FALSE);
2666 if (current_state.ga_len == 0)
2667 break;
2668 }
2669
2670 cur_si = &CUR_STATE(current_state.ga_len - 1);
2671
2672 /*
2673 * Only for a region the search for the end continues after
2674 * the end of the contained item. If the contained match
2675 * included the end-of-line, break here, the region continues.
2676 * Don't do this when:
2677 * - "keepend" is used for the contained item
2678 * - not at the end of the line (could be end="x$"me=e-1).
2679 * - "excludenl" is used (HL_HAS_EOL won't be set)
2680 */
2681 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002682 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002683 == SPTYPE_START
2684 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2685 {
2686 update_si_end(cur_si, (int)current_col, TRUE);
2687 check_keepend();
2688 if ((current_next_flags & HL_HAS_EOL)
2689 && keepend_level < 0
2690 && syn_getcurline()[current_col] == NUL)
2691 break;
2692 }
2693 }
2694 }
2695 else
2696 break;
2697 }
2698}
2699
2700/*
2701 * Update an entry in the current_state stack for a match or region. This
2702 * fills in si_attr, si_next_list and si_cont_list.
2703 */
2704 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002705update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002706{
2707 stateitem_T *sip = &CUR_STATE(idx);
2708 synpat_T *spp;
2709
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002710 /* This should not happen... */
2711 if (sip->si_idx < 0)
2712 return;
2713
Bram Moolenaar860cae12010-06-05 23:22:07 +02002714 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715 if (sip->si_flags & HL_MATCH)
2716 sip->si_id = spp->sp_syn_match_id;
2717 else
2718 sip->si_id = spp->sp_syn.id;
2719 sip->si_attr = syn_id2attr(sip->si_id);
2720 sip->si_trans_id = sip->si_id;
2721 if (sip->si_flags & HL_MATCH)
2722 sip->si_cont_list = NULL;
2723 else
2724 sip->si_cont_list = spp->sp_cont_list;
2725
2726 /*
2727 * For transparent items, take attr from outer item.
2728 * Also take cont_list, if there is none.
2729 * Don't do this for the matchgroup of a start or end pattern.
2730 */
2731 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2732 {
2733 if (idx == 0)
2734 {
2735 sip->si_attr = 0;
2736 sip->si_trans_id = 0;
2737 if (sip->si_cont_list == NULL)
2738 sip->si_cont_list = ID_LIST_ALL;
2739 }
2740 else
2741 {
2742 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2743 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002744 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2745 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746 if (sip->si_cont_list == NULL)
2747 {
2748 sip->si_flags |= HL_TRANS_CONT;
2749 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2750 }
2751 }
2752 }
2753}
2754
2755/*
2756 * Check the current stack for patterns with "keepend" flag.
2757 * Propagate the match-end to contained items, until a "skipend" item is found.
2758 */
2759 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002760check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761{
2762 int i;
2763 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002764 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 stateitem_T *sip;
2766
2767 /*
2768 * This check can consume a lot of time; only do it from the level where
2769 * there really is a keepend.
2770 */
2771 if (keepend_level < 0)
2772 return;
2773
2774 /*
2775 * Find the last index of an "extend" item. "keepend" items before that
2776 * won't do anything. If there is no "extend" item "i" will be
2777 * "keepend_level" and all "keepend" items will work normally.
2778 */
2779 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2780 if (CUR_STATE(i).si_flags & HL_EXTEND)
2781 break;
2782
2783 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002784 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002785 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002786 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002787 for ( ; i < current_state.ga_len; ++i)
2788 {
2789 sip = &CUR_STATE(i);
2790 if (maxpos.lnum != 0)
2791 {
2792 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002793 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2795 sip->si_ends = TRUE;
2796 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002797 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2798 {
2799 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 || maxpos.lnum > sip->si_m_endpos.lnum
2801 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002802 && maxpos.col > sip->si_m_endpos.col))
2803 maxpos = sip->si_m_endpos;
2804 if (maxpos_h.lnum == 0
2805 || maxpos_h.lnum > sip->si_h_endpos.lnum
2806 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2807 && maxpos_h.col > sip->si_h_endpos.col))
2808 maxpos_h = sip->si_h_endpos;
2809 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810 }
2811}
2812
2813/*
2814 * Update an entry in the current_state stack for a start-skip-end pattern.
2815 * This finds the end of the current item, if it's in the current line.
2816 *
2817 * Return the flags for the matched END.
2818 */
2819 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002820update_si_end(
2821 stateitem_T *sip,
2822 int startcol, /* where to start searching for the end */
2823 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824{
2825 lpos_T startpos;
2826 lpos_T endpos;
2827 lpos_T hl_endpos;
2828 lpos_T end_endpos;
2829 int end_idx;
2830
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002831 /* return quickly for a keyword */
2832 if (sip->si_idx < 0)
2833 return;
2834
Bram Moolenaar071d4272004-06-13 20:20:40 +00002835 /* Don't update when it's already done. Can be a match of an end pattern
2836 * that started in a previous line. Watch out: can also be a "keepend"
2837 * from a containing item. */
2838 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2839 return;
2840
2841 /*
2842 * We need to find the end of the region. It may continue in the next
2843 * line.
2844 */
2845 end_idx = 0;
2846 startpos.lnum = current_lnum;
2847 startpos.col = startcol;
2848 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2849 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2850
2851 if (endpos.lnum == 0)
2852 {
2853 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002854 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855 {
2856 /* a "oneline" never continues in the next line */
2857 sip->si_ends = TRUE;
2858 sip->si_m_endpos.lnum = current_lnum;
2859 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2860 }
2861 else
2862 {
2863 /* continues in the next line */
2864 sip->si_ends = FALSE;
2865 sip->si_m_endpos.lnum = 0;
2866 }
2867 sip->si_h_endpos = sip->si_m_endpos;
2868 }
2869 else
2870 {
2871 /* match within this line */
2872 sip->si_m_endpos = endpos;
2873 sip->si_h_endpos = hl_endpos;
2874 sip->si_eoe_pos = end_endpos;
2875 sip->si_ends = TRUE;
2876 sip->si_end_idx = end_idx;
2877 }
2878}
2879
2880/*
2881 * Add a new state to the current state stack.
2882 * It is cleared and the index set to "idx".
2883 * Return FAIL if it's not possible (out of memory).
2884 */
2885 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002886push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887{
2888 if (ga_grow(&current_state, 1) == FAIL)
2889 return FAIL;
2890 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2891 CUR_STATE(current_state.ga_len).si_idx = idx;
2892 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 return OK;
2894}
2895
2896/*
2897 * Remove a state from the current_state stack.
2898 */
2899 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002900pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901{
2902 if (current_state.ga_len)
2903 {
2904 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2905 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 }
2907 /* after the end of a pattern, try matching a keyword or pattern */
2908 next_match_idx = -1;
2909
2910 /* if first state with "keepend" is popped, reset keepend_level */
2911 if (keepend_level >= current_state.ga_len)
2912 keepend_level = -1;
2913}
2914
2915/*
2916 * Find the end of a start/skip/end syntax region after "startpos".
2917 * Only checks one line.
2918 * Also handles a match item that continued from a previous line.
2919 * If not found, the syntax item continues in the next line. m_endpos->lnum
2920 * will be 0.
2921 * If found, the end of the region and the end of the highlighting is
2922 * computed.
2923 */
2924 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002925find_endpos(
2926 int idx, /* index of the pattern */
2927 lpos_T *startpos, /* where to start looking for an END match */
2928 lpos_T *m_endpos, /* return: end of match */
2929 lpos_T *hl_endpos, /* return: end of highlighting */
2930 long *flagsp, /* return: flags of matching END */
2931 lpos_T *end_endpos, /* return: end of end pattern match */
2932 int *end_idx, /* return: group ID for end pat. match, or 0 */
2933 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934{
2935 colnr_T matchcol;
2936 synpat_T *spp, *spp_skip;
2937 int start_idx;
2938 int best_idx;
2939 regmmatch_T regmatch;
2940 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2941 lpos_T pos;
2942 char_u *line;
2943 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002944 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002946 /* just in case we are invoked for a keyword */
2947 if (idx < 0)
2948 return;
2949
Bram Moolenaar071d4272004-06-13 20:20:40 +00002950 /*
2951 * Check for being called with a START pattern.
2952 * Can happen with a match that continues to the next line, because it
2953 * contained a region.
2954 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002955 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002956 if (spp->sp_type != SPTYPE_START)
2957 {
2958 *hl_endpos = *startpos;
2959 return;
2960 }
2961
2962 /*
2963 * Find the SKIP or first END pattern after the last START pattern.
2964 */
2965 for (;;)
2966 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002967 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968 if (spp->sp_type != SPTYPE_START)
2969 break;
2970 ++idx;
2971 }
2972
2973 /*
2974 * Lookup the SKIP pattern (if present)
2975 */
2976 if (spp->sp_type == SPTYPE_SKIP)
2977 {
2978 spp_skip = spp;
2979 ++idx;
2980 }
2981 else
2982 spp_skip = NULL;
2983
2984 /* Setup external matches for syn_regexec(). */
2985 unref_extmatch(re_extmatch_in);
2986 re_extmatch_in = ref_extmatch(start_ext);
2987
2988 matchcol = startpos->col; /* start looking for a match at sstart */
2989 start_idx = idx; /* remember the first END pattern. */
2990 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002991
2992 /* use syntax iskeyword option */
2993 save_chartab(buf_chartab);
2994
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995 for (;;)
2996 {
2997 /*
2998 * Find end pattern that matches first after "matchcol".
2999 */
3000 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003001 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002 {
3003 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003004 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005
Bram Moolenaar860cae12010-06-05 23:22:07 +02003006 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003007 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3008 break;
3009 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3010 if (lc_col < 0)
3011 lc_col = 0;
3012
3013 regmatch.rmm_ic = spp->sp_ic;
3014 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003015 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3016 IF_SYN_TIME(&spp->sp_time));
3017 spp->sp_prog = regmatch.regprog;
3018 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003019 {
3020 if (best_idx == -1 || regmatch.startpos[0].col
3021 < best_regmatch.startpos[0].col)
3022 {
3023 best_idx = idx;
3024 best_regmatch.startpos[0] = regmatch.startpos[0];
3025 best_regmatch.endpos[0] = regmatch.endpos[0];
3026 }
3027 }
3028 }
3029
3030 /*
3031 * If all end patterns have been tried, and there is no match, the
3032 * item continues until end-of-line.
3033 */
3034 if (best_idx == -1)
3035 break;
3036
3037 /*
3038 * If the skip pattern matches before the end pattern,
3039 * continue searching after the skip pattern.
3040 */
3041 if (spp_skip != NULL)
3042 {
3043 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003044 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003045
3046 if (lc_col < 0)
3047 lc_col = 0;
3048 regmatch.rmm_ic = spp_skip->sp_ic;
3049 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003050 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3051 IF_SYN_TIME(&spp_skip->sp_time));
3052 spp_skip->sp_prog = regmatch.regprog;
3053 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054 <= best_regmatch.startpos[0].col)
3055 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003056 int line_len;
3057
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058 /* Add offset to skip pattern match */
3059 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3060
3061 /* If the skip pattern goes on to the next line, there is no
3062 * match with an end pattern in this line. */
3063 if (pos.lnum > startpos->lnum)
3064 break;
3065
3066 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003067 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068
3069 /* take care of an empty match or negative offset */
3070 if (pos.col <= matchcol)
3071 ++matchcol;
3072 else if (pos.col <= regmatch.endpos[0].col)
3073 matchcol = pos.col;
3074 else
3075 /* Be careful not to jump over the NUL at the end-of-line */
3076 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003077 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078 ++matchcol)
3079 ;
3080
3081 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003082 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003083 break;
3084
3085 continue; /* start with first end pattern again */
3086 }
3087 }
3088
3089 /*
3090 * Match from start pattern to end pattern.
3091 * Correct for match and highlight offset of end pattern.
3092 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003093 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003094 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3095 /* can't end before the start */
3096 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3097 m_endpos->col = startpos->col;
3098
3099 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3100 /* can't end before the start */
3101 if (end_endpos->lnum == startpos->lnum
3102 && end_endpos->col < startpos->col)
3103 end_endpos->col = startpos->col;
3104 /* can't end after the match */
3105 limit_pos(end_endpos, m_endpos);
3106
3107 /*
3108 * If the end group is highlighted differently, adjust the pointers.
3109 */
3110 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3111 {
3112 *end_idx = best_idx;
3113 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3114 {
3115 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3116 hl_endpos->col = best_regmatch.endpos[0].col;
3117 }
3118 else
3119 {
3120 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3121 hl_endpos->col = best_regmatch.startpos[0].col;
3122 }
3123 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3124
3125 /* can't end before the start */
3126 if (hl_endpos->lnum == startpos->lnum
3127 && hl_endpos->col < startpos->col)
3128 hl_endpos->col = startpos->col;
3129 limit_pos(hl_endpos, m_endpos);
3130
3131 /* now the match ends where the highlighting ends, it is turned
3132 * into the matchgroup for the end */
3133 *m_endpos = *hl_endpos;
3134 }
3135 else
3136 {
3137 *end_idx = 0;
3138 *hl_endpos = *end_endpos;
3139 }
3140
3141 *flagsp = spp->sp_flags;
3142
3143 had_match = TRUE;
3144 break;
3145 }
3146
3147 /* no match for an END pattern in this line */
3148 if (!had_match)
3149 m_endpos->lnum = 0;
3150
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003151 restore_chartab(buf_chartab);
3152
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153 /* Remove external matches. */
3154 unref_extmatch(re_extmatch_in);
3155 re_extmatch_in = NULL;
3156}
3157
3158/*
3159 * Limit "pos" not to be after "limit".
3160 */
3161 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003162limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003163{
3164 if (pos->lnum > limit->lnum)
3165 *pos = *limit;
3166 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3167 pos->col = limit->col;
3168}
3169
3170/*
3171 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3172 */
3173 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003174limit_pos_zero(
3175 lpos_T *pos,
3176 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177{
3178 if (pos->lnum == 0)
3179 *pos = *limit;
3180 else
3181 limit_pos(pos, limit);
3182}
3183
3184/*
3185 * Add offset to matched text for end of match or highlight.
3186 */
3187 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003188syn_add_end_off(
3189 lpos_T *result, /* returned position */
3190 regmmatch_T *regmatch, /* start/end of match */
3191 synpat_T *spp, /* matched pattern */
3192 int idx, /* index of offset */
3193 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194{
3195 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003196 int off;
3197 char_u *base;
3198 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199
3200 if (spp->sp_off_flags & (1 << idx))
3201 {
3202 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003203 col = regmatch->startpos[0].col;
3204 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205 }
3206 else
3207 {
3208 result->lnum = regmatch->endpos[0].lnum;
3209 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003210 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003211 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003212 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3213 * is a matchgroup. Watch out for match with last NL in the buffer. */
3214 if (result->lnum > syn_buf->b_ml.ml_line_count)
3215 col = 0;
3216 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003217 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003218 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3219 p = base + col;
3220 if (off > 0)
3221 {
3222 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003223 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003224 }
3225 else if (off < 0)
3226 {
3227 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003228 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003229 }
3230 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003231 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003232 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233}
3234
3235/*
3236 * Add offset to matched text for start of match or highlight.
3237 * Avoid resulting column to become negative.
3238 */
3239 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003240syn_add_start_off(
3241 lpos_T *result, /* returned position */
3242 regmmatch_T *regmatch, /* start/end of match */
3243 synpat_T *spp,
3244 int idx,
3245 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246{
3247 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003248 int off;
3249 char_u *base;
3250 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251
3252 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3253 {
3254 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003255 col = regmatch->endpos[0].col;
3256 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 }
3258 else
3259 {
3260 result->lnum = regmatch->startpos[0].lnum;
3261 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003262 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003263 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003264 if (result->lnum > syn_buf->b_ml.ml_line_count)
3265 {
3266 /* a "\n" at the end of the pattern may take us below the last line */
3267 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003268 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003269 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003270 if (off != 0)
3271 {
3272 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3273 p = base + col;
3274 if (off > 0)
3275 {
3276 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003277 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003278 }
3279 else if (off < 0)
3280 {
3281 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003282 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003283 }
3284 col = (int)(p - base);
3285 }
3286 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003287}
3288
3289/*
3290 * Get current line in syntax buffer.
3291 */
3292 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003293syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294{
3295 return ml_get_buf(syn_buf, current_lnum, FALSE);
3296}
3297
3298/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003299 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300 * Returns TRUE when there is a match.
3301 */
3302 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003303syn_regexec(
3304 regmmatch_T *rmp,
3305 linenr_T lnum,
3306 colnr_T col,
3307 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003309 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003310#ifdef FEAT_RELTIME
3311 int timed_out = FALSE;
3312#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003313#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003314 proftime_T pt;
3315
3316 if (syn_time_on)
3317 profile_start(&pt);
3318#endif
3319
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003320 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003321 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3322#ifdef FEAT_RELTIME
3323 syn_tm, &timed_out
3324#else
3325 NULL, NULL
3326#endif
3327 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003328
Bram Moolenaarf7512552013-06-06 14:55:19 +02003329#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003330 if (syn_time_on)
3331 {
3332 profile_end(&pt);
3333 profile_add(&st->total, &pt);
3334 if (profile_cmp(&pt, &st->slowest) < 0)
3335 st->slowest = pt;
3336 ++st->count;
3337 if (r > 0)
3338 ++st->match;
3339 }
3340#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003341#ifdef FEAT_RELTIME
3342 if (timed_out)
3343 syn_win->w_s->b_syn_slow = TRUE;
3344#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003345
3346 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347 {
3348 rmp->startpos[0].lnum += lnum;
3349 rmp->endpos[0].lnum += lnum;
3350 return TRUE;
3351 }
3352 return FALSE;
3353}
3354
3355/*
3356 * Check one position in a line for a matching keyword.
3357 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003358 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 */
3360 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003361check_keyword_id(
3362 char_u *line,
3363 int startcol, /* position in line to check for keyword */
3364 int *endcolp, /* return: character after found keyword */
3365 long *flagsp, /* return: flags of matching keyword */
3366 short **next_listp, /* return: next_list of matching keyword */
3367 stateitem_T *cur_si, /* item at the top of the stack */
3368 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003370 keyentry_T *kp;
3371 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003373 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003375 hashtab_T *ht;
3376 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003377
3378 /* Find first character after the keyword. First character was already
3379 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003380 kwp = line + startcol;
3381 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 do
3383 {
3384#ifdef FEAT_MBYTE
3385 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003386 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 else
3388#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003389 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003391 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392
Bram Moolenaardad6b692005-01-25 22:14:34 +00003393 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394 return 0;
3395
3396 /*
3397 * Must make a copy of the keyword, so we can add a NUL and make it
3398 * lowercase.
3399 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003400 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401
3402 /*
3403 * Try twice:
3404 * 1. matching case
3405 * 2. ignoring case
3406 */
3407 for (round = 1; round <= 2; ++round)
3408 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003409 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003410 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003412 if (round == 2) /* ignore case */
3413 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414
3415 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003416 * Find keywords that match. There can be several with different
3417 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003418 * When current_next_list is non-zero accept only that group, otherwise:
3419 * Accept a not-contained keyword at toplevel.
3420 * Accept a keyword at other levels only if it is in the contains list.
3421 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003422 hi = hash_find(ht, keyword);
3423 if (!HASHITEM_EMPTY(hi))
3424 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003425 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003426 if (current_next_list != 0
3427 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3428 : (cur_si == NULL
3429 ? !(kp->flags & HL_CONTAINED)
3430 : in_id_list(cur_si, cur_si->si_cont_list,
3431 &kp->k_syn, kp->flags & HL_CONTAINED)))
3432 {
3433 *endcolp = startcol + kwlen;
3434 *flagsp = kp->flags;
3435 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003436#ifdef FEAT_CONCEAL
3437 *ccharp = kp->k_char;
3438#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003439 return kp->k_syn.id;
3440 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441 }
3442 }
3443 return 0;
3444}
3445
3446/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003447 * Handle ":syntax conceal" command.
3448 */
3449 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003450syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003451{
3452#ifdef FEAT_CONCEAL
3453 char_u *arg = eap->arg;
3454 char_u *next;
3455
3456 eap->nextcmd = find_nextcmd(arg);
3457 if (eap->skip)
3458 return;
3459
3460 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003461 if (*arg == NUL)
3462 {
3463 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003464 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003465 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003466 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003467 }
3468 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003469 curwin->w_s->b_syn_conceal = TRUE;
3470 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3471 curwin->w_s->b_syn_conceal = FALSE;
3472 else
3473 EMSG2(_("E390: Illegal argument: %s"), arg);
3474#endif
3475}
3476
3477/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 * Handle ":syntax case" command.
3479 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003481syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482{
3483 char_u *arg = eap->arg;
3484 char_u *next;
3485
3486 eap->nextcmd = find_nextcmd(arg);
3487 if (eap->skip)
3488 return;
3489
3490 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003491 if (*arg == NUL)
3492 {
3493 if (curwin->w_s->b_syn_ic)
3494 MSG(_("syntax case ignore"));
3495 else
3496 MSG(_("syntax case match"));
3497 }
3498 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003499 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003500 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003501 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502 else
3503 EMSG2(_("E390: Illegal argument: %s"), arg);
3504}
3505
3506/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003507 * Handle ":syntax spell" command.
3508 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003509 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003510syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003511{
3512 char_u *arg = eap->arg;
3513 char_u *next;
3514
3515 eap->nextcmd = find_nextcmd(arg);
3516 if (eap->skip)
3517 return;
3518
3519 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003520 if (*arg == NUL)
3521 {
3522 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3523 MSG(_("syntax spell toplevel"));
3524 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3525 MSG(_("syntax spell notoplevel"));
3526 else
3527 MSG(_("syntax spell default"));
3528 }
3529 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003530 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003531 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003532 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003533 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003534 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003535 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003536 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003537 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003538 return;
3539 }
3540
3541 /* assume spell checking changed, force a redraw */
3542 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003543}
3544
3545/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003546 * Handle ":syntax iskeyword" command.
3547 */
3548 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003549syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003550{
3551 char_u *arg = eap->arg;
3552 char_u save_chartab[32];
3553 char_u *save_isk;
3554
3555 if (eap->skip)
3556 return;
3557
3558 arg = skipwhite(arg);
3559 if (*arg == NUL)
3560 {
3561 MSG_PUTS("\n");
3562 MSG_PUTS(_("syntax iskeyword "));
3563 if (curwin->w_s->b_syn_isk != empty_option)
3564 msg_outtrans(curwin->w_s->b_syn_isk);
3565 else
3566 msg_outtrans((char_u *)"not set");
3567 }
3568 else
3569 {
3570 if (STRNICMP(arg, "clear", 5) == 0)
3571 {
3572 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3573 (size_t)32);
3574 clear_string_option(&curwin->w_s->b_syn_isk);
3575 }
3576 else
3577 {
3578 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3579 save_isk = curbuf->b_p_isk;
3580 curbuf->b_p_isk = vim_strsave(arg);
3581
3582 buf_init_chartab(curbuf, FALSE);
3583 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3584 (size_t)32);
3585 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3586 clear_string_option(&curwin->w_s->b_syn_isk);
3587 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3588 curbuf->b_p_isk = save_isk;
3589 }
3590 }
3591 redraw_win_later(curwin, NOT_VALID);
3592}
3593
3594/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595 * Clear all syntax info for one buffer.
3596 */
3597 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003598syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599{
3600 int i;
3601
Bram Moolenaar860cae12010-06-05 23:22:07 +02003602 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003603#ifdef FEAT_RELTIME
3604 block->b_syn_slow = FALSE; /* clear previous timeout */
3605#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 block->b_syn_ic = FALSE; /* Use case, by default */
3607 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3608 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003609#ifdef FEAT_CONCEAL
3610 block->b_syn_conceal = FALSE;
3611#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612
3613 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 clear_keywtab(&block->b_keywtab);
3615 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616
3617 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003618 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3619 syn_clear_pattern(block, i);
3620 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621
3622 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003623 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3624 syn_clear_cluster(block, i);
3625 ga_clear(&block->b_syn_clusters);
3626 block->b_spell_cluster_id = 0;
3627 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628
Bram Moolenaar860cae12010-06-05 23:22:07 +02003629 block->b_syn_sync_flags = 0;
3630 block->b_syn_sync_minlines = 0;
3631 block->b_syn_sync_maxlines = 0;
3632 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633
Bram Moolenaar473de612013-06-08 18:19:48 +02003634 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003635 block->b_syn_linecont_prog = NULL;
3636 vim_free(block->b_syn_linecont_pat);
3637 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003639 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003641 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642
3643 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003644 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003646
3647 /* Reset the counter for ":syn include" */
3648 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649}
3650
3651/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003652 * Get rid of ownsyntax for window "wp".
3653 */
3654 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003655reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003656{
3657 if (wp->w_s != &wp->w_buffer->b_s)
3658 {
3659 syntax_clear(wp->w_s);
3660 vim_free(wp->w_s);
3661 wp->w_s = &wp->w_buffer->b_s;
3662 }
3663}
3664
3665/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666 * Clear syncing info for one buffer.
3667 */
3668 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003669syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003670{
3671 int i;
3672
3673 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003674 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3675 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3676 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677
Bram Moolenaar860cae12010-06-05 23:22:07 +02003678 curwin->w_s->b_syn_sync_flags = 0;
3679 curwin->w_s->b_syn_sync_minlines = 0;
3680 curwin->w_s->b_syn_sync_maxlines = 0;
3681 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682
Bram Moolenaar473de612013-06-08 18:19:48 +02003683 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003684 curwin->w_s->b_syn_linecont_prog = NULL;
3685 vim_free(curwin->w_s->b_syn_linecont_pat);
3686 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003687 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003689 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690}
3691
3692/*
3693 * Remove one pattern from the buffer's pattern list.
3694 */
3695 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003696syn_remove_pattern(
3697 synblock_T *block,
3698 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003699{
3700 synpat_T *spp;
3701
Bram Moolenaar860cae12010-06-05 23:22:07 +02003702 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703#ifdef FEAT_FOLDING
3704 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003705 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003707 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003709 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3710 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003711}
3712
3713/*
3714 * Clear and free one syntax pattern. When clearing all, must be called from
3715 * last to first!
3716 */
3717 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003718syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003720 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003721 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003723 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003725 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3726 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3727 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003728 }
3729}
3730
3731/*
3732 * Clear and free one syntax cluster.
3733 */
3734 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003735syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003737 vim_free(SYN_CLSTR(block)[i].scl_name);
3738 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3739 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740}
3741
3742/*
3743 * Handle ":syntax clear" command.
3744 */
3745 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003746syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747{
3748 char_u *arg = eap->arg;
3749 char_u *arg_end;
3750 int id;
3751
3752 eap->nextcmd = find_nextcmd(arg);
3753 if (eap->skip)
3754 return;
3755
3756 /*
3757 * We have to disable this within ":syn include @group filename",
3758 * because otherwise @group would get deleted.
3759 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3760 * clear".
3761 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003762 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 return;
3764
3765 if (ends_excmd(*arg))
3766 {
3767 /*
3768 * No argument: Clear all syntax items.
3769 */
3770 if (syncing)
3771 syntax_sync_clear();
3772 else
3773 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003774 syntax_clear(curwin->w_s);
3775 if (curwin->w_s == &curwin->w_buffer->b_s)
3776 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003777 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778 }
3779 }
3780 else
3781 {
3782 /*
3783 * Clear the group IDs that are in the argument.
3784 */
3785 while (!ends_excmd(*arg))
3786 {
3787 arg_end = skiptowhite(arg);
3788 if (*arg == '@')
3789 {
3790 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3791 if (id == 0)
3792 {
3793 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3794 break;
3795 }
3796 else
3797 {
3798 /*
3799 * We can't physically delete a cluster without changing
3800 * the IDs of other clusters, so we do the next best thing
3801 * and make it empty.
3802 */
3803 short scl_id = id - SYNID_CLUSTER;
3804
Bram Moolenaar860cae12010-06-05 23:22:07 +02003805 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3806 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807 }
3808 }
3809 else
3810 {
3811 id = syn_namen2id(arg, (int)(arg_end - arg));
3812 if (id == 0)
3813 {
3814 EMSG2(_(e_nogroup), arg);
3815 break;
3816 }
3817 else
3818 syn_clear_one(id, syncing);
3819 }
3820 arg = skipwhite(arg_end);
3821 }
3822 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003823 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003824 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825}
3826
3827/*
3828 * Clear one syntax group for the current buffer.
3829 */
3830 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003831syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832{
3833 synpat_T *spp;
3834 int idx;
3835
3836 /* Clear keywords only when not ":syn sync clear group-name" */
3837 if (!syncing)
3838 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003839 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3840 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 }
3842
3843 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003844 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3848 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003849 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 }
3851}
3852
3853/*
3854 * Handle ":syntax on" command.
3855 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003857syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858{
3859 syn_cmd_onoff(eap, "syntax");
3860}
3861
3862/*
3863 * Handle ":syntax enable" command.
3864 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003866syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867{
3868 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3869 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003870 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871}
3872
3873/*
3874 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003875 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003878syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003879{
3880 eap->nextcmd = check_nextcmd(eap->arg);
3881 if (!eap->skip)
3882 {
3883 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3884 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003885 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886 }
3887}
3888
3889/*
3890 * Handle ":syntax manual" command.
3891 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003893syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894{
3895 syn_cmd_onoff(eap, "manual");
3896}
3897
3898/*
3899 * Handle ":syntax off" command.
3900 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003902syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903{
3904 syn_cmd_onoff(eap, "nosyntax");
3905}
3906
3907 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003908syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909{
3910 char_u buf[100];
3911
3912 eap->nextcmd = check_nextcmd(eap->arg);
3913 if (!eap->skip)
3914 {
3915 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003916 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917 do_cmdline_cmd(buf);
3918 }
3919}
3920
3921/*
3922 * Handle ":syntax [list]" command: list current syntax words.
3923 */
3924 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003925syn_cmd_list(
3926 exarg_T *eap,
3927 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928{
3929 char_u *arg = eap->arg;
3930 int id;
3931 char_u *arg_end;
3932
3933 eap->nextcmd = find_nextcmd(arg);
3934 if (eap->skip)
3935 return;
3936
Bram Moolenaar860cae12010-06-05 23:22:07 +02003937 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003939 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003940 return;
3941 }
3942
3943 if (syncing)
3944 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003945 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003946 {
3947 MSG_PUTS(_("syncing on C-style comments"));
3948 syn_lines_msg();
3949 syn_match_msg();
3950 return;
3951 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003952 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003954 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 MSG_PUTS(_("no syncing"));
3956 else
3957 {
3958 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003959 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960 MSG_PUTS(_(" lines before top line"));
3961 syn_match_msg();
3962 }
3963 return;
3964 }
3965 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003966 if (curwin->w_s->b_syn_sync_minlines > 0
3967 || curwin->w_s->b_syn_sync_maxlines > 0
3968 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003969 {
3970 MSG_PUTS(_("\nsyncing on items"));
3971 syn_lines_msg();
3972 syn_match_msg();
3973 }
3974 }
3975 else
3976 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3977 if (ends_excmd(*arg))
3978 {
3979 /*
3980 * No argument: List all group IDs and all syntax clusters.
3981 */
3982 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3983 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003984 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985 syn_list_cluster(id);
3986 }
3987 else
3988 {
3989 /*
3990 * List the group IDs and syntax clusters that are in the argument.
3991 */
3992 while (!ends_excmd(*arg) && !got_int)
3993 {
3994 arg_end = skiptowhite(arg);
3995 if (*arg == '@')
3996 {
3997 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3998 if (id == 0)
3999 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4000 else
4001 syn_list_cluster(id - SYNID_CLUSTER);
4002 }
4003 else
4004 {
4005 id = syn_namen2id(arg, (int)(arg_end - arg));
4006 if (id == 0)
4007 EMSG2(_(e_nogroup), arg);
4008 else
4009 syn_list_one(id, syncing, TRUE);
4010 }
4011 arg = skipwhite(arg_end);
4012 }
4013 }
4014 eap->nextcmd = check_nextcmd(arg);
4015}
4016
4017 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004018syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004020 if (curwin->w_s->b_syn_sync_maxlines > 0
4021 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004022 {
4023 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004024 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004025 {
4026 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004027 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4028 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 MSG_PUTS(", ");
4030 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004031 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 {
4033 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004034 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 }
4036 MSG_PUTS(_(" lines before top line"));
4037 }
4038}
4039
4040 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004041syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004042{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004043 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 {
4045 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004046 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004047 MSG_PUTS(_(" line breaks"));
4048 }
4049}
4050
4051static int last_matchgroup;
4052
4053struct name_list
4054{
4055 int flag;
4056 char *name;
4057};
4058
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004059static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060
4061/*
4062 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4063 */
4064 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004065syn_list_one(
4066 int id,
4067 int syncing, /* when TRUE: list syncing items */
4068 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069{
4070 int attr;
4071 int idx;
4072 int did_header = FALSE;
4073 synpat_T *spp;
4074 static struct name_list namelist1[] =
4075 {
4076 {HL_DISPLAY, "display"},
4077 {HL_CONTAINED, "contained"},
4078 {HL_ONELINE, "oneline"},
4079 {HL_KEEPEND, "keepend"},
4080 {HL_EXTEND, "extend"},
4081 {HL_EXCLUDENL, "excludenl"},
4082 {HL_TRANSP, "transparent"},
4083 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004084#ifdef FEAT_CONCEAL
4085 {HL_CONCEAL, "conceal"},
4086 {HL_CONCEALENDS, "concealends"},
4087#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 {0, NULL}
4089 };
4090 static struct name_list namelist2[] =
4091 {
4092 {HL_SKIPWHITE, "skipwhite"},
4093 {HL_SKIPNL, "skipnl"},
4094 {HL_SKIPEMPTY, "skipempty"},
4095 {0, NULL}
4096 };
4097
Bram Moolenaar8820b482017-03-16 17:23:31 +01004098 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099
4100 /* list the keywords for "id" */
4101 if (!syncing)
4102 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004103 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4104 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105 did_header, attr);
4106 }
4107
4108 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004109 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004111 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4113 continue;
4114
4115 (void)syn_list_header(did_header, 999, id);
4116 did_header = TRUE;
4117 last_matchgroup = 0;
4118 if (spp->sp_type == SPTYPE_MATCH)
4119 {
4120 put_pattern("match", ' ', spp, attr);
4121 msg_putchar(' ');
4122 }
4123 else if (spp->sp_type == SPTYPE_START)
4124 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004125 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4126 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4127 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4128 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4129 while (idx < curwin->w_s->b_syn_patterns.ga_len
4130 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4131 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 --idx;
4133 msg_putchar(' ');
4134 }
4135 syn_list_flags(namelist1, spp->sp_flags, attr);
4136
4137 if (spp->sp_cont_list != NULL)
4138 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4139
4140 if (spp->sp_syn.cont_in_list != NULL)
4141 put_id_list((char_u *)"containedin",
4142 spp->sp_syn.cont_in_list, attr);
4143
4144 if (spp->sp_next_list != NULL)
4145 {
4146 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4147 syn_list_flags(namelist2, spp->sp_flags, attr);
4148 }
4149 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4150 {
4151 if (spp->sp_flags & HL_SYNC_HERE)
4152 msg_puts_attr((char_u *)"grouphere", attr);
4153 else
4154 msg_puts_attr((char_u *)"groupthere", attr);
4155 msg_putchar(' ');
4156 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004157 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4159 else
4160 MSG_PUTS("NONE");
4161 msg_putchar(' ');
4162 }
4163 }
4164
4165 /* list the link, if there is one */
4166 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4167 {
4168 (void)syn_list_header(did_header, 999, id);
4169 msg_puts_attr((char_u *)"links to", attr);
4170 msg_putchar(' ');
4171 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4172 }
4173}
4174
4175 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004176syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177{
4178 int i;
4179
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004180 for (i = 0; nlist[i].flag != 0; ++i)
4181 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004183 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 msg_putchar(' ');
4185 }
4186}
4187
4188/*
4189 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4190 */
4191 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004192syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193{
4194 int endcol = 15;
4195
4196 /* slight hack: roughly duplicate the guts of syn_list_header() */
4197 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004198 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199
4200 if (msg_col >= endcol) /* output at least one space */
4201 endcol = msg_col + 1;
4202 if (Columns <= endcol) /* avoid hang for tiny window */
4203 endcol = Columns - 1;
4204
4205 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004206 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004208 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004209 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 }
4211 else
4212 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004213 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 msg_puts((char_u *)"=NONE");
4215 }
4216}
4217
4218 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004219put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220{
4221 short *p;
4222
4223 msg_puts_attr(name, attr);
4224 msg_putchar('=');
4225 for (p = list; *p; ++p)
4226 {
4227 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4228 {
4229 if (p[1])
4230 MSG_PUTS("ALLBUT");
4231 else
4232 MSG_PUTS("ALL");
4233 }
4234 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4235 {
4236 MSG_PUTS("TOP");
4237 }
4238 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4239 {
4240 MSG_PUTS("CONTAINED");
4241 }
4242 else if (*p >= SYNID_CLUSTER)
4243 {
4244 short scl_id = *p - SYNID_CLUSTER;
4245
4246 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004247 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 }
4249 else
4250 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4251 if (p[1])
4252 msg_putchar(',');
4253 }
4254 msg_putchar(' ');
4255}
4256
4257 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004258put_pattern(
4259 char *s,
4260 int c,
4261 synpat_T *spp,
4262 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004263{
4264 long n;
4265 int mask;
4266 int first;
4267 static char *sepchars = "/+=-#@\"|'^&";
4268 int i;
4269
4270 /* May have to write "matchgroup=group" */
4271 if (last_matchgroup != spp->sp_syn_match_id)
4272 {
4273 last_matchgroup = spp->sp_syn_match_id;
4274 msg_puts_attr((char_u *)"matchgroup", attr);
4275 msg_putchar('=');
4276 if (last_matchgroup == 0)
4277 msg_outtrans((char_u *)"NONE");
4278 else
4279 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4280 msg_putchar(' ');
4281 }
4282
4283 /* output the name of the pattern and an '=' or ' ' */
4284 msg_puts_attr((char_u *)s, attr);
4285 msg_putchar(c);
4286
4287 /* output the pattern, in between a char that is not in the pattern */
4288 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4289 if (sepchars[++i] == NUL)
4290 {
4291 i = 0; /* no good char found, just use the first one */
4292 break;
4293 }
4294 msg_putchar(sepchars[i]);
4295 msg_outtrans(spp->sp_pattern);
4296 msg_putchar(sepchars[i]);
4297
4298 /* output any pattern options */
4299 first = TRUE;
4300 for (i = 0; i < SPO_COUNT; ++i)
4301 {
4302 mask = (1 << i);
4303 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4304 {
4305 if (!first)
4306 msg_putchar(','); /* separate with commas */
4307 msg_puts((char_u *)spo_name_tab[i]);
4308 n = spp->sp_offsets[i];
4309 if (i != SPO_LC_OFF)
4310 {
4311 if (spp->sp_off_flags & mask)
4312 msg_putchar('s');
4313 else
4314 msg_putchar('e');
4315 if (n > 0)
4316 msg_putchar('+');
4317 }
4318 if (n || i == SPO_LC_OFF)
4319 msg_outnum(n);
4320 first = FALSE;
4321 }
4322 }
4323 msg_putchar(' ');
4324}
4325
4326/*
4327 * List or clear the keywords for one syntax group.
4328 * Return TRUE if the header has been printed.
4329 */
4330 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004331syn_list_keywords(
4332 int id,
4333 hashtab_T *ht,
4334 int did_header, /* header has already been printed */
4335 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 hashitem_T *hi;
4339 keyentry_T *kp;
4340 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 int prev_contained = 0;
4342 short *prev_next_list = NULL;
4343 short *prev_cont_in_list = NULL;
4344 int prev_skipnl = 0;
4345 int prev_skipwhite = 0;
4346 int prev_skipempty = 0;
4347
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 /*
4349 * Unfortunately, this list of keywords is not sorted on alphabet but on
4350 * hash value...
4351 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004352 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004353 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 --todo;
4358 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 if (prev_contained != (kp->flags & HL_CONTAINED)
4363 || prev_skipnl != (kp->flags & HL_SKIPNL)
4364 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4365 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4366 || prev_cont_in_list != kp->k_syn.cont_in_list
4367 || prev_next_list != kp->next_list)
4368 outlen = 9999;
4369 else
4370 outlen = (int)STRLEN(kp->keyword);
4371 /* output "contained" and "nextgroup" on each line */
4372 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 prev_contained = 0;
4375 prev_next_list = NULL;
4376 prev_cont_in_list = NULL;
4377 prev_skipnl = 0;
4378 prev_skipwhite = 0;
4379 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 did_header = TRUE;
4382 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004386 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 put_id_list((char_u *)"containedin",
4391 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004393 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004395 if (kp->next_list != prev_next_list)
4396 {
4397 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4398 msg_putchar(' ');
4399 prev_next_list = kp->next_list;
4400 if (kp->flags & HL_SKIPNL)
4401 {
4402 msg_puts_attr((char_u *)"skipnl", attr);
4403 msg_putchar(' ');
4404 prev_skipnl = (kp->flags & HL_SKIPNL);
4405 }
4406 if (kp->flags & HL_SKIPWHITE)
4407 {
4408 msg_puts_attr((char_u *)"skipwhite", attr);
4409 msg_putchar(' ');
4410 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4411 }
4412 if (kp->flags & HL_SKIPEMPTY)
4413 {
4414 msg_puts_attr((char_u *)"skipempty", attr);
4415 msg_putchar(' ');
4416 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4417 }
4418 }
4419 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004421 }
4422 }
4423 }
4424
4425 return did_header;
4426}
4427
4428 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004429syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004431 hashitem_T *hi;
4432 keyentry_T *kp;
4433 keyentry_T *kp_prev;
4434 keyentry_T *kp_next;
4435 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436
Bram Moolenaardad6b692005-01-25 22:14:34 +00004437 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004438 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004441 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004443 --todo;
4444 kp_prev = NULL;
4445 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 if (kp->k_syn.id == id)
4448 {
4449 kp_next = kp->ke_next;
4450 if (kp_prev == NULL)
4451 {
4452 if (kp_next == NULL)
4453 hash_remove(ht, hi);
4454 else
4455 hi->hi_key = KE2HIKEY(kp_next);
4456 }
4457 else
4458 kp_prev->ke_next = kp_next;
4459 vim_free(kp->next_list);
4460 vim_free(kp->k_syn.cont_in_list);
4461 vim_free(kp);
4462 kp = kp_next;
4463 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004465 {
4466 kp_prev = kp;
4467 kp = kp->ke_next;
4468 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469 }
4470 }
4471 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004472 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473}
4474
4475/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004476 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 */
4478 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004479clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004481 hashitem_T *hi;
4482 int todo;
4483 keyentry_T *kp;
4484 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004486 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004487 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004489 if (!HASHITEM_EMPTY(hi))
4490 {
4491 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004492 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 kp_next = kp->ke_next;
4495 vim_free(kp->next_list);
4496 vim_free(kp->k_syn.cont_in_list);
4497 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004499 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004501 hash_clear(ht);
4502 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503}
4504
4505/*
4506 * Add a keyword to the list of keywords.
4507 */
4508 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004509add_keyword(
4510 char_u *name, /* name of keyword */
4511 int id, /* group ID for this keyword */
4512 int flags, /* flags for this keyword */
4513 short *cont_in_list, /* containedin for this keyword */
4514 short *next_list, /* nextgroup for this keyword */
4515 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004517 keyentry_T *kp;
4518 hashtab_T *ht;
4519 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004520 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004521 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004522 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523
Bram Moolenaar860cae12010-06-05 23:22:07 +02004524 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004525 name_ic = str_foldcase(name, (int)STRLEN(name),
4526 name_folded, MAXKEYWLEN + 1);
4527 else
4528 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004529 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4530 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004532 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004533 kp->k_syn.id = id;
4534 kp->k_syn.inc_tag = current_syn_inc_tag;
4535 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004536 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004537 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004539 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004540 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541
Bram Moolenaar860cae12010-06-05 23:22:07 +02004542 if (curwin->w_s->b_syn_ic)
4543 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004545 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546
Bram Moolenaardad6b692005-01-25 22:14:34 +00004547 hash = hash_hash(kp->keyword);
4548 hi = hash_lookup(ht, kp->keyword, hash);
4549 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004551 /* new keyword, add to hashtable */
4552 kp->ke_next = NULL;
4553 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004555 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004557 /* keyword already exists, prepend to list */
4558 kp->ke_next = HI2KE(hi);
4559 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561}
4562
4563/*
4564 * Get the start and end of the group name argument.
4565 * Return a pointer to the first argument.
4566 * Return NULL if the end of the command was found instead of further args.
4567 */
4568 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004569get_group_name(
4570 char_u *arg, /* start of the argument */
4571 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572{
4573 char_u *rest;
4574
4575 *name_end = skiptowhite(arg);
4576 rest = skipwhite(*name_end);
4577
4578 /*
4579 * Check if there are enough arguments. The first argument may be a
4580 * pattern, where '|' is allowed, so only check for NUL.
4581 */
4582 if (ends_excmd(*arg) || *rest == NUL)
4583 return NULL;
4584 return rest;
4585}
4586
4587/*
4588 * Check for syntax command option arguments.
4589 * This can be called at any place in the list of arguments, and just picks
4590 * out the arguments that are known. Can be called several times in a row to
4591 * collect all options in between other arguments.
4592 * Return a pointer to the next argument (which isn't an option).
4593 * Return NULL for any error;
4594 */
4595 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004596get_syn_options(
4597 char_u *arg, /* next argument to be checked */
4598 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004599 int *conceal_char UNUSED,
4600 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004602 char_u *gname_start, *gname;
4603 int syn_id;
4604 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004605 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606 int i;
4607 int fidx;
4608 static struct flag
4609 {
4610 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004611 int argtype;
4612 int flags;
4613 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4614 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4615 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4616 {"eExXtTeEnNdD", 0, HL_EXTEND},
4617 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4618 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4619 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4620 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4621 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4622 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4623 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4624 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4625 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004626 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4627 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4628 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004629 {"cCoOnNtTaAiInNsS", 1, 0},
4630 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4631 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004633 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634
4635 if (arg == NULL) /* already detected error */
4636 return NULL;
4637
Bram Moolenaar860cae12010-06-05 23:22:07 +02004638#ifdef FEAT_CONCEAL
4639 if (curwin->w_s->b_syn_conceal)
4640 opt->flags |= HL_CONCEAL;
4641#endif
4642
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 for (;;)
4644 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004645 /*
4646 * This is used very often when a large number of keywords is defined.
4647 * Need to skip quickly when no option name is found.
4648 * Also avoid tolower(), it's slow.
4649 */
4650 if (strchr(first_letters, *arg) == NULL)
4651 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652
4653 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4654 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004655 p = flagtab[fidx].name;
4656 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4657 if (arg[len] != p[i] && arg[len] != p[i + 1])
4658 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004659 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004660 || (flagtab[fidx].argtype > 0
4661 ? arg[len] == '='
4662 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004663 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004664 if (opt->keyword
4665 && (flagtab[fidx].flags == HL_DISPLAY
4666 || flagtab[fidx].flags == HL_FOLD
4667 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668 /* treat "display", "fold" and "extend" as a keyword */
4669 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004670 break;
4671 }
4672 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004673 if (fidx < 0) /* no match found */
4674 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004676 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004678 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004679 {
4680 EMSG(_("E395: contains argument not accepted here"));
4681 return NULL;
4682 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004683 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 return NULL;
4685 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004686 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004687 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004688 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689 return NULL;
4690 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004691 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004693 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694 return NULL;
4695 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004696 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4697 {
4698#ifdef FEAT_MBYTE
4699 /* cchar=? */
4700 if (has_mbyte)
4701 {
4702# ifdef FEAT_CONCEAL
4703 *conceal_char = mb_ptr2char(arg + 6);
4704# endif
4705 arg += mb_ptr2len(arg + 6) - 1;
4706 }
4707 else
4708#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004709 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004710#ifdef FEAT_CONCEAL
4711 *conceal_char = arg[6];
4712#else
4713 ;
4714#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004715 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004716#ifdef FEAT_CONCEAL
4717 if (!vim_isprintc_strict(*conceal_char))
4718 {
4719 EMSG(_("E844: invalid cchar value"));
4720 return NULL;
4721 }
4722#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004723 arg = skipwhite(arg + 7);
4724 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004726 {
4727 opt->flags |= flagtab[fidx].flags;
4728 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004730 if (flagtab[fidx].flags == HL_SYNC_HERE
4731 || flagtab[fidx].flags == HL_SYNC_THERE)
4732 {
4733 if (opt->sync_idx == NULL)
4734 {
4735 EMSG(_("E393: group[t]here not accepted here"));
4736 return NULL;
4737 }
4738 gname_start = arg;
4739 arg = skiptowhite(arg);
4740 if (gname_start == arg)
4741 return NULL;
4742 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4743 if (gname == NULL)
4744 return NULL;
4745 if (STRCMP(gname, "NONE") == 0)
4746 *opt->sync_idx = NONE_IDX;
4747 else
4748 {
4749 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004750 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4751 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4752 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004753 {
4754 *opt->sync_idx = i;
4755 break;
4756 }
4757 if (i < 0)
4758 {
4759 EMSG2(_("E394: Didn't find region item for %s"), gname);
4760 vim_free(gname);
4761 return NULL;
4762 }
4763 }
4764
4765 vim_free(gname);
4766 arg = skipwhite(arg);
4767 }
4768#ifdef FEAT_FOLDING
4769 else if (flagtab[fidx].flags == HL_FOLD
4770 && foldmethodIsSyntax(curwin))
4771 /* Need to update folds later. */
4772 foldUpdateAll(curwin);
4773#endif
4774 }
4775 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776
4777 return arg;
4778}
4779
4780/*
4781 * Adjustments to syntax item when declared in a ":syn include"'d file.
4782 * Set the contained flag, and if the item is not already contained, add it
4783 * to the specified top-level group, if any.
4784 */
4785 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004786syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004788 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004789 return;
4790 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004791 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792 {
4793 /* We have to alloc this, because syn_combine_list() will free it. */
4794 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004795 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796
4797 if (grp_list != NULL)
4798 {
4799 grp_list[0] = id;
4800 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004801 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802 CLUSTER_ADD);
4803 }
4804 }
4805}
4806
4807/*
4808 * Handle ":syntax include [@{group-name}] filename" command.
4809 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004811syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004812{
4813 char_u *arg = eap->arg;
4814 int sgl_id = 1;
4815 char_u *group_name_end;
4816 char_u *rest;
4817 char_u *errormsg = NULL;
4818 int prev_toplvl_grp;
4819 int prev_syn_inc_tag;
4820 int source = FALSE;
4821
4822 eap->nextcmd = find_nextcmd(arg);
4823 if (eap->skip)
4824 return;
4825
4826 if (arg[0] == '@')
4827 {
4828 ++arg;
4829 rest = get_group_name(arg, &group_name_end);
4830 if (rest == NULL)
4831 {
4832 EMSG((char_u *)_("E397: Filename required"));
4833 return;
4834 }
4835 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004836 if (sgl_id == 0)
4837 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838 /* separate_nextcmd() and expand_filename() depend on this */
4839 eap->arg = rest;
4840 }
4841
4842 /*
4843 * Everything that's left, up to the next command, should be the
4844 * filename to include.
4845 */
4846 eap->argt |= (XFILE | NOSPC);
4847 separate_nextcmd(eap);
4848 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4849 {
4850 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4851 * file. Need to expand the file name first. In other cases
4852 * ":runtime!" is used. */
4853 source = TRUE;
4854 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4855 {
4856 if (errormsg != NULL)
4857 EMSG(errormsg);
4858 return;
4859 }
4860 }
4861
4862 /*
4863 * Save and restore the existing top-level grouplist id and ":syn
4864 * include" tag around the actual inclusion.
4865 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004866 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4867 {
4868 EMSG((char_u *)_("E847: Too many syntax includes"));
4869 return;
4870 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871 prev_syn_inc_tag = current_syn_inc_tag;
4872 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004873 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4874 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004875 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004876 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004877 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004878 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879 current_syn_inc_tag = prev_syn_inc_tag;
4880}
4881
4882/*
4883 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4884 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004886syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887{
4888 char_u *arg = eap->arg;
4889 char_u *group_name_end;
4890 int syn_id;
4891 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004892 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004894 char_u *kw;
4895 syn_opt_arg_T syn_opt_arg;
4896 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004897 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898
4899 rest = get_group_name(arg, &group_name_end);
4900
4901 if (rest != NULL)
4902 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004903 if (eap->skip)
4904 syn_id = -1;
4905 else
4906 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004907 if (syn_id != 0)
4908 /* allocate a buffer, for removing backslashes in the keyword */
4909 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004910 if (keyword_copy != NULL)
4911 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004912 syn_opt_arg.flags = 0;
4913 syn_opt_arg.keyword = TRUE;
4914 syn_opt_arg.sync_idx = NULL;
4915 syn_opt_arg.has_cont_list = FALSE;
4916 syn_opt_arg.cont_in_list = NULL;
4917 syn_opt_arg.next_list = NULL;
4918
Bram Moolenaar071d4272004-06-13 20:20:40 +00004919 /*
4920 * The options given apply to ALL keywords, so all options must be
4921 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004922 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004924 cnt = 0;
4925 p = keyword_copy;
4926 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004928 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4929 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 if (rest == NULL || ends_excmd(*rest))
4931 break;
4932 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004933 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004935 if (*rest == '\\' && rest[1] != NUL)
4936 ++rest;
4937 *p++ = *rest++;
4938 }
4939 *p++ = NUL;
4940 ++cnt;
4941 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004943 if (!eap->skip)
4944 {
4945 /* Adjust flags for use of ":syn include". */
4946 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4947
4948 /*
4949 * 2: Add an entry for each keyword.
4950 */
4951 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4952 {
4953 for (p = vim_strchr(kw, '['); ; )
4954 {
4955 if (p != NULL)
4956 *p = NUL;
4957 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004958 syn_opt_arg.cont_in_list,
4959 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004960 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004961 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004962 if (p[1] == NUL)
4963 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004964 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004965 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004966 }
4967 if (p[1] == ']')
4968 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004969 if (p[2] != NUL)
4970 {
4971 EMSG3(_("E890: trailing char after ']': %s]%s"),
4972 kw, &p[2]);
4973 goto error;
4974 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004975 kw = p + 1; /* skip over the "]" */
4976 break;
4977 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004978#ifdef FEAT_MBYTE
4979 if (has_mbyte)
4980 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004981 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004982
4983 mch_memmove(p, p + 1, l);
4984 p += l;
4985 }
4986 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004988 {
4989 p[0] = p[1];
4990 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004991 }
4992 }
4993 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004995error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004997 vim_free(syn_opt_arg.cont_in_list);
4998 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999 }
5000 }
5001
5002 if (rest != NULL)
5003 eap->nextcmd = check_nextcmd(rest);
5004 else
5005 EMSG2(_(e_invarg2), arg);
5006
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005007 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005008 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009}
5010
5011/*
5012 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5013 *
5014 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5015 */
5016 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005017syn_cmd_match(
5018 exarg_T *eap,
5019 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005020{
5021 char_u *arg = eap->arg;
5022 char_u *group_name_end;
5023 char_u *rest;
5024 synpat_T item; /* the item found in the line */
5025 int syn_id;
5026 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005027 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005029 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030
5031 /* Isolate the group name, check for validity */
5032 rest = get_group_name(arg, &group_name_end);
5033
5034 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005035 syn_opt_arg.flags = 0;
5036 syn_opt_arg.keyword = FALSE;
5037 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5038 syn_opt_arg.has_cont_list = TRUE;
5039 syn_opt_arg.cont_list = NULL;
5040 syn_opt_arg.cont_in_list = NULL;
5041 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005042 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043
5044 /* get the pattern. */
5045 init_syn_patterns();
5046 vim_memset(&item, 0, sizeof(item));
5047 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005048 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5049 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005050
5051 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005052 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005053
5054 if (rest != NULL) /* all arguments are valid */
5055 {
5056 /*
5057 * Check for trailing command and illegal trailing arguments.
5058 */
5059 eap->nextcmd = check_nextcmd(rest);
5060 if (!ends_excmd(*rest) || eap->skip)
5061 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005062 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063 && (syn_id = syn_check_group(arg,
5064 (int)(group_name_end - arg))) != 0)
5065 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067 /*
5068 * Store the pattern in the syn_items list
5069 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005070 idx = curwin->w_s->b_syn_patterns.ga_len;
5071 SYN_ITEMS(curwin->w_s)[idx] = item;
5072 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5073 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5074 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5075 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5076 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5077 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5078 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5079 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005080 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005081#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005082 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005083#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005084 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005085 curwin->w_s->b_syn_containedin = TRUE;
5086 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5087 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005088
5089 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005090 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005091 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005093 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005094 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005095#endif
5096
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005097 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005098 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099 return; /* don't free the progs and patterns now */
5100 }
5101 }
5102
5103 /*
5104 * Something failed, free the allocated memory.
5105 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005106 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005108 vim_free(syn_opt_arg.cont_list);
5109 vim_free(syn_opt_arg.cont_in_list);
5110 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111
5112 if (rest == NULL)
5113 EMSG2(_(e_invarg2), arg);
5114}
5115
5116/*
5117 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5118 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5119 */
5120 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005121syn_cmd_region(
5122 exarg_T *eap,
5123 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005124{
5125 char_u *arg = eap->arg;
5126 char_u *group_name_end;
5127 char_u *rest; /* next arg, NULL on error */
5128 char_u *key_end;
5129 char_u *key = NULL;
5130 char_u *p;
5131 int item;
5132#define ITEM_START 0
5133#define ITEM_SKIP 1
5134#define ITEM_END 2
5135#define ITEM_MATCHGROUP 3
5136 struct pat_ptr
5137 {
5138 synpat_T *pp_synp; /* pointer to syn_pattern */
5139 int pp_matchgroup_id; /* matchgroup ID */
5140 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5141 } *(pat_ptrs[3]);
5142 /* patterns found in the line */
5143 struct pat_ptr *ppp;
5144 struct pat_ptr *ppp_next;
5145 int pat_count = 0; /* nr of syn_patterns found */
5146 int syn_id;
5147 int matchgroup_id = 0;
5148 int not_enough = FALSE; /* not enough arguments */
5149 int illegal = FALSE; /* illegal arguments */
5150 int success = FALSE;
5151 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005152 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005153 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154
5155 /* Isolate the group name, check for validity */
5156 rest = get_group_name(arg, &group_name_end);
5157
5158 pat_ptrs[0] = NULL;
5159 pat_ptrs[1] = NULL;
5160 pat_ptrs[2] = NULL;
5161
5162 init_syn_patterns();
5163
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005164 syn_opt_arg.flags = 0;
5165 syn_opt_arg.keyword = FALSE;
5166 syn_opt_arg.sync_idx = NULL;
5167 syn_opt_arg.has_cont_list = TRUE;
5168 syn_opt_arg.cont_list = NULL;
5169 syn_opt_arg.cont_in_list = NULL;
5170 syn_opt_arg.next_list = NULL;
5171
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172 /*
5173 * get the options, patterns and matchgroup.
5174 */
5175 while (rest != NULL && !ends_excmd(*rest))
5176 {
5177 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005178 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 if (rest == NULL || ends_excmd(*rest))
5180 break;
5181
5182 /* must be a pattern or matchgroup then */
5183 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005184 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005185 ++key_end;
5186 vim_free(key);
5187 key = vim_strnsave_up(rest, (int)(key_end - rest));
5188 if (key == NULL) /* out of memory */
5189 {
5190 rest = NULL;
5191 break;
5192 }
5193 if (STRCMP(key, "MATCHGROUP") == 0)
5194 item = ITEM_MATCHGROUP;
5195 else if (STRCMP(key, "START") == 0)
5196 item = ITEM_START;
5197 else if (STRCMP(key, "END") == 0)
5198 item = ITEM_END;
5199 else if (STRCMP(key, "SKIP") == 0)
5200 {
5201 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5202 {
5203 illegal = TRUE;
5204 break;
5205 }
5206 item = ITEM_SKIP;
5207 }
5208 else
5209 break;
5210 rest = skipwhite(key_end);
5211 if (*rest != '=')
5212 {
5213 rest = NULL;
5214 EMSG2(_("E398: Missing '=': %s"), arg);
5215 break;
5216 }
5217 rest = skipwhite(rest + 1);
5218 if (*rest == NUL)
5219 {
5220 not_enough = TRUE;
5221 break;
5222 }
5223
5224 if (item == ITEM_MATCHGROUP)
5225 {
5226 p = skiptowhite(rest);
5227 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5228 matchgroup_id = 0;
5229 else
5230 {
5231 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5232 if (matchgroup_id == 0)
5233 {
5234 illegal = TRUE;
5235 break;
5236 }
5237 }
5238 rest = skipwhite(p);
5239 }
5240 else
5241 {
5242 /*
5243 * Allocate room for a syn_pattern, and link it in the list of
5244 * syn_patterns for this item, at the start (because the list is
5245 * used from end to start).
5246 */
5247 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5248 if (ppp == NULL)
5249 {
5250 rest = NULL;
5251 break;
5252 }
5253 ppp->pp_next = pat_ptrs[item];
5254 pat_ptrs[item] = ppp;
5255 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5256 if (ppp->pp_synp == NULL)
5257 {
5258 rest = NULL;
5259 break;
5260 }
5261
5262 /*
5263 * Get the syntax pattern and the following offset(s).
5264 */
5265 /* Enable the appropriate \z specials. */
5266 if (item == ITEM_START)
5267 reg_do_extmatch = REX_SET;
5268 else if (item == ITEM_SKIP || item == ITEM_END)
5269 reg_do_extmatch = REX_USE;
5270 rest = get_syn_pattern(rest, ppp->pp_synp);
5271 reg_do_extmatch = 0;
5272 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005273 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005274 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5275 ppp->pp_matchgroup_id = matchgroup_id;
5276 ++pat_count;
5277 }
5278 }
5279 vim_free(key);
5280 if (illegal || not_enough)
5281 rest = NULL;
5282
5283 /*
5284 * Must have a "start" and "end" pattern.
5285 */
5286 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5287 pat_ptrs[ITEM_END] == NULL))
5288 {
5289 not_enough = TRUE;
5290 rest = NULL;
5291 }
5292
5293 if (rest != NULL)
5294 {
5295 /*
5296 * Check for trailing garbage or command.
5297 * If OK, add the item.
5298 */
5299 eap->nextcmd = check_nextcmd(rest);
5300 if (!ends_excmd(*rest) || eap->skip)
5301 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005302 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303 && (syn_id = syn_check_group(arg,
5304 (int)(group_name_end - arg))) != 0)
5305 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005306 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005307 /*
5308 * Store the start/skip/end in the syn_items list
5309 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005310 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005311 for (item = ITEM_START; item <= ITEM_END; ++item)
5312 {
5313 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5314 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005315 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5316 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5317 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318 (item == ITEM_START) ? SPTYPE_START :
5319 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005320 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5321 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005322 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5323 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005324 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005325 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005326#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005327 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005328#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329 if (item == ITEM_START)
5330 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005331 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005332 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005333 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005334 syn_opt_arg.cont_in_list;
5335 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005336 curwin->w_s->b_syn_containedin = TRUE;
5337 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005338 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005339 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005340 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005341 ++idx;
5342#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005343 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005344 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345#endif
5346 }
5347 }
5348
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005349 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005350 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 success = TRUE; /* don't free the progs and patterns now */
5352 }
5353 }
5354
5355 /*
5356 * Free the allocated memory.
5357 */
5358 for (item = ITEM_START; item <= ITEM_END; ++item)
5359 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5360 {
5361 if (!success)
5362 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005363 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005364 vim_free(ppp->pp_synp->sp_pattern);
5365 }
5366 vim_free(ppp->pp_synp);
5367 ppp_next = ppp->pp_next;
5368 vim_free(ppp);
5369 }
5370
5371 if (!success)
5372 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005373 vim_free(syn_opt_arg.cont_list);
5374 vim_free(syn_opt_arg.cont_in_list);
5375 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005376 if (not_enough)
5377 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5378 else if (illegal || rest == NULL)
5379 EMSG2(_(e_invarg2), arg);
5380 }
5381}
5382
5383/*
5384 * A simple syntax group ID comparison function suitable for use in qsort()
5385 */
5386 static int
5387#ifdef __BORLANDC__
5388_RTLENTRYF
5389#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005390syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005391{
5392 const short *s1 = v1;
5393 const short *s2 = v2;
5394
5395 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5396}
5397
5398/*
5399 * Combines lists of syntax clusters.
5400 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5401 */
5402 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005403syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404{
5405 int count1 = 0;
5406 int count2 = 0;
5407 short *g1;
5408 short *g2;
5409 short *clstr = NULL;
5410 int count;
5411 int round;
5412
5413 /*
5414 * Handle degenerate cases.
5415 */
5416 if (*clstr2 == NULL)
5417 return;
5418 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5419 {
5420 if (list_op == CLUSTER_REPLACE)
5421 vim_free(*clstr1);
5422 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5423 *clstr1 = *clstr2;
5424 else
5425 vim_free(*clstr2);
5426 return;
5427 }
5428
5429 for (g1 = *clstr1; *g1; g1++)
5430 ++count1;
5431 for (g2 = *clstr2; *g2; g2++)
5432 ++count2;
5433
5434 /*
5435 * For speed purposes, sort both lists.
5436 */
5437 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5438 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5439
5440 /*
5441 * We proceed in two passes; in round 1, we count the elements to place
5442 * in the new list, and in round 2, we allocate and populate the new
5443 * list. For speed, we use a mergesort-like method, adding the smaller
5444 * of the current elements in each list to the new list.
5445 */
5446 for (round = 1; round <= 2; round++)
5447 {
5448 g1 = *clstr1;
5449 g2 = *clstr2;
5450 count = 0;
5451
5452 /*
5453 * First, loop through the lists until one of them is empty.
5454 */
5455 while (*g1 && *g2)
5456 {
5457 /*
5458 * We always want to add from the first list.
5459 */
5460 if (*g1 < *g2)
5461 {
5462 if (round == 2)
5463 clstr[count] = *g1;
5464 count++;
5465 g1++;
5466 continue;
5467 }
5468 /*
5469 * We only want to add from the second list if we're adding the
5470 * lists.
5471 */
5472 if (list_op == CLUSTER_ADD)
5473 {
5474 if (round == 2)
5475 clstr[count] = *g2;
5476 count++;
5477 }
5478 if (*g1 == *g2)
5479 g1++;
5480 g2++;
5481 }
5482
5483 /*
5484 * Now add the leftovers from whichever list didn't get finished
5485 * first. As before, we only want to add from the second list if
5486 * we're adding the lists.
5487 */
5488 for (; *g1; g1++, count++)
5489 if (round == 2)
5490 clstr[count] = *g1;
5491 if (list_op == CLUSTER_ADD)
5492 for (; *g2; g2++, count++)
5493 if (round == 2)
5494 clstr[count] = *g2;
5495
5496 if (round == 1)
5497 {
5498 /*
5499 * If the group ended up empty, we don't need to allocate any
5500 * space for it.
5501 */
5502 if (count == 0)
5503 {
5504 clstr = NULL;
5505 break;
5506 }
5507 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5508 if (clstr == NULL)
5509 break;
5510 clstr[count] = 0;
5511 }
5512 }
5513
5514 /*
5515 * Finally, put the new list in place.
5516 */
5517 vim_free(*clstr1);
5518 vim_free(*clstr2);
5519 *clstr1 = clstr;
5520}
5521
5522/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005523 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005524 * If it is not found, 0 is returned.
5525 */
5526 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005527syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528{
5529 int i;
5530 char_u *name_u;
5531
5532 /* Avoid using stricmp() too much, it's slow on some systems */
5533 name_u = vim_strsave_up(name);
5534 if (name_u == NULL)
5535 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005536 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5537 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5538 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 break;
5540 vim_free(name_u);
5541 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5542}
5543
5544/*
5545 * Like syn_scl_name2id(), but take a pointer + length argument.
5546 */
5547 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005548syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549{
5550 char_u *name;
5551 int id = 0;
5552
5553 name = vim_strnsave(linep, len);
5554 if (name != NULL)
5555 {
5556 id = syn_scl_name2id(name);
5557 vim_free(name);
5558 }
5559 return id;
5560}
5561
5562/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005563 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005564 * The argument is a pointer to the name and the length of the name.
5565 * If it doesn't exist yet, a new entry is created.
5566 * Return 0 for failure.
5567 */
5568 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005569syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005570{
5571 int id;
5572 char_u *name;
5573
5574 name = vim_strnsave(pp, len);
5575 if (name == NULL)
5576 return 0;
5577
5578 id = syn_scl_name2id(name);
5579 if (id == 0) /* doesn't exist yet */
5580 id = syn_add_cluster(name);
5581 else
5582 vim_free(name);
5583 return id;
5584}
5585
5586/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005587 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588 * "name" must be an allocated string, it will be consumed.
5589 * Return 0 for failure.
5590 */
5591 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005592syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005593{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005594 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005595
5596 /*
5597 * First call for this growarray: init growing array.
5598 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005599 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005601 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5602 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005603 }
5604
Bram Moolenaar42431a72011-04-01 14:44:59 +02005605 len = curwin->w_s->b_syn_clusters.ga_len;
5606 if (len >= MAX_CLUSTER_ID)
5607 {
5608 EMSG((char_u *)_("E848: Too many syntax clusters"));
5609 vim_free(name);
5610 return 0;
5611 }
5612
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613 /*
5614 * Make room for at least one other cluster entry.
5615 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005616 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005617 {
5618 vim_free(name);
5619 return 0;
5620 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621
Bram Moolenaar860cae12010-06-05 23:22:07 +02005622 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5623 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5624 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5625 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5626 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005627
Bram Moolenaar217ad922005-03-20 22:37:15 +00005628 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005629 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005630 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005631 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005632
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633 return len + SYNID_CLUSTER;
5634}
5635
5636/*
5637 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5638 * [add={groupname},..] [remove={groupname},..]".
5639 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005640 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005641syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642{
5643 char_u *arg = eap->arg;
5644 char_u *group_name_end;
5645 char_u *rest;
5646 int scl_id;
5647 short *clstr_list;
5648 int got_clstr = FALSE;
5649 int opt_len;
5650 int list_op;
5651
5652 eap->nextcmd = find_nextcmd(arg);
5653 if (eap->skip)
5654 return;
5655
5656 rest = get_group_name(arg, &group_name_end);
5657
5658 if (rest != NULL)
5659 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005660 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5661 if (scl_id == 0)
5662 return;
5663 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005664
5665 for (;;)
5666 {
5667 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005668 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005669 {
5670 opt_len = 3;
5671 list_op = CLUSTER_ADD;
5672 }
5673 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005674 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 {
5676 opt_len = 6;
5677 list_op = CLUSTER_SUBTRACT;
5678 }
5679 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005680 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681 {
5682 opt_len = 8;
5683 list_op = CLUSTER_REPLACE;
5684 }
5685 else
5686 break;
5687
5688 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005689 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690 {
5691 EMSG2(_(e_invarg2), rest);
5692 break;
5693 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005694 if (scl_id >= 0)
5695 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005696 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005697 else
5698 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699 got_clstr = TRUE;
5700 }
5701
5702 if (got_clstr)
5703 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005704 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005705 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706 }
5707 }
5708
5709 if (!got_clstr)
5710 EMSG(_("E400: No cluster specified"));
5711 if (rest == NULL || !ends_excmd(*rest))
5712 EMSG2(_(e_invarg2), arg);
5713}
5714
5715/*
5716 * On first call for current buffer: Init growing array.
5717 */
5718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005719init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005721 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5722 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005723}
5724
5725/*
5726 * Get one pattern for a ":syntax match" or ":syntax region" command.
5727 * Stores the pattern and program in a synpat_T.
5728 * Returns a pointer to the next argument, or NULL in case of an error.
5729 */
5730 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005731get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732{
5733 char_u *end;
5734 int *p;
5735 int idx;
5736 char_u *cpo_save;
5737
5738 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005739 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740 return NULL;
5741
5742 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5743 if (*end != *arg) /* end delimiter not found */
5744 {
5745 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5746 return NULL;
5747 }
5748 /* store the pattern and compiled regexp program */
5749 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5750 return NULL;
5751
5752 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5753 cpo_save = p_cpo;
5754 p_cpo = (char_u *)"";
5755 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5756 p_cpo = cpo_save;
5757
5758 if (ci->sp_prog == NULL)
5759 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005760 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005761#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005762 syn_clear_time(&ci->sp_time);
5763#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005764
5765 /*
5766 * Check for a match, highlight or region offset.
5767 */
5768 ++end;
5769 do
5770 {
5771 for (idx = SPO_COUNT; --idx >= 0; )
5772 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5773 break;
5774 if (idx >= 0)
5775 {
5776 p = &(ci->sp_offsets[idx]);
5777 if (idx != SPO_LC_OFF)
5778 switch (end[3])
5779 {
5780 case 's': break;
5781 case 'b': break;
5782 case 'e': idx += SPO_COUNT; break;
5783 default: idx = -1; break;
5784 }
5785 if (idx >= 0)
5786 {
5787 ci->sp_off_flags |= (1 << idx);
5788 if (idx == SPO_LC_OFF) /* lc=99 */
5789 {
5790 end += 3;
5791 *p = getdigits(&end);
5792
5793 /* "lc=" offset automatically sets "ms=" offset */
5794 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5795 {
5796 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5797 ci->sp_offsets[SPO_MS_OFF] = *p;
5798 }
5799 }
5800 else /* yy=x+99 */
5801 {
5802 end += 4;
5803 if (*end == '+')
5804 {
5805 ++end;
5806 *p = getdigits(&end); /* positive offset */
5807 }
5808 else if (*end == '-')
5809 {
5810 ++end;
5811 *p = -getdigits(&end); /* negative offset */
5812 }
5813 }
5814 if (*end != ',')
5815 break;
5816 ++end;
5817 }
5818 }
5819 } while (idx >= 0);
5820
Bram Moolenaar1c465442017-03-12 20:10:05 +01005821 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005822 {
5823 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5824 return NULL;
5825 }
5826 return skipwhite(end);
5827}
5828
5829/*
5830 * Handle ":syntax sync .." command.
5831 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005832 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005833syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005834{
5835 char_u *arg_start = eap->arg;
5836 char_u *arg_end;
5837 char_u *key = NULL;
5838 char_u *next_arg;
5839 int illegal = FALSE;
5840 int finished = FALSE;
5841 long n;
5842 char_u *cpo_save;
5843
5844 if (ends_excmd(*arg_start))
5845 {
5846 syn_cmd_list(eap, TRUE);
5847 return;
5848 }
5849
5850 while (!ends_excmd(*arg_start))
5851 {
5852 arg_end = skiptowhite(arg_start);
5853 next_arg = skipwhite(arg_end);
5854 vim_free(key);
5855 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5856 if (STRCMP(key, "CCOMMENT") == 0)
5857 {
5858 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005859 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005860 if (!ends_excmd(*next_arg))
5861 {
5862 arg_end = skiptowhite(next_arg);
5863 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005864 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865 (int)(arg_end - next_arg));
5866 next_arg = skipwhite(arg_end);
5867 }
5868 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005869 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005870 }
5871 else if ( STRNCMP(key, "LINES", 5) == 0
5872 || STRNCMP(key, "MINLINES", 8) == 0
5873 || STRNCMP(key, "MAXLINES", 8) == 0
5874 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5875 {
5876 if (key[4] == 'S')
5877 arg_end = key + 6;
5878 else if (key[0] == 'L')
5879 arg_end = key + 11;
5880 else
5881 arg_end = key + 9;
5882 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5883 {
5884 illegal = TRUE;
5885 break;
5886 }
5887 n = getdigits(&arg_end);
5888 if (!eap->skip)
5889 {
5890 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005891 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005892 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005893 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005894 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005895 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005896 }
5897 }
5898 else if (STRCMP(key, "FROMSTART") == 0)
5899 {
5900 if (!eap->skip)
5901 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005902 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5903 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005904 }
5905 }
5906 else if (STRCMP(key, "LINECONT") == 0)
5907 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005908 if (*next_arg == NUL) /* missing pattern */
5909 {
5910 illegal = TRUE;
5911 break;
5912 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005913 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914 {
5915 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5916 finished = TRUE;
5917 break;
5918 }
5919 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5920 if (*arg_end != *next_arg) /* end delimiter not found */
5921 {
5922 illegal = TRUE;
5923 break;
5924 }
5925
5926 if (!eap->skip)
5927 {
5928 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005929 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005930 (int)(arg_end - next_arg - 1))) == NULL)
5931 {
5932 finished = TRUE;
5933 break;
5934 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005935 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005936
5937 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5938 cpo_save = p_cpo;
5939 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005940 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005941 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005942 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005943#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005944 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5945#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005946
Bram Moolenaar860cae12010-06-05 23:22:07 +02005947 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005948 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005949 vim_free(curwin->w_s->b_syn_linecont_pat);
5950 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005951 finished = TRUE;
5952 break;
5953 }
5954 }
5955 next_arg = skipwhite(arg_end + 1);
5956 }
5957 else
5958 {
5959 eap->arg = next_arg;
5960 if (STRCMP(key, "MATCH") == 0)
5961 syn_cmd_match(eap, TRUE);
5962 else if (STRCMP(key, "REGION") == 0)
5963 syn_cmd_region(eap, TRUE);
5964 else if (STRCMP(key, "CLEAR") == 0)
5965 syn_cmd_clear(eap, TRUE);
5966 else
5967 illegal = TRUE;
5968 finished = TRUE;
5969 break;
5970 }
5971 arg_start = next_arg;
5972 }
5973 vim_free(key);
5974 if (illegal)
5975 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5976 else if (!finished)
5977 {
5978 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005979 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005980 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005981 }
5982}
5983
5984/*
5985 * Convert a line of highlight group names into a list of group ID numbers.
5986 * "arg" should point to the "contains" or "nextgroup" keyword.
5987 * "arg" is advanced to after the last group name.
5988 * Careful: the argument is modified (NULs added).
5989 * returns FAIL for some error, OK for success.
5990 */
5991 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005992get_id_list(
5993 char_u **arg,
5994 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005995 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005996 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005997 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005998{
5999 char_u *p = NULL;
6000 char_u *end;
6001 int round;
6002 int count;
6003 int total_count = 0;
6004 short *retval = NULL;
6005 char_u *name;
6006 regmatch_T regmatch;
6007 int id;
6008 int i;
6009 int failed = FALSE;
6010
6011 /*
6012 * We parse the list twice:
6013 * round == 1: count the number of items, allocate the array.
6014 * round == 2: fill the array with the items.
6015 * In round 1 new groups may be added, causing the number of items to
6016 * grow when a regexp is used. In that case round 1 is done once again.
6017 */
6018 for (round = 1; round <= 2; ++round)
6019 {
6020 /*
6021 * skip "contains"
6022 */
6023 p = skipwhite(*arg + keylen);
6024 if (*p != '=')
6025 {
6026 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6027 break;
6028 }
6029 p = skipwhite(p + 1);
6030 if (ends_excmd(*p))
6031 {
6032 EMSG2(_("E406: Empty argument: %s"), *arg);
6033 break;
6034 }
6035
6036 /*
6037 * parse the arguments after "contains"
6038 */
6039 count = 0;
6040 while (!ends_excmd(*p))
6041 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006042 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006043 ;
6044 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6045 if (name == NULL)
6046 {
6047 failed = TRUE;
6048 break;
6049 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006050 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006051 if ( STRCMP(name + 1, "ALLBUT") == 0
6052 || STRCMP(name + 1, "ALL") == 0
6053 || STRCMP(name + 1, "TOP") == 0
6054 || STRCMP(name + 1, "CONTAINED") == 0)
6055 {
6056 if (TOUPPER_ASC(**arg) != 'C')
6057 {
6058 EMSG2(_("E407: %s not allowed here"), name + 1);
6059 failed = TRUE;
6060 vim_free(name);
6061 break;
6062 }
6063 if (count != 0)
6064 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006065 EMSG2(_("E408: %s must be first in contains list"),
6066 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006067 failed = TRUE;
6068 vim_free(name);
6069 break;
6070 }
6071 if (name[1] == 'A')
6072 id = SYNID_ALLBUT;
6073 else if (name[1] == 'T')
6074 id = SYNID_TOP;
6075 else
6076 id = SYNID_CONTAINED;
6077 id += current_syn_inc_tag;
6078 }
6079 else if (name[1] == '@')
6080 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006081 if (skip)
6082 id = -1;
6083 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006084 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006085 }
6086 else
6087 {
6088 /*
6089 * Handle full group name.
6090 */
6091 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6092 id = syn_check_group(name + 1, (int)(end - p));
6093 else
6094 {
6095 /*
6096 * Handle match of regexp with group names.
6097 */
6098 *name = '^';
6099 STRCAT(name, "$");
6100 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6101 if (regmatch.regprog == NULL)
6102 {
6103 failed = TRUE;
6104 vim_free(name);
6105 break;
6106 }
6107
6108 regmatch.rm_ic = TRUE;
6109 id = 0;
6110 for (i = highlight_ga.ga_len; --i >= 0; )
6111 {
6112 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6113 (colnr_T)0))
6114 {
6115 if (round == 2)
6116 {
6117 /* Got more items than expected; can happen
6118 * when adding items that match:
6119 * "contains=a.*b,axb".
6120 * Go back to first round */
6121 if (count >= total_count)
6122 {
6123 vim_free(retval);
6124 round = 1;
6125 }
6126 else
6127 retval[count] = i + 1;
6128 }
6129 ++count;
6130 id = -1; /* remember that we found one */
6131 }
6132 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006133 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134 }
6135 }
6136 vim_free(name);
6137 if (id == 0)
6138 {
6139 EMSG2(_("E409: Unknown group name: %s"), p);
6140 failed = TRUE;
6141 break;
6142 }
6143 if (id > 0)
6144 {
6145 if (round == 2)
6146 {
6147 /* Got more items than expected, go back to first round */
6148 if (count >= total_count)
6149 {
6150 vim_free(retval);
6151 round = 1;
6152 }
6153 else
6154 retval[count] = id;
6155 }
6156 ++count;
6157 }
6158 p = skipwhite(end);
6159 if (*p != ',')
6160 break;
6161 p = skipwhite(p + 1); /* skip comma in between arguments */
6162 }
6163 if (failed)
6164 break;
6165 if (round == 1)
6166 {
6167 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6168 if (retval == NULL)
6169 break;
6170 retval[count] = 0; /* zero means end of the list */
6171 total_count = count;
6172 }
6173 }
6174
6175 *arg = p;
6176 if (failed || retval == NULL)
6177 {
6178 vim_free(retval);
6179 return FAIL;
6180 }
6181
6182 if (*list == NULL)
6183 *list = retval;
6184 else
6185 vim_free(retval); /* list already found, don't overwrite it */
6186
6187 return OK;
6188}
6189
6190/*
6191 * Make a copy of an ID list.
6192 */
6193 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006194copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006195{
6196 int len;
6197 int count;
6198 short *retval;
6199
6200 if (list == NULL)
6201 return NULL;
6202
6203 for (count = 0; list[count]; ++count)
6204 ;
6205 len = (count + 1) * sizeof(short);
6206 retval = (short *)alloc((unsigned)len);
6207 if (retval != NULL)
6208 mch_memmove(retval, list, (size_t)len);
6209
6210 return retval;
6211}
6212
6213/*
6214 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6215 * "cur_si" can be NULL if not checking the "containedin" list.
6216 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6217 * the current item.
6218 * This function is called very often, keep it fast!!
6219 */
6220 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006221in_id_list(
6222 stateitem_T *cur_si, /* current item or NULL */
6223 short *list, /* id list */
6224 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6225 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226{
6227 int retval;
6228 short *scl_list;
6229 short item;
6230 short id = ssp->id;
6231 static int depth = 0;
6232 int r;
6233
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006234 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006235 if (cur_si != NULL && ssp->cont_in_list != NULL
6236 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006237 {
6238 /* Ignore transparent items without a contains argument. Double check
6239 * that we don't go back past the first one. */
6240 while ((cur_si->si_flags & HL_TRANS_CONT)
6241 && cur_si > (stateitem_T *)(current_state.ga_data))
6242 --cur_si;
6243 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6244 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006245 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6246 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006247 return TRUE;
6248 }
6249
6250 if (list == NULL)
6251 return FALSE;
6252
6253 /*
6254 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6255 * inside anything. Only allow not-contained groups.
6256 */
6257 if (list == ID_LIST_ALL)
6258 return !contained;
6259
6260 /*
6261 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6262 * contains list. We also require that "id" is at the same ":syn include"
6263 * level as the list.
6264 */
6265 item = *list;
6266 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6267 {
6268 if (item < SYNID_TOP)
6269 {
6270 /* ALL or ALLBUT: accept all groups in the same file */
6271 if (item - SYNID_ALLBUT != ssp->inc_tag)
6272 return FALSE;
6273 }
6274 else if (item < SYNID_CONTAINED)
6275 {
6276 /* TOP: accept all not-contained groups in the same file */
6277 if (item - SYNID_TOP != ssp->inc_tag || contained)
6278 return FALSE;
6279 }
6280 else
6281 {
6282 /* CONTAINED: accept all contained groups in the same file */
6283 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6284 return FALSE;
6285 }
6286 item = *++list;
6287 retval = FALSE;
6288 }
6289 else
6290 retval = TRUE;
6291
6292 /*
6293 * Return "retval" if id is in the contains list.
6294 */
6295 while (item != 0)
6296 {
6297 if (item == id)
6298 return retval;
6299 if (item >= SYNID_CLUSTER)
6300 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006301 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 /* restrict recursiveness to 30 to avoid an endless loop for a
6303 * cluster that includes itself (indirectly) */
6304 if (scl_list != NULL && depth < 30)
6305 {
6306 ++depth;
6307 r = in_id_list(NULL, scl_list, ssp, contained);
6308 --depth;
6309 if (r)
6310 return retval;
6311 }
6312 }
6313 item = *++list;
6314 }
6315 return !retval;
6316}
6317
6318struct subcommand
6319{
Bram Moolenaard99df422016-01-29 23:20:40 +01006320 char *name; /* subcommand name */
6321 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006322};
6323
6324static struct subcommand subcommands[] =
6325{
6326 {"case", syn_cmd_case},
6327 {"clear", syn_cmd_clear},
6328 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006329 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330 {"enable", syn_cmd_enable},
6331 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006332 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006333 {"keyword", syn_cmd_keyword},
6334 {"list", syn_cmd_list},
6335 {"manual", syn_cmd_manual},
6336 {"match", syn_cmd_match},
6337 {"on", syn_cmd_on},
6338 {"off", syn_cmd_off},
6339 {"region", syn_cmd_region},
6340 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006341 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006342 {"sync", syn_cmd_sync},
6343 {"", syn_cmd_list},
6344 {NULL, NULL}
6345};
6346
6347/*
6348 * ":syntax".
6349 * This searches the subcommands[] table for the subcommand name, and calls a
6350 * syntax_subcommand() function to do the rest.
6351 */
6352 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006353ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006354{
6355 char_u *arg = eap->arg;
6356 char_u *subcmd_end;
6357 char_u *subcmd_name;
6358 int i;
6359
6360 syn_cmdlinep = eap->cmdlinep;
6361
6362 /* isolate subcommand name */
6363 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6364 ;
6365 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6366 if (subcmd_name != NULL)
6367 {
6368 if (eap->skip) /* skip error messages for all subcommands */
6369 ++emsg_skip;
6370 for (i = 0; ; ++i)
6371 {
6372 if (subcommands[i].name == NULL)
6373 {
6374 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6375 break;
6376 }
6377 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6378 {
6379 eap->arg = skipwhite(subcmd_end);
6380 (subcommands[i].func)(eap, FALSE);
6381 break;
6382 }
6383 }
6384 vim_free(subcmd_name);
6385 if (eap->skip)
6386 --emsg_skip;
6387 }
6388}
6389
Bram Moolenaar860cae12010-06-05 23:22:07 +02006390 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006391ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006393 char_u *old_value;
6394 char_u *new_value;
6395
Bram Moolenaar860cae12010-06-05 23:22:07 +02006396 if (curwin->w_s == &curwin->w_buffer->b_s)
6397 {
6398 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6399 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006400 hash_init(&curwin->w_s->b_keywtab);
6401 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006402#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006403 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006404 curwin->w_p_spell = FALSE; /* No spell checking */
6405 clear_string_option(&curwin->w_s->b_p_spc);
6406 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006407 clear_string_option(&curwin->w_s->b_p_spl);
6408#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006409 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006410 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006411
6412 /* save value of b:current_syntax */
6413 old_value = get_var_value((char_u *)"b:current_syntax");
6414 if (old_value != NULL)
6415 old_value = vim_strsave(old_value);
6416
Bram Moolenaard1413d92016-03-02 21:51:56 +01006417#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006418 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6419 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006420 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006421#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006422
6423 /* move value of b:current_syntax to w:current_syntax */
6424 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006425 if (new_value != NULL)
6426 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006427
6428 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006429 if (old_value == NULL)
6430 do_unlet((char_u *)"b:current_syntax", TRUE);
6431 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006432 {
6433 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6434 vim_free(old_value);
6435 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006436}
6437
6438 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006439syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006440{
6441 return (win->w_s->b_syn_patterns.ga_len != 0
6442 || win->w_s->b_syn_clusters.ga_len != 0
6443 || win->w_s->b_keywtab.ht_used > 0
6444 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006445}
6446
6447#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6448
6449static enum
6450{
6451 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006452 EXP_CASE, /* expand ":syn case" arguments */
6453 EXP_SPELL, /* expand ":syn spell" arguments */
6454 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006455} expand_what;
6456
Bram Moolenaar4f688582007-07-24 12:34:30 +00006457/*
6458 * Reset include_link, include_default, include_none to 0.
6459 * Called when we are done expanding.
6460 */
6461 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006462reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006463{
6464 include_link = include_default = include_none = 0;
6465}
6466
6467/*
6468 * Handle command line completion for :match and :echohl command: Add "None"
6469 * as highlight group.
6470 */
6471 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006472set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006473{
6474 xp->xp_context = EXPAND_HIGHLIGHT;
6475 xp->xp_pattern = arg;
6476 include_none = 1;
6477}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006478
6479/*
6480 * Handle command line completion for :syntax command.
6481 */
6482 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006483set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484{
6485 char_u *p;
6486
6487 /* Default: expand subcommands */
6488 xp->xp_context = EXPAND_SYNTAX;
6489 expand_what = EXP_SUBCMD;
6490 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006491 include_link = 0;
6492 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006493
6494 /* (part of) subcommand already typed */
6495 if (*arg != NUL)
6496 {
6497 p = skiptowhite(arg);
6498 if (*p != NUL) /* past first word */
6499 {
6500 xp->xp_pattern = skipwhite(p);
6501 if (*skiptowhite(xp->xp_pattern) != NUL)
6502 xp->xp_context = EXPAND_NOTHING;
6503 else if (STRNICMP(arg, "case", p - arg) == 0)
6504 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006505 else if (STRNICMP(arg, "spell", p - arg) == 0)
6506 expand_what = EXP_SPELL;
6507 else if (STRNICMP(arg, "sync", p - arg) == 0)
6508 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006509 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6510 || STRNICMP(arg, "region", p - arg) == 0
6511 || STRNICMP(arg, "match", p - arg) == 0
6512 || STRNICMP(arg, "list", p - arg) == 0)
6513 xp->xp_context = EXPAND_HIGHLIGHT;
6514 else
6515 xp->xp_context = EXPAND_NOTHING;
6516 }
6517 }
6518}
6519
Bram Moolenaar071d4272004-06-13 20:20:40 +00006520/*
6521 * Function given to ExpandGeneric() to obtain the list syntax names for
6522 * expansion.
6523 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006525get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006527 switch (expand_what)
6528 {
6529 case EXP_SUBCMD:
6530 return (char_u *)subcommands[idx].name;
6531 case EXP_CASE:
6532 {
6533 static char *case_args[] = {"match", "ignore", NULL};
6534 return (char_u *)case_args[idx];
6535 }
6536 case EXP_SPELL:
6537 {
6538 static char *spell_args[] =
6539 {"toplevel", "notoplevel", "default", NULL};
6540 return (char_u *)spell_args[idx];
6541 }
6542 case EXP_SYNC:
6543 {
6544 static char *sync_args[] =
6545 {"ccomment", "clear", "fromstart",
6546 "linebreaks=", "linecont", "lines=", "match",
6547 "maxlines=", "minlines=", "region", NULL};
6548 return (char_u *)sync_args[idx];
6549 }
6550 }
6551 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006552}
6553
6554#endif /* FEAT_CMDL_COMPL */
6555
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556/*
6557 * Function called for expression evaluation: get syntax ID at file position.
6558 */
6559 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006560syn_get_id(
6561 win_T *wp,
6562 long lnum,
6563 colnr_T col,
6564 int trans, /* remove transparency */
6565 int *spellp, /* return: can do spell checking */
6566 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006567{
6568 /* When the position is not after the current position and in the same
6569 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006570 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006572 || col < current_col)
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006573 syntax_start(wp, lnum, NULL);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006574 else if (wp->w_buffer == syn_buf
6575 && lnum == current_lnum
6576 && col > current_col)
6577 /* next_match may not be correct when moving around, e.g. with the
6578 * "skip" expression in searchpair() */
6579 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006580
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006581 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006582
6583 return (trans ? current_trans_id : current_id);
6584}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006585
Bram Moolenaar860cae12010-06-05 23:22:07 +02006586#if defined(FEAT_CONCEAL) || defined(PROTO)
6587/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006588 * Get extra information about the syntax item. Must be called right after
6589 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006590 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006591 * Returns the current flags.
6592 */
6593 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006594get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006595{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006596 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006597 return current_flags;
6598}
6599
6600/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006601 * Return conceal substitution character
6602 */
6603 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006604syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006605{
6606 return current_sub_char;
6607}
6608#endif
6609
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006610#if defined(FEAT_EVAL) || defined(PROTO)
6611/*
6612 * Return the syntax ID at position "i" in the current stack.
6613 * The caller must have called syn_get_id() before to fill the stack.
6614 * Returns -1 when "i" is out of range.
6615 */
6616 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006617syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006618{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006619 if (i >= current_state.ga_len)
6620 {
6621 /* Need to invalidate the state, because we didn't properly finish it
6622 * for the last character, "keep_state" was TRUE. */
6623 invalidate_current_state();
6624 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006625 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006626 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006627 return CUR_STATE(i).si_id;
6628}
6629#endif
6630
Bram Moolenaar071d4272004-06-13 20:20:40 +00006631#if defined(FEAT_FOLDING) || defined(PROTO)
6632/*
6633 * Function called to get folding level for line "lnum" in window "wp".
6634 */
6635 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006636syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006637{
6638 int level = 0;
6639 int i;
6640
6641 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006642 if (wp->w_s->b_syn_folditems != 0
6643 && !wp->w_s->b_syn_error
6644# ifdef SYN_TIME_LIMIT
6645 && !wp->w_s->b_syn_slow
6646# endif
6647 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006648 {
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006649 syntax_start(wp, lnum, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006650
6651 for (i = 0; i < current_state.ga_len; ++i)
6652 if (CUR_STATE(i).si_flags & HL_FOLD)
6653 ++level;
6654 }
6655 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006656 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006657 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006658 if (level < 0)
6659 level = 0;
6660 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006661 return level;
6662}
6663#endif
6664
Bram Moolenaar01615492015-02-03 13:00:38 +01006665#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006666/*
6667 * ":syntime".
6668 */
6669 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006670ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006671{
6672 if (STRCMP(eap->arg, "on") == 0)
6673 syn_time_on = TRUE;
6674 else if (STRCMP(eap->arg, "off") == 0)
6675 syn_time_on = FALSE;
6676 else if (STRCMP(eap->arg, "clear") == 0)
6677 syntime_clear();
6678 else if (STRCMP(eap->arg, "report") == 0)
6679 syntime_report();
6680 else
6681 EMSG2(_(e_invarg2), eap->arg);
6682}
6683
6684 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006685syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006686{
6687 profile_zero(&st->total);
6688 profile_zero(&st->slowest);
6689 st->count = 0;
6690 st->match = 0;
6691}
6692
6693/*
6694 * Clear the syntax timing for the current buffer.
6695 */
6696 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006697syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006698{
6699 int idx;
6700 synpat_T *spp;
6701
6702 if (!syntax_present(curwin))
6703 {
6704 MSG(_(msg_no_items));
6705 return;
6706 }
6707 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6708 {
6709 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6710 syn_clear_time(&spp->sp_time);
6711 }
6712}
6713
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006714#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6715/*
6716 * Function given to ExpandGeneric() to obtain the possible arguments of the
6717 * ":syntime {on,off,clear,report}" command.
6718 */
6719 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006720get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006721{
6722 switch (idx)
6723 {
6724 case 0: return (char_u *)"on";
6725 case 1: return (char_u *)"off";
6726 case 2: return (char_u *)"clear";
6727 case 3: return (char_u *)"report";
6728 }
6729 return NULL;
6730}
6731#endif
6732
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006733typedef struct
6734{
6735 proftime_T total;
6736 int count;
6737 int match;
6738 proftime_T slowest;
6739 proftime_T average;
6740 int id;
6741 char_u *pattern;
6742} time_entry_T;
6743
6744 static int
6745#ifdef __BORLANDC__
6746_RTLENTRYF
6747#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006748syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006749{
6750 const time_entry_T *s1 = v1;
6751 const time_entry_T *s2 = v2;
6752
6753 return profile_cmp(&s1->total, &s2->total);
6754}
6755
6756/*
6757 * Clear the syntax timing for the current buffer.
6758 */
6759 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006760syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006761{
6762 int idx;
6763 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006764# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006765 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006766# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006767 int len;
6768 proftime_T total_total;
6769 int total_count = 0;
6770 garray_T ga;
6771 time_entry_T *p;
6772
6773 if (!syntax_present(curwin))
6774 {
6775 MSG(_(msg_no_items));
6776 return;
6777 }
6778
6779 ga_init2(&ga, sizeof(time_entry_T), 50);
6780 profile_zero(&total_total);
6781 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6782 {
6783 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6784 if (spp->sp_time.count > 0)
6785 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006786 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006787 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6788 p->total = spp->sp_time.total;
6789 profile_add(&total_total, &spp->sp_time.total);
6790 p->count = spp->sp_time.count;
6791 p->match = spp->sp_time.match;
6792 total_count += spp->sp_time.count;
6793 p->slowest = spp->sp_time.slowest;
6794# ifdef FEAT_FLOAT
6795 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6796 p->average = tm;
6797# endif
6798 p->id = spp->sp_syn.id;
6799 p->pattern = spp->sp_pattern;
6800 ++ga.ga_len;
6801 }
6802 }
6803
Bram Moolenaara2162552017-01-08 17:46:20 +01006804 /* Sort on total time. Skip if there are no items to avoid passing NULL
6805 * pointer to qsort(). */
6806 if (ga.ga_len > 1)
6807 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006808 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006809
6810 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6811 MSG_PUTS("\n");
6812 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6813 {
6814 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6815 p = ((time_entry_T *)ga.ga_data) + idx;
6816
6817 MSG_PUTS(profile_msg(&p->total));
6818 MSG_PUTS(" "); /* make sure there is always a separating space */
6819 msg_advance(13);
6820 msg_outnum(p->count);
6821 MSG_PUTS(" ");
6822 msg_advance(20);
6823 msg_outnum(p->match);
6824 MSG_PUTS(" ");
6825 msg_advance(26);
6826 MSG_PUTS(profile_msg(&p->slowest));
6827 MSG_PUTS(" ");
6828 msg_advance(38);
6829# ifdef FEAT_FLOAT
6830 MSG_PUTS(profile_msg(&p->average));
6831 MSG_PUTS(" ");
6832# endif
6833 msg_advance(50);
6834 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6835 MSG_PUTS(" ");
6836
6837 msg_advance(69);
6838 if (Columns < 80)
6839 len = 20; /* will wrap anyway */
6840 else
6841 len = Columns - 70;
6842 if (len > (int)STRLEN(p->pattern))
6843 len = (int)STRLEN(p->pattern);
6844 msg_outtrans_len(p->pattern, len);
6845 MSG_PUTS("\n");
6846 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006847 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006848 if (!got_int)
6849 {
6850 MSG_PUTS("\n");
6851 MSG_PUTS(profile_msg(&total_total));
6852 msg_advance(13);
6853 msg_outnum(total_count);
6854 MSG_PUTS("\n");
6855 }
6856}
6857#endif
6858
Bram Moolenaar071d4272004-06-13 20:20:40 +00006859#endif /* FEAT_SYN_HL */
6860
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861/**************************************
6862 * Highlighting stuff *
6863 **************************************/
6864
6865/*
6866 * The default highlight groups. These are compiled-in for fast startup and
6867 * they still work when the runtime files can't be found.
6868 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006869 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6870 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006872#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006873# define CENT(a, b) b
6874#else
6875# define CENT(a, b) a
6876#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006877static char *(highlight_init_both[]) =
6878 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006879 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6880 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6881 CENT("IncSearch term=reverse cterm=reverse",
6882 "IncSearch term=reverse cterm=reverse gui=reverse"),
6883 CENT("ModeMsg term=bold cterm=bold",
6884 "ModeMsg term=bold cterm=bold gui=bold"),
6885 CENT("NonText term=bold ctermfg=Blue",
6886 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6887 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6888 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6889 CENT("StatusLineNC term=reverse cterm=reverse",
6890 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar3633cf52017-07-31 22:29:35 +02006891#ifdef FEAT_TERMINAL
6892 CENT("StatusLineTerm term=reverse cterm=reverse ctermFg=DarkGreen",
6893 "StatusLineTerm term=reverse cterm=reverse ctermFg=DarkGreen gui=reverse guifg=DarkGreen"),
6894#endif
Bram Moolenaar58b85342016-08-14 19:54:54 +02006895 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006896#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006897 CENT("VertSplit term=reverse cterm=reverse",
6898 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006899#endif
6900#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006901 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6902 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006903#endif
6904#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006905 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6906 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006907#endif
6908#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006909 CENT("PmenuSbar ctermbg=Grey",
6910 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006911#endif
6912#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006913 CENT("TabLineSel term=bold cterm=bold",
6914 "TabLineSel term=bold cterm=bold gui=bold"),
6915 CENT("TabLineFill term=reverse cterm=reverse",
6916 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006917#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006918#ifdef FEAT_GUI
6919 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006920 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006921#endif
Bram Moolenaarc768a202017-06-22 16:04:27 +02006922 "default link QuickFixLine Search",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006923 NULL
6924 };
6925
6926static char *(highlight_init_light[]) =
6927 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006928 CENT("Directory term=bold ctermfg=DarkBlue",
6929 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6930 CENT("LineNr term=underline ctermfg=Brown",
6931 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006932 CENT("CursorLineNr term=bold ctermfg=Brown",
6933 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006934 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6935 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6936 CENT("Question term=standout ctermfg=DarkGreen",
6937 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6938 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6939 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006940#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006941 CENT("SpellBad term=reverse ctermbg=LightRed",
6942 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6943 CENT("SpellCap term=reverse ctermbg=LightBlue",
6944 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6945 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6946 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6947 CENT("SpellLocal term=underline ctermbg=Cyan",
6948 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006949#endif
6950#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006951 CENT("PmenuThumb ctermbg=Black",
6952 "PmenuThumb ctermbg=Black guibg=Black"),
6953 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6954 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6955 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6956 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006957#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006958 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6959 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6960 CENT("Title term=bold ctermfg=DarkMagenta",
6961 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6962 CENT("WarningMsg term=standout ctermfg=DarkRed",
6963 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006964#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006965 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6966 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006967#endif
6968#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006969 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6970 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6971 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6972 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006973#endif
6974#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006975 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6976 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006977#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006978 CENT("Visual term=reverse",
6979 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006980#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006981 CENT("DiffAdd term=bold ctermbg=LightBlue",
6982 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6983 CENT("DiffChange term=bold ctermbg=LightMagenta",
6984 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6985 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6986 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006987#endif
6988#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006989 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6990 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006991#endif
6992#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006993 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006994 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006995 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006996 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006997 CENT("ColorColumn term=reverse ctermbg=LightRed",
6998 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006999#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007000#ifdef FEAT_CONCEAL
7001 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7002 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7003#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007004#ifdef FEAT_AUTOCMD
7005 CENT("MatchParen term=reverse ctermbg=Cyan",
7006 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
7007#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007008#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007009 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007010#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007011 NULL
7012 };
7013
7014static char *(highlight_init_dark[]) =
7015 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007016 CENT("Directory term=bold ctermfg=LightCyan",
7017 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7018 CENT("LineNr term=underline ctermfg=Yellow",
7019 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01007020 CENT("CursorLineNr term=bold ctermfg=Yellow",
7021 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007022 CENT("MoreMsg term=bold ctermfg=LightGreen",
7023 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7024 CENT("Question term=standout ctermfg=LightGreen",
7025 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7026 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7027 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7028 CENT("SpecialKey term=bold ctermfg=LightBlue",
7029 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007030#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007031 CENT("SpellBad term=reverse ctermbg=Red",
7032 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7033 CENT("SpellCap term=reverse ctermbg=Blue",
7034 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7035 CENT("SpellRare term=reverse ctermbg=Magenta",
7036 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7037 CENT("SpellLocal term=underline ctermbg=Cyan",
7038 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007039#endif
7040#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007041 CENT("PmenuThumb ctermbg=White",
7042 "PmenuThumb ctermbg=White guibg=White"),
7043 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7044 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007045 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7046 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007047#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007048 CENT("Title term=bold ctermfg=LightMagenta",
7049 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7050 CENT("WarningMsg term=standout ctermfg=LightRed",
7051 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007052#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007053 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7054 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007055#endif
7056#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007057 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7058 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7059 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7060 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007061#endif
7062#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007063 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7064 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007065#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007066 CENT("Visual term=reverse",
7067 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007068#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007069 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7070 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7071 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7072 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7073 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7074 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007075#endif
7076#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007077 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7078 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007079#endif
7080#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007081 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007082 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007083 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007084 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007085 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7086 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007087#endif
7088#ifdef FEAT_AUTOCMD
7089 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7090 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007091#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007092#ifdef FEAT_CONCEAL
7093 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7094 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7095#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007096#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007097 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007098#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007099 NULL
7100 };
7101
7102 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007103init_highlight(
7104 int both, /* include groups where 'bg' doesn't matter */
7105 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106{
7107 int i;
7108 char **pp;
7109 static int had_both = FALSE;
7110#ifdef FEAT_EVAL
7111 char_u *p;
7112
7113 /*
7114 * Try finding the color scheme file. Used when a color file was loaded
7115 * and 'background' or 't_Co' is changed.
7116 */
7117 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007118 if (p != NULL)
7119 {
7120 /* The value of g:colors_name could be freed when sourcing the script,
7121 * making "p" invalid, so copy it. */
7122 char_u *copy_p = vim_strsave(p);
7123 int r;
7124
7125 if (copy_p != NULL)
7126 {
7127 r = load_colors(copy_p);
7128 vim_free(copy_p);
7129 if (r == OK)
7130 return;
7131 }
7132 }
7133
Bram Moolenaar071d4272004-06-13 20:20:40 +00007134#endif
7135
7136 /*
7137 * Didn't use a color file, use the compiled-in colors.
7138 */
7139 if (both)
7140 {
7141 had_both = TRUE;
7142 pp = highlight_init_both;
7143 for (i = 0; pp[i] != NULL; ++i)
7144 do_highlight((char_u *)pp[i], reset, TRUE);
7145 }
7146 else if (!had_both)
7147 /* Don't do anything before the call with both == TRUE from main().
7148 * Not everything has been setup then, and that call will overrule
7149 * everything anyway. */
7150 return;
7151
7152 if (*p_bg == 'l')
7153 pp = highlight_init_light;
7154 else
7155 pp = highlight_init_dark;
7156 for (i = 0; pp[i] != NULL; ++i)
7157 do_highlight((char_u *)pp[i], reset, TRUE);
7158
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007159 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007160 * depend on the number of colors available.
7161 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007162 * to avoid Statement highlighted text disappears.
7163 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007164 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007165 do_highlight((char_u *)(*p_bg == 'l'
7166 ? "Visual cterm=NONE ctermbg=LightGrey"
7167 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007168 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007169 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007170 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7171 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007172 if (*p_bg == 'l')
7173 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7174 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007175
Bram Moolenaar071d4272004-06-13 20:20:40 +00007176#ifdef FEAT_SYN_HL
7177 /*
7178 * If syntax highlighting is enabled load the highlighting for it.
7179 */
7180 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007181 {
7182 static int recursive = 0;
7183
7184 if (recursive >= 5)
7185 EMSG(_("E679: recursive loop loading syncolor.vim"));
7186 else
7187 {
7188 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007189 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007190 --recursive;
7191 }
7192 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007193#endif
7194}
7195
7196/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007197 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007198 * Return OK for success, FAIL for failure.
7199 */
7200 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007201load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007202{
7203 char_u *buf;
7204 int retval = FAIL;
7205 static int recursive = FALSE;
7206
7207 /* When being called recursively, this is probably because setting
7208 * 'background' caused the highlighting to be reloaded. This means it is
7209 * working, thus we should return OK. */
7210 if (recursive)
7211 return OK;
7212
7213 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007214 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007215 if (buf != NULL)
7216 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007217 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007218 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007219 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007220#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007221 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007222#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007223 }
7224 recursive = FALSE;
7225
7226 return retval;
7227}
7228
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007229static char *(color_names[28]) = {
7230 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7231 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7232 "Gray", "Grey", "LightGray", "LightGrey",
7233 "DarkGray", "DarkGrey",
7234 "Blue", "LightBlue", "Green", "LightGreen",
7235 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7236 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7237 /* indices:
7238 * 0, 1, 2, 3,
7239 * 4, 5, 6, 7,
7240 * 8, 9, 10, 11,
7241 * 12, 13,
7242 * 14, 15, 16, 17,
7243 * 18, 19, 20, 21, 22,
7244 * 23, 24, 25, 26, 27 */
7245static int color_numbers_16[28] = {0, 1, 2, 3,
7246 4, 5, 6, 6,
7247 7, 7, 7, 7,
7248 8, 8,
7249 9, 9, 10, 10,
7250 11, 11, 12, 12, 13,
7251 13, 14, 14, 15, -1};
7252/* for xterm with 88 colors... */
7253static int color_numbers_88[28] = {0, 4, 2, 6,
7254 1, 5, 32, 72,
7255 84, 84, 7, 7,
7256 82, 82,
7257 12, 43, 10, 61,
7258 14, 63, 9, 74, 13,
7259 75, 11, 78, 15, -1};
7260/* for xterm with 256 colors... */
7261static int color_numbers_256[28] = {0, 4, 2, 6,
7262 1, 5, 130, 130,
7263 248, 248, 7, 7,
7264 242, 242,
7265 12, 81, 10, 121,
7266 14, 159, 9, 224, 13,
7267 225, 11, 229, 15, -1};
7268/* for terminals with less than 16 colors... */
7269static int color_numbers_8[28] = {0, 4, 2, 6,
7270 1, 5, 3, 3,
7271 7, 7, 7, 7,
7272 0+8, 0+8,
7273 4+8, 4+8, 2+8, 2+8,
7274 6+8, 6+8, 1+8, 1+8, 5+8,
7275 5+8, 3+8, 3+8, 7+8, -1};
7276
7277/*
7278 * Lookup the "cterm" value to be used for color with index "idx" in
7279 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007280 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7281 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007282 */
7283 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007284lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007285{
7286 int color = color_numbers_16[idx];
7287 char_u *p;
7288
7289 /* Use the _16 table to check if it's a valid color name. */
7290 if (color < 0)
7291 return -1;
7292
7293 if (t_colors == 8)
7294 {
7295 /* t_Co is 8: use the 8 colors table */
7296#if defined(__QNXNTO__)
7297 color = color_numbers_8_qansi[idx];
7298#else
7299 color = color_numbers_8[idx];
7300#endif
7301 if (foreground)
7302 {
7303 /* set/reset bold attribute to get light foreground
7304 * colors (on some terminals, e.g. "linux") */
7305 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007306 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007307 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007308 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007309 }
7310 color &= 7; /* truncate to 8 colors */
7311 }
7312 else if (t_colors == 16 || t_colors == 88
7313 || t_colors >= 256)
7314 {
7315 /*
7316 * Guess: if the termcap entry ends in 'm', it is
7317 * probably an xterm-like terminal. Use the changed
7318 * order for colors.
7319 */
7320 if (*T_CAF != NUL)
7321 p = T_CAF;
7322 else
7323 p = T_CSF;
7324 if (*p != NUL && (t_colors > 256
7325 || *(p + STRLEN(p) - 1) == 'm'))
7326 {
7327 if (t_colors == 88)
7328 color = color_numbers_88[idx];
7329 else if (t_colors >= 256)
7330 color = color_numbers_256[idx];
7331 else
7332 color = color_numbers_8[idx];
7333 }
7334 }
7335 return color;
7336}
7337
Bram Moolenaar071d4272004-06-13 20:20:40 +00007338/*
7339 * Handle the ":highlight .." command.
7340 * When using ":hi clear" this is called recursively for each group with
7341 * "forceit" and "init" both TRUE.
7342 */
7343 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007344do_highlight(
7345 char_u *line,
7346 int forceit,
7347 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007348{
7349 char_u *name_end;
7350 char_u *p;
7351 char_u *linep;
7352 char_u *key_start;
7353 char_u *arg_start;
7354 char_u *key = NULL, *arg = NULL;
7355 long i;
7356 int off;
7357 int len;
7358 int attr;
7359 int id;
7360 int idx;
7361 int dodefault = FALSE;
7362 int doclear = FALSE;
7363 int dolink = FALSE;
7364 int error = FALSE;
7365 int color;
7366 int is_normal_group = FALSE; /* "Normal" group */
7367#ifdef FEAT_GUI_X11
7368 int is_menu_group = FALSE; /* "Menu" group */
7369 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7370 int is_tooltip_group = FALSE; /* "Tooltip" group */
7371 int do_colors = FALSE; /* need to update colors? */
7372#else
7373# define is_menu_group 0
7374# define is_tooltip_group 0
7375#endif
7376
7377 /*
7378 * If no argument, list current highlighting.
7379 */
7380 if (ends_excmd(*line))
7381 {
7382 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7383 /* TODO: only call when the group has attributes set */
7384 highlight_list_one((int)i);
7385 return;
7386 }
7387
7388 /*
7389 * Isolate the name.
7390 */
7391 name_end = skiptowhite(line);
7392 linep = skipwhite(name_end);
7393
7394 /*
7395 * Check for "default" argument.
7396 */
7397 if (STRNCMP(line, "default", name_end - line) == 0)
7398 {
7399 dodefault = TRUE;
7400 line = linep;
7401 name_end = skiptowhite(line);
7402 linep = skipwhite(name_end);
7403 }
7404
7405 /*
7406 * Check for "clear" or "link" argument.
7407 */
7408 if (STRNCMP(line, "clear", name_end - line) == 0)
7409 doclear = TRUE;
7410 if (STRNCMP(line, "link", name_end - line) == 0)
7411 dolink = TRUE;
7412
7413 /*
7414 * ":highlight {group-name}": list highlighting for one group.
7415 */
7416 if (!doclear && !dolink && ends_excmd(*linep))
7417 {
7418 id = syn_namen2id(line, (int)(name_end - line));
7419 if (id == 0)
7420 EMSG2(_("E411: highlight group not found: %s"), line);
7421 else
7422 highlight_list_one(id);
7423 return;
7424 }
7425
7426 /*
7427 * Handle ":highlight link {from} {to}" command.
7428 */
7429 if (dolink)
7430 {
7431 char_u *from_start = linep;
7432 char_u *from_end;
7433 char_u *to_start;
7434 char_u *to_end;
7435 int from_id;
7436 int to_id;
7437
7438 from_end = skiptowhite(from_start);
7439 to_start = skipwhite(from_end);
7440 to_end = skiptowhite(to_start);
7441
7442 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7443 {
7444 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7445 from_start);
7446 return;
7447 }
7448
7449 if (!ends_excmd(*skipwhite(to_end)))
7450 {
7451 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7452 return;
7453 }
7454
7455 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7456 if (STRNCMP(to_start, "NONE", 4) == 0)
7457 to_id = 0;
7458 else
7459 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7460
7461 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7462 {
7463 /*
7464 * Don't allow a link when there already is some highlighting
7465 * for the group, unless '!' is used
7466 */
7467 if (to_id > 0 && !forceit && !init
7468 && hl_has_settings(from_id - 1, dodefault))
7469 {
7470 if (sourcing_name == NULL && !dodefault)
7471 EMSG(_("E414: group has settings, highlight link ignored"));
7472 }
7473 else
7474 {
7475 if (!init)
7476 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7477 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007478#ifdef FEAT_EVAL
7479 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7480#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01007481 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007482 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007483 }
7484 }
7485
7486 /* Only call highlight_changed() once, after sourcing a syntax file */
7487 need_highlight_changed = TRUE;
7488
7489 return;
7490 }
7491
7492 if (doclear)
7493 {
7494 /*
7495 * ":highlight clear [group]" command.
7496 */
7497 line = linep;
7498 if (ends_excmd(*line))
7499 {
7500#ifdef FEAT_GUI
7501 /* First, we do not destroy the old values, but allocate the new
7502 * ones and update the display. THEN we destroy the old values.
7503 * If we destroy the old values first, then the old values
7504 * (such as GuiFont's or GuiFontset's) will still be displayed but
7505 * invalid because they were free'd.
7506 */
7507 if (gui.in_use)
7508 {
7509# ifdef FEAT_BEVAL_TIP
7510 gui_init_tooltip_font();
7511# endif
7512# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7513 gui_init_menu_font();
7514# endif
7515 }
7516# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7517 gui_mch_def_colors();
7518# endif
7519# ifdef FEAT_GUI_X11
7520# ifdef FEAT_MENU
7521
7522 /* This only needs to be done when there is no Menu highlight
7523 * group defined by default, which IS currently the case.
7524 */
7525 gui_mch_new_menu_colors();
7526# endif
7527 if (gui.in_use)
7528 {
7529 gui_new_scrollbar_colors();
7530# ifdef FEAT_BEVAL
7531 gui_mch_new_tooltip_colors();
7532# endif
7533# ifdef FEAT_MENU
7534 gui_mch_new_menu_font();
7535# endif
7536 }
7537# endif
7538
7539 /* Ok, we're done allocating the new default graphics items.
7540 * The screen should already be refreshed at this point.
7541 * It is now Ok to clear out the old data.
7542 */
7543#endif
7544#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007545 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007546#endif
7547 restore_cterm_colors();
7548
7549 /*
7550 * Clear all default highlight groups and load the defaults.
7551 */
7552 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7553 highlight_clear(idx);
7554 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007555#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007556 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007557 highlight_gui_started();
7558#endif
7559 highlight_changed();
7560 redraw_later_clear();
7561 return;
7562 }
7563 name_end = skiptowhite(line);
7564 linep = skipwhite(name_end);
7565 }
7566
7567 /*
7568 * Find the group name in the table. If it does not exist yet, add it.
7569 */
7570 id = syn_check_group(line, (int)(name_end - line));
7571 if (id == 0) /* failed (out of memory) */
7572 return;
7573 idx = id - 1; /* index is ID minus one */
7574
7575 /* Return if "default" was used and the group already has settings. */
7576 if (dodefault && hl_has_settings(idx, TRUE))
7577 return;
7578
7579 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7580 is_normal_group = TRUE;
7581#ifdef FEAT_GUI_X11
7582 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7583 is_menu_group = TRUE;
7584 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7585 is_scrollbar_group = TRUE;
7586 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7587 is_tooltip_group = TRUE;
7588#endif
7589
7590 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7591 if (doclear || (forceit && init))
7592 {
7593 highlight_clear(idx);
7594 if (!doclear)
7595 HL_TABLE()[idx].sg_set = 0;
7596 }
7597
7598 if (!doclear)
7599 while (!ends_excmd(*linep))
7600 {
7601 key_start = linep;
7602 if (*linep == '=')
7603 {
7604 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7605 error = TRUE;
7606 break;
7607 }
7608
7609 /*
7610 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7611 * "guibg").
7612 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007613 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007614 ++linep;
7615 vim_free(key);
7616 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7617 if (key == NULL)
7618 {
7619 error = TRUE;
7620 break;
7621 }
7622 linep = skipwhite(linep);
7623
7624 if (STRCMP(key, "NONE") == 0)
7625 {
7626 if (!init || HL_TABLE()[idx].sg_set == 0)
7627 {
7628 if (!init)
7629 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7630 highlight_clear(idx);
7631 }
7632 continue;
7633 }
7634
7635 /*
7636 * Check for the equal sign.
7637 */
7638 if (*linep != '=')
7639 {
7640 EMSG2(_("E416: missing equal sign: %s"), key_start);
7641 error = TRUE;
7642 break;
7643 }
7644 ++linep;
7645
7646 /*
7647 * Isolate the argument.
7648 */
7649 linep = skipwhite(linep);
7650 if (*linep == '\'') /* guifg='color name' */
7651 {
7652 arg_start = ++linep;
7653 linep = vim_strchr(linep, '\'');
7654 if (linep == NULL)
7655 {
7656 EMSG2(_(e_invarg2), key_start);
7657 error = TRUE;
7658 break;
7659 }
7660 }
7661 else
7662 {
7663 arg_start = linep;
7664 linep = skiptowhite(linep);
7665 }
7666 if (linep == arg_start)
7667 {
7668 EMSG2(_("E417: missing argument: %s"), key_start);
7669 error = TRUE;
7670 break;
7671 }
7672 vim_free(arg);
7673 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7674 if (arg == NULL)
7675 {
7676 error = TRUE;
7677 break;
7678 }
7679 if (*linep == '\'')
7680 ++linep;
7681
7682 /*
7683 * Store the argument.
7684 */
7685 if ( STRCMP(key, "TERM") == 0
7686 || STRCMP(key, "CTERM") == 0
7687 || STRCMP(key, "GUI") == 0)
7688 {
7689 attr = 0;
7690 off = 0;
7691 while (arg[off] != NUL)
7692 {
7693 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7694 {
7695 len = (int)STRLEN(hl_name_table[i]);
7696 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7697 {
7698 attr |= hl_attr_table[i];
7699 off += len;
7700 break;
7701 }
7702 }
7703 if (i < 0)
7704 {
7705 EMSG2(_("E418: Illegal value: %s"), arg);
7706 error = TRUE;
7707 break;
7708 }
7709 if (arg[off] == ',') /* another one follows */
7710 ++off;
7711 }
7712 if (error)
7713 break;
7714 if (*key == 'T')
7715 {
7716 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7717 {
7718 if (!init)
7719 HL_TABLE()[idx].sg_set |= SG_TERM;
7720 HL_TABLE()[idx].sg_term = attr;
7721 }
7722 }
7723 else if (*key == 'C')
7724 {
7725 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7726 {
7727 if (!init)
7728 HL_TABLE()[idx].sg_set |= SG_CTERM;
7729 HL_TABLE()[idx].sg_cterm = attr;
7730 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7731 }
7732 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007733#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007734 else
7735 {
7736 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7737 {
7738 if (!init)
7739 HL_TABLE()[idx].sg_set |= SG_GUI;
7740 HL_TABLE()[idx].sg_gui = attr;
7741 }
7742 }
7743#endif
7744 }
7745 else if (STRCMP(key, "FONT") == 0)
7746 {
7747 /* in non-GUI fonts are simply ignored */
7748#ifdef FEAT_GUI
7749 if (!gui.shell_created)
7750 {
7751 /* GUI not started yet, always accept the name. */
7752 vim_free(HL_TABLE()[idx].sg_font_name);
7753 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7754 }
7755 else
7756 {
7757 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7758# ifdef FEAT_XFONTSET
7759 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7760# endif
7761 /* First, save the current font/fontset.
7762 * Then try to allocate the font/fontset.
7763 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7764 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7765 */
7766
7767 HL_TABLE()[idx].sg_font = NOFONT;
7768# ifdef FEAT_XFONTSET
7769 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7770# endif
7771 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007772 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007773
7774# ifdef FEAT_XFONTSET
7775 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7776 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007777 /* New fontset was accepted. Free the old one, if there
7778 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779 gui_mch_free_fontset(temp_sg_fontset);
7780 vim_free(HL_TABLE()[idx].sg_font_name);
7781 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7782 }
7783 else
7784 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7785# endif
7786 if (HL_TABLE()[idx].sg_font != NOFONT)
7787 {
7788 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007789 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007790 gui_mch_free_font(temp_sg_font);
7791 vim_free(HL_TABLE()[idx].sg_font_name);
7792 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7793 }
7794 else
7795 HL_TABLE()[idx].sg_font = temp_sg_font;
7796 }
7797#endif
7798 }
7799 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7800 {
7801 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7802 {
7803 if (!init)
7804 HL_TABLE()[idx].sg_set |= SG_CTERM;
7805
7806 /* When setting the foreground color, and previously the "bold"
7807 * flag was set for a light color, reset it now */
7808 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7809 {
7810 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7811 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7812 }
7813
7814 if (VIM_ISDIGIT(*arg))
7815 color = atoi((char *)arg);
7816 else if (STRICMP(arg, "fg") == 0)
7817 {
7818 if (cterm_normal_fg_color)
7819 color = cterm_normal_fg_color - 1;
7820 else
7821 {
7822 EMSG(_("E419: FG color unknown"));
7823 error = TRUE;
7824 break;
7825 }
7826 }
7827 else if (STRICMP(arg, "bg") == 0)
7828 {
7829 if (cterm_normal_bg_color > 0)
7830 color = cterm_normal_bg_color - 1;
7831 else
7832 {
7833 EMSG(_("E420: BG color unknown"));
7834 error = TRUE;
7835 break;
7836 }
7837 }
7838 else
7839 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007840 int bold = MAYBE;
7841
Bram Moolenaar071d4272004-06-13 20:20:40 +00007842#if defined(__QNXNTO__)
7843 static int *color_numbers_8_qansi = color_numbers_8;
7844 /* On qnx, the 8 & 16 color arrays are the same */
7845 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7846 color_numbers_8_qansi = color_numbers_16;
7847#endif
7848
7849 /* reduce calls to STRICMP a bit, it can be slow */
7850 off = TOUPPER_ASC(*arg);
7851 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7852 if (off == color_names[i][0]
7853 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7854 break;
7855 if (i < 0)
7856 {
7857 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7858 error = TRUE;
7859 break;
7860 }
7861
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007862 color = lookup_color(i, key[5] == 'F', &bold);
7863
7864 /* set/reset bold attribute to get light foreground
7865 * colors (on some terminals, e.g. "linux") */
7866 if (bold == TRUE)
7867 {
7868 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7869 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7870 }
7871 else if (bold == FALSE)
7872 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007873 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007874
Bram Moolenaarccbab932010-05-13 15:40:30 +02007875 /* Add one to the argument, to avoid zero. Zero is used for
7876 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007877 if (key[5] == 'F')
7878 {
7879 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7880 if (is_normal_group)
7881 {
7882 cterm_normal_fg_color = color + 1;
7883 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7884#ifdef FEAT_GUI
7885 /* Don't do this if the GUI is used. */
7886 if (!gui.in_use && !gui.starting)
7887#endif
7888 {
7889 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007890 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007891 term_fg_color(color);
7892 }
7893 }
7894 }
7895 else
7896 {
7897 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7898 if (is_normal_group)
7899 {
7900 cterm_normal_bg_color = color + 1;
7901#ifdef FEAT_GUI
7902 /* Don't mess with 'background' if the GUI is used. */
7903 if (!gui.in_use && !gui.starting)
7904#endif
7905 {
7906 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007907 if (color >= 0)
7908 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007909 int dark = -1;
7910
Bram Moolenaarccbab932010-05-13 15:40:30 +02007911 if (termcap_active)
7912 term_bg_color(color);
7913 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007914 dark = (color == 0 || color == 4);
7915 /* Limit the heuristic to the standard 16 colors */
7916 else if (color < 16)
7917 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007918 /* Set the 'background' option if the value is
7919 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007920 if (dark != -1
7921 && dark != (*p_bg == 'd')
7922 && !option_was_set((char_u *)"bg"))
7923 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007924 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007925 (char_u *)(dark ? "dark" : "light"), 0);
7926 reset_option_was_set((char_u *)"bg");
7927 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007928 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007929 }
7930 }
7931 }
7932 }
7933 }
7934 else if (STRCMP(key, "GUIFG") == 0)
7935 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007936#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007937 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007938 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007939 if (!init)
7940 HL_TABLE()[idx].sg_set |= SG_GUI;
7941
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007942# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007943 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007944 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007945 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007946 {
7947 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007948# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007949 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
Bram Moolenaar26af85d2017-07-23 16:45:10 +02007950 if (STRCMP(arg, "NONE") != 0)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007951 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7952 else
7953 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007954# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007955# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007956 if (is_menu_group)
7957 gui.menu_fg_pixel = i;
7958 if (is_scrollbar_group)
7959 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007960# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007961 if (is_tooltip_group)
7962 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007963# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007964 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007965# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007966 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007967# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007968 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007969#endif
7970 }
7971 else if (STRCMP(key, "GUIBG") == 0)
7972 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007973#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007974 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007975 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007976 if (!init)
7977 HL_TABLE()[idx].sg_set |= SG_GUI;
7978
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007979# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007980 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007981 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007982 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007983 {
7984 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007985# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007986 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7987 if (STRCMP(arg, "NONE") != 0)
7988 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7989 else
7990 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007991# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007992# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007993 if (is_menu_group)
7994 gui.menu_bg_pixel = i;
7995 if (is_scrollbar_group)
7996 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007997# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007998 if (is_tooltip_group)
7999 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008000# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008001 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02008002# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008003 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008004# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008005 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008006#endif
8007 }
8008 else if (STRCMP(key, "GUISP") == 0)
8009 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008010#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008011 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
8012 {
8013 if (!init)
8014 HL_TABLE()[idx].sg_set |= SG_GUI;
8015
Bram Moolenaar61623362010-07-14 22:04:22 +02008016# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008017 i = color_name2handle(arg);
8018 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8019 {
8020 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008021# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008022 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8023 if (STRCMP(arg, "NONE") != 0)
8024 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
8025 else
8026 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008027# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008028 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008029# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008030 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008031#endif
8032 }
8033 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8034 {
8035 char_u buf[100];
8036 char_u *tname;
8037
8038 if (!init)
8039 HL_TABLE()[idx].sg_set |= SG_TERM;
8040
8041 /*
8042 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008043 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008044 */
8045 if (STRNCMP(arg, "t_", 2) == 0)
8046 {
8047 off = 0;
8048 buf[0] = 0;
8049 while (arg[off] != NUL)
8050 {
8051 /* Isolate one termcap name */
8052 for (len = 0; arg[off + len] &&
8053 arg[off + len] != ','; ++len)
8054 ;
8055 tname = vim_strnsave(arg + off, len);
8056 if (tname == NULL) /* out of memory */
8057 {
8058 error = TRUE;
8059 break;
8060 }
8061 /* lookup the escape sequence for the item */
8062 p = get_term_code(tname);
8063 vim_free(tname);
8064 if (p == NULL) /* ignore non-existing things */
8065 p = (char_u *)"";
8066
8067 /* Append it to the already found stuff */
8068 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8069 {
8070 EMSG2(_("E422: terminal code too long: %s"), arg);
8071 error = TRUE;
8072 break;
8073 }
8074 STRCAT(buf, p);
8075
8076 /* Advance to the next item */
8077 off += len;
8078 if (arg[off] == ',') /* another one follows */
8079 ++off;
8080 }
8081 }
8082 else
8083 {
8084 /*
8085 * Copy characters from arg[] to buf[], translating <> codes.
8086 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008087 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008088 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008089 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008090 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008091 off += len;
8092 else /* copy as normal char */
8093 buf[off++] = *p++;
8094 }
8095 buf[off] = NUL;
8096 }
8097 if (error)
8098 break;
8099
8100 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8101 p = NULL;
8102 else
8103 p = vim_strsave(buf);
8104 if (key[2] == 'A')
8105 {
8106 vim_free(HL_TABLE()[idx].sg_start);
8107 HL_TABLE()[idx].sg_start = p;
8108 }
8109 else
8110 {
8111 vim_free(HL_TABLE()[idx].sg_stop);
8112 HL_TABLE()[idx].sg_stop = p;
8113 }
8114 }
8115 else
8116 {
8117 EMSG2(_("E423: Illegal argument: %s"), key_start);
8118 error = TRUE;
8119 break;
8120 }
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008121 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008122
8123 /*
8124 * When highlighting has been given for a group, don't link it.
8125 */
8126 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8127 HL_TABLE()[idx].sg_link = 0;
8128
8129 /*
8130 * Continue with next argument.
8131 */
8132 linep = skipwhite(linep);
8133 }
8134
8135 /*
8136 * If there is an error, and it's a new entry, remove it from the table.
8137 */
8138 if (error && idx == highlight_ga.ga_len)
8139 syn_unadd_group();
8140 else
8141 {
8142 if (is_normal_group)
8143 {
8144 HL_TABLE()[idx].sg_term_attr = 0;
8145 HL_TABLE()[idx].sg_cterm_attr = 0;
8146#ifdef FEAT_GUI
8147 HL_TABLE()[idx].sg_gui_attr = 0;
8148 /*
8149 * Need to update all groups, because they might be using "bg"
8150 * and/or "fg", which have been changed now.
8151 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008152#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008153#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008154 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008155 highlight_gui_started();
8156#endif
8157 }
8158#ifdef FEAT_GUI_X11
8159# ifdef FEAT_MENU
8160 else if (is_menu_group)
8161 {
8162 if (gui.in_use && do_colors)
8163 gui_mch_new_menu_colors();
8164 }
8165# endif
8166 else if (is_scrollbar_group)
8167 {
8168 if (gui.in_use && do_colors)
8169 gui_new_scrollbar_colors();
8170 }
8171# ifdef FEAT_BEVAL
8172 else if (is_tooltip_group)
8173 {
8174 if (gui.in_use && do_colors)
8175 gui_mch_new_tooltip_colors();
8176 }
8177# endif
8178#endif
8179 else
8180 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008181#ifdef FEAT_EVAL
8182 HL_TABLE()[idx].sg_scriptID = current_SID;
8183#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008184 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008185 }
8186 vim_free(key);
8187 vim_free(arg);
8188
8189 /* Only call highlight_changed() once, after sourcing a syntax file */
8190 need_highlight_changed = TRUE;
8191}
8192
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008193#if defined(EXITFREE) || defined(PROTO)
8194 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008195free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008196{
8197 int i;
8198
8199 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008200 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008201 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008202 vim_free(HL_TABLE()[i].sg_name);
8203 vim_free(HL_TABLE()[i].sg_name_u);
8204 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008205 ga_clear(&highlight_ga);
8206}
8207#endif
8208
Bram Moolenaar071d4272004-06-13 20:20:40 +00008209/*
8210 * Reset the cterm colors to what they were before Vim was started, if
8211 * possible. Otherwise reset them to zero.
8212 */
8213 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008214restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008215{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008216#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217 /* Since t_me has been set, this probably means that the user
8218 * wants to use this as default colors. Need to reset default
8219 * background/foreground colors. */
8220 mch_set_normal_colors();
8221#else
8222 cterm_normal_fg_color = 0;
8223 cterm_normal_fg_bold = 0;
8224 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008225# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008226 cterm_normal_fg_gui_color = INVALCOLOR;
8227 cterm_normal_bg_gui_color = INVALCOLOR;
8228# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008229#endif
8230}
8231
8232/*
8233 * Return TRUE if highlight group "idx" has any settings.
8234 * When "check_link" is TRUE also check for an existing link.
8235 */
8236 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008237hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238{
8239 return ( HL_TABLE()[idx].sg_term_attr != 0
8240 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008241 || HL_TABLE()[idx].sg_cterm_fg != 0
8242 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008243#ifdef FEAT_GUI
8244 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008245 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8246 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8247 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008248 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249#endif
8250 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8251}
8252
8253/*
8254 * Clear highlighting for one group.
8255 */
8256 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008257highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008258{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008259 HL_TABLE()[idx].sg_cleared = TRUE;
8260
Bram Moolenaar071d4272004-06-13 20:20:40 +00008261 HL_TABLE()[idx].sg_term = 0;
8262 vim_free(HL_TABLE()[idx].sg_start);
8263 HL_TABLE()[idx].sg_start = NULL;
8264 vim_free(HL_TABLE()[idx].sg_stop);
8265 HL_TABLE()[idx].sg_stop = NULL;
8266 HL_TABLE()[idx].sg_term_attr = 0;
8267 HL_TABLE()[idx].sg_cterm = 0;
8268 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8269 HL_TABLE()[idx].sg_cterm_fg = 0;
8270 HL_TABLE()[idx].sg_cterm_bg = 0;
8271 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008272#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008273 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8275 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008276 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8277 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008278 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8279 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008280#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008281#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008282 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8283 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008284#endif
8285#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008286 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008287 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8288 HL_TABLE()[idx].sg_font = NOFONT;
8289# ifdef FEAT_XFONTSET
8290 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8291 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8292# endif
8293 vim_free(HL_TABLE()[idx].sg_font_name);
8294 HL_TABLE()[idx].sg_font_name = NULL;
8295 HL_TABLE()[idx].sg_gui_attr = 0;
8296#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008297#ifdef FEAT_EVAL
8298 /* Clear the script ID only when there is no link, since that is not
8299 * cleared. */
8300 if (HL_TABLE()[idx].sg_link == 0)
8301 HL_TABLE()[idx].sg_scriptID = 0;
8302#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008303}
8304
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008305#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008306/*
8307 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008308 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008309 * "Tooltip" colors.
8310 */
8311 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008312set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008314#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008315# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008316 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008317# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008318 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008319 if (set_group_colors((char_u *)"Normal",
8320 &gui.norm_pixel, &gui.back_pixel,
8321 FALSE, TRUE, FALSE))
8322 {
8323 gui_mch_new_colors();
8324 must_redraw = CLEAR;
8325 }
8326# ifdef FEAT_GUI_X11
8327 if (set_group_colors((char_u *)"Menu",
8328 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8329 TRUE, FALSE, FALSE))
8330 {
8331# ifdef FEAT_MENU
8332 gui_mch_new_menu_colors();
8333# endif
8334 must_redraw = CLEAR;
8335 }
8336# ifdef FEAT_BEVAL
8337 if (set_group_colors((char_u *)"Tooltip",
8338 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8339 FALSE, FALSE, TRUE))
8340 {
8341# ifdef FEAT_TOOLBAR
8342 gui_mch_new_tooltip_colors();
8343# endif
8344 must_redraw = CLEAR;
8345 }
8346# endif
8347 if (set_group_colors((char_u *)"Scrollbar",
8348 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8349 FALSE, FALSE, FALSE))
8350 {
8351 gui_new_scrollbar_colors();
8352 must_redraw = CLEAR;
8353 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008354# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008355 }
8356#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008357#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008358# ifdef FEAT_GUI
8359 else
8360# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008361 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008362 int idx;
8363
8364 idx = syn_name2id((char_u *)"Normal") - 1;
8365 if (idx >= 0)
8366 {
8367 gui_do_one_color(idx, FALSE, FALSE);
8368
8369 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8370 {
8371 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8372 must_redraw = CLEAR;
8373 }
8374 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8375 {
8376 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8377 must_redraw = CLEAR;
8378 }
8379 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008380 }
8381#endif
8382}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008383#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008384
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008385#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386/*
8387 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8388 */
8389 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008390set_group_colors(
8391 char_u *name,
8392 guicolor_T *fgp,
8393 guicolor_T *bgp,
8394 int do_menu,
8395 int use_norm,
8396 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008397{
8398 int idx;
8399
8400 idx = syn_name2id(name) - 1;
8401 if (idx >= 0)
8402 {
8403 gui_do_one_color(idx, do_menu, do_tooltip);
8404
8405 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8406 *fgp = HL_TABLE()[idx].sg_gui_fg;
8407 else if (use_norm)
8408 *fgp = gui.def_norm_pixel;
8409 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8410 *bgp = HL_TABLE()[idx].sg_gui_bg;
8411 else if (use_norm)
8412 *bgp = gui.def_back_pixel;
8413 return TRUE;
8414 }
8415 return FALSE;
8416}
8417
8418/*
8419 * Get the font of the "Normal" group.
8420 * Returns "" when it's not found or not set.
8421 */
8422 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008423hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008424{
8425 int id;
8426 char_u *s;
8427
8428 id = syn_name2id((char_u *)"Normal");
8429 if (id > 0)
8430 {
8431 s = HL_TABLE()[id - 1].sg_font_name;
8432 if (s != NULL)
8433 return s;
8434 }
8435 return (char_u *)"";
8436}
8437
8438/*
8439 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8440 * actually chosen to be used.
8441 */
8442 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008443hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008444{
8445 int id;
8446
8447 id = syn_name2id((char_u *)"Normal");
8448 if (id > 0)
8449 {
8450 vim_free(HL_TABLE()[id - 1].sg_font_name);
8451 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8452 }
8453}
8454
8455/*
8456 * Set background color for "Normal" group. Called by gui_set_bg_color()
8457 * when the color is known.
8458 */
8459 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008460hl_set_bg_color_name(
8461 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008462{
8463 int id;
8464
8465 if (name != NULL)
8466 {
8467 id = syn_name2id((char_u *)"Normal");
8468 if (id > 0)
8469 {
8470 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8471 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8472 }
8473 }
8474}
8475
8476/*
8477 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8478 * when the color is known.
8479 */
8480 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008481hl_set_fg_color_name(
8482 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483{
8484 int id;
8485
8486 if (name != NULL)
8487 {
8488 id = syn_name2id((char_u *)"Normal");
8489 if (id > 0)
8490 {
8491 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8492 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8493 }
8494 }
8495}
8496
8497/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008498 * Return the handle for a font name.
8499 * Returns NOFONT when failed.
8500 */
8501 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008502font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008503{
8504 if (STRCMP(name, "NONE") == 0)
8505 return NOFONT;
8506
8507 return gui_mch_get_font(name, TRUE);
8508}
8509
8510# ifdef FEAT_XFONTSET
8511/*
8512 * Return the handle for a fontset name.
8513 * Returns NOFONTSET when failed.
8514 */
8515 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008516fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008517{
8518 if (STRCMP(name, "NONE") == 0)
8519 return NOFONTSET;
8520
8521 return gui_mch_get_fontset(name, TRUE, fixed_width);
8522}
8523# endif
8524
8525/*
8526 * Get the font or fontset for one highlight group.
8527 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008528 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008529hl_do_font(
8530 int idx,
8531 char_u *arg,
8532 int do_normal, /* set normal font */
8533 int do_menu UNUSED, /* set menu font */
8534 int do_tooltip UNUSED, /* set tooltip font */
8535 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008536{
8537# ifdef FEAT_XFONTSET
8538 /* If 'guifontset' is not empty, first try using the name as a
8539 * fontset. If that doesn't work, use it as a font name. */
8540 if (*p_guifontset != NUL
8541# ifdef FONTSET_ALWAYS
8542 || do_menu
8543# endif
8544# ifdef FEAT_BEVAL_TIP
8545 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8546 || do_tooltip
8547# endif
8548 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008549 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008550 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008551 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008552 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8553# ifdef FONTSET_ALWAYS
8554 || do_menu
8555# endif
8556# ifdef FEAT_BEVAL_TIP
8557 || do_tooltip
8558# endif
8559 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008560 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008561 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8562 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008563 /* If it worked and it's the Normal group, use it as the normal
8564 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008565 if (do_normal)
8566 gui_init_font(arg, TRUE);
8567# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8568 if (do_menu)
8569 {
8570# ifdef FONTSET_ALWAYS
8571 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8572# else
8573 /* YIKES! This is a bug waiting to crash the program */
8574 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8575# endif
8576 gui_mch_new_menu_font();
8577 }
8578# ifdef FEAT_BEVAL
8579 if (do_tooltip)
8580 {
8581 /* The Athena widget set cannot currently handle switching between
8582 * displaying a single font and a fontset.
8583 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008584 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585 * XFontStruct is used.
8586 */
8587 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8588 gui_mch_new_tooltip_font();
8589 }
8590# endif
8591# endif
8592 }
8593 else
8594# endif
8595 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008596 if (free_font)
8597 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008598 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8599 /* If it worked and it's the Normal group, use it as the
8600 * normal font. Same for the Menu group. */
8601 if (HL_TABLE()[idx].sg_font != NOFONT)
8602 {
8603 if (do_normal)
8604 gui_init_font(arg, FALSE);
8605#ifndef FONTSET_ALWAYS
8606# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8607 if (do_menu)
8608 {
8609 gui.menu_font = HL_TABLE()[idx].sg_font;
8610 gui_mch_new_menu_font();
8611 }
8612# endif
8613#endif
8614 }
8615 }
8616}
8617
8618#endif /* FEAT_GUI */
8619
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008620#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008621/*
8622 * Return the handle for a color name.
8623 * Returns INVALCOLOR when failed.
8624 */
8625 static guicolor_T
8626color_name2handle(char_u *name)
8627{
8628 if (STRCMP(name, "NONE") == 0)
8629 return INVALCOLOR;
8630
8631 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8632 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008633#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008634 if (gui.in_use)
8635#endif
8636#ifdef FEAT_GUI
8637 return gui.norm_pixel;
8638#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008639#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008640 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008641 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008642 /* Guess that the foreground is black or white. */
8643 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008644#endif
8645 }
8646 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8647 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008648#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008649 if (gui.in_use)
8650#endif
8651#ifdef FEAT_GUI
8652 return gui.back_pixel;
8653#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008654#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008655 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008656 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008657 /* Guess that the background is white or black. */
8658 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008659#endif
8660 }
8661
8662 return GUI_GET_COLOR(name);
8663}
8664#endif
8665
Bram Moolenaar071d4272004-06-13 20:20:40 +00008666/*
8667 * Table with the specifications for an attribute number.
8668 * Note that this table is used by ALL buffers. This is required because the
8669 * GUI can redraw at any time for any buffer.
8670 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008671static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008672
8673#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8674
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008675static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008676
8677#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8678
8679#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008680static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681
8682#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8683#endif
8684
8685/*
8686 * Return the attr number for a set of colors and font.
8687 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8688 * if the combination is new.
8689 * Return 0 for error (no more room).
8690 */
8691 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008692get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008693{
8694 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008695 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 static int recursive = FALSE;
8697
8698 /*
8699 * Init the table, in case it wasn't done yet.
8700 */
8701 table->ga_itemsize = sizeof(attrentry_T);
8702 table->ga_growsize = 7;
8703
8704 /*
8705 * Try to find an entry with the same specifications.
8706 */
8707 for (i = 0; i < table->ga_len; ++i)
8708 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008709 taep = &(((attrentry_T *)table->ga_data)[i]);
8710 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008711 && (
8712#ifdef FEAT_GUI
8713 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008714 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8715 && aep->ae_u.gui.bg_color
8716 == taep->ae_u.gui.bg_color
8717 && aep->ae_u.gui.sp_color
8718 == taep->ae_u.gui.sp_color
8719 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008720# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008721 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008722# endif
8723 ))
8724 ||
8725#endif
8726 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008727 && (aep->ae_u.term.start == NULL)
8728 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008729 && (aep->ae_u.term.start == NULL
8730 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008731 taep->ae_u.term.start) == 0)
8732 && (aep->ae_u.term.stop == NULL)
8733 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008734 && (aep->ae_u.term.stop == NULL
8735 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008736 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008737 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008738 && aep->ae_u.cterm.fg_color
8739 == taep->ae_u.cterm.fg_color
8740 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008741 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008742#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008743 && aep->ae_u.cterm.fg_rgb
8744 == taep->ae_u.cterm.fg_rgb
8745 && aep->ae_u.cterm.bg_rgb
8746 == taep->ae_u.cterm.bg_rgb
8747#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008748 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008749
8750 return i + ATTR_OFF;
8751 }
8752
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008753 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008754 {
8755 /*
8756 * Running out of attribute entries! remove all attributes, and
8757 * compute new ones for all groups.
8758 * When called recursively, we are really out of numbers.
8759 */
8760 if (recursive)
8761 {
8762 EMSG(_("E424: Too many different highlighting attributes in use"));
8763 return 0;
8764 }
8765 recursive = TRUE;
8766
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008767 clear_hl_tables();
8768
Bram Moolenaar071d4272004-06-13 20:20:40 +00008769 must_redraw = CLEAR;
8770
8771 for (i = 0; i < highlight_ga.ga_len; ++i)
8772 set_hl_attr(i);
8773
8774 recursive = FALSE;
8775 }
8776
8777 /*
8778 * This is a new combination of colors and font, add an entry.
8779 */
8780 if (ga_grow(table, 1) == FAIL)
8781 return 0;
8782
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008783 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8784 vim_memset(taep, 0, sizeof(attrentry_T));
8785 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008786#ifdef FEAT_GUI
8787 if (table == &gui_attr_table)
8788 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008789 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8790 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8791 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8792 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008793# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008794 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008795# endif
8796 }
8797#endif
8798 if (table == &term_attr_table)
8799 {
8800 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008801 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008802 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008803 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008804 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008805 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008806 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008807 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008808 }
8809 else if (table == &cterm_attr_table)
8810 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008811 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8812 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008813#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008814 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8815 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8816#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008817 }
8818 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008819 return (table->ga_len - 1 + ATTR_OFF);
8820}
8821
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008822/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008823 * Get an attribute index for a cterm entry.
8824 * Uses an existing entry when possible or adds one when needed.
8825 */
8826 int
8827get_cterm_attr_idx(int attr, int fg, int bg)
8828{
8829 attrentry_T at_en;
8830
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008831 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaareeac6772017-07-23 15:48:37 +02008832 at_en.ae_attr = attr;
8833 at_en.ae_u.cterm.fg_color = fg;
8834 at_en.ae_u.cterm.bg_color = bg;
8835 return get_attr_entry(&cterm_attr_table, &at_en);
8836}
8837
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008838#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8839/*
8840 * Get an attribute index for a 'termguicolors' entry.
8841 * Uses an existing entry when possible or adds one when needed.
8842 */
8843 int
8844get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8845{
8846 attrentry_T at_en;
8847
8848 vim_memset(&at_en, 0, sizeof(attrentry_T));
8849 at_en.ae_attr = attr;
8850 at_en.ae_u.cterm.fg_rgb = fg;
8851 at_en.ae_u.cterm.bg_rgb = bg;
8852 return get_attr_entry(&cterm_attr_table, &at_en);
8853}
8854#endif
8855
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008856#if defined(FEAT_GUI) || defined(PROTO)
8857/*
8858 * Get an attribute index for a cterm entry.
8859 * Uses an existing entry when possible or adds one when needed.
8860 */
8861 int
8862get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8863{
8864 attrentry_T at_en;
8865
8866 vim_memset(&at_en, 0, sizeof(attrentry_T));
8867 at_en.ae_attr = attr;
8868 at_en.ae_u.gui.fg_color = fg;
8869 at_en.ae_u.gui.bg_color = bg;
8870 return get_attr_entry(&gui_attr_table, &at_en);
8871}
8872#endif
8873
Bram Moolenaareeac6772017-07-23 15:48:37 +02008874/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008875 * Clear all highlight tables.
8876 */
8877 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008878clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008879{
8880 int i;
8881 attrentry_T *taep;
8882
8883#ifdef FEAT_GUI
8884 ga_clear(&gui_attr_table);
8885#endif
8886 for (i = 0; i < term_attr_table.ga_len; ++i)
8887 {
8888 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8889 vim_free(taep->ae_u.term.start);
8890 vim_free(taep->ae_u.term.stop);
8891 }
8892 ga_clear(&term_attr_table);
8893 ga_clear(&cterm_attr_table);
8894}
8895
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008896#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008897/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008898 * Combine special attributes (e.g., for spelling) with other attributes
8899 * (e.g., for syntax highlighting).
8900 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008901 * This creates a new group when required.
8902 * Since we expect there to be few spelling mistakes we don't cache the
8903 * result.
8904 * Return the resulting attributes.
8905 */
8906 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008907hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008908{
8909 attrentry_T *char_aep = NULL;
8910 attrentry_T *spell_aep;
8911 attrentry_T new_en;
8912
8913 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008914 return prim_attr;
8915 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008916 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008917#ifdef FEAT_GUI
8918 if (gui.in_use)
8919 {
8920 if (char_attr > HL_ALL)
8921 char_aep = syn_gui_attr2entry(char_attr);
8922 if (char_aep != NULL)
8923 new_en = *char_aep;
8924 else
8925 {
8926 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008927 new_en.ae_u.gui.fg_color = INVALCOLOR;
8928 new_en.ae_u.gui.bg_color = INVALCOLOR;
8929 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008930 if (char_attr <= HL_ALL)
8931 new_en.ae_attr = char_attr;
8932 }
8933
Bram Moolenaar30abd282005-06-22 22:35:10 +00008934 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008935 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008936 else
8937 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008938 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008939 if (spell_aep != NULL)
8940 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008941 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
8942 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008943 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8944 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8945 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8946 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8947 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8948 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8949 if (spell_aep->ae_u.gui.font != NOFONT)
8950 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8951# ifdef FEAT_XFONTSET
8952 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8953 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8954# endif
8955 }
8956 }
8957 return get_attr_entry(&gui_attr_table, &new_en);
8958 }
8959#endif
8960
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008961 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008962 {
8963 if (char_attr > HL_ALL)
8964 char_aep = syn_cterm_attr2entry(char_attr);
8965 if (char_aep != NULL)
8966 new_en = *char_aep;
8967 else
8968 {
8969 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008970#ifdef FEAT_TERMGUICOLORS
8971 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8972 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8973#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008974 if (char_attr <= HL_ALL)
8975 new_en.ae_attr = char_attr;
8976 }
8977
Bram Moolenaar30abd282005-06-22 22:35:10 +00008978 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008979 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008980 else
8981 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008982 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008983 if (spell_aep != NULL)
8984 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02008985 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
8986 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008987 if (spell_aep->ae_u.cterm.fg_color > 0)
8988 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8989 if (spell_aep->ae_u.cterm.bg_color > 0)
8990 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008991#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008992 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008993 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008994 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008995 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8996#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008997 }
8998 }
8999 return get_attr_entry(&cterm_attr_table, &new_en);
9000 }
9001
9002 if (char_attr > HL_ALL)
9003 char_aep = syn_term_attr2entry(char_attr);
9004 if (char_aep != NULL)
9005 new_en = *char_aep;
9006 else
9007 {
9008 vim_memset(&new_en, 0, sizeof(new_en));
9009 if (char_attr <= HL_ALL)
9010 new_en.ae_attr = char_attr;
9011 }
9012
Bram Moolenaar30abd282005-06-22 22:35:10 +00009013 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009014 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009015 else
9016 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009017 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009018 if (spell_aep != NULL)
9019 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009020 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009021 if (spell_aep->ae_u.term.start != NULL)
9022 {
9023 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9024 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9025 }
9026 }
9027 }
9028 return get_attr_entry(&term_attr_table, &new_en);
9029}
9030#endif
9031
Bram Moolenaar071d4272004-06-13 20:20:40 +00009032#ifdef FEAT_GUI
9033
9034 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009035syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009036{
9037 attr -= ATTR_OFF;
9038 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9039 return NULL;
9040 return &(GUI_ATTR_ENTRY(attr));
9041}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009042#endif /* FEAT_GUI */
9043
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009044/*
9045 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9046 * Only to be used when "attr" > HL_ALL.
9047 */
9048 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009049syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009050{
9051 attrentry_T *aep;
9052
9053#ifdef FEAT_GUI
9054 if (gui.in_use)
9055 aep = syn_gui_attr2entry(attr);
9056 else
9057#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009058 if (IS_CTERM)
9059 aep = syn_cterm_attr2entry(attr);
9060 else
9061 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009062
9063 if (aep == NULL) /* highlighting not set */
9064 return 0;
9065 return aep->ae_attr;
9066}
9067
9068
Bram Moolenaar071d4272004-06-13 20:20:40 +00009069 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009070syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009071{
9072 attr -= ATTR_OFF;
9073 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9074 return NULL;
9075 return &(TERM_ATTR_ENTRY(attr));
9076}
9077
9078 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009079syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009080{
9081 attr -= ATTR_OFF;
9082 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9083 return NULL;
9084 return &(CTERM_ATTR_ENTRY(attr));
9085}
9086
9087#define LIST_ATTR 1
9088#define LIST_STRING 2
9089#define LIST_INT 3
9090
9091 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009092highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009093{
9094 struct hl_group *sgp;
9095 int didh = FALSE;
9096
9097 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9098
9099 didh = highlight_list_arg(id, didh, LIST_ATTR,
9100 sgp->sg_term, NULL, "term");
9101 didh = highlight_list_arg(id, didh, LIST_STRING,
9102 0, sgp->sg_start, "start");
9103 didh = highlight_list_arg(id, didh, LIST_STRING,
9104 0, sgp->sg_stop, "stop");
9105
9106 didh = highlight_list_arg(id, didh, LIST_ATTR,
9107 sgp->sg_cterm, NULL, "cterm");
9108 didh = highlight_list_arg(id, didh, LIST_INT,
9109 sgp->sg_cterm_fg, NULL, "ctermfg");
9110 didh = highlight_list_arg(id, didh, LIST_INT,
9111 sgp->sg_cterm_bg, NULL, "ctermbg");
9112
Bram Moolenaar61623362010-07-14 22:04:22 +02009113#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009114 didh = highlight_list_arg(id, didh, LIST_ATTR,
9115 sgp->sg_gui, NULL, "gui");
9116 didh = highlight_list_arg(id, didh, LIST_STRING,
9117 0, sgp->sg_gui_fg_name, "guifg");
9118 didh = highlight_list_arg(id, didh, LIST_STRING,
9119 0, sgp->sg_gui_bg_name, "guibg");
9120 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009121 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009122#endif
9123#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009124 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009125 0, sgp->sg_font_name, "font");
9126#endif
9127
Bram Moolenaar661b1822005-07-28 22:36:45 +00009128 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009129 {
9130 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009131 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009132 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009133 msg_putchar(' ');
9134 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9135 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009136
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009137 if (!didh)
9138 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009139#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009140 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009141 last_set_msg(sgp->sg_scriptID);
9142#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009143}
9144
9145 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009146highlight_list_arg(
9147 int id,
9148 int didh,
9149 int type,
9150 int iarg,
9151 char_u *sarg,
9152 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009153{
9154 char_u buf[100];
9155 char_u *ts;
9156 int i;
9157
Bram Moolenaar661b1822005-07-28 22:36:45 +00009158 if (got_int)
9159 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009160 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9161 {
9162 ts = buf;
9163 if (type == LIST_INT)
9164 sprintf((char *)buf, "%d", iarg - 1);
9165 else if (type == LIST_STRING)
9166 ts = sarg;
9167 else /* type == LIST_ATTR */
9168 {
9169 buf[0] = NUL;
9170 for (i = 0; hl_attr_table[i] != 0; ++i)
9171 {
9172 if (iarg & hl_attr_table[i])
9173 {
9174 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009175 vim_strcat(buf, (char_u *)",", 100);
9176 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009177 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9178 }
9179 }
9180 }
9181
9182 (void)syn_list_header(didh,
9183 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9184 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009185 if (!got_int)
9186 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009187 if (*name != NUL)
9188 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009189 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9190 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009191 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009192 msg_outtrans(ts);
9193 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009194 }
9195 return didh;
9196}
9197
9198#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9199/*
9200 * Return "1" if highlight group "id" has attribute "flag".
9201 * Return NULL otherwise.
9202 */
9203 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009204highlight_has_attr(
9205 int id,
9206 int flag,
9207 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009208{
9209 int attr;
9210
9211 if (id <= 0 || id > highlight_ga.ga_len)
9212 return NULL;
9213
Bram Moolenaar61623362010-07-14 22:04:22 +02009214#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009215 if (modec == 'g')
9216 attr = HL_TABLE()[id - 1].sg_gui;
9217 else
9218#endif
9219 if (modec == 'c')
9220 attr = HL_TABLE()[id - 1].sg_cterm;
9221 else
9222 attr = HL_TABLE()[id - 1].sg_term;
9223
9224 if (attr & flag)
9225 return (char_u *)"1";
9226 return NULL;
9227}
9228#endif
9229
9230#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9231/*
9232 * Return color name of highlight group "id".
9233 */
9234 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009235highlight_color(
9236 int id,
9237 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9238 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009239{
9240 static char_u name[20];
9241 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009242 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009243 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009244 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009245
9246 if (id <= 0 || id > highlight_ga.ga_len)
9247 return NULL;
9248
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009249 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009250 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009251 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009252 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009253 font = TRUE;
9254 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009255 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009256 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9257 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009258 if (modec == 'g')
9259 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009260# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009261# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009262 /* return font name */
9263 if (font)
9264 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009265# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009266
Bram Moolenaar071d4272004-06-13 20:20:40 +00009267 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009268 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009269 {
9270 guicolor_T color;
9271 long_u rgb;
9272 static char_u buf[10];
9273
9274 if (fg)
9275 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009276 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009277# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009278 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009279# else
9280 color = INVALCOLOR;
9281# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009282 else
9283 color = HL_TABLE()[id - 1].sg_gui_bg;
9284 if (color == INVALCOLOR)
9285 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009286 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009287 sprintf((char *)buf, "#%02x%02x%02x",
9288 (unsigned)(rgb >> 16),
9289 (unsigned)(rgb >> 8) & 255,
9290 (unsigned)rgb & 255);
9291 return buf;
9292 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009293# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009294 if (fg)
9295 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009296 if (sp)
9297 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009298 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9299 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009300 if (font || sp)
9301 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009302 if (modec == 'c')
9303 {
9304 if (fg)
9305 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9306 else
9307 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009308 if (n < 0)
9309 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009310 sprintf((char *)name, "%d", n);
9311 return name;
9312 }
9313 /* term doesn't have color */
9314 return NULL;
9315}
9316#endif
9317
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009318#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009319 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009320 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009321/*
9322 * Return color name of highlight group "id" as RGB value.
9323 */
9324 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009325highlight_gui_color_rgb(
9326 int id,
9327 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009328{
9329 guicolor_T color;
9330
9331 if (id <= 0 || id > highlight_ga.ga_len)
9332 return 0L;
9333
9334 if (fg)
9335 color = HL_TABLE()[id - 1].sg_gui_fg;
9336 else
9337 color = HL_TABLE()[id - 1].sg_gui_bg;
9338
9339 if (color == INVALCOLOR)
9340 return 0L;
9341
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009342 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009343}
9344#endif
9345
9346/*
9347 * Output the syntax list header.
9348 * Return TRUE when started a new line.
9349 */
9350 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009351syn_list_header(
9352 int did_header, /* did header already */
9353 int outlen, /* length of string that comes */
9354 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355{
9356 int endcol = 19;
9357 int newline = TRUE;
9358
9359 if (!did_header)
9360 {
9361 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009362 if (got_int)
9363 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009364 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9365 endcol = 15;
9366 }
9367 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009368 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009369 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009370 if (got_int)
9371 return TRUE;
9372 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009373 else
9374 {
9375 if (msg_col >= endcol) /* wrap around is like starting a new line */
9376 newline = FALSE;
9377 }
9378
9379 if (msg_col >= endcol) /* output at least one space */
9380 endcol = msg_col + 1;
9381 if (Columns <= endcol) /* avoid hang for tiny window */
9382 endcol = Columns - 1;
9383
9384 msg_advance(endcol);
9385
9386 /* Show "xxx" with the attributes. */
9387 if (!did_header)
9388 {
9389 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9390 msg_putchar(' ');
9391 }
9392
9393 return newline;
9394}
9395
9396/*
9397 * Set the attribute numbers for a highlight group.
9398 * Called after one of the attributes has changed.
9399 */
9400 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009401set_hl_attr(
9402 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009403{
9404 attrentry_T at_en;
9405 struct hl_group *sgp = HL_TABLE() + idx;
9406
9407 /* The "Normal" group doesn't need an attribute number */
9408 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9409 return;
9410
9411#ifdef FEAT_GUI
9412 /*
9413 * For the GUI mode: If there are other than "normal" highlighting
9414 * attributes, need to allocate an attr number.
9415 */
9416 if (sgp->sg_gui_fg == INVALCOLOR
9417 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009418 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009419 && sgp->sg_font == NOFONT
9420# ifdef FEAT_XFONTSET
9421 && sgp->sg_fontset == NOFONTSET
9422# endif
9423 )
9424 {
9425 sgp->sg_gui_attr = sgp->sg_gui;
9426 }
9427 else
9428 {
9429 at_en.ae_attr = sgp->sg_gui;
9430 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9431 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009432 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009433 at_en.ae_u.gui.font = sgp->sg_font;
9434# ifdef FEAT_XFONTSET
9435 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9436# endif
9437 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9438 }
9439#endif
9440 /*
9441 * For the term mode: If there are other than "normal" highlighting
9442 * attributes, need to allocate an attr number.
9443 */
9444 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9445 sgp->sg_term_attr = sgp->sg_term;
9446 else
9447 {
9448 at_en.ae_attr = sgp->sg_term;
9449 at_en.ae_u.term.start = sgp->sg_start;
9450 at_en.ae_u.term.stop = sgp->sg_stop;
9451 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9452 }
9453
9454 /*
9455 * For the color term mode: If there are other than "normal"
9456 * highlighting attributes, need to allocate an attr number.
9457 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009458 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009459# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009460 && sgp->sg_gui_fg == INVALCOLOR
9461 && sgp->sg_gui_bg == INVALCOLOR
9462# endif
9463 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009464 sgp->sg_cterm_attr = sgp->sg_cterm;
9465 else
9466 {
9467 at_en.ae_attr = sgp->sg_cterm;
9468 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9469 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009470# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009471 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9472 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009473# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009474 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9475 }
9476}
9477
9478/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009479 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009480 * If it is not found, 0 is returned.
9481 */
9482 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009483syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009484{
9485 int i;
9486 char_u name_u[200];
9487
9488 /* Avoid using stricmp() too much, it's slow on some systems */
9489 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9490 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009491 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009492 vim_strup(name_u);
9493 for (i = highlight_ga.ga_len; --i >= 0; )
9494 if (HL_TABLE()[i].sg_name_u != NULL
9495 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9496 break;
9497 return i + 1;
9498}
9499
9500#if defined(FEAT_EVAL) || defined(PROTO)
9501/*
9502 * Return TRUE if highlight group "name" exists.
9503 */
9504 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009505highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009506{
9507 return (syn_name2id(name) > 0);
9508}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009509
9510# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9511/*
9512 * Return the name of highlight group "id".
9513 * When not a valid ID return an empty string.
9514 */
9515 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009516syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009517{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009518 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009519 return (char_u *)"";
9520 return HL_TABLE()[id - 1].sg_name;
9521}
9522# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009523#endif
9524
9525/*
9526 * Like syn_name2id(), but take a pointer + length argument.
9527 */
9528 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009529syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009530{
9531 char_u *name;
9532 int id = 0;
9533
9534 name = vim_strnsave(linep, len);
9535 if (name != NULL)
9536 {
9537 id = syn_name2id(name);
9538 vim_free(name);
9539 }
9540 return id;
9541}
9542
9543/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009544 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009545 * The argument is a pointer to the name and the length of the name.
9546 * If it doesn't exist yet, a new entry is created.
9547 * Return 0 for failure.
9548 */
9549 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009550syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009551{
9552 int id;
9553 char_u *name;
9554
9555 name = vim_strnsave(pp, len);
9556 if (name == NULL)
9557 return 0;
9558
9559 id = syn_name2id(name);
9560 if (id == 0) /* doesn't exist yet */
9561 id = syn_add_group(name);
9562 else
9563 vim_free(name);
9564 return id;
9565}
9566
9567/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009568 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009569 * "name" must be an allocated string, it will be consumed.
9570 * Return 0 for failure.
9571 */
9572 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009573syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009574{
9575 char_u *p;
9576
9577 /* Check that the name is ASCII letters, digits and underscore. */
9578 for (p = name; *p != NUL; ++p)
9579 {
9580 if (!vim_isprintc(*p))
9581 {
9582 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009583 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009584 return 0;
9585 }
9586 else if (!ASCII_ISALNUM(*p) && *p != '_')
9587 {
9588 /* This is an error, but since there previously was no check only
9589 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009590 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009591 MSG(_("W18: Invalid character in group name"));
9592 break;
9593 }
9594 }
9595
9596 /*
9597 * First call for this growarray: init growing array.
9598 */
9599 if (highlight_ga.ga_data == NULL)
9600 {
9601 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9602 highlight_ga.ga_growsize = 10;
9603 }
9604
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009605 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009606 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009607 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009608 vim_free(name);
9609 return 0;
9610 }
9611
Bram Moolenaar071d4272004-06-13 20:20:40 +00009612 /*
9613 * Make room for at least one other syntax_highlight entry.
9614 */
9615 if (ga_grow(&highlight_ga, 1) == FAIL)
9616 {
9617 vim_free(name);
9618 return 0;
9619 }
9620
9621 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9622 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9623 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009624#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009625 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9626 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009627# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009628 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009629# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009630#endif
9631 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009632
9633 return highlight_ga.ga_len; /* ID is index plus one */
9634}
9635
9636/*
9637 * When, just after calling syn_add_group(), an error is discovered, this
9638 * function deletes the new name.
9639 */
9640 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009641syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009642{
9643 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009644 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9645 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9646}
9647
9648/*
9649 * Translate a group ID to highlight attributes.
9650 */
9651 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009652syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009653{
9654 int attr;
9655 struct hl_group *sgp;
9656
9657 hl_id = syn_get_final_id(hl_id);
9658 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9659
9660#ifdef FEAT_GUI
9661 /*
9662 * Only use GUI attr when the GUI is being used.
9663 */
9664 if (gui.in_use)
9665 attr = sgp->sg_gui_attr;
9666 else
9667#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009668 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009669 attr = sgp->sg_cterm_attr;
9670 else
9671 attr = sgp->sg_term_attr;
9672
9673 return attr;
9674}
9675
9676#ifdef FEAT_GUI
9677/*
9678 * Get the GUI colors and attributes for a group ID.
9679 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9680 */
9681 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009682syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009683{
9684 struct hl_group *sgp;
9685
9686 hl_id = syn_get_final_id(hl_id);
9687 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9688
9689 *fgp = sgp->sg_gui_fg;
9690 *bgp = sgp->sg_gui_bg;
9691 return sgp->sg_gui;
9692}
9693#endif
9694
9695/*
9696 * Translate a group ID to the final group ID (following links).
9697 */
9698 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009699syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009700{
9701 int count;
9702 struct hl_group *sgp;
9703
9704 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9705 return 0; /* Can be called from eval!! */
9706
9707 /*
9708 * Follow links until there is no more.
9709 * Look out for loops! Break after 100 links.
9710 */
9711 for (count = 100; --count >= 0; )
9712 {
9713 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9714 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9715 break;
9716 hl_id = sgp->sg_link;
9717 }
9718
9719 return hl_id;
9720}
9721
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009722#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009723/*
9724 * Call this function just after the GUI has started.
9725 * It finds the font and color handles for the highlighting groups.
9726 */
9727 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009728highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009729{
9730 int idx;
9731
9732 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009733# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9734# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009735 if (USE_24BIT)
9736# endif
9737 set_normal_colors();
9738# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009739
9740 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9741 gui_do_one_color(idx, FALSE, FALSE);
9742
9743 highlight_changed();
9744}
9745
9746 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009747gui_do_one_color(
9748 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009749 int do_menu UNUSED, /* TRUE: might set the menu font */
9750 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009751{
9752 int didit = FALSE;
9753
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009754# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009755# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009756 if (gui.in_use)
9757# endif
9758 if (HL_TABLE()[idx].sg_font_name != NULL)
9759 {
9760 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009761 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009762 didit = TRUE;
9763 }
9764# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009765 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9766 {
9767 HL_TABLE()[idx].sg_gui_fg =
9768 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9769 didit = TRUE;
9770 }
9771 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9772 {
9773 HL_TABLE()[idx].sg_gui_bg =
9774 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9775 didit = TRUE;
9776 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009777# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009778 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9779 {
9780 HL_TABLE()[idx].sg_gui_sp =
9781 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9782 didit = TRUE;
9783 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009784# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009785 if (didit) /* need to get a new attr number */
9786 set_hl_attr(idx);
9787}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009788#endif
9789
9790/*
9791 * Translate the 'highlight' option into attributes in highlight_attr[] and
9792 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9793 * corresponding highlights to use on top of HLF_SNC is computed.
9794 * Called only when the 'highlight' option has been changed and upon first
9795 * screen redraw after any :highlight command.
9796 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9797 */
9798 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009799highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009800{
9801 int hlf;
9802 int i;
9803 char_u *p;
9804 int attr;
9805 char_u *end;
9806 int id;
9807#ifdef USER_HIGHLIGHT
9808 char_u userhl[10];
9809# ifdef FEAT_STL_OPT
9810 int id_SNC = -1;
9811 int id_S = -1;
9812 int hlcnt;
9813# endif
9814#endif
9815 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9816
9817 need_highlight_changed = FALSE;
9818
9819 /*
9820 * Clear all attributes.
9821 */
9822 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9823 highlight_attr[hlf] = 0;
9824
9825 /*
9826 * First set all attributes to their default value.
9827 * Then use the attributes from the 'highlight' option.
9828 */
9829 for (i = 0; i < 2; ++i)
9830 {
9831 if (i)
9832 p = p_hl;
9833 else
9834 p = get_highlight_default();
9835 if (p == NULL) /* just in case */
9836 continue;
9837
9838 while (*p)
9839 {
9840 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9841 if (hl_flags[hlf] == *p)
9842 break;
9843 ++p;
9844 if (hlf == (int)HLF_COUNT || *p == NUL)
9845 return FAIL;
9846
9847 /*
9848 * Allow several hl_flags to be combined, like "bu" for
9849 * bold-underlined.
9850 */
9851 attr = 0;
9852 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9853 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01009854 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009855 continue;
9856
9857 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9858 return FAIL;
9859
9860 switch (*p)
9861 {
9862 case 'b': attr |= HL_BOLD;
9863 break;
9864 case 'i': attr |= HL_ITALIC;
9865 break;
9866 case '-':
9867 case 'n': /* no highlighting */
9868 break;
9869 case 'r': attr |= HL_INVERSE;
9870 break;
9871 case 's': attr |= HL_STANDOUT;
9872 break;
9873 case 'u': attr |= HL_UNDERLINE;
9874 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009875 case 'c': attr |= HL_UNDERCURL;
9876 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009877 case ':': ++p; /* highlight group name */
9878 if (attr || *p == NUL) /* no combinations */
9879 return FAIL;
9880 end = vim_strchr(p, ',');
9881 if (end == NULL)
9882 end = p + STRLEN(p);
9883 id = syn_check_group(p, (int)(end - p));
9884 if (id == 0)
9885 return FAIL;
9886 attr = syn_id2attr(id);
9887 p = end - 1;
9888#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9889 if (hlf == (int)HLF_SNC)
9890 id_SNC = syn_get_final_id(id);
9891 else if (hlf == (int)HLF_S)
9892 id_S = syn_get_final_id(id);
9893#endif
9894 break;
9895 default: return FAIL;
9896 }
9897 }
9898 highlight_attr[hlf] = attr;
9899
9900 p = skip_to_option_part(p); /* skip comma and spaces */
9901 }
9902 }
9903
9904#ifdef USER_HIGHLIGHT
9905 /* Setup the user highlights
9906 *
9907 * Temporarily utilize 10 more hl entries. Have to be in there
9908 * simultaneously in case of table overflows in get_attr_entry()
9909 */
9910# ifdef FEAT_STL_OPT
9911 if (ga_grow(&highlight_ga, 10) == FAIL)
9912 return FAIL;
9913 hlcnt = highlight_ga.ga_len;
9914 if (id_S == 0)
9915 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009916 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009917 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9918 id_S = hlcnt + 10;
9919 }
9920# endif
9921 for (i = 0; i < 9; i++)
9922 {
9923 sprintf((char *)userhl, "User%d", i + 1);
9924 id = syn_name2id(userhl);
9925 if (id == 0)
9926 {
9927 highlight_user[i] = 0;
9928# ifdef FEAT_STL_OPT
9929 highlight_stlnc[i] = 0;
9930# endif
9931 }
9932 else
9933 {
9934# ifdef FEAT_STL_OPT
9935 struct hl_group *hlt = HL_TABLE();
9936# endif
9937
9938 highlight_user[i] = syn_id2attr(id);
9939# ifdef FEAT_STL_OPT
9940 if (id_SNC == 0)
9941 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009942 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009943 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9944 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009945# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009946 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9947# endif
9948 }
9949 else
9950 mch_memmove(&hlt[hlcnt + i],
9951 &hlt[id_SNC - 1],
9952 sizeof(struct hl_group));
9953 hlt[hlcnt + i].sg_link = 0;
9954
9955 /* Apply difference between UserX and HLF_S to HLF_SNC */
9956 hlt[hlcnt + i].sg_term ^=
9957 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9958 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9959 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9960 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9961 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9962 hlt[hlcnt + i].sg_cterm ^=
9963 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9964 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9965 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9966 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9967 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009968# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009969 hlt[hlcnt + i].sg_gui ^=
9970 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009971# endif
9972# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009973 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9974 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9975 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9976 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009977 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9978 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009979 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9980 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9981# ifdef FEAT_XFONTSET
9982 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9983 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9984# endif
9985# endif
9986 highlight_ga.ga_len = hlcnt + i + 1;
9987 set_hl_attr(hlcnt + i); /* At long last we can apply */
9988 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9989# endif
9990 }
9991 }
9992# ifdef FEAT_STL_OPT
9993 highlight_ga.ga_len = hlcnt;
9994# endif
9995
9996#endif /* USER_HIGHLIGHT */
9997
9998 return OK;
9999}
10000
Bram Moolenaar4f688582007-07-24 12:34:30 +000010001#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010002
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010003static void highlight_list(void);
10004static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010005
10006/*
10007 * Handle command line completion for :highlight command.
10008 */
10009 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010010set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010011{
10012 char_u *p;
10013
10014 /* Default: expand group names */
10015 xp->xp_context = EXPAND_HIGHLIGHT;
10016 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010017 include_link = 2;
10018 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010019
10020 /* (part of) subcommand already typed */
10021 if (*arg != NUL)
10022 {
10023 p = skiptowhite(arg);
10024 if (*p != NUL) /* past "default" or group name */
10025 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010026 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010027 if (STRNCMP("default", arg, p - arg) == 0)
10028 {
10029 arg = skipwhite(p);
10030 xp->xp_pattern = arg;
10031 p = skiptowhite(arg);
10032 }
10033 if (*p != NUL) /* past group name */
10034 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010035 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010036 if (arg[1] == 'i' && arg[0] == 'N')
10037 highlight_list();
10038 if (STRNCMP("link", arg, p - arg) == 0
10039 || STRNCMP("clear", arg, p - arg) == 0)
10040 {
10041 xp->xp_pattern = skipwhite(p);
10042 p = skiptowhite(xp->xp_pattern);
10043 if (*p != NUL) /* past first group name */
10044 {
10045 xp->xp_pattern = skipwhite(p);
10046 p = skiptowhite(xp->xp_pattern);
10047 }
10048 }
10049 if (*p != NUL) /* past group name(s) */
10050 xp->xp_context = EXPAND_NOTHING;
10051 }
10052 }
10053 }
10054}
10055
10056/*
10057 * List highlighting matches in a nice way.
10058 */
10059 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010060highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010061{
10062 int i;
10063
10064 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010065 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010066 for (i = 40; --i >= 0; )
10067 highlight_list_two(99, 0);
10068}
10069
10070 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010071highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010072{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010073 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010074 msg_clr_eos();
10075 out_flush();
10076 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10077}
10078
10079#endif /* FEAT_CMDL_COMPL */
10080
10081#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10082 || defined(FEAT_SIGNS) || defined(PROTO)
10083/*
10084 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010085 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010086 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010087get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010088{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010089 return get_highlight_name_ext(xp, idx, TRUE);
10090}
10091
10092/*
10093 * Obtain a highlight group name.
10094 * When "skip_cleared" is TRUE don't return a cleared entry.
10095 */
10096 char_u *
10097get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10098{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010099 if (idx < 0)
10100 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010101
10102 /* Items are never removed from the table, skip the ones that were
10103 * cleared. */
10104 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10105 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010106
Bram Moolenaar071d4272004-06-13 20:20:40 +000010107#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010108 if (idx == highlight_ga.ga_len && include_none != 0)
10109 return (char_u *)"none";
10110 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010111 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010112 if (idx == highlight_ga.ga_len + include_none + include_default
10113 && include_link != 0)
10114 return (char_u *)"link";
10115 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10116 && include_link != 0)
10117 return (char_u *)"clear";
10118#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010119 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010120 return NULL;
10121 return HL_TABLE()[idx].sg_name;
10122}
10123#endif
10124
Bram Moolenaar4f688582007-07-24 12:34:30 +000010125#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010126/*
10127 * Free all the highlight group fonts.
10128 * Used when quitting for systems which need it.
10129 */
10130 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010131free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010132{
10133 int idx;
10134
10135 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10136 {
10137 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10138 HL_TABLE()[idx].sg_font = NOFONT;
10139# ifdef FEAT_XFONTSET
10140 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10141 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10142# endif
10143 }
10144
10145 gui_mch_free_font(gui.norm_font);
10146# ifdef FEAT_XFONTSET
10147 gui_mch_free_fontset(gui.fontset);
10148# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010149# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010150 gui_mch_free_font(gui.bold_font);
10151 gui_mch_free_font(gui.ital_font);
10152 gui_mch_free_font(gui.boldital_font);
10153# endif
10154}
10155#endif
10156
10157/**************************************
10158 * End of Highlighting stuff *
10159 **************************************/