blob: 8c11c2b64396bb04283d1ca46eb77c5f68fcbbad [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 Moolenaarcf4b00c2017-09-02 18:33:56 +020089 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000090static int hl_attr_table[] =
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020091 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
Bram Moolenaar0cd2a942017-08-12 15:12:30 +020092#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 Moolenaar8a633e32016-04-21 21:10:14 +0200106#endif
107#ifdef FEAT_GUI
108static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100109static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100111static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000112# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100113static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#endif
115
116/*
117 * An attribute number is the index in attr_table plus ATTR_OFF.
118 */
119#define ATTR_OFF (HL_ALL + 1)
120
121#if defined(FEAT_SYN_HL) || defined(PROTO)
122
123#define SYN_NAMELEN 50 /* maximum length of a syntax name */
124
125/* different types of offsets that are possible */
126#define SPO_MS_OFF 0 /* match start offset */
127#define SPO_ME_OFF 1 /* match end offset */
128#define SPO_HS_OFF 2 /* highl. start offset */
129#define SPO_HE_OFF 3 /* highl. end offset */
130#define SPO_RS_OFF 4 /* region start offset */
131#define SPO_RE_OFF 5 /* region end offset */
132#define SPO_LC_OFF 6 /* leading context offset */
133#define SPO_COUNT 7
134
135static char *(spo_name_tab[SPO_COUNT]) =
136 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
137
138/*
139 * The patterns that are being searched for are stored in a syn_pattern.
140 * A match item consists of one pattern.
141 * A start/end item consists of n start patterns and m end patterns.
142 * A start/skip/end item consists of n start patterns, one skip pattern and m
143 * end patterns.
144 * For the latter two, the patterns are always consecutive: start-skip-end.
145 *
146 * A character offset can be given for the matched text (_m_start and _m_end)
147 * and for the actually highlighted text (_h_start and _h_end).
Bram Moolenaar36f92302018-02-24 21:36:34 +0100148 *
149 * Note that ordering of members is optimized to reduce padding.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 */
151typedef struct syn_pattern
152{
153 char sp_type; /* see SPTYPE_ defines below */
154 char sp_syncing; /* this item used for syncing */
Bram Moolenaar36f92302018-02-24 21:36:34 +0100155 short sp_syn_match_id; /* highlight group ID of pattern */
156 short sp_off_flags; /* see below */
157 int sp_offsets[SPO_COUNT]; /* offsets */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200158 int sp_flags; /* see HL_ defines below */
159#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200160 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200161#endif
Bram Moolenaar36f92302018-02-24 21:36:34 +0100162 int sp_ic; /* ignore-case flag for sp_prog */
163 int sp_sync_idx; /* sync item index (syncing only) */
164 int sp_line_id; /* ID of last line where tried */
165 int sp_startcol; /* next match in sp_line_id line */
166 short *sp_cont_list; /* cont. group IDs, if non-zero */
167 short *sp_next_list; /* next group IDs, if non-zero */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000168 struct sp_syn sp_syn; /* struct passed to in_id_list() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000169 char_u *sp_pattern; /* regexp to match, pattern */
170 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200171#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200172 syn_time_T sp_time;
173#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174} synpat_T;
175
176/* The sp_off_flags are computed like this:
177 * offset from the start of the matched text: (1 << SPO_XX_OFF)
178 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
179 * When both are present, only one is used.
180 */
181
182#define SPTYPE_MATCH 1 /* match keyword with this group ID */
183#define SPTYPE_START 2 /* match a regexp, start of item */
184#define SPTYPE_END 3 /* match a regexp, end of item */
185#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
186
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187
188#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
189
190#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
191
192/*
193 * Flags for b_syn_sync_flags:
194 */
195#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
196#define SF_MATCH 0x02 /* sync by matching a pattern */
197
198#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
199
Bram Moolenaar071d4272004-06-13 20:20:40 +0000200#define MAXKEYWLEN 80 /* maximum length of a keyword */
201
202/*
203 * The attributes of the syntax item that has been recognized.
204 */
205static int current_attr = 0; /* attr of current syntax word */
206#ifdef FEAT_EVAL
207static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000208static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200210#ifdef FEAT_CONCEAL
211static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200212static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200213static int current_sub_char = 0;
214#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215
Bram Moolenaar217ad922005-03-20 22:37:15 +0000216typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217{
218 char_u *scl_name; /* syntax cluster name */
219 char_u *scl_name_u; /* uppercase of scl_name */
220 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000221} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222
223/*
224 * Methods of combining two clusters
225 */
226#define CLUSTER_REPLACE 1 /* replace first list with second */
227#define CLUSTER_ADD 2 /* add second list to first */
228#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
229
Bram Moolenaar217ad922005-03-20 22:37:15 +0000230#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231
232/*
233 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200234 * 0 - 19999 normal syntax groups
235 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
236 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
237 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
238 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200240#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200241#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
242#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
243#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
244
Bram Moolenaar42431a72011-04-01 14:44:59 +0200245#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
246#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247
248/*
249 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
250 * expand_filename(). Most of the other syntax commands don't need it, so
251 * instead of passing it to them, we stow it here.
252 */
253static char_u **syn_cmdlinep;
254
255/*
256 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200257 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000258 * rules in each ":syn include"'d file.
259 */
260static int current_syn_inc_tag = 0;
261static int running_syn_inc_tag = 0;
262
263/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000264 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
265 * This avoids adding a pointer to the hashtable item.
266 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
267 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
268 * HI2KE() converts a hashitem pointer to a var pointer.
269 */
270static keyentry_T dumkey;
271#define KE2HIKEY(kp) ((kp)->keyword)
272#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
273#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
274
275/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276 * To reduce the time spent in keepend(), remember at which level in the state
277 * stack the first item with "keepend" is present. When "-1", there is no
278 * "keepend" on the stack.
279 */
280static int keepend_level = -1;
281
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200282static char msg_no_items[] = N_("No Syntax items defined for this buffer");
283
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284/*
285 * For the current state we need to remember more than just the idx.
286 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
287 * (The end positions have the column number of the next char)
288 */
289typedef struct state_item
290{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000291 int si_idx; /* index of syntax pattern or
292 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000294 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295 int si_m_lnum; /* lnum of the match */
296 int si_m_startcol; /* starting column of the match */
297 lpos_T si_m_endpos; /* just after end posn of the match */
298 lpos_T si_h_startpos; /* start position of the highlighting */
299 lpos_T si_h_endpos; /* end position of the highlighting */
300 lpos_T si_eoe_pos; /* end position of end pattern */
301 int si_end_idx; /* group ID for end pattern or zero */
302 int si_ends; /* if match ends before si_m_endpos */
303 int si_attr; /* attributes in this state */
304 long si_flags; /* HL_HAS_EOL flag in this state, and
305 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200306#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200307 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200308 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200309#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000310 short *si_cont_list; /* list of contained groups */
311 short *si_next_list; /* nextgroup IDs after this item ends */
312 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
313 * pattern */
314} stateitem_T;
315
316#define KEYWORD_IDX -1 /* value of si_idx for keywords */
317#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
318 but contained groups */
319
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200320#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100321static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200322#endif
323
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000325 * Struct to reduce the number of arguments to get_syn_options(), it's used
326 * very often.
327 */
328typedef struct
329{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000330 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000331 int keyword; /* TRUE for ":syn keyword" */
332 int *sync_idx; /* syntax item for "grouphere" argument, NULL
333 if not allowed */
334 char has_cont_list; /* TRUE if "cont_list" can be used */
335 short *cont_list; /* group IDs for "contains" argument */
336 short *cont_in_list; /* group IDs for "containedin" argument */
337 short *next_list; /* group IDs for "nextgroup" argument */
338} syn_opt_arg_T;
339
340/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 * The next possible match in the current line for any pattern is remembered,
342 * to avoid having to try for a match in each column.
343 * If next_match_idx == -1, not tried (in this line) yet.
344 * If next_match_col == MAXCOL, no match found in this line.
345 * (All end positions have the column of the char after the end)
346 */
347static int next_match_col; /* column for start of next match */
348static lpos_T next_match_m_endpos; /* position for end of next match */
349static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
350static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
351static int next_match_idx; /* index of matched item */
352static long next_match_flags; /* flags for next match */
353static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
354static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
355static int next_match_end_idx; /* ID of group for end pattn or zero */
356static reg_extmatch_T *next_match_extmatch = NULL;
357
358/*
359 * A state stack is an array of integers or stateitem_T, stored in a
360 * garray_T. A state stack is invalid if it's itemsize entry is zero.
361 */
362#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
363#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
364
365/*
366 * The current state (within the line) of the recognition engine.
367 * When current_state.ga_itemsize is 0 the current state is invalid.
368 */
369static win_T *syn_win; /* current window for highlighting */
370static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200371static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200372#ifdef FEAT_RELTIME
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200373static proftime_T *syn_tm; /* timeout limit */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +0200374#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375static linenr_T current_lnum = 0; /* lnum of current state */
376static colnr_T current_col = 0; /* column of current state */
377static int current_state_stored = 0; /* TRUE if stored current state
378 * after setting current_finished */
379static int current_finished = 0; /* current line has been finished */
380static garray_T current_state /* current stack of state_items */
381 = {0, 0, 0, 0, NULL};
382static short *current_next_list = NULL; /* when non-zero, nextgroup list */
383static int current_next_flags = 0; /* flags for current_next_list */
384static int current_line_id = 0; /* unique number for current line */
385
386#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
387
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100388static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100389static void save_chartab(char_u *chartab);
390static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100391static int syn_match_linecont(linenr_T lnum);
392static void syn_start_line(void);
393static void syn_update_ends(int startofline);
394static void syn_stack_alloc(void);
395static int syn_stack_cleanup(void);
396static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
397static synstate_T *syn_stack_find_entry(linenr_T lnum);
398static synstate_T *store_current_state(void);
399static void load_current_state(synstate_T *from);
400static void invalidate_current_state(void);
401static int syn_stack_equal(synstate_T *sp);
402static void validate_current_state(void);
403static int syn_finish_line(int syncing);
404static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
405static int did_match_already(int idx, garray_T *gap);
406static stateitem_T *push_next_match(stateitem_T *cur_si);
407static void check_state_ends(void);
408static void update_si_attr(int idx);
409static void check_keepend(void);
410static void update_si_end(stateitem_T *sip, int startcol, int force);
411static short *copy_id_list(short *list);
412static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
413static int push_current_state(int idx);
414static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200415#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100416static void syn_clear_time(syn_time_T *tt);
417static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100419static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200420#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200422#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100423static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200424static int syn_time_on = FALSE;
425# define IF_SYN_TIME(p) (p)
426#else
427# define IF_SYN_TIME(p) NULL
428typedef int syn_time_T;
429#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100431static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
432static 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);
433static void clear_syn_state(synstate_T *p);
434static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100436static void limit_pos(lpos_T *pos, lpos_T *limit);
437static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
438static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
439static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
440static char_u *syn_getcurline(void);
441static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
442static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
443static void syn_cmd_case(exarg_T *eap, int syncing);
444static void syn_cmd_spell(exarg_T *eap, int syncing);
445static void syntax_sync_clear(void);
446static void syn_remove_pattern(synblock_T *block, int idx);
447static void syn_clear_pattern(synblock_T *block, int i);
448static void syn_clear_cluster(synblock_T *block, int i);
449static void syn_cmd_clear(exarg_T *eap, int syncing);
450static void syn_cmd_conceal(exarg_T *eap, int syncing);
451static void syn_clear_one(int id, int syncing);
452static void syn_cmd_on(exarg_T *eap, int syncing);
453static void syn_cmd_enable(exarg_T *eap, int syncing);
454static void syn_cmd_reset(exarg_T *eap, int syncing);
455static void syn_cmd_manual(exarg_T *eap, int syncing);
456static void syn_cmd_off(exarg_T *eap, int syncing);
457static void syn_cmd_onoff(exarg_T *eap, char *name);
458static void syn_cmd_list(exarg_T *eap, int syncing);
459static void syn_lines_msg(void);
460static void syn_match_msg(void);
461static void syn_stack_free_block(synblock_T *block);
462static void syn_list_one(int id, int syncing, int link_only);
463static void syn_list_cluster(int id);
464static void put_id_list(char_u *name, short *list, int attr);
465static void put_pattern(char *s, int c, synpat_T *spp, int attr);
466static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
467static void syn_clear_keyword(int id, hashtab_T *ht);
468static void clear_keywtab(hashtab_T *ht);
469static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
470static char_u *get_group_name(char_u *arg, char_u **name_end);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100471static 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 +0100472static void syn_cmd_include(exarg_T *eap, int syncing);
473static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
474static void syn_cmd_keyword(exarg_T *eap, int syncing);
475static void syn_cmd_match(exarg_T *eap, int syncing);
476static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100478static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100480static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100482static void syn_cmd_cluster(exarg_T *eap, int syncing);
483static int syn_scl_name2id(char_u *name);
484static int syn_scl_namen2id(char_u *linep, int len);
485static int syn_check_cluster(char_u *pp, int len);
486static int syn_add_cluster(char_u *name);
487static void init_syn_patterns(void);
488static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
489static void syn_cmd_sync(exarg_T *eap, int syncing);
Bram Moolenaarde318c52017-01-17 16:27:10 +0100490static int get_id_list(char_u **arg, int keylen, short **list, int skip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100491static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
492static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200494#if defined(FEAT_RELTIME) || defined(PROTO)
495/*
496 * Set the timeout used for syntax highlighting.
497 * Use NULL to reset, no timeout.
498 */
499 void
500syn_set_timeout(proftime_T *tm)
501{
502 syn_tm = tm;
503}
504#endif
505
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506/*
507 * Start the syntax recognition for a line. This function is normally called
508 * from the screen updating, once for each displayed line.
509 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
510 * it. Careful: curbuf and curwin are likely to point to another buffer and
511 * window.
512 */
513 void
Bram Moolenaarf3d769a2017-09-22 13:44:56 +0200514syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515{
516 synstate_T *p;
517 synstate_T *last_valid = NULL;
518 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000519 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 linenr_T parsed_lnum;
521 linenr_T first_stored;
522 int dist;
Bram Moolenaar79518e22017-02-17 16:31:35 +0100523 static varnumber_T changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000524
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200525#ifdef FEAT_CONCEAL
526 current_sub_char = NUL;
527#endif
528
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 /*
530 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000531 * Also do this when a change was made, the current state may be invalid
532 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200534 if (syn_block != wp->w_s
535 || syn_buf != wp->w_buffer
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100536 || changedtick != CHANGEDTICK(syn_buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537 {
538 invalidate_current_state();
539 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 }
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100542 changedtick = CHANGEDTICK(syn_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 syn_win = wp;
544
545 /*
546 * Allocate syntax stack when needed.
547 */
548 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200549 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000550 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200551 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000552
553 /*
554 * If the state of the end of the previous line is useful, store it.
555 */
556 if (VALID_STATE(&current_state)
557 && current_lnum < lnum
558 && current_lnum < syn_buf->b_ml.ml_line_count)
559 {
560 (void)syn_finish_line(FALSE);
561 if (!current_state_stored)
562 {
563 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000564 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565 }
566
567 /*
568 * If the current_lnum is now the same as "lnum", keep the current
569 * state (this happens very often!). Otherwise invalidate
570 * current_state and figure it out below.
571 */
572 if (current_lnum != lnum)
573 invalidate_current_state();
574 }
575 else
576 invalidate_current_state();
577
578 /*
579 * Try to synchronize from a saved state in b_sst_array[].
580 * Only do this if lnum is not before and not to far beyond a saved state.
581 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200582 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 {
584 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200585 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586 {
587 if (p->sst_lnum > lnum)
588 break;
589 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
590 {
591 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200592 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593 last_min_valid = p;
594 }
595 }
596 if (last_min_valid != NULL)
597 load_current_state(last_min_valid);
598 }
599
600 /*
601 * If "lnum" is before or far beyond a line with a saved state, need to
602 * re-synchronize.
603 */
604 if (INVALID_STATE(&current_state))
605 {
606 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200607 if (current_lnum == 1)
608 /* First line is always valid, no matter "minlines". */
609 first_stored = 1;
610 else
611 /* Need to parse "minlines" lines before state can be considered
612 * valid to store. */
613 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000614 }
615 else
616 first_stored = current_lnum;
617
618 /*
619 * Advance from the sync point or saved state until the current line.
620 * Save some entries for syncing with later on.
621 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200622 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000623 dist = 999999;
624 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200625 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626 while (current_lnum < lnum)
627 {
628 syn_start_line();
629 (void)syn_finish_line(FALSE);
630 ++current_lnum;
631
632 /* If we parsed at least "minlines" lines or started at a valid
633 * state, the current state is considered valid. */
634 if (current_lnum >= first_stored)
635 {
636 /* Check if the saved state entry is for the current line and is
637 * equal to the current state. If so, then validate all saved
638 * states that depended on a change before the parsed line. */
639 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000640 prev = syn_stack_find_entry(current_lnum - 1);
641 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200642 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000644 sp = prev;
645 while (sp != NULL && sp->sst_lnum < current_lnum)
646 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 if (sp != NULL
648 && sp->sst_lnum == current_lnum
649 && syn_stack_equal(sp))
650 {
651 parsed_lnum = current_lnum;
652 prev = sp;
653 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
654 {
655 if (sp->sst_lnum <= lnum)
656 /* valid state before desired line, use this one */
657 prev = sp;
658 else if (sp->sst_change_lnum == 0)
659 /* past saved states depending on change, break here. */
660 break;
661 sp->sst_change_lnum = 0;
662 sp = sp->sst_next;
663 }
664 load_current_state(prev);
665 }
666 /* Store the state at this line when it's the first one, the line
667 * where we start parsing, or some distance from the previously
668 * saved state. But only when parsed at least 'minlines'. */
669 else if (prev == NULL
670 || current_lnum == lnum
671 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000672 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000673 }
674
675 /* This can take a long time: break when CTRL-C pressed. The current
676 * state will be wrong then. */
677 line_breakcheck();
678 if (got_int)
679 {
680 current_lnum = lnum;
681 break;
682 }
683 }
684
685 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686}
687
688/*
689 * We cannot simply discard growarrays full of state_items or buf_states; we
690 * have to manually release their extmatch pointers first.
691 */
692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100693clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694{
695 int i;
696 garray_T *gap;
697
698 if (p->sst_stacksize > SST_FIX_STATES)
699 {
700 gap = &(p->sst_union.sst_ga);
701 for (i = 0; i < gap->ga_len; i++)
702 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
703 ga_clear(gap);
704 }
705 else
706 {
707 for (i = 0; i < p->sst_stacksize; i++)
708 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
709 }
710}
711
712/*
713 * Cleanup the current_state stack.
714 */
715 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100716clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717{
718 int i;
719 stateitem_T *sip;
720
721 sip = (stateitem_T *)(current_state.ga_data);
722 for (i = 0; i < current_state.ga_len; i++)
723 unref_extmatch(sip[i].si_extmatch);
724 ga_clear(&current_state);
725}
726
727/*
728 * Try to find a synchronisation point for line "lnum".
729 *
730 * This sets current_lnum and the current state. One of three methods is
731 * used:
732 * 1. Search backwards for the end of a C-comment.
733 * 2. Search backwards for given sync patterns.
734 * 3. Simply start on a given number of lines above "lnum".
735 */
736 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100737syn_sync(
738 win_T *wp,
739 linenr_T start_lnum,
740 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741{
742 buf_T *curbuf_save;
743 win_T *curwin_save;
744 pos_T cursor_save;
745 int idx;
746 linenr_T lnum;
747 linenr_T end_lnum;
748 linenr_T break_lnum;
749 int had_sync_point;
750 stateitem_T *cur_si;
751 synpat_T *spp;
752 char_u *line;
753 int found_flags = 0;
754 int found_match_idx = 0;
755 linenr_T found_current_lnum = 0;
756 int found_current_col= 0;
757 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000758 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759
760 /*
761 * Clear any current state that might be hanging around.
762 */
763 invalidate_current_state();
764
765 /*
766 * Start at least "minlines" back. Default starting point for parsing is
767 * there.
768 * Start further back, to avoid that scrolling backwards will result in
769 * resyncing for every line. Now it resyncs only one out of N lines,
770 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
771 * Watch out for overflow when minlines is MAXLNUM.
772 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200773 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 start_lnum = 1;
775 else
776 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200777 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200779 else if (syn_block->b_syn_sync_minlines < 10)
780 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200782 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
783 if (syn_block->b_syn_sync_maxlines != 0
784 && lnum > syn_block->b_syn_sync_maxlines)
785 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 if (lnum >= start_lnum)
787 start_lnum = 1;
788 else
789 start_lnum -= lnum;
790 }
791 current_lnum = start_lnum;
792
793 /*
794 * 1. Search backwards for the end of a C-style comment.
795 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200796 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000797 {
798 /* Need to make syn_buf the current buffer for a moment, to be able to
799 * use find_start_comment(). */
800 curwin_save = curwin;
801 curwin = wp;
802 curbuf_save = curbuf;
803 curbuf = syn_buf;
804
805 /*
806 * Skip lines that end in a backslash.
807 */
808 for ( ; start_lnum > 1; --start_lnum)
809 {
810 line = ml_get(start_lnum - 1);
811 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
812 break;
813 }
814 current_lnum = start_lnum;
815
816 /* set cursor to start of search */
817 cursor_save = wp->w_cursor;
818 wp->w_cursor.lnum = start_lnum;
819 wp->w_cursor.col = 0;
820
821 /*
822 * If the line is inside a comment, need to find the syntax item that
823 * defines the comment.
824 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
825 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200826 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000827 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200828 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
829 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
830 == syn_block->b_syn_sync_id
831 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 {
833 validate_current_state();
834 if (push_current_state(idx) == OK)
835 update_si_attr(current_state.ga_len - 1);
836 break;
837 }
838 }
839
840 /* restore cursor and buffer */
841 wp->w_cursor = cursor_save;
842 curwin = curwin_save;
843 curbuf = curbuf_save;
844 }
845
846 /*
847 * 2. Search backwards for given sync patterns.
848 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200849 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000850 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200851 if (syn_block->b_syn_sync_maxlines != 0
852 && start_lnum > syn_block->b_syn_sync_maxlines)
853 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854 else
855 break_lnum = 0;
856
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000857 found_m_endpos.lnum = 0;
858 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000859 end_lnum = start_lnum;
860 lnum = start_lnum;
861 while (--lnum > break_lnum)
862 {
863 /* This can take a long time: break when CTRL-C pressed. */
864 line_breakcheck();
865 if (got_int)
866 {
867 invalidate_current_state();
868 current_lnum = start_lnum;
869 break;
870 }
871
872 /* Check if we have run into a valid saved state stack now. */
873 if (last_valid != NULL && lnum == last_valid->sst_lnum)
874 {
875 load_current_state(last_valid);
876 break;
877 }
878
879 /*
880 * Check if the previous line has the line-continuation pattern.
881 */
882 if (lnum > 1 && syn_match_linecont(lnum - 1))
883 continue;
884
885 /*
886 * Start with nothing on the state stack
887 */
888 validate_current_state();
889
890 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
891 {
892 syn_start_line();
893 for (;;)
894 {
895 had_sync_point = syn_finish_line(TRUE);
896 /*
897 * When a sync point has been found, remember where, and
898 * continue to look for another one, further on in the line.
899 */
900 if (had_sync_point && current_state.ga_len)
901 {
902 cur_si = &CUR_STATE(current_state.ga_len - 1);
903 if (cur_si->si_m_endpos.lnum > start_lnum)
904 {
905 /* ignore match that goes to after where started */
906 current_lnum = end_lnum;
907 break;
908 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000909 if (cur_si->si_idx < 0)
910 {
911 /* Cannot happen? */
912 found_flags = 0;
913 found_match_idx = KEYWORD_IDX;
914 }
915 else
916 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200917 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000918 found_flags = spp->sp_flags;
919 found_match_idx = spp->sp_sync_idx;
920 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921 found_current_lnum = current_lnum;
922 found_current_col = current_col;
923 found_m_endpos = cur_si->si_m_endpos;
924 /*
925 * Continue after the match (be aware of a zero-length
926 * match).
927 */
928 if (found_m_endpos.lnum > current_lnum)
929 {
930 current_lnum = found_m_endpos.lnum;
931 current_col = found_m_endpos.col;
932 if (current_lnum >= end_lnum)
933 break;
934 }
935 else if (found_m_endpos.col > current_col)
936 current_col = found_m_endpos.col;
937 else
938 ++current_col;
939
940 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000941 * an item that ends here, need to do that now. Be
942 * careful not to go past the NUL. */
943 prev_current_col = current_col;
944 if (syn_getcurline()[current_col] != NUL)
945 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000946 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000947 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000948 }
949 else
950 break;
951 }
952 }
953
954 /*
955 * If a sync point was encountered, break here.
956 */
957 if (found_flags)
958 {
959 /*
960 * Put the item that was specified by the sync point on the
961 * state stack. If there was no item specified, make the
962 * state stack empty.
963 */
964 clear_current_state();
965 if (found_match_idx >= 0
966 && push_current_state(found_match_idx) == OK)
967 update_si_attr(current_state.ga_len - 1);
968
969 /*
970 * When using "grouphere", continue from the sync point
971 * match, until the end of the line. Parsing starts at
972 * the next line.
973 * For "groupthere" the parsing starts at start_lnum.
974 */
975 if (found_flags & HL_SYNC_HERE)
976 {
977 if (current_state.ga_len)
978 {
979 cur_si = &CUR_STATE(current_state.ga_len - 1);
980 cur_si->si_h_startpos.lnum = found_current_lnum;
981 cur_si->si_h_startpos.col = found_current_col;
982 update_si_end(cur_si, (int)current_col, TRUE);
983 check_keepend();
984 }
985 current_col = found_m_endpos.col;
986 current_lnum = found_m_endpos.lnum;
987 (void)syn_finish_line(FALSE);
988 ++current_lnum;
989 }
990 else
991 current_lnum = start_lnum;
992
993 break;
994 }
995
996 end_lnum = lnum;
997 invalidate_current_state();
998 }
999
1000 /* Ran into start of the file or exceeded maximum number of lines */
1001 if (lnum <= break_lnum)
1002 {
1003 invalidate_current_state();
1004 current_lnum = break_lnum + 1;
1005 }
1006 }
1007
1008 validate_current_state();
1009}
1010
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001011 static void
1012save_chartab(char_u *chartab)
1013{
1014 if (syn_block->b_syn_isk != empty_option)
1015 {
1016 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
1017 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1018 (size_t)32);
1019 }
1020}
1021
1022 static void
1023restore_chartab(char_u *chartab)
1024{
1025 if (syn_win->w_s->b_syn_isk != empty_option)
1026 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1027}
1028
Bram Moolenaar071d4272004-06-13 20:20:40 +00001029/*
1030 * Return TRUE if the line-continuation pattern matches in line "lnum".
1031 */
1032 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001033syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034{
1035 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001036 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001037 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001038
Bram Moolenaar860cae12010-06-05 23:22:07 +02001039 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001041 /* use syntax iskeyword option */
1042 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001043 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1044 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001045 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001046 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001047 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001048 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001049 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 }
1051 return FALSE;
1052}
1053
1054/*
1055 * Prepare the current state for the start of a line.
1056 */
1057 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001058syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059{
1060 current_finished = FALSE;
1061 current_col = 0;
1062
1063 /*
1064 * Need to update the end of a start/skip/end that continues from the
1065 * previous line and regions that have "keepend".
1066 */
1067 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001068 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001070 check_state_ends();
1071 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072
1073 next_match_idx = -1;
1074 ++current_line_id;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001075#ifdef FEAT_CONCEAL
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001076 next_seqnr = 1;
Bram Moolenaarea20de82017-06-24 22:52:24 +02001077#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001078}
1079
1080/*
1081 * Check for items in the stack that need their end updated.
1082 * When "startofline" is TRUE the last item is always updated.
1083 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1084 */
1085 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001086syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087{
1088 stateitem_T *cur_si;
1089 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001090 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091
1092 if (startofline)
1093 {
1094 /* Check for a match carried over from a previous line with a
1095 * contained region. The match ends as soon as the region ends. */
1096 for (i = 0; i < current_state.ga_len; ++i)
1097 {
1098 cur_si = &CUR_STATE(i);
1099 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001100 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 == SPTYPE_MATCH
1102 && cur_si->si_m_endpos.lnum < current_lnum)
1103 {
1104 cur_si->si_flags |= HL_MATCHCONT;
1105 cur_si->si_m_endpos.lnum = 0;
1106 cur_si->si_m_endpos.col = 0;
1107 cur_si->si_h_endpos = cur_si->si_m_endpos;
1108 cur_si->si_ends = TRUE;
1109 }
1110 }
1111 }
1112
1113 /*
1114 * Need to update the end of a start/skip/end that continues from the
1115 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001116 * influence contained items. If we've just removed "extend"
1117 * (startofline == 0) then we should update ends of normal regions
1118 * contained inside "keepend" because "extend" could have extended
1119 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 * Then check for items ending in column 0.
1121 */
1122 i = current_state.ga_len - 1;
1123 if (keepend_level >= 0)
1124 for ( ; i > keepend_level; --i)
1125 if (CUR_STATE(i).si_flags & HL_EXTEND)
1126 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001127
1128 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 for ( ; i < current_state.ga_len; ++i)
1130 {
1131 cur_si = &CUR_STATE(i);
1132 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001133 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 || (i == current_state.ga_len - 1 && startofline))
1135 {
1136 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1137 cur_si->si_h_startpos.lnum = current_lnum;
1138
1139 if (!(cur_si->si_flags & HL_MATCHCONT))
1140 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001141
1142 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1143 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 }
1145 }
1146 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147}
1148
1149/****************************************
1150 * Handling of the state stack cache.
1151 */
1152
1153/*
1154 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1155 *
1156 * To speed up syntax highlighting, the state stack for the start of some
1157 * lines is cached. These entries can be used to start parsing at that point.
1158 *
1159 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1160 * valid entries. b_sst_first points to the first one, then follow sst_next.
1161 * The entries are sorted on line number. The first entry is often for line 2
1162 * (line 1 always starts with an empty stack).
1163 * There is also a list for free entries. This construction is used to avoid
1164 * having to allocate and free memory blocks too often.
1165 *
1166 * When making changes to the buffer, this is logged in b_mod_*. When calling
1167 * update_screen() to update the display, it will call
1168 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1169 * entries. The entries which are inside the changed area are removed,
1170 * because they must be recomputed. Entries below the changed have their line
1171 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1172 * set to indicate that a check must be made if the changed lines would change
1173 * the cached entry.
1174 *
1175 * When later displaying lines, an entry is stored for each line. Displayed
1176 * lines are likely to be displayed again, in which case the state at the
1177 * start of the line is needed.
1178 * For not displayed lines, an entry is stored for every so many lines. These
1179 * entries will be used e.g., when scrolling backwards. The distance between
1180 * entries depends on the number of lines in the buffer. For small buffers
1181 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1182 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1183 */
1184
Bram Moolenaar860cae12010-06-05 23:22:07 +02001185 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001186syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187{
1188 synstate_T *p;
1189
1190 if (block->b_sst_array != NULL)
1191 {
1192 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1193 clear_syn_state(p);
Bram Moolenaard23a8232018-02-10 18:45:26 +01001194 VIM_CLEAR(block->b_sst_array);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001195 block->b_sst_len = 0;
1196 }
1197}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198/*
1199 * Free b_sst_array[] for buffer "buf".
1200 * Used when syntax items changed to force resyncing everywhere.
1201 */
1202 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001203syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204{
Bram Moolenaara6c07602017-03-05 21:18:27 +01001205#ifdef FEAT_FOLDING
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 win_T *wp;
Bram Moolenaara6c07602017-03-05 21:18:27 +01001207#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208
Bram Moolenaar860cae12010-06-05 23:22:07 +02001209 syn_stack_free_block(block);
1210
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211#ifdef FEAT_FOLDING
1212 /* When using "syntax" fold method, must update all folds. */
1213 FOR_ALL_WINDOWS(wp)
1214 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001215 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216 foldUpdateAll(wp);
1217 }
1218#endif
1219}
1220
1221/*
1222 * Allocate the syntax state stack for syn_buf when needed.
1223 * If the number of entries in b_sst_array[] is much too big or a bit too
1224 * small, reallocate it.
1225 * Also used to allocate b_sst_array[] for the first time.
1226 */
1227 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001228syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229{
1230 long len;
1231 synstate_T *to, *from;
1232 synstate_T *sstp;
1233
1234 len = syn_buf->b_ml.ml_line_count / 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;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001239 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240 {
1241 /* Allocate 50% too much, to avoid reallocating too often. */
1242 len = syn_buf->b_ml.ml_line_count;
1243 len = (len + len / 2) / SST_DIST + Rows * 2;
1244 if (len < SST_MIN_ENTRIES)
1245 len = SST_MIN_ENTRIES;
1246 else if (len > SST_MAX_ENTRIES)
1247 len = SST_MAX_ENTRIES;
1248
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 {
1251 /* When shrinking the array, cleanup the existing stack.
1252 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 && syn_stack_cleanup())
1255 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1257 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 }
1259
1260 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1261 if (sstp == NULL) /* out of memory! */
1262 return;
1263
1264 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 {
1267 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 from = from->sst_next)
1270 {
1271 ++to;
1272 *to = *from;
1273 to->sst_next = to + 1;
1274 }
1275 }
1276 if (to != sstp - 1)
1277 {
1278 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001279 syn_block->b_sst_first = sstp;
1280 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 }
1282 else
1283 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 syn_block->b_sst_first = NULL;
1285 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 }
1287
1288 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001289 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 while (++to < sstp + len)
1291 to->sst_next = to + 1;
1292 (sstp + len - 1)->sst_next = NULL;
1293
Bram Moolenaar860cae12010-06-05 23:22:07 +02001294 vim_free(syn_block->b_sst_array);
1295 syn_block->b_sst_array = sstp;
1296 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 }
1298}
1299
1300/*
1301 * Check for changes in a buffer to affect stored syntax states. Uses the
1302 * b_mod_* fields.
1303 * Called from update_screen(), before screen is being updated, once for each
1304 * displayed buffer.
1305 */
1306 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001307syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 win_T *wp;
1310
1311 syn_stack_apply_changes_block(&buf->b_s, buf);
1312
1313 FOR_ALL_WINDOWS(wp)
1314 {
1315 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1316 syn_stack_apply_changes_block(wp->w_s, buf);
1317 }
1318}
1319
1320 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001321syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 synstate_T *p, *prev, *np;
1324 linenr_T n;
1325
Bram Moolenaar860cae12010-06-05 23:22:07 +02001326 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 return;
1328
1329 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001332 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 {
1334 n = p->sst_lnum + buf->b_mod_xlines;
1335 if (n <= buf->b_mod_bot)
1336 {
1337 /* this state is inside the changed area, remove it */
1338 np = p->sst_next;
1339 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341 else
1342 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 p = np;
1345 continue;
1346 }
1347 /* This state is below the changed area. Remember the line
1348 * that needs to be parsed before this entry can be made valid
1349 * again. */
1350 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1351 {
1352 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1353 p->sst_change_lnum += buf->b_mod_xlines;
1354 else
1355 p->sst_change_lnum = buf->b_mod_top;
1356 }
1357 if (p->sst_change_lnum == 0
1358 || p->sst_change_lnum < buf->b_mod_bot)
1359 p->sst_change_lnum = buf->b_mod_bot;
1360
1361 p->sst_lnum = n;
1362 }
1363 prev = p;
1364 p = p->sst_next;
1365 }
1366}
1367
1368/*
1369 * Reduce the number of entries in the state stack for syn_buf.
1370 * Returns TRUE if at least one entry was freed.
1371 */
1372 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001373syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374{
1375 synstate_T *p, *prev;
1376 disptick_T tick;
1377 int above;
1378 int dist;
1379 int retval = FALSE;
1380
Bram Moolenaar860cae12010-06-05 23:22:07 +02001381 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 return retval;
1383
1384 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001386 dist = 999999;
1387 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001388 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389
1390 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001391 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001392 * be removed. Set "above" when the "tick" for the oldest entry is above
1393 * "b_sst_lasttick" (the display tick wraps around).
1394 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001396 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001397 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1399 {
1400 if (prev->sst_lnum + dist > p->sst_lnum)
1401 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001402 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403 {
1404 if (!above || p->sst_tick < tick)
1405 tick = p->sst_tick;
1406 above = TRUE;
1407 }
1408 else if (!above && p->sst_tick < tick)
1409 tick = p->sst_tick;
1410 }
1411 }
1412
1413 /*
1414 * Go through the list to make the entries for the oldest tick at an
1415 * interval of several lines.
1416 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001417 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1419 {
1420 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1421 {
1422 /* Move this entry from used list to free list */
1423 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001424 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001425 p = prev;
1426 retval = TRUE;
1427 }
1428 }
1429 return retval;
1430}
1431
1432/*
1433 * Free the allocated memory for a syn_state item.
1434 * Move the entry into the free list.
1435 */
1436 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001437syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438{
1439 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 p->sst_next = block->b_sst_firstfree;
1441 block->b_sst_firstfree = p;
1442 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443}
1444
1445/*
1446 * Find an entry in the list of state stacks at or before "lnum".
1447 * Returns NULL when there is no entry or the first entry is after "lnum".
1448 */
1449 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001450syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451{
1452 synstate_T *p, *prev;
1453
1454 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001455 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 {
1457 if (p->sst_lnum == lnum)
1458 return p;
1459 if (p->sst_lnum > lnum)
1460 break;
1461 }
1462 return prev;
1463}
1464
1465/*
1466 * Try saving the current state in b_sst_array[].
1467 * The current state must be valid for the start of the current_lnum line!
1468 */
1469 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001470store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471{
1472 int i;
1473 synstate_T *p;
1474 bufstate_T *bp;
1475 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001476 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477
1478 /*
1479 * If the current state contains a start or end pattern that continues
1480 * from the previous line, we can't use it. Don't store it then.
1481 */
1482 for (i = current_state.ga_len - 1; i >= 0; --i)
1483 {
1484 cur_si = &CUR_STATE(i);
1485 if (cur_si->si_h_startpos.lnum >= current_lnum
1486 || cur_si->si_m_endpos.lnum >= current_lnum
1487 || cur_si->si_h_endpos.lnum >= current_lnum
1488 || (cur_si->si_end_idx
1489 && cur_si->si_eoe_pos.lnum >= current_lnum))
1490 break;
1491 }
1492 if (i >= 0)
1493 {
1494 if (sp != NULL)
1495 {
1496 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001497 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 else
1501 {
1502 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001503 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001504 if (p->sst_next == sp)
1505 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001506 if (p != NULL) /* just in case */
1507 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001509 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 sp = NULL;
1511 }
1512 }
1513 else if (sp == NULL || sp->sst_lnum != current_lnum)
1514 {
1515 /*
1516 * Add a new entry
1517 */
1518 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001519 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001520 {
1521 (void)syn_stack_cleanup();
1522 /* "sp" may have been moved to the freelist now */
1523 sp = syn_stack_find_entry(current_lnum);
1524 }
1525 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001526 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 sp = NULL;
1528 else
1529 {
1530 /* Take the first item from the free list and put it in the used
1531 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001532 p = syn_block->b_sst_firstfree;
1533 syn_block->b_sst_firstfree = p->sst_next;
1534 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001535 if (sp == NULL)
1536 {
1537 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001538 p->sst_next = syn_block->b_sst_first;
1539 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540 }
1541 else
1542 {
1543 /* insert in list after *sp */
1544 p->sst_next = sp->sst_next;
1545 sp->sst_next = p;
1546 }
1547 sp = p;
1548 sp->sst_stacksize = 0;
1549 sp->sst_lnum = current_lnum;
1550 }
1551 }
1552 if (sp != NULL)
1553 {
1554 /* When overwriting an existing state stack, clear it first */
1555 clear_syn_state(sp);
1556 sp->sst_stacksize = current_state.ga_len;
1557 if (current_state.ga_len > SST_FIX_STATES)
1558 {
1559 /* Need to clear it, might be something remaining from when the
1560 * length was less than SST_FIX_STATES. */
1561 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1562 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1563 sp->sst_stacksize = 0;
1564 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001565 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1567 }
1568 else
1569 bp = sp->sst_union.sst_stack;
1570 for (i = 0; i < sp->sst_stacksize; ++i)
1571 {
1572 bp[i].bs_idx = CUR_STATE(i).si_idx;
1573 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001574#ifdef FEAT_CONCEAL
1575 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1576 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1579 }
1580 sp->sst_next_flags = current_next_flags;
1581 sp->sst_next_list = current_next_list;
1582 sp->sst_tick = display_tick;
1583 sp->sst_change_lnum = 0;
1584 }
1585 current_state_stored = TRUE;
1586 return sp;
1587}
1588
1589/*
1590 * Copy a state stack from "from" in b_sst_array[] to current_state;
1591 */
1592 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001593load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001594{
1595 int i;
1596 bufstate_T *bp;
1597
1598 clear_current_state();
1599 validate_current_state();
1600 keepend_level = -1;
1601 if (from->sst_stacksize
1602 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1603 {
1604 if (from->sst_stacksize > SST_FIX_STATES)
1605 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1606 else
1607 bp = from->sst_union.sst_stack;
1608 for (i = 0; i < from->sst_stacksize; ++i)
1609 {
1610 CUR_STATE(i).si_idx = bp[i].bs_idx;
1611 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001612#ifdef FEAT_CONCEAL
1613 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1614 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1615#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1617 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1618 keepend_level = i;
1619 CUR_STATE(i).si_ends = FALSE;
1620 CUR_STATE(i).si_m_lnum = 0;
1621 if (CUR_STATE(i).si_idx >= 0)
1622 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001623 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 else
1625 CUR_STATE(i).si_next_list = NULL;
1626 update_si_attr(i);
1627 }
1628 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 }
1630 current_next_list = from->sst_next_list;
1631 current_next_flags = from->sst_next_flags;
1632 current_lnum = from->sst_lnum;
1633}
1634
1635/*
1636 * Compare saved state stack "*sp" with the current state.
1637 * Return TRUE when they are equal.
1638 */
1639 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001640syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001641{
1642 int i, j;
1643 bufstate_T *bp;
1644 reg_extmatch_T *six, *bsx;
1645
1646 /* First a quick check if the stacks have the same size end nextlist. */
1647 if (sp->sst_stacksize == current_state.ga_len
1648 && sp->sst_next_list == current_next_list)
1649 {
1650 /* Need to compare all states on both stacks. */
1651 if (sp->sst_stacksize > SST_FIX_STATES)
1652 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1653 else
1654 bp = sp->sst_union.sst_stack;
1655
1656 for (i = current_state.ga_len; --i >= 0; )
1657 {
1658 /* If the item has another index the state is different. */
1659 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1660 break;
1661 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1662 {
1663 /* When the extmatch pointers are different, the strings in
1664 * them can still be the same. Check if the extmatch
1665 * references are equal. */
1666 bsx = bp[i].bs_extmatch;
1667 six = CUR_STATE(i).si_extmatch;
1668 /* If one of the extmatch pointers is NULL the states are
1669 * different. */
1670 if (bsx == NULL || six == NULL)
1671 break;
1672 for (j = 0; j < NSUBEXP; ++j)
1673 {
1674 /* Check each referenced match string. They must all be
1675 * equal. */
1676 if (bsx->matches[j] != six->matches[j])
1677 {
1678 /* If the pointer is different it can still be the
1679 * same text. Compare the strings, ignore case when
1680 * the start item has the sp_ic flag set. */
1681 if (bsx->matches[j] == NULL
1682 || six->matches[j] == NULL)
1683 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001684 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685 ? MB_STRICMP(bsx->matches[j],
1686 six->matches[j]) != 0
1687 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1688 break;
1689 }
1690 }
1691 if (j != NSUBEXP)
1692 break;
1693 }
1694 }
1695 if (i < 0)
1696 return TRUE;
1697 }
1698 return FALSE;
1699}
1700
1701/*
1702 * We stop parsing syntax above line "lnum". If the stored state at or below
1703 * this line depended on a change before it, it now depends on the line below
1704 * the last parsed line.
1705 * The window looks like this:
1706 * line which changed
1707 * displayed line
1708 * displayed line
1709 * lnum -> line below window
1710 */
1711 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001712syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713{
1714 synstate_T *sp;
1715
1716 sp = syn_stack_find_entry(lnum);
1717 if (sp != NULL && sp->sst_lnum < lnum)
1718 sp = sp->sst_next;
1719
1720 if (sp != NULL && sp->sst_change_lnum != 0)
1721 sp->sst_change_lnum = lnum;
1722}
1723
1724/*
1725 * End of handling of the state stack.
1726 ****************************************/
1727
1728 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731 clear_current_state();
1732 current_state.ga_itemsize = 0; /* mark current_state invalid */
1733 current_next_list = NULL;
1734 keepend_level = -1;
1735}
1736
1737 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001738validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739{
1740 current_state.ga_itemsize = sizeof(stateitem_T);
1741 current_state.ga_growsize = 3;
1742}
1743
1744/*
1745 * Return TRUE if the syntax at start of lnum changed since last time.
1746 * This will only be called just after get_syntax_attr() for the previous
1747 * line, to check if the next line needs to be redrawn too.
1748 */
1749 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001750syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751{
1752 int retval = TRUE;
1753 synstate_T *sp;
1754
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 /*
1756 * Check the state stack when:
1757 * - lnum is just below the previously syntaxed line.
1758 * - lnum is not before the lines with saved states.
1759 * - lnum is not past the lines with saved states.
1760 * - lnum is at or before the last changed line.
1761 */
1762 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1763 {
1764 sp = syn_stack_find_entry(lnum);
1765 if (sp != NULL && sp->sst_lnum == lnum)
1766 {
1767 /*
1768 * finish the previous line (needed when not all of the line was
1769 * drawn)
1770 */
1771 (void)syn_finish_line(FALSE);
1772
1773 /*
1774 * Compare the current state with the previously saved state of
1775 * the line.
1776 */
1777 if (syn_stack_equal(sp))
1778 retval = FALSE;
1779
1780 /*
1781 * Store the current state in b_sst_array[] for later use.
1782 */
1783 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001784 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 }
1786 }
1787
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 return retval;
1789}
1790
1791/*
1792 * Finish the current line.
1793 * This doesn't return any attributes, it only gets the state at the end of
1794 * the line. It can start anywhere in the line, as long as the current state
1795 * is valid.
1796 */
1797 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001798syn_finish_line(
1799 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800{
1801 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001802 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001804 while (!current_finished)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 {
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001806 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
1807 /*
1808 * When syncing, and found some item, need to check the item.
1809 */
1810 if (syncing && current_state.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 /*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001813 * Check for match with sync item.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 */
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001815 cur_si = &CUR_STATE(current_state.ga_len - 1);
1816 if (cur_si->si_idx >= 0
1817 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
1818 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1819 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001821 /* syn_current_attr() will have skipped the check for an item
1822 * that ends here, need to do that now. Be careful not to go
1823 * past the NUL. */
1824 prev_current_col = current_col;
1825 if (syn_getcurline()[current_col] != NUL)
1826 ++current_col;
1827 check_state_ends();
1828 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 }
Bram Moolenaaraab93b12017-03-18 21:37:28 +01001830 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831 }
1832 return FALSE;
1833}
1834
1835/*
1836 * Return highlight attributes for next character.
1837 * Must first call syntax_start() once for the line.
1838 * "col" is normally 0 for the first use in a line, and increments by one each
1839 * time. It's allowed to skip characters and to stop before the end of the
1840 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001841 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1842 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 */
1844 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001845get_syntax_attr(
1846 colnr_T col,
1847 int *can_spell,
1848 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849{
1850 int attr = 0;
1851
Bram Moolenaar349955a2007-08-14 21:07:36 +00001852 if (can_spell != NULL)
1853 /* Default: Only do spelling when there is no @Spell cluster or when
1854 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001855 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1856 ? (syn_block->b_spell_cluster_id == 0)
1857 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001858
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001860 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861 return 0;
1862
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001863 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001864 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001865 {
1866 clear_current_state();
1867#ifdef FEAT_EVAL
1868 current_id = 0;
1869 current_trans_id = 0;
1870#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001871#ifdef FEAT_CONCEAL
1872 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02001873 current_seqnr = 0;
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001874#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001875 return 0;
1876 }
1877
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 /* Make sure current_state is valid */
1879 if (INVALID_STATE(&current_state))
1880 validate_current_state();
1881
1882 /*
1883 * Skip from the current column to "col", get the attributes for "col".
1884 */
1885 while (current_col <= col)
1886 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001887 attr = syn_current_attr(FALSE, TRUE, can_spell,
1888 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001889 ++current_col;
1890 }
1891
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 return attr;
1893}
1894
1895/*
1896 * Get syntax attributes for current_lnum, current_col.
1897 */
1898 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001899syn_current_attr(
1900 int syncing, /* When 1: called for syncing */
1901 int displaying, /* result will be displayed */
1902 int *can_spell, /* return: do spell checking */
1903 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001904{
1905 int syn_id;
1906 lpos_T endpos; /* was: char_u *endp; */
1907 lpos_T hl_startpos; /* was: int hl_startcol; */
1908 lpos_T hl_endpos;
1909 lpos_T eos_pos; /* end-of-start match (start region) */
1910 lpos_T eoe_pos; /* end-of-end pattern */
1911 int end_idx; /* group ID for end pattern */
1912 int idx;
1913 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001914 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001915 int startcol;
1916 int endcol;
1917 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001918 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919 short *next_list;
1920 int found_match; /* found usable match */
1921 static int try_next_column = FALSE; /* must try in next col */
1922 int do_keywords;
1923 regmmatch_T regmatch;
1924 lpos_T pos;
1925 int lc_col;
1926 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001927 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928 char_u *line; /* current line. NOTE: becomes invalid after
1929 looking for a pattern match! */
1930
1931 /* variables for zero-width matches that have a "nextgroup" argument */
1932 int keep_next_list;
1933 int zero_width_next_list = FALSE;
1934 garray_T zero_width_next_ga;
1935
1936 /*
1937 * No character, no attributes! Past end of line?
1938 * Do try matching with an empty line (could be the start of a region).
1939 */
1940 line = syn_getcurline();
1941 if (line[current_col] == NUL && current_col != 0)
1942 {
1943 /*
1944 * If we found a match after the last column, use it.
1945 */
1946 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1947 && next_match_col != MAXCOL)
1948 (void)push_next_match(NULL);
1949
1950 current_finished = TRUE;
1951 current_state_stored = FALSE;
1952 return 0;
1953 }
1954
1955 /* if the current or next character is NUL, we will finish the line now */
1956 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1957 {
1958 current_finished = TRUE;
1959 current_state_stored = FALSE;
1960 }
1961
1962 /*
1963 * When in the previous column there was a match but it could not be used
1964 * (empty match or already matched in this column) need to try again in
1965 * the next column.
1966 */
1967 if (try_next_column)
1968 {
1969 next_match_idx = -1;
1970 try_next_column = FALSE;
1971 }
1972
1973 /* Only check for keywords when not syncing and there are some. */
1974 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001975 && (syn_block->b_keywtab.ht_used > 0
1976 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977
1978 /* Init the list of zero-width matches with a nextlist. This is used to
1979 * avoid matching the same item in the same position twice. */
1980 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1981
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001982 /* use syntax iskeyword option */
1983 save_chartab(buf_chartab);
1984
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 /*
1986 * Repeat matching keywords and patterns, to find contained items at the
1987 * same column. This stops when there are no extra matches at the current
1988 * column.
1989 */
1990 do
1991 {
1992 found_match = FALSE;
1993 keep_next_list = FALSE;
1994 syn_id = 0;
1995
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001996
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 /*
1998 * 1. Check for a current state.
1999 * Only when there is no current state, or if the current state may
2000 * contain other things, we need to check for keywords and patterns.
2001 * Always need to check for contained items if some item has the
2002 * "containedin" argument (takes extra time!).
2003 */
2004 if (current_state.ga_len)
2005 cur_si = &CUR_STATE(current_state.ga_len - 1);
2006 else
2007 cur_si = NULL;
2008
Bram Moolenaar860cae12010-06-05 23:22:07 +02002009 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 || cur_si->si_cont_list != NULL)
2011 {
2012 /*
2013 * 2. Check for keywords, if on a keyword char after a non-keyword
2014 * char. Don't do this when syncing.
2015 */
2016 if (do_keywords)
2017 {
2018 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002019 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002021 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022#ifdef FEAT_MBYTE
2023 - (has_mbyte
2024 ? (*mb_head_off)(line, line + current_col - 1)
2025 : 0)
2026#endif
2027 , syn_buf)))
2028 {
2029 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002030 &endcol, &flags, &next_list, cur_si,
2031 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002032 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 {
2034 if (push_current_state(KEYWORD_IDX) == OK)
2035 {
2036 cur_si = &CUR_STATE(current_state.ga_len - 1);
2037 cur_si->si_m_startcol = current_col;
2038 cur_si->si_h_startpos.lnum = current_lnum;
2039 cur_si->si_h_startpos.col = 0; /* starts right away */
2040 cur_si->si_m_endpos.lnum = current_lnum;
2041 cur_si->si_m_endpos.col = endcol;
2042 cur_si->si_h_endpos.lnum = current_lnum;
2043 cur_si->si_h_endpos.col = endcol;
2044 cur_si->si_ends = TRUE;
2045 cur_si->si_end_idx = 0;
2046 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002047#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002048 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002049 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002050 if (current_state.ga_len > 1)
2051 cur_si->si_flags |=
2052 CUR_STATE(current_state.ga_len - 2).si_flags
2053 & HL_CONCEAL;
2054#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002055 cur_si->si_id = syn_id;
2056 cur_si->si_trans_id = syn_id;
2057 if (flags & HL_TRANSP)
2058 {
2059 if (current_state.ga_len < 2)
2060 {
2061 cur_si->si_attr = 0;
2062 cur_si->si_trans_id = 0;
2063 }
2064 else
2065 {
2066 cur_si->si_attr = CUR_STATE(
2067 current_state.ga_len - 2).si_attr;
2068 cur_si->si_trans_id = CUR_STATE(
2069 current_state.ga_len - 2).si_trans_id;
2070 }
2071 }
2072 else
2073 cur_si->si_attr = syn_id2attr(syn_id);
2074 cur_si->si_cont_list = NULL;
2075 cur_si->si_next_list = next_list;
2076 check_keepend();
2077 }
2078 else
2079 vim_free(next_list);
2080 }
2081 }
2082 }
2083
2084 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002085 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002086 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002087 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002088 {
2089 /*
2090 * If we didn't check for a match yet, or we are past it, check
2091 * for any match with a pattern.
2092 */
2093 if (next_match_idx < 0 || next_match_col < (int)current_col)
2094 {
2095 /*
2096 * Check all relevant patterns for a match at this
2097 * position. This is complicated, because matching with a
2098 * pattern takes quite a bit of time, thus we want to
2099 * avoid doing it when it's not needed.
2100 */
2101 next_match_idx = 0; /* no match in this line yet */
2102 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002103 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002105 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 if ( spp->sp_syncing == syncing
2107 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2108 && (spp->sp_type == SPTYPE_MATCH
2109 || spp->sp_type == SPTYPE_START)
2110 && (current_next_list != NULL
2111 ? in_id_list(NULL, current_next_list,
2112 &spp->sp_syn, 0)
2113 : (cur_si == NULL
2114 ? !(spp->sp_flags & HL_CONTAINED)
2115 : in_id_list(cur_si,
2116 cur_si->si_cont_list, &spp->sp_syn,
2117 spp->sp_flags & HL_CONTAINED))))
2118 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002119 int r;
2120
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121 /* If we already tried matching in this line, and
2122 * there isn't a match before next_match_col, skip
2123 * this item. */
2124 if (spp->sp_line_id == current_line_id
2125 && spp->sp_startcol >= next_match_col)
2126 continue;
2127 spp->sp_line_id = current_line_id;
2128
2129 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2130 if (lc_col < 0)
2131 lc_col = 0;
2132
2133 regmatch.rmm_ic = spp->sp_ic;
2134 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002135 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002136 current_lnum,
2137 (colnr_T)lc_col,
Bram Moolenaard23a8232018-02-10 18:45:26 +01002138 IF_SYN_TIME(&spp->sp_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002139 spp->sp_prog = regmatch.regprog;
2140 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 {
2142 /* no match in this line, try another one */
2143 spp->sp_startcol = MAXCOL;
2144 continue;
2145 }
2146
2147 /*
2148 * Compute the first column of the match.
2149 */
2150 syn_add_start_off(&pos, &regmatch,
2151 spp, SPO_MS_OFF, -1);
2152 if (pos.lnum > current_lnum)
2153 {
2154 /* must have used end of match in a next line,
2155 * we can't handle that */
2156 spp->sp_startcol = MAXCOL;
2157 continue;
2158 }
2159 startcol = pos.col;
2160
2161 /* remember the next column where this pattern
2162 * matches in the current line */
2163 spp->sp_startcol = startcol;
2164
2165 /*
2166 * If a previously found match starts at a lower
2167 * column number, don't use this one.
2168 */
2169 if (startcol >= next_match_col)
2170 continue;
2171
2172 /*
2173 * If we matched this pattern at this position
2174 * before, skip it. Must retry in the next
2175 * column, because it may match from there.
2176 */
2177 if (did_match_already(idx, &zero_width_next_ga))
2178 {
2179 try_next_column = TRUE;
2180 continue;
2181 }
2182
2183 endpos.lnum = regmatch.endpos[0].lnum;
2184 endpos.col = regmatch.endpos[0].col;
2185
2186 /* Compute the highlight start. */
2187 syn_add_start_off(&hl_startpos, &regmatch,
2188 spp, SPO_HS_OFF, -1);
2189
2190 /* Compute the region start. */
2191 /* Default is to use the end of the match. */
2192 syn_add_end_off(&eos_pos, &regmatch,
2193 spp, SPO_RS_OFF, 0);
2194
2195 /*
2196 * Grab the external submatches before they get
2197 * overwritten. Reference count doesn't change.
2198 */
2199 unref_extmatch(cur_extmatch);
2200 cur_extmatch = re_extmatch_out;
2201 re_extmatch_out = NULL;
2202
2203 flags = 0;
2204 eoe_pos.lnum = 0; /* avoid warning */
2205 eoe_pos.col = 0;
2206 end_idx = 0;
2207 hl_endpos.lnum = 0;
2208
2209 /*
2210 * For a "oneline" the end must be found in the
2211 * same line too. Search for it after the end of
2212 * the match with the start pattern. Set the
2213 * resulting end positions at the same time.
2214 */
2215 if (spp->sp_type == SPTYPE_START
2216 && (spp->sp_flags & HL_ONELINE))
2217 {
2218 lpos_T startpos;
2219
2220 startpos = endpos;
2221 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2222 &flags, &eoe_pos, &end_idx, cur_extmatch);
2223 if (endpos.lnum == 0)
2224 continue; /* not found */
2225 }
2226
2227 /*
2228 * For a "match" the size must be > 0 after the
2229 * end offset needs has been added. Except when
2230 * syncing.
2231 */
2232 else if (spp->sp_type == SPTYPE_MATCH)
2233 {
2234 syn_add_end_off(&hl_endpos, &regmatch, spp,
2235 SPO_HE_OFF, 0);
2236 syn_add_end_off(&endpos, &regmatch, spp,
2237 SPO_ME_OFF, 0);
2238 if (endpos.lnum == current_lnum
2239 && (int)endpos.col + syncing < startcol)
2240 {
2241 /*
2242 * If an empty string is matched, may need
2243 * to try matching again at next column.
2244 */
2245 if (regmatch.startpos[0].col
2246 == regmatch.endpos[0].col)
2247 try_next_column = TRUE;
2248 continue;
2249 }
2250 }
2251
2252 /*
2253 * keep the best match so far in next_match_*
2254 */
2255 /* Highlighting must start after startpos and end
2256 * before endpos. */
2257 if (hl_startpos.lnum == current_lnum
2258 && (int)hl_startpos.col < startcol)
2259 hl_startpos.col = startcol;
2260 limit_pos_zero(&hl_endpos, &endpos);
2261
2262 next_match_idx = idx;
2263 next_match_col = startcol;
2264 next_match_m_endpos = endpos;
2265 next_match_h_endpos = hl_endpos;
2266 next_match_h_startpos = hl_startpos;
2267 next_match_flags = flags;
2268 next_match_eos_pos = eos_pos;
2269 next_match_eoe_pos = eoe_pos;
2270 next_match_end_idx = end_idx;
2271 unref_extmatch(next_match_extmatch);
2272 next_match_extmatch = cur_extmatch;
2273 cur_extmatch = NULL;
2274 }
2275 }
2276 }
2277
2278 /*
2279 * If we found a match at the current column, use it.
2280 */
2281 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2282 {
2283 synpat_T *lspp;
2284
2285 /* When a zero-width item matched which has a nextgroup,
2286 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002287 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 if (next_match_m_endpos.lnum == current_lnum
2289 && next_match_m_endpos.col == current_col
2290 && lspp->sp_next_list != NULL)
2291 {
2292 current_next_list = lspp->sp_next_list;
2293 current_next_flags = lspp->sp_flags;
2294 keep_next_list = TRUE;
2295 zero_width_next_list = TRUE;
2296
2297 /* Add the index to a list, so that we can check
2298 * later that we don't match it again (and cause an
2299 * endless loop). */
2300 if (ga_grow(&zero_width_next_ga, 1) == OK)
2301 {
2302 ((int *)(zero_width_next_ga.ga_data))
2303 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 }
2305 next_match_idx = -1;
2306 }
2307 else
2308 cur_si = push_next_match(cur_si);
2309 found_match = TRUE;
2310 }
2311 }
2312 }
2313
2314 /*
2315 * Handle searching for nextgroup match.
2316 */
2317 if (current_next_list != NULL && !keep_next_list)
2318 {
2319 /*
2320 * If a nextgroup was not found, continue looking for one if:
2321 * - this is an empty line and the "skipempty" option was given
2322 * - we are on white space and the "skipwhite" option was given
2323 */
2324 if (!found_match)
2325 {
2326 line = syn_getcurline();
2327 if (((current_next_flags & HL_SKIPWHITE)
Bram Moolenaar1c465442017-03-12 20:10:05 +01002328 && VIM_ISWHITE(line[current_col]))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 || ((current_next_flags & HL_SKIPEMPTY)
2330 && *line == NUL))
2331 break;
2332 }
2333
2334 /*
2335 * If a nextgroup was found: Use it, and continue looking for
2336 * contained matches.
2337 * If a nextgroup was not found: Continue looking for a normal
2338 * match.
2339 * When did set current_next_list for a zero-width item and no
2340 * match was found don't loop (would get stuck).
2341 */
2342 current_next_list = NULL;
2343 next_match_idx = -1;
2344 if (!zero_width_next_list)
2345 found_match = TRUE;
2346 }
2347
2348 } while (found_match);
2349
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002350 restore_chartab(buf_chartab);
2351
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 /*
2353 * Use attributes from the current state, if within its highlighting.
2354 * If not, use attributes from the current-but-one state, etc.
2355 */
2356 current_attr = 0;
2357#ifdef FEAT_EVAL
2358 current_id = 0;
2359 current_trans_id = 0;
2360#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002361#ifdef FEAT_CONCEAL
2362 current_flags = 0;
Bram Moolenaarcc0750d2017-06-24 22:29:24 +02002363 current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002364#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 if (cur_si != NULL)
2366 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002367#ifndef FEAT_EVAL
2368 int current_trans_id = 0;
2369#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002370 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2371 {
2372 sip = &CUR_STATE(idx);
2373 if ((current_lnum > sip->si_h_startpos.lnum
2374 || (current_lnum == sip->si_h_startpos.lnum
2375 && current_col >= sip->si_h_startpos.col))
2376 && (sip->si_h_endpos.lnum == 0
2377 || current_lnum < sip->si_h_endpos.lnum
2378 || (current_lnum == sip->si_h_endpos.lnum
2379 && current_col < sip->si_h_endpos.col)))
2380 {
2381 current_attr = sip->si_attr;
2382#ifdef FEAT_EVAL
2383 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002386#ifdef FEAT_CONCEAL
2387 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002388 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002389 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002390#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391 break;
2392 }
2393 }
2394
Bram Moolenaar217ad922005-03-20 22:37:15 +00002395 if (can_spell != NULL)
2396 {
2397 struct sp_syn sps;
2398
2399 /*
2400 * set "can_spell" to TRUE if spell checking is supposed to be
2401 * done in the current item.
2402 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002403 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002404 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002405 /* There is no @Spell cluster: Do spelling for items without
2406 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002407 if (syn_block->b_nospell_cluster_id == 0
2408 || current_trans_id == 0)
2409 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002410 else
2411 {
2412 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002413 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002414 sps.cont_in_list = NULL;
2415 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2416 }
2417 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002418 else
2419 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002420 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002421 * the @Spell cluster. But not when @NoSpell is also there.
2422 * At the toplevel only spell check when ":syn spell toplevel"
2423 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002424 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002425 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002426 else
2427 {
2428 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002429 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002430 sps.cont_in_list = NULL;
2431 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2432
Bram Moolenaar860cae12010-06-05 23:22:07 +02002433 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002434 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002435 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002436 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2437 *can_spell = FALSE;
2438 }
2439 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002440 }
2441 }
2442
2443
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 /*
2445 * Check for end of current state (and the states before it) at the
2446 * next column. Don't do this for syncing, because we would miss a
2447 * single character match.
2448 * First check if the current state ends at the current column. It
2449 * may be for an empty match and a containing item might end in the
2450 * current column.
2451 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002452 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002453 {
2454 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002455 if (current_state.ga_len > 0
2456 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 {
2458 ++current_col;
2459 check_state_ends();
2460 --current_col;
2461 }
2462 }
2463 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002464 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002465 /* Default: Only do spelling when there is no @Spell cluster or when
2466 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002467 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2468 ? (syn_block->b_spell_cluster_id == 0)
2469 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002471 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 if (current_next_list != NULL
2473 && syn_getcurline()[current_col + 1] == NUL
2474 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2475 current_next_list = NULL;
2476
2477 if (zero_width_next_ga.ga_len > 0)
2478 ga_clear(&zero_width_next_ga);
2479
2480 /* No longer need external matches. But keep next_match_extmatch. */
2481 unref_extmatch(re_extmatch_out);
2482 re_extmatch_out = NULL;
2483 unref_extmatch(cur_extmatch);
2484
2485 return current_attr;
2486}
2487
2488
2489/*
2490 * Check if we already matched pattern "idx" at the current column.
2491 */
2492 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002493did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494{
2495 int i;
2496
2497 for (i = current_state.ga_len; --i >= 0; )
2498 if (CUR_STATE(i).si_m_startcol == (int)current_col
2499 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2500 && CUR_STATE(i).si_idx == idx)
2501 return TRUE;
2502
2503 /* Zero-width matches with a nextgroup argument are not put on the syntax
2504 * stack, and can only be matched once anyway. */
2505 for (i = gap->ga_len; --i >= 0; )
2506 if (((int *)(gap->ga_data))[i] == idx)
2507 return TRUE;
2508
2509 return FALSE;
2510}
2511
2512/*
2513 * Push the next match onto the stack.
2514 */
2515 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002516push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002517{
2518 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002519#ifdef FEAT_CONCEAL
2520 int save_flags;
2521#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522
Bram Moolenaar860cae12010-06-05 23:22:07 +02002523 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002524
2525 /*
2526 * Push the item in current_state stack;
2527 */
2528 if (push_current_state(next_match_idx) == OK)
2529 {
2530 /*
2531 * If it's a start-skip-end type that crosses lines, figure out how
2532 * much it continues in this line. Otherwise just fill in the length.
2533 */
2534 cur_si = &CUR_STATE(current_state.ga_len - 1);
2535 cur_si->si_h_startpos = next_match_h_startpos;
2536 cur_si->si_m_startcol = current_col;
2537 cur_si->si_m_lnum = current_lnum;
2538 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002539#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002540 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002541 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002542 if (current_state.ga_len > 1)
2543 cur_si->si_flags |=
2544 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2545#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002546 cur_si->si_next_list = spp->sp_next_list;
2547 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2548 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2549 {
2550 /* Try to find the end pattern in the current line */
2551 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2552 check_keepend();
2553 }
2554 else
2555 {
2556 cur_si->si_m_endpos = next_match_m_endpos;
2557 cur_si->si_h_endpos = next_match_h_endpos;
2558 cur_si->si_ends = TRUE;
2559 cur_si->si_flags |= next_match_flags;
2560 cur_si->si_eoe_pos = next_match_eoe_pos;
2561 cur_si->si_end_idx = next_match_end_idx;
2562 }
2563 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2564 keepend_level = current_state.ga_len - 1;
2565 check_keepend();
2566 update_si_attr(current_state.ga_len - 1);
2567
Bram Moolenaar860cae12010-06-05 23:22:07 +02002568#ifdef FEAT_CONCEAL
2569 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2570#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571 /*
2572 * If the start pattern has another highlight group, push another item
2573 * on the stack for the start pattern.
2574 */
2575 if ( spp->sp_type == SPTYPE_START
2576 && spp->sp_syn_match_id != 0
2577 && push_current_state(next_match_idx) == OK)
2578 {
2579 cur_si = &CUR_STATE(current_state.ga_len - 1);
2580 cur_si->si_h_startpos = next_match_h_startpos;
2581 cur_si->si_m_startcol = current_col;
2582 cur_si->si_m_lnum = current_lnum;
2583 cur_si->si_m_endpos = next_match_eos_pos;
2584 cur_si->si_h_endpos = next_match_eos_pos;
2585 cur_si->si_ends = TRUE;
2586 cur_si->si_end_idx = 0;
2587 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002588#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002589 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002590 cur_si->si_flags |= save_flags;
2591 if (cur_si->si_flags & HL_CONCEALENDS)
2592 cur_si->si_flags |= HL_CONCEAL;
2593#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594 cur_si->si_next_list = NULL;
2595 check_keepend();
2596 update_si_attr(current_state.ga_len - 1);
2597 }
2598 }
2599
2600 next_match_idx = -1; /* try other match next time */
2601
2602 return cur_si;
2603}
2604
2605/*
2606 * Check for end of current state (and the states before it).
2607 */
2608 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002609check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002610{
2611 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002612 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613
2614 cur_si = &CUR_STATE(current_state.ga_len - 1);
2615 for (;;)
2616 {
2617 if (cur_si->si_ends
2618 && (cur_si->si_m_endpos.lnum < current_lnum
2619 || (cur_si->si_m_endpos.lnum == current_lnum
2620 && cur_si->si_m_endpos.col <= current_col)))
2621 {
2622 /*
2623 * If there is an end pattern group ID, highlight the end pattern
2624 * now. No need to pop the current item from the stack.
2625 * Only do this if the end pattern continues beyond the current
2626 * position.
2627 */
2628 if (cur_si->si_end_idx
2629 && (cur_si->si_eoe_pos.lnum > current_lnum
2630 || (cur_si->si_eoe_pos.lnum == current_lnum
2631 && cur_si->si_eoe_pos.col > current_col)))
2632 {
2633 cur_si->si_idx = cur_si->si_end_idx;
2634 cur_si->si_end_idx = 0;
2635 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2636 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2637 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002638#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002639 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002640 if (cur_si->si_flags & HL_CONCEALENDS)
2641 cur_si->si_flags |= HL_CONCEAL;
2642#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002643 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002644
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002645 /* nextgroup= should not match in the end pattern */
2646 current_next_list = NULL;
2647
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002648 /* what matches next may be different now, clear it */
2649 next_match_idx = 0;
2650 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651 break;
2652 }
2653 else
2654 {
2655 /* handle next_list, unless at end of line and no "skipnl" or
2656 * "skipempty" */
2657 current_next_list = cur_si->si_next_list;
2658 current_next_flags = cur_si->si_flags;
2659 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2660 && syn_getcurline()[current_col] == NUL)
2661 current_next_list = NULL;
2662
2663 /* When the ended item has "extend", another item with
2664 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002665 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666
2667 pop_current_state();
2668
2669 if (current_state.ga_len == 0)
2670 break;
2671
Bram Moolenaar81993f42008-01-11 20:27:45 +00002672 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 {
2674 syn_update_ends(FALSE);
2675 if (current_state.ga_len == 0)
2676 break;
2677 }
2678
2679 cur_si = &CUR_STATE(current_state.ga_len - 1);
2680
2681 /*
2682 * Only for a region the search for the end continues after
2683 * the end of the contained item. If the contained match
2684 * included the end-of-line, break here, the region continues.
2685 * Don't do this when:
2686 * - "keepend" is used for the contained item
2687 * - not at the end of the line (could be end="x$"me=e-1).
2688 * - "excludenl" is used (HL_HAS_EOL won't be set)
2689 */
2690 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002691 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 == SPTYPE_START
2693 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2694 {
2695 update_si_end(cur_si, (int)current_col, TRUE);
2696 check_keepend();
2697 if ((current_next_flags & HL_HAS_EOL)
2698 && keepend_level < 0
2699 && syn_getcurline()[current_col] == NUL)
2700 break;
2701 }
2702 }
2703 }
2704 else
2705 break;
2706 }
2707}
2708
2709/*
2710 * Update an entry in the current_state stack for a match or region. This
2711 * fills in si_attr, si_next_list and si_cont_list.
2712 */
2713 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002714update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715{
2716 stateitem_T *sip = &CUR_STATE(idx);
2717 synpat_T *spp;
2718
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002719 /* This should not happen... */
2720 if (sip->si_idx < 0)
2721 return;
2722
Bram Moolenaar860cae12010-06-05 23:22:07 +02002723 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002724 if (sip->si_flags & HL_MATCH)
2725 sip->si_id = spp->sp_syn_match_id;
2726 else
2727 sip->si_id = spp->sp_syn.id;
2728 sip->si_attr = syn_id2attr(sip->si_id);
2729 sip->si_trans_id = sip->si_id;
2730 if (sip->si_flags & HL_MATCH)
2731 sip->si_cont_list = NULL;
2732 else
2733 sip->si_cont_list = spp->sp_cont_list;
2734
2735 /*
2736 * For transparent items, take attr from outer item.
2737 * Also take cont_list, if there is none.
2738 * Don't do this for the matchgroup of a start or end pattern.
2739 */
2740 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2741 {
2742 if (idx == 0)
2743 {
2744 sip->si_attr = 0;
2745 sip->si_trans_id = 0;
2746 if (sip->si_cont_list == NULL)
2747 sip->si_cont_list = ID_LIST_ALL;
2748 }
2749 else
2750 {
2751 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2752 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002753 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2754 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002755 if (sip->si_cont_list == NULL)
2756 {
2757 sip->si_flags |= HL_TRANS_CONT;
2758 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2759 }
2760 }
2761 }
2762}
2763
2764/*
2765 * Check the current stack for patterns with "keepend" flag.
2766 * Propagate the match-end to contained items, until a "skipend" item is found.
2767 */
2768 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002769check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770{
2771 int i;
2772 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002773 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002774 stateitem_T *sip;
2775
2776 /*
2777 * This check can consume a lot of time; only do it from the level where
2778 * there really is a keepend.
2779 */
2780 if (keepend_level < 0)
2781 return;
2782
2783 /*
2784 * Find the last index of an "extend" item. "keepend" items before that
2785 * won't do anything. If there is no "extend" item "i" will be
2786 * "keepend_level" and all "keepend" items will work normally.
2787 */
2788 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2789 if (CUR_STATE(i).si_flags & HL_EXTEND)
2790 break;
2791
2792 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002793 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002794 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002795 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796 for ( ; i < current_state.ga_len; ++i)
2797 {
2798 sip = &CUR_STATE(i);
2799 if (maxpos.lnum != 0)
2800 {
2801 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002802 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002803 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2804 sip->si_ends = TRUE;
2805 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002806 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2807 {
2808 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809 || maxpos.lnum > sip->si_m_endpos.lnum
2810 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002811 && maxpos.col > sip->si_m_endpos.col))
2812 maxpos = sip->si_m_endpos;
2813 if (maxpos_h.lnum == 0
2814 || maxpos_h.lnum > sip->si_h_endpos.lnum
2815 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2816 && maxpos_h.col > sip->si_h_endpos.col))
2817 maxpos_h = sip->si_h_endpos;
2818 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002819 }
2820}
2821
2822/*
2823 * Update an entry in the current_state stack for a start-skip-end pattern.
2824 * This finds the end of the current item, if it's in the current line.
2825 *
2826 * Return the flags for the matched END.
2827 */
2828 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002829update_si_end(
2830 stateitem_T *sip,
2831 int startcol, /* where to start searching for the end */
2832 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002833{
2834 lpos_T startpos;
2835 lpos_T endpos;
2836 lpos_T hl_endpos;
2837 lpos_T end_endpos;
2838 int end_idx;
2839
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002840 /* return quickly for a keyword */
2841 if (sip->si_idx < 0)
2842 return;
2843
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 /* Don't update when it's already done. Can be a match of an end pattern
2845 * that started in a previous line. Watch out: can also be a "keepend"
2846 * from a containing item. */
2847 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2848 return;
2849
2850 /*
2851 * We need to find the end of the region. It may continue in the next
2852 * line.
2853 */
2854 end_idx = 0;
2855 startpos.lnum = current_lnum;
2856 startpos.col = startcol;
2857 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2858 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2859
2860 if (endpos.lnum == 0)
2861 {
2862 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002863 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002864 {
2865 /* a "oneline" never continues in the next line */
2866 sip->si_ends = TRUE;
2867 sip->si_m_endpos.lnum = current_lnum;
2868 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2869 }
2870 else
2871 {
2872 /* continues in the next line */
2873 sip->si_ends = FALSE;
2874 sip->si_m_endpos.lnum = 0;
2875 }
2876 sip->si_h_endpos = sip->si_m_endpos;
2877 }
2878 else
2879 {
2880 /* match within this line */
2881 sip->si_m_endpos = endpos;
2882 sip->si_h_endpos = hl_endpos;
2883 sip->si_eoe_pos = end_endpos;
2884 sip->si_ends = TRUE;
2885 sip->si_end_idx = end_idx;
2886 }
2887}
2888
2889/*
2890 * Add a new state to the current state stack.
2891 * It is cleared and the index set to "idx".
2892 * Return FAIL if it's not possible (out of memory).
2893 */
2894 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002895push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896{
2897 if (ga_grow(&current_state, 1) == FAIL)
2898 return FAIL;
2899 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2900 CUR_STATE(current_state.ga_len).si_idx = idx;
2901 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902 return OK;
2903}
2904
2905/*
2906 * Remove a state from the current_state stack.
2907 */
2908 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002909pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002910{
2911 if (current_state.ga_len)
2912 {
2913 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2914 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002915 }
2916 /* after the end of a pattern, try matching a keyword or pattern */
2917 next_match_idx = -1;
2918
2919 /* if first state with "keepend" is popped, reset keepend_level */
2920 if (keepend_level >= current_state.ga_len)
2921 keepend_level = -1;
2922}
2923
2924/*
2925 * Find the end of a start/skip/end syntax region after "startpos".
2926 * Only checks one line.
2927 * Also handles a match item that continued from a previous line.
2928 * If not found, the syntax item continues in the next line. m_endpos->lnum
2929 * will be 0.
2930 * If found, the end of the region and the end of the highlighting is
2931 * computed.
2932 */
2933 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002934find_endpos(
2935 int idx, /* index of the pattern */
2936 lpos_T *startpos, /* where to start looking for an END match */
2937 lpos_T *m_endpos, /* return: end of match */
2938 lpos_T *hl_endpos, /* return: end of highlighting */
2939 long *flagsp, /* return: flags of matching END */
2940 lpos_T *end_endpos, /* return: end of end pattern match */
2941 int *end_idx, /* return: group ID for end pat. match, or 0 */
2942 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943{
2944 colnr_T matchcol;
2945 synpat_T *spp, *spp_skip;
2946 int start_idx;
2947 int best_idx;
2948 regmmatch_T regmatch;
2949 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2950 lpos_T pos;
2951 char_u *line;
2952 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002953 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002955 /* just in case we are invoked for a keyword */
2956 if (idx < 0)
2957 return;
2958
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 /*
2960 * Check for being called with a START pattern.
2961 * Can happen with a match that continues to the next line, because it
2962 * contained a region.
2963 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002964 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002965 if (spp->sp_type != SPTYPE_START)
2966 {
2967 *hl_endpos = *startpos;
2968 return;
2969 }
2970
2971 /*
2972 * Find the SKIP or first END pattern after the last START pattern.
2973 */
2974 for (;;)
2975 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002976 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002977 if (spp->sp_type != SPTYPE_START)
2978 break;
2979 ++idx;
2980 }
2981
2982 /*
2983 * Lookup the SKIP pattern (if present)
2984 */
2985 if (spp->sp_type == SPTYPE_SKIP)
2986 {
2987 spp_skip = spp;
2988 ++idx;
2989 }
2990 else
2991 spp_skip = NULL;
2992
2993 /* Setup external matches for syn_regexec(). */
2994 unref_extmatch(re_extmatch_in);
2995 re_extmatch_in = ref_extmatch(start_ext);
2996
2997 matchcol = startpos->col; /* start looking for a match at sstart */
2998 start_idx = idx; /* remember the first END pattern. */
2999 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003000
3001 /* use syntax iskeyword option */
3002 save_chartab(buf_chartab);
3003
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004 for (;;)
3005 {
3006 /*
3007 * Find end pattern that matches first after "matchcol".
3008 */
3009 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003010 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003011 {
3012 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003013 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014
Bram Moolenaar860cae12010-06-05 23:22:07 +02003015 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3017 break;
3018 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3019 if (lc_col < 0)
3020 lc_col = 0;
3021
3022 regmatch.rmm_ic = spp->sp_ic;
3023 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003024 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3025 IF_SYN_TIME(&spp->sp_time));
3026 spp->sp_prog = regmatch.regprog;
3027 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028 {
3029 if (best_idx == -1 || regmatch.startpos[0].col
3030 < best_regmatch.startpos[0].col)
3031 {
3032 best_idx = idx;
3033 best_regmatch.startpos[0] = regmatch.startpos[0];
3034 best_regmatch.endpos[0] = regmatch.endpos[0];
3035 }
3036 }
3037 }
3038
3039 /*
3040 * If all end patterns have been tried, and there is no match, the
3041 * item continues until end-of-line.
3042 */
3043 if (best_idx == -1)
3044 break;
3045
3046 /*
3047 * If the skip pattern matches before the end pattern,
3048 * continue searching after the skip pattern.
3049 */
3050 if (spp_skip != NULL)
3051 {
3052 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003053 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054
3055 if (lc_col < 0)
3056 lc_col = 0;
3057 regmatch.rmm_ic = spp_skip->sp_ic;
3058 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003059 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3060 IF_SYN_TIME(&spp_skip->sp_time));
3061 spp_skip->sp_prog = regmatch.regprog;
3062 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063 <= best_regmatch.startpos[0].col)
3064 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003065 int line_len;
3066
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 /* Add offset to skip pattern match */
3068 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3069
3070 /* If the skip pattern goes on to the next line, there is no
3071 * match with an end pattern in this line. */
3072 if (pos.lnum > startpos->lnum)
3073 break;
3074
3075 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003076 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077
3078 /* take care of an empty match or negative offset */
3079 if (pos.col <= matchcol)
3080 ++matchcol;
3081 else if (pos.col <= regmatch.endpos[0].col)
3082 matchcol = pos.col;
3083 else
3084 /* Be careful not to jump over the NUL at the end-of-line */
3085 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003086 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087 ++matchcol)
3088 ;
3089
3090 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003091 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092 break;
3093
3094 continue; /* start with first end pattern again */
3095 }
3096 }
3097
3098 /*
3099 * Match from start pattern to end pattern.
3100 * Correct for match and highlight offset of end pattern.
3101 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003102 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003103 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3104 /* can't end before the start */
3105 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3106 m_endpos->col = startpos->col;
3107
3108 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3109 /* can't end before the start */
3110 if (end_endpos->lnum == startpos->lnum
3111 && end_endpos->col < startpos->col)
3112 end_endpos->col = startpos->col;
3113 /* can't end after the match */
3114 limit_pos(end_endpos, m_endpos);
3115
3116 /*
3117 * If the end group is highlighted differently, adjust the pointers.
3118 */
3119 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3120 {
3121 *end_idx = best_idx;
3122 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3123 {
3124 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3125 hl_endpos->col = best_regmatch.endpos[0].col;
3126 }
3127 else
3128 {
3129 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3130 hl_endpos->col = best_regmatch.startpos[0].col;
3131 }
3132 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3133
3134 /* can't end before the start */
3135 if (hl_endpos->lnum == startpos->lnum
3136 && hl_endpos->col < startpos->col)
3137 hl_endpos->col = startpos->col;
3138 limit_pos(hl_endpos, m_endpos);
3139
3140 /* now the match ends where the highlighting ends, it is turned
3141 * into the matchgroup for the end */
3142 *m_endpos = *hl_endpos;
3143 }
3144 else
3145 {
3146 *end_idx = 0;
3147 *hl_endpos = *end_endpos;
3148 }
3149
3150 *flagsp = spp->sp_flags;
3151
3152 had_match = TRUE;
3153 break;
3154 }
3155
3156 /* no match for an END pattern in this line */
3157 if (!had_match)
3158 m_endpos->lnum = 0;
3159
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003160 restore_chartab(buf_chartab);
3161
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162 /* Remove external matches. */
3163 unref_extmatch(re_extmatch_in);
3164 re_extmatch_in = NULL;
3165}
3166
3167/*
3168 * Limit "pos" not to be after "limit".
3169 */
3170 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003171limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003172{
3173 if (pos->lnum > limit->lnum)
3174 *pos = *limit;
3175 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3176 pos->col = limit->col;
3177}
3178
3179/*
3180 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3181 */
3182 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003183limit_pos_zero(
3184 lpos_T *pos,
3185 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186{
3187 if (pos->lnum == 0)
3188 *pos = *limit;
3189 else
3190 limit_pos(pos, limit);
3191}
3192
3193/*
3194 * Add offset to matched text for end of match or highlight.
3195 */
3196 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003197syn_add_end_off(
3198 lpos_T *result, /* returned position */
3199 regmmatch_T *regmatch, /* start/end of match */
3200 synpat_T *spp, /* matched pattern */
3201 int idx, /* index of offset */
3202 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003203{
3204 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003205 int off;
3206 char_u *base;
3207 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003208
3209 if (spp->sp_off_flags & (1 << idx))
3210 {
3211 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003212 col = regmatch->startpos[0].col;
3213 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003214 }
3215 else
3216 {
3217 result->lnum = regmatch->endpos[0].lnum;
3218 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003219 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003221 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3222 * is a matchgroup. Watch out for match with last NL in the buffer. */
3223 if (result->lnum > syn_buf->b_ml.ml_line_count)
3224 col = 0;
3225 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003226 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003227 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3228 p = base + col;
3229 if (off > 0)
3230 {
3231 while (off-- > 0 && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003232 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003233 }
3234 else if (off < 0)
3235 {
3236 while (off++ < 0 && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003237 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003238 }
3239 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003240 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003241 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003242}
3243
3244/*
3245 * Add offset to matched text for start of match or highlight.
3246 * Avoid resulting column to become negative.
3247 */
3248 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003249syn_add_start_off(
3250 lpos_T *result, /* returned position */
3251 regmmatch_T *regmatch, /* start/end of match */
3252 synpat_T *spp,
3253 int idx,
3254 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255{
3256 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003257 int off;
3258 char_u *base;
3259 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260
3261 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3262 {
3263 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003264 col = regmatch->endpos[0].col;
3265 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 }
3267 else
3268 {
3269 result->lnum = regmatch->startpos[0].lnum;
3270 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003271 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003273 if (result->lnum > syn_buf->b_ml.ml_line_count)
3274 {
3275 /* a "\n" at the end of the pattern may take us below the last line */
3276 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003277 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003278 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003279 if (off != 0)
3280 {
3281 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3282 p = base + col;
3283 if (off > 0)
3284 {
3285 while (off-- && *p != NUL)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003286 MB_PTR_ADV(p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003287 }
3288 else if (off < 0)
3289 {
3290 while (off++ && base < p)
Bram Moolenaar91acfff2017-03-12 19:22:36 +01003291 MB_PTR_BACK(base, p);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003292 }
3293 col = (int)(p - base);
3294 }
3295 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296}
3297
3298/*
3299 * Get current line in syntax buffer.
3300 */
3301 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003302syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303{
3304 return ml_get_buf(syn_buf, current_lnum, FALSE);
3305}
3306
3307/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003308 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003309 * Returns TRUE when there is a match.
3310 */
3311 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003312syn_regexec(
3313 regmmatch_T *rmp,
3314 linenr_T lnum,
3315 colnr_T col,
3316 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003318 int r;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003319#ifdef FEAT_RELTIME
3320 int timed_out = FALSE;
3321#endif
Bram Moolenaarf7512552013-06-06 14:55:19 +02003322#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003323 proftime_T pt;
3324
3325 if (syn_time_on)
3326 profile_start(&pt);
3327#endif
3328
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003329 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003330 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
3331#ifdef FEAT_RELTIME
3332 syn_tm, &timed_out
3333#else
3334 NULL, NULL
3335#endif
3336 );
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003337
Bram Moolenaarf7512552013-06-06 14:55:19 +02003338#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003339 if (syn_time_on)
3340 {
3341 profile_end(&pt);
3342 profile_add(&st->total, &pt);
3343 if (profile_cmp(&pt, &st->slowest) < 0)
3344 st->slowest = pt;
3345 ++st->count;
3346 if (r > 0)
3347 ++st->match;
3348 }
3349#endif
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003350#ifdef FEAT_RELTIME
3351 if (timed_out)
3352 syn_win->w_s->b_syn_slow = TRUE;
3353#endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003354
3355 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 {
3357 rmp->startpos[0].lnum += lnum;
3358 rmp->endpos[0].lnum += lnum;
3359 return TRUE;
3360 }
3361 return FALSE;
3362}
3363
3364/*
3365 * Check one position in a line for a matching keyword.
3366 * The caller must check if a keyword can start at startcol.
Bram Moolenaaraab93b12017-03-18 21:37:28 +01003367 * Return its ID if found, 0 otherwise.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368 */
3369 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003370check_keyword_id(
3371 char_u *line,
3372 int startcol, /* position in line to check for keyword */
3373 int *endcolp, /* return: character after found keyword */
3374 long *flagsp, /* return: flags of matching keyword */
3375 short **next_listp, /* return: next_list of matching keyword */
3376 stateitem_T *cur_si, /* item at the top of the stack */
3377 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003379 keyentry_T *kp;
3380 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003382 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003384 hashtab_T *ht;
3385 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386
3387 /* Find first character after the keyword. First character was already
3388 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003389 kwp = line + startcol;
3390 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003391 do
3392 {
3393#ifdef FEAT_MBYTE
3394 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003395 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396 else
3397#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003398 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003400 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401
Bram Moolenaardad6b692005-01-25 22:14:34 +00003402 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003403 return 0;
3404
3405 /*
3406 * Must make a copy of the keyword, so we can add a NUL and make it
3407 * lowercase.
3408 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003409 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003410
3411 /*
3412 * Try twice:
3413 * 1. matching case
3414 * 2. ignoring case
3415 */
3416 for (round = 1; round <= 2; ++round)
3417 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003418 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003419 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003421 if (round == 2) /* ignore case */
3422 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003423
3424 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003425 * Find keywords that match. There can be several with different
3426 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003427 * When current_next_list is non-zero accept only that group, otherwise:
3428 * Accept a not-contained keyword at toplevel.
3429 * Accept a keyword at other levels only if it is in the contains list.
3430 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003431 hi = hash_find(ht, keyword);
3432 if (!HASHITEM_EMPTY(hi))
3433 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003434 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003435 if (current_next_list != 0
3436 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3437 : (cur_si == NULL
3438 ? !(kp->flags & HL_CONTAINED)
3439 : in_id_list(cur_si, cur_si->si_cont_list,
3440 &kp->k_syn, kp->flags & HL_CONTAINED)))
3441 {
3442 *endcolp = startcol + kwlen;
3443 *flagsp = kp->flags;
3444 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003445#ifdef FEAT_CONCEAL
3446 *ccharp = kp->k_char;
3447#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003448 return kp->k_syn.id;
3449 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003450 }
3451 }
3452 return 0;
3453}
3454
3455/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003456 * Handle ":syntax conceal" command.
3457 */
3458 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003459syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003460{
3461#ifdef FEAT_CONCEAL
3462 char_u *arg = eap->arg;
3463 char_u *next;
3464
3465 eap->nextcmd = find_nextcmd(arg);
3466 if (eap->skip)
3467 return;
3468
3469 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003470 if (*arg == NUL)
3471 {
3472 if (curwin->w_s->b_syn_conceal)
Bram Moolenaar83064062017-06-13 16:34:54 +02003473 MSG(_("syntax conceal on"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003474 else
Bram Moolenaar83064062017-06-13 16:34:54 +02003475 MSG(_("syntax conceal off"));
Bram Moolenaarde318c52017-01-17 16:27:10 +01003476 }
3477 else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003478 curwin->w_s->b_syn_conceal = TRUE;
3479 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3480 curwin->w_s->b_syn_conceal = FALSE;
3481 else
3482 EMSG2(_("E390: Illegal argument: %s"), arg);
3483#endif
3484}
3485
3486/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 * Handle ":syntax case" command.
3488 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003490syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003491{
3492 char_u *arg = eap->arg;
3493 char_u *next;
3494
3495 eap->nextcmd = find_nextcmd(arg);
3496 if (eap->skip)
3497 return;
3498
3499 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003500 if (*arg == NUL)
3501 {
3502 if (curwin->w_s->b_syn_ic)
3503 MSG(_("syntax case ignore"));
3504 else
3505 MSG(_("syntax case match"));
3506 }
3507 else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511 else
3512 EMSG2(_("E390: Illegal argument: %s"), arg);
3513}
3514
3515/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003516 * Handle ":syntax spell" command.
3517 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003518 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003519syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003520{
3521 char_u *arg = eap->arg;
3522 char_u *next;
3523
3524 eap->nextcmd = find_nextcmd(arg);
3525 if (eap->skip)
3526 return;
3527
3528 next = skiptowhite(arg);
Bram Moolenaarde318c52017-01-17 16:27:10 +01003529 if (*arg == NUL)
3530 {
3531 if (curwin->w_s->b_syn_spell == SYNSPL_TOP)
3532 MSG(_("syntax spell toplevel"));
3533 else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP)
3534 MSG(_("syntax spell notoplevel"));
3535 else
3536 MSG(_("syntax spell default"));
3537 }
3538 else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003539 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003540 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003541 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003542 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003543 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003544 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003545 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003546 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003547 return;
3548 }
3549
3550 /* assume spell checking changed, force a redraw */
3551 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003552}
3553
3554/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003555 * Handle ":syntax iskeyword" command.
3556 */
3557 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003558syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003559{
3560 char_u *arg = eap->arg;
3561 char_u save_chartab[32];
3562 char_u *save_isk;
3563
3564 if (eap->skip)
3565 return;
3566
3567 arg = skipwhite(arg);
3568 if (*arg == NUL)
3569 {
3570 MSG_PUTS("\n");
3571 MSG_PUTS(_("syntax iskeyword "));
3572 if (curwin->w_s->b_syn_isk != empty_option)
3573 msg_outtrans(curwin->w_s->b_syn_isk);
3574 else
3575 msg_outtrans((char_u *)"not set");
3576 }
3577 else
3578 {
3579 if (STRNICMP(arg, "clear", 5) == 0)
3580 {
3581 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3582 (size_t)32);
3583 clear_string_option(&curwin->w_s->b_syn_isk);
3584 }
3585 else
3586 {
3587 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3588 save_isk = curbuf->b_p_isk;
3589 curbuf->b_p_isk = vim_strsave(arg);
3590
3591 buf_init_chartab(curbuf, FALSE);
3592 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3593 (size_t)32);
3594 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3595 clear_string_option(&curwin->w_s->b_syn_isk);
3596 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3597 curbuf->b_p_isk = save_isk;
3598 }
3599 }
3600 redraw_win_later(curwin, NOT_VALID);
3601}
3602
3603/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604 * Clear all syntax info for one buffer.
3605 */
3606 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003607syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608{
3609 int i;
3610
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 block->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02003612#ifdef FEAT_RELTIME
3613 block->b_syn_slow = FALSE; /* clear previous timeout */
3614#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003615 block->b_syn_ic = FALSE; /* Use case, by default */
3616 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3617 block->b_syn_containedin = FALSE;
Bram Moolenaarde318c52017-01-17 16:27:10 +01003618#ifdef FEAT_CONCEAL
3619 block->b_syn_conceal = FALSE;
3620#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621
3622 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003623 clear_keywtab(&block->b_keywtab);
3624 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625
3626 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003627 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3628 syn_clear_pattern(block, i);
3629 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003630
3631 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003632 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3633 syn_clear_cluster(block, i);
3634 ga_clear(&block->b_syn_clusters);
3635 block->b_spell_cluster_id = 0;
3636 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 block->b_syn_sync_flags = 0;
3639 block->b_syn_sync_minlines = 0;
3640 block->b_syn_sync_maxlines = 0;
3641 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642
Bram Moolenaar473de612013-06-08 18:19:48 +02003643 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003644 block->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003645 VIM_CLEAR(block->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003647 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003648#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003649 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650
3651 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003654
3655 /* Reset the counter for ":syn include" */
3656 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657}
3658
3659/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003660 * Get rid of ownsyntax for window "wp".
3661 */
3662 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003663reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003664{
3665 if (wp->w_s != &wp->w_buffer->b_s)
3666 {
3667 syntax_clear(wp->w_s);
3668 vim_free(wp->w_s);
3669 wp->w_s = &wp->w_buffer->b_s;
3670 }
3671}
3672
3673/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 * Clear syncing info for one buffer.
3675 */
3676 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003677syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678{
3679 int i;
3680
3681 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003682 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3683 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3684 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685
Bram Moolenaar860cae12010-06-05 23:22:07 +02003686 curwin->w_s->b_syn_sync_flags = 0;
3687 curwin->w_s->b_syn_sync_minlines = 0;
3688 curwin->w_s->b_syn_sync_maxlines = 0;
3689 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690
Bram Moolenaar473de612013-06-08 18:19:48 +02003691 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 curwin->w_s->b_syn_linecont_prog = NULL;
Bram Moolenaard23a8232018-02-10 18:45:26 +01003693 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003694 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003696 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697}
3698
3699/*
3700 * Remove one pattern from the buffer's pattern list.
3701 */
3702 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003703syn_remove_pattern(
3704 synblock_T *block,
3705 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706{
3707 synpat_T *spp;
3708
Bram Moolenaar860cae12010-06-05 23:22:07 +02003709 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710#ifdef FEAT_FOLDING
3711 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003712 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003714 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003716 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3717 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718}
3719
3720/*
3721 * Clear and free one syntax pattern. When clearing all, must be called from
3722 * last to first!
3723 */
3724 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003725syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003726{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003727 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003728 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003730 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003732 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3733 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3734 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735 }
3736}
3737
3738/*
3739 * Clear and free one syntax cluster.
3740 */
3741 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003742syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003744 vim_free(SYN_CLSTR(block)[i].scl_name);
3745 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3746 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747}
3748
3749/*
3750 * Handle ":syntax clear" command.
3751 */
3752 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003753syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754{
3755 char_u *arg = eap->arg;
3756 char_u *arg_end;
3757 int id;
3758
3759 eap->nextcmd = find_nextcmd(arg);
3760 if (eap->skip)
3761 return;
3762
3763 /*
3764 * We have to disable this within ":syn include @group filename",
3765 * because otherwise @group would get deleted.
3766 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3767 * clear".
3768 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003769 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003770 return;
3771
3772 if (ends_excmd(*arg))
3773 {
3774 /*
3775 * No argument: Clear all syntax items.
3776 */
3777 if (syncing)
3778 syntax_sync_clear();
3779 else
3780 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003781 syntax_clear(curwin->w_s);
3782 if (curwin->w_s == &curwin->w_buffer->b_s)
3783 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003784 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003785 }
3786 }
3787 else
3788 {
3789 /*
3790 * Clear the group IDs that are in the argument.
3791 */
3792 while (!ends_excmd(*arg))
3793 {
3794 arg_end = skiptowhite(arg);
3795 if (*arg == '@')
3796 {
3797 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3798 if (id == 0)
3799 {
3800 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3801 break;
3802 }
3803 else
3804 {
3805 /*
3806 * We can't physically delete a cluster without changing
3807 * the IDs of other clusters, so we do the next best thing
3808 * and make it empty.
3809 */
3810 short scl_id = id - SYNID_CLUSTER;
3811
Bram Moolenaard23a8232018-02-10 18:45:26 +01003812 VIM_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 }
3814 }
3815 else
3816 {
3817 id = syn_namen2id(arg, (int)(arg_end - arg));
3818 if (id == 0)
3819 {
3820 EMSG2(_(e_nogroup), arg);
3821 break;
3822 }
3823 else
3824 syn_clear_one(id, syncing);
3825 }
3826 arg = skipwhite(arg_end);
3827 }
3828 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003829 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003830 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831}
3832
3833/*
3834 * Clear one syntax group for the current buffer.
3835 */
3836 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003837syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838{
3839 synpat_T *spp;
3840 int idx;
3841
3842 /* Clear keywords only when not ":syn sync clear group-name" */
3843 if (!syncing)
3844 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003845 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3846 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847 }
3848
3849 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003850 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003852 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3854 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003855 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856 }
3857}
3858
3859/*
3860 * Handle ":syntax on" command.
3861 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003863syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864{
3865 syn_cmd_onoff(eap, "syntax");
3866}
3867
3868/*
3869 * Handle ":syntax enable" command.
3870 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003872syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873{
3874 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3875 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003876 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877}
3878
3879/*
3880 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003881 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003884syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885{
3886 eap->nextcmd = check_nextcmd(eap->arg);
3887 if (!eap->skip)
3888 {
3889 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3890 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003891 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003892 }
3893}
3894
3895/*
3896 * Handle ":syntax manual" command.
3897 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003899syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900{
3901 syn_cmd_onoff(eap, "manual");
3902}
3903
3904/*
3905 * Handle ":syntax off" command.
3906 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003908syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909{
3910 syn_cmd_onoff(eap, "nosyntax");
3911}
3912
3913 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003914syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003915{
3916 char_u buf[100];
3917
3918 eap->nextcmd = check_nextcmd(eap->arg);
3919 if (!eap->skip)
3920 {
3921 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003922 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003923 do_cmdline_cmd(buf);
3924 }
3925}
3926
3927/*
3928 * Handle ":syntax [list]" command: list current syntax words.
3929 */
3930 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003931syn_cmd_list(
3932 exarg_T *eap,
3933 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934{
3935 char_u *arg = eap->arg;
3936 int id;
3937 char_u *arg_end;
3938
3939 eap->nextcmd = find_nextcmd(arg);
3940 if (eap->skip)
3941 return;
3942
Bram Moolenaar860cae12010-06-05 23:22:07 +02003943 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003945 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003946 return;
3947 }
3948
3949 if (syncing)
3950 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003951 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 {
3953 MSG_PUTS(_("syncing on C-style comments"));
3954 syn_lines_msg();
3955 syn_match_msg();
3956 return;
3957 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003958 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003960 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003961 MSG_PUTS(_("no syncing"));
3962 else
3963 {
3964 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003965 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003966 MSG_PUTS(_(" lines before top line"));
3967 syn_match_msg();
3968 }
3969 return;
3970 }
3971 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003972 if (curwin->w_s->b_syn_sync_minlines > 0
3973 || curwin->w_s->b_syn_sync_maxlines > 0
3974 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 {
3976 MSG_PUTS(_("\nsyncing on items"));
3977 syn_lines_msg();
3978 syn_match_msg();
3979 }
3980 }
3981 else
3982 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3983 if (ends_excmd(*arg))
3984 {
3985 /*
3986 * No argument: List all group IDs and all syntax clusters.
3987 */
3988 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3989 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003990 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 syn_list_cluster(id);
3992 }
3993 else
3994 {
3995 /*
3996 * List the group IDs and syntax clusters that are in the argument.
3997 */
3998 while (!ends_excmd(*arg) && !got_int)
3999 {
4000 arg_end = skiptowhite(arg);
4001 if (*arg == '@')
4002 {
4003 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
4004 if (id == 0)
4005 EMSG2(_("E392: No such syntax cluster: %s"), arg);
4006 else
4007 syn_list_cluster(id - SYNID_CLUSTER);
4008 }
4009 else
4010 {
4011 id = syn_namen2id(arg, (int)(arg_end - arg));
4012 if (id == 0)
4013 EMSG2(_(e_nogroup), arg);
4014 else
4015 syn_list_one(id, syncing, TRUE);
4016 }
4017 arg = skipwhite(arg_end);
4018 }
4019 }
4020 eap->nextcmd = check_nextcmd(arg);
4021}
4022
4023 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004024syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004025{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004026 if (curwin->w_s->b_syn_sync_maxlines > 0
4027 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 {
4029 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004030 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 {
4032 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004033 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4034 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 MSG_PUTS(", ");
4036 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004037 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 {
4039 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004040 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041 }
4042 MSG_PUTS(_(" lines before top line"));
4043 }
4044}
4045
4046 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004047syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004049 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 {
4051 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004052 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 MSG_PUTS(_(" line breaks"));
4054 }
4055}
4056
4057static int last_matchgroup;
4058
4059struct name_list
4060{
4061 int flag;
4062 char *name;
4063};
4064
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004065static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004066
4067/*
4068 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4069 */
4070 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004071syn_list_one(
4072 int id,
4073 int syncing, /* when TRUE: list syncing items */
4074 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004075{
4076 int attr;
4077 int idx;
4078 int did_header = FALSE;
4079 synpat_T *spp;
4080 static struct name_list namelist1[] =
4081 {
4082 {HL_DISPLAY, "display"},
4083 {HL_CONTAINED, "contained"},
4084 {HL_ONELINE, "oneline"},
4085 {HL_KEEPEND, "keepend"},
4086 {HL_EXTEND, "extend"},
4087 {HL_EXCLUDENL, "excludenl"},
4088 {HL_TRANSP, "transparent"},
4089 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004090#ifdef FEAT_CONCEAL
4091 {HL_CONCEAL, "conceal"},
4092 {HL_CONCEALENDS, "concealends"},
4093#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004094 {0, NULL}
4095 };
4096 static struct name_list namelist2[] =
4097 {
4098 {HL_SKIPWHITE, "skipwhite"},
4099 {HL_SKIPNL, "skipnl"},
4100 {HL_SKIPEMPTY, "skipempty"},
4101 {0, NULL}
4102 };
4103
Bram Moolenaar8820b482017-03-16 17:23:31 +01004104 attr = HL_ATTR(HLF_D); /* highlight like directories */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105
4106 /* list the keywords for "id" */
4107 if (!syncing)
4108 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004109 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4110 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 did_header, attr);
4112 }
4113
4114 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004115 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004117 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4119 continue;
4120
4121 (void)syn_list_header(did_header, 999, id);
4122 did_header = TRUE;
4123 last_matchgroup = 0;
4124 if (spp->sp_type == SPTYPE_MATCH)
4125 {
4126 put_pattern("match", ' ', spp, attr);
4127 msg_putchar(' ');
4128 }
4129 else if (spp->sp_type == SPTYPE_START)
4130 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004131 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4132 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4133 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4134 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4135 while (idx < curwin->w_s->b_syn_patterns.ga_len
4136 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4137 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138 --idx;
4139 msg_putchar(' ');
4140 }
4141 syn_list_flags(namelist1, spp->sp_flags, attr);
4142
4143 if (spp->sp_cont_list != NULL)
4144 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4145
4146 if (spp->sp_syn.cont_in_list != NULL)
4147 put_id_list((char_u *)"containedin",
4148 spp->sp_syn.cont_in_list, attr);
4149
4150 if (spp->sp_next_list != NULL)
4151 {
4152 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4153 syn_list_flags(namelist2, spp->sp_flags, attr);
4154 }
4155 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4156 {
4157 if (spp->sp_flags & HL_SYNC_HERE)
4158 msg_puts_attr((char_u *)"grouphere", attr);
4159 else
4160 msg_puts_attr((char_u *)"groupthere", attr);
4161 msg_putchar(' ');
4162 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004163 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004164 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4165 else
4166 MSG_PUTS("NONE");
4167 msg_putchar(' ');
4168 }
4169 }
4170
4171 /* list the link, if there is one */
4172 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4173 {
4174 (void)syn_list_header(did_header, 999, id);
4175 msg_puts_attr((char_u *)"links to", attr);
4176 msg_putchar(' ');
4177 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4178 }
4179}
4180
4181 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004182syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183{
4184 int i;
4185
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004186 for (i = 0; nlist[i].flag != 0; ++i)
4187 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004189 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 msg_putchar(' ');
4191 }
4192}
4193
4194/*
4195 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4196 */
4197 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004198syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199{
4200 int endcol = 15;
4201
4202 /* slight hack: roughly duplicate the guts of syn_list_header() */
4203 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004204 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205
4206 if (msg_col >= endcol) /* output at least one space */
4207 endcol = msg_col + 1;
4208 if (Columns <= endcol) /* avoid hang for tiny window */
4209 endcol = Columns - 1;
4210
4211 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004212 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004214 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar8820b482017-03-16 17:23:31 +01004215 HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 }
4217 else
4218 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01004219 msg_puts_attr((char_u *)"cluster", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220 msg_puts((char_u *)"=NONE");
4221 }
4222}
4223
4224 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004225put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226{
4227 short *p;
4228
4229 msg_puts_attr(name, attr);
4230 msg_putchar('=');
4231 for (p = list; *p; ++p)
4232 {
4233 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4234 {
4235 if (p[1])
4236 MSG_PUTS("ALLBUT");
4237 else
4238 MSG_PUTS("ALL");
4239 }
4240 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4241 {
4242 MSG_PUTS("TOP");
4243 }
4244 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4245 {
4246 MSG_PUTS("CONTAINED");
4247 }
4248 else if (*p >= SYNID_CLUSTER)
4249 {
4250 short scl_id = *p - SYNID_CLUSTER;
4251
4252 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004253 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254 }
4255 else
4256 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4257 if (p[1])
4258 msg_putchar(',');
4259 }
4260 msg_putchar(' ');
4261}
4262
4263 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004264put_pattern(
4265 char *s,
4266 int c,
4267 synpat_T *spp,
4268 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269{
4270 long n;
4271 int mask;
4272 int first;
4273 static char *sepchars = "/+=-#@\"|'^&";
4274 int i;
4275
4276 /* May have to write "matchgroup=group" */
4277 if (last_matchgroup != spp->sp_syn_match_id)
4278 {
4279 last_matchgroup = spp->sp_syn_match_id;
4280 msg_puts_attr((char_u *)"matchgroup", attr);
4281 msg_putchar('=');
4282 if (last_matchgroup == 0)
4283 msg_outtrans((char_u *)"NONE");
4284 else
4285 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4286 msg_putchar(' ');
4287 }
4288
4289 /* output the name of the pattern and an '=' or ' ' */
4290 msg_puts_attr((char_u *)s, attr);
4291 msg_putchar(c);
4292
4293 /* output the pattern, in between a char that is not in the pattern */
4294 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4295 if (sepchars[++i] == NUL)
4296 {
4297 i = 0; /* no good char found, just use the first one */
4298 break;
4299 }
4300 msg_putchar(sepchars[i]);
4301 msg_outtrans(spp->sp_pattern);
4302 msg_putchar(sepchars[i]);
4303
4304 /* output any pattern options */
4305 first = TRUE;
4306 for (i = 0; i < SPO_COUNT; ++i)
4307 {
4308 mask = (1 << i);
4309 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4310 {
4311 if (!first)
4312 msg_putchar(','); /* separate with commas */
4313 msg_puts((char_u *)spo_name_tab[i]);
4314 n = spp->sp_offsets[i];
4315 if (i != SPO_LC_OFF)
4316 {
4317 if (spp->sp_off_flags & mask)
4318 msg_putchar('s');
4319 else
4320 msg_putchar('e');
4321 if (n > 0)
4322 msg_putchar('+');
4323 }
4324 if (n || i == SPO_LC_OFF)
4325 msg_outnum(n);
4326 first = FALSE;
4327 }
4328 }
4329 msg_putchar(' ');
4330}
4331
4332/*
4333 * List or clear the keywords for one syntax group.
4334 * Return TRUE if the header has been printed.
4335 */
4336 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004337syn_list_keywords(
4338 int id,
4339 hashtab_T *ht,
4340 int did_header, /* header has already been printed */
4341 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004344 hashitem_T *hi;
4345 keyentry_T *kp;
4346 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 int prev_contained = 0;
4348 short *prev_next_list = NULL;
4349 short *prev_cont_in_list = NULL;
4350 int prev_skipnl = 0;
4351 int prev_skipwhite = 0;
4352 int prev_skipempty = 0;
4353
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 /*
4355 * Unfortunately, this list of keywords is not sorted on alphabet but on
4356 * hash value...
4357 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004358 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 --todo;
4364 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 if (prev_contained != (kp->flags & HL_CONTAINED)
4369 || prev_skipnl != (kp->flags & HL_SKIPNL)
4370 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4371 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4372 || prev_cont_in_list != kp->k_syn.cont_in_list
4373 || prev_next_list != kp->next_list)
4374 outlen = 9999;
4375 else
4376 outlen = (int)STRLEN(kp->keyword);
4377 /* output "contained" and "nextgroup" on each line */
4378 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 prev_contained = 0;
4381 prev_next_list = NULL;
4382 prev_cont_in_list = NULL;
4383 prev_skipnl = 0;
4384 prev_skipwhite = 0;
4385 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 did_header = TRUE;
4388 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004396 put_id_list((char_u *)"containedin",
4397 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004399 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004400 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004401 if (kp->next_list != prev_next_list)
4402 {
4403 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4404 msg_putchar(' ');
4405 prev_next_list = kp->next_list;
4406 if (kp->flags & HL_SKIPNL)
4407 {
4408 msg_puts_attr((char_u *)"skipnl", attr);
4409 msg_putchar(' ');
4410 prev_skipnl = (kp->flags & HL_SKIPNL);
4411 }
4412 if (kp->flags & HL_SKIPWHITE)
4413 {
4414 msg_puts_attr((char_u *)"skipwhite", attr);
4415 msg_putchar(' ');
4416 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4417 }
4418 if (kp->flags & HL_SKIPEMPTY)
4419 {
4420 msg_puts_attr((char_u *)"skipempty", attr);
4421 msg_putchar(' ');
4422 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4423 }
4424 }
4425 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004426 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004427 }
4428 }
4429 }
4430
4431 return did_header;
4432}
4433
4434 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004435syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004437 hashitem_T *hi;
4438 keyentry_T *kp;
4439 keyentry_T *kp_prev;
4440 keyentry_T *kp_next;
4441 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442
Bram Moolenaardad6b692005-01-25 22:14:34 +00004443 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004444 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 --todo;
4450 kp_prev = NULL;
4451 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004453 if (kp->k_syn.id == id)
4454 {
4455 kp_next = kp->ke_next;
4456 if (kp_prev == NULL)
4457 {
4458 if (kp_next == NULL)
4459 hash_remove(ht, hi);
4460 else
4461 hi->hi_key = KE2HIKEY(kp_next);
4462 }
4463 else
4464 kp_prev->ke_next = kp_next;
4465 vim_free(kp->next_list);
4466 vim_free(kp->k_syn.cont_in_list);
4467 vim_free(kp);
4468 kp = kp_next;
4469 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004471 {
4472 kp_prev = kp;
4473 kp = kp->ke_next;
4474 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475 }
4476 }
4477 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004478 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479}
4480
4481/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004482 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004483 */
4484 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004485clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004487 hashitem_T *hi;
4488 int todo;
4489 keyentry_T *kp;
4490 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004492 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004495 if (!HASHITEM_EMPTY(hi))
4496 {
4497 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 kp_next = kp->ke_next;
4501 vim_free(kp->next_list);
4502 vim_free(kp->k_syn.cont_in_list);
4503 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004507 hash_clear(ht);
4508 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509}
4510
4511/*
4512 * Add a keyword to the list of keywords.
4513 */
4514 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004515add_keyword(
4516 char_u *name, /* name of keyword */
4517 int id, /* group ID for this keyword */
4518 int flags, /* flags for this keyword */
4519 short *cont_in_list, /* containedin for this keyword */
4520 short *next_list, /* nextgroup for this keyword */
4521 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004523 keyentry_T *kp;
4524 hashtab_T *ht;
4525 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004526 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004527 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529
Bram Moolenaar860cae12010-06-05 23:22:07 +02004530 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004531 name_ic = str_foldcase(name, (int)STRLEN(name),
4532 name_folded, MAXKEYWLEN + 1);
4533 else
4534 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004535 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4536 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004537 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004538 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004539 kp->k_syn.id = id;
4540 kp->k_syn.inc_tag = current_syn_inc_tag;
4541 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004542 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004543 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004545 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004546 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547
Bram Moolenaar860cae12010-06-05 23:22:07 +02004548 if (curwin->w_s->b_syn_ic)
4549 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004551 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552
Bram Moolenaardad6b692005-01-25 22:14:34 +00004553 hash = hash_hash(kp->keyword);
4554 hi = hash_lookup(ht, kp->keyword, hash);
4555 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004557 /* new keyword, add to hashtable */
4558 kp->ke_next = NULL;
4559 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004561 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004563 /* keyword already exists, prepend to list */
4564 kp->ke_next = HI2KE(hi);
4565 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567}
4568
4569/*
4570 * Get the start and end of the group name argument.
4571 * Return a pointer to the first argument.
4572 * Return NULL if the end of the command was found instead of further args.
4573 */
4574 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004575get_group_name(
4576 char_u *arg, /* start of the argument */
4577 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578{
4579 char_u *rest;
4580
4581 *name_end = skiptowhite(arg);
4582 rest = skipwhite(*name_end);
4583
4584 /*
4585 * Check if there are enough arguments. The first argument may be a
4586 * pattern, where '|' is allowed, so only check for NUL.
4587 */
4588 if (ends_excmd(*arg) || *rest == NUL)
4589 return NULL;
4590 return rest;
4591}
4592
4593/*
4594 * Check for syntax command option arguments.
4595 * This can be called at any place in the list of arguments, and just picks
4596 * out the arguments that are known. Can be called several times in a row to
4597 * collect all options in between other arguments.
4598 * Return a pointer to the next argument (which isn't an option).
4599 * Return NULL for any error;
4600 */
4601 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004602get_syn_options(
4603 char_u *arg, /* next argument to be checked */
4604 syn_opt_arg_T *opt, /* various things */
Bram Moolenaarde318c52017-01-17 16:27:10 +01004605 int *conceal_char UNUSED,
4606 int skip) /* TRUE if skipping over command */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608 char_u *gname_start, *gname;
4609 int syn_id;
4610 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004611 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 int i;
4613 int fidx;
4614 static struct flag
4615 {
4616 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004617 int argtype;
4618 int flags;
4619 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4620 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4621 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4622 {"eExXtTeEnNdD", 0, HL_EXTEND},
4623 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4624 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4625 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4626 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4627 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4628 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4629 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4630 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4631 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004632 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4633 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4634 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004635 {"cCoOnNtTaAiInNsS", 1, 0},
4636 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4637 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004639 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640
4641 if (arg == NULL) /* already detected error */
4642 return NULL;
4643
Bram Moolenaar860cae12010-06-05 23:22:07 +02004644#ifdef FEAT_CONCEAL
4645 if (curwin->w_s->b_syn_conceal)
4646 opt->flags |= HL_CONCEAL;
4647#endif
4648
Bram Moolenaar071d4272004-06-13 20:20:40 +00004649 for (;;)
4650 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004651 /*
4652 * This is used very often when a large number of keywords is defined.
4653 * Need to skip quickly when no option name is found.
4654 * Also avoid tolower(), it's slow.
4655 */
4656 if (strchr(first_letters, *arg) == NULL)
4657 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658
4659 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4660 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004661 p = flagtab[fidx].name;
4662 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4663 if (arg[len] != p[i] && arg[len] != p[i + 1])
4664 break;
Bram Moolenaar1c465442017-03-12 20:10:05 +01004665 if (p[i] == NUL && (VIM_ISWHITE(arg[len])
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004666 || (flagtab[fidx].argtype > 0
4667 ? arg[len] == '='
4668 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004670 if (opt->keyword
4671 && (flagtab[fidx].flags == HL_DISPLAY
4672 || flagtab[fidx].flags == HL_FOLD
4673 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 /* treat "display", "fold" and "extend" as a keyword */
4675 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 break;
4677 }
4678 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004679 if (fidx < 0) /* no match found */
4680 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004682 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004684 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685 {
4686 EMSG(_("E395: contains argument not accepted here"));
4687 return NULL;
4688 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01004689 if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690 return NULL;
4691 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004692 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004694 if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 return NULL;
4696 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004697 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004699 if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 return NULL;
4701 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004702 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4703 {
4704#ifdef FEAT_MBYTE
4705 /* cchar=? */
4706 if (has_mbyte)
4707 {
4708# ifdef FEAT_CONCEAL
4709 *conceal_char = mb_ptr2char(arg + 6);
4710# endif
4711 arg += mb_ptr2len(arg + 6) - 1;
4712 }
4713 else
4714#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004715 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004716#ifdef FEAT_CONCEAL
4717 *conceal_char = arg[6];
4718#else
4719 ;
4720#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004721 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004722#ifdef FEAT_CONCEAL
4723 if (!vim_isprintc_strict(*conceal_char))
4724 {
4725 EMSG(_("E844: invalid cchar value"));
4726 return NULL;
4727 }
4728#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004729 arg = skipwhite(arg + 7);
4730 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004732 {
4733 opt->flags |= flagtab[fidx].flags;
4734 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004736 if (flagtab[fidx].flags == HL_SYNC_HERE
4737 || flagtab[fidx].flags == HL_SYNC_THERE)
4738 {
4739 if (opt->sync_idx == NULL)
4740 {
4741 EMSG(_("E393: group[t]here not accepted here"));
4742 return NULL;
4743 }
4744 gname_start = arg;
4745 arg = skiptowhite(arg);
4746 if (gname_start == arg)
4747 return NULL;
4748 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4749 if (gname == NULL)
4750 return NULL;
4751 if (STRCMP(gname, "NONE") == 0)
4752 *opt->sync_idx = NONE_IDX;
4753 else
4754 {
4755 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004756 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4757 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4758 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004759 {
4760 *opt->sync_idx = i;
4761 break;
4762 }
4763 if (i < 0)
4764 {
4765 EMSG2(_("E394: Didn't find region item for %s"), gname);
4766 vim_free(gname);
4767 return NULL;
4768 }
4769 }
4770
4771 vim_free(gname);
4772 arg = skipwhite(arg);
4773 }
4774#ifdef FEAT_FOLDING
4775 else if (flagtab[fidx].flags == HL_FOLD
4776 && foldmethodIsSyntax(curwin))
4777 /* Need to update folds later. */
4778 foldUpdateAll(curwin);
4779#endif
4780 }
4781 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004782
4783 return arg;
4784}
4785
4786/*
4787 * Adjustments to syntax item when declared in a ":syn include"'d file.
4788 * Set the contained flag, and if the item is not already contained, add it
4789 * to the specified top-level group, if any.
4790 */
4791 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004792syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004794 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795 return;
4796 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004797 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004798 {
4799 /* We have to alloc this, because syn_combine_list() will free it. */
4800 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004801 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802
4803 if (grp_list != NULL)
4804 {
4805 grp_list[0] = id;
4806 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004807 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 CLUSTER_ADD);
4809 }
4810 }
4811}
4812
4813/*
4814 * Handle ":syntax include [@{group-name}] filename" command.
4815 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004817syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818{
4819 char_u *arg = eap->arg;
4820 int sgl_id = 1;
4821 char_u *group_name_end;
4822 char_u *rest;
4823 char_u *errormsg = NULL;
4824 int prev_toplvl_grp;
4825 int prev_syn_inc_tag;
4826 int source = FALSE;
4827
4828 eap->nextcmd = find_nextcmd(arg);
4829 if (eap->skip)
4830 return;
4831
4832 if (arg[0] == '@')
4833 {
4834 ++arg;
4835 rest = get_group_name(arg, &group_name_end);
4836 if (rest == NULL)
4837 {
4838 EMSG((char_u *)_("E397: Filename required"));
4839 return;
4840 }
4841 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004842 if (sgl_id == 0)
4843 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004844 /* separate_nextcmd() and expand_filename() depend on this */
4845 eap->arg = rest;
4846 }
4847
4848 /*
4849 * Everything that's left, up to the next command, should be the
4850 * filename to include.
4851 */
4852 eap->argt |= (XFILE | NOSPC);
4853 separate_nextcmd(eap);
4854 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4855 {
4856 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4857 * file. Need to expand the file name first. In other cases
4858 * ":runtime!" is used. */
4859 source = TRUE;
4860 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4861 {
4862 if (errormsg != NULL)
4863 EMSG(errormsg);
4864 return;
4865 }
4866 }
4867
4868 /*
4869 * Save and restore the existing top-level grouplist id and ":syn
4870 * include" tag around the actual inclusion.
4871 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004872 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4873 {
4874 EMSG((char_u *)_("E847: Too many syntax includes"));
4875 return;
4876 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004877 prev_syn_inc_tag = current_syn_inc_tag;
4878 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004879 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4880 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004881 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004882 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004884 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885 current_syn_inc_tag = prev_syn_inc_tag;
4886}
4887
4888/*
4889 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4890 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004891 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004892syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004893{
4894 char_u *arg = eap->arg;
4895 char_u *group_name_end;
4896 int syn_id;
4897 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004898 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004900 char_u *kw;
4901 syn_opt_arg_T syn_opt_arg;
4902 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004903 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904
4905 rest = get_group_name(arg, &group_name_end);
4906
4907 if (rest != NULL)
4908 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004909 if (eap->skip)
4910 syn_id = -1;
4911 else
4912 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004913 if (syn_id != 0)
4914 /* allocate a buffer, for removing backslashes in the keyword */
4915 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 if (keyword_copy != NULL)
4917 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004918 syn_opt_arg.flags = 0;
4919 syn_opt_arg.keyword = TRUE;
4920 syn_opt_arg.sync_idx = NULL;
4921 syn_opt_arg.has_cont_list = FALSE;
4922 syn_opt_arg.cont_in_list = NULL;
4923 syn_opt_arg.next_list = NULL;
4924
Bram Moolenaar071d4272004-06-13 20:20:40 +00004925 /*
4926 * The options given apply to ALL keywords, so all options must be
4927 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004928 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 cnt = 0;
4931 p = keyword_copy;
4932 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933 {
Bram Moolenaarde318c52017-01-17 16:27:10 +01004934 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char,
4935 eap->skip);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004936 if (rest == NULL || ends_excmd(*rest))
4937 break;
4938 /* Copy the keyword, removing backslashes, and add a NUL. */
Bram Moolenaar1c465442017-03-12 20:10:05 +01004939 while (*rest != NUL && !VIM_ISWHITE(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004940 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004941 if (*rest == '\\' && rest[1] != NUL)
4942 ++rest;
4943 *p++ = *rest++;
4944 }
4945 *p++ = NUL;
4946 ++cnt;
4947 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004949 if (!eap->skip)
4950 {
4951 /* Adjust flags for use of ":syn include". */
4952 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4953
4954 /*
4955 * 2: Add an entry for each keyword.
4956 */
4957 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4958 {
4959 for (p = vim_strchr(kw, '['); ; )
4960 {
4961 if (p != NULL)
4962 *p = NUL;
4963 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004964 syn_opt_arg.cont_in_list,
4965 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004966 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004967 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004968 if (p[1] == NUL)
4969 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004970 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004971 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004972 }
4973 if (p[1] == ']')
4974 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004975 if (p[2] != NUL)
4976 {
4977 EMSG3(_("E890: trailing char after ']': %s]%s"),
4978 kw, &p[2]);
4979 goto error;
4980 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004981 kw = p + 1; /* skip over the "]" */
4982 break;
4983 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004984#ifdef FEAT_MBYTE
4985 if (has_mbyte)
4986 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004987 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004988
4989 mch_memmove(p, p + 1, l);
4990 p += l;
4991 }
4992 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004994 {
4995 p[0] = p[1];
4996 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004997 }
4998 }
4999 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005000 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02005001error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005003 vim_free(syn_opt_arg.cont_in_list);
5004 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005 }
5006 }
5007
5008 if (rest != NULL)
5009 eap->nextcmd = check_nextcmd(rest);
5010 else
5011 EMSG2(_(e_invarg2), arg);
5012
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005013 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005014 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015}
5016
5017/*
5018 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5019 *
5020 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5021 */
5022 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005023syn_cmd_match(
5024 exarg_T *eap,
5025 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026{
5027 char_u *arg = eap->arg;
5028 char_u *group_name_end;
5029 char_u *rest;
5030 synpat_T item; /* the item found in the line */
5031 int syn_id;
5032 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005033 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005034 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005035 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005036
5037 /* Isolate the group name, check for validity */
5038 rest = get_group_name(arg, &group_name_end);
5039
5040 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005041 syn_opt_arg.flags = 0;
5042 syn_opt_arg.keyword = FALSE;
5043 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5044 syn_opt_arg.has_cont_list = TRUE;
5045 syn_opt_arg.cont_list = NULL;
5046 syn_opt_arg.cont_in_list = NULL;
5047 syn_opt_arg.next_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005048 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049
5050 /* get the pattern. */
5051 init_syn_patterns();
5052 vim_memset(&item, 0, sizeof(item));
5053 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005054 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5055 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005056
5057 /* Get options after the pattern */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005058 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005059
5060 if (rest != NULL) /* all arguments are valid */
5061 {
5062 /*
5063 * Check for trailing command and illegal trailing arguments.
5064 */
5065 eap->nextcmd = check_nextcmd(rest);
5066 if (!ends_excmd(*rest) || eap->skip)
5067 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005068 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069 && (syn_id = syn_check_group(arg,
5070 (int)(group_name_end - arg))) != 0)
5071 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005072 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005073 /*
5074 * Store the pattern in the syn_items list
5075 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005076 idx = curwin->w_s->b_syn_patterns.ga_len;
5077 SYN_ITEMS(curwin->w_s)[idx] = item;
5078 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5079 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5080 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5081 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5082 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5083 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5084 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5085 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005086 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005087#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005088 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005089#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005090 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005091 curwin->w_s->b_syn_containedin = TRUE;
5092 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5093 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094
5095 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005096 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005097 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005099 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005100 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005101#endif
5102
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005103 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005104 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005105 return; /* don't free the progs and patterns now */
5106 }
5107 }
5108
5109 /*
5110 * Something failed, free the allocated memory.
5111 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005112 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005114 vim_free(syn_opt_arg.cont_list);
5115 vim_free(syn_opt_arg.cont_in_list);
5116 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117
5118 if (rest == NULL)
5119 EMSG2(_(e_invarg2), arg);
5120}
5121
5122/*
5123 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5124 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5125 */
5126 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005127syn_cmd_region(
5128 exarg_T *eap,
5129 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130{
5131 char_u *arg = eap->arg;
5132 char_u *group_name_end;
5133 char_u *rest; /* next arg, NULL on error */
5134 char_u *key_end;
5135 char_u *key = NULL;
5136 char_u *p;
5137 int item;
5138#define ITEM_START 0
5139#define ITEM_SKIP 1
5140#define ITEM_END 2
5141#define ITEM_MATCHGROUP 3
5142 struct pat_ptr
5143 {
5144 synpat_T *pp_synp; /* pointer to syn_pattern */
5145 int pp_matchgroup_id; /* matchgroup ID */
5146 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5147 } *(pat_ptrs[3]);
5148 /* patterns found in the line */
5149 struct pat_ptr *ppp;
5150 struct pat_ptr *ppp_next;
5151 int pat_count = 0; /* nr of syn_patterns found */
5152 int syn_id;
5153 int matchgroup_id = 0;
5154 int not_enough = FALSE; /* not enough arguments */
5155 int illegal = FALSE; /* illegal arguments */
5156 int success = FALSE;
5157 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005158 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005159 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005160
5161 /* Isolate the group name, check for validity */
5162 rest = get_group_name(arg, &group_name_end);
5163
5164 pat_ptrs[0] = NULL;
5165 pat_ptrs[1] = NULL;
5166 pat_ptrs[2] = NULL;
5167
5168 init_syn_patterns();
5169
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005170 syn_opt_arg.flags = 0;
5171 syn_opt_arg.keyword = FALSE;
5172 syn_opt_arg.sync_idx = NULL;
5173 syn_opt_arg.has_cont_list = TRUE;
5174 syn_opt_arg.cont_list = NULL;
5175 syn_opt_arg.cont_in_list = NULL;
5176 syn_opt_arg.next_list = NULL;
5177
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178 /*
5179 * get the options, patterns and matchgroup.
5180 */
5181 while (rest != NULL && !ends_excmd(*rest))
5182 {
5183 /* Check for option arguments */
Bram Moolenaarde318c52017-01-17 16:27:10 +01005184 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005185 if (rest == NULL || ends_excmd(*rest))
5186 break;
5187
5188 /* must be a pattern or matchgroup then */
5189 key_end = rest;
Bram Moolenaar1c465442017-03-12 20:10:05 +01005190 while (*key_end && !VIM_ISWHITE(*key_end) && *key_end != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005191 ++key_end;
5192 vim_free(key);
5193 key = vim_strnsave_up(rest, (int)(key_end - rest));
5194 if (key == NULL) /* out of memory */
5195 {
5196 rest = NULL;
5197 break;
5198 }
5199 if (STRCMP(key, "MATCHGROUP") == 0)
5200 item = ITEM_MATCHGROUP;
5201 else if (STRCMP(key, "START") == 0)
5202 item = ITEM_START;
5203 else if (STRCMP(key, "END") == 0)
5204 item = ITEM_END;
5205 else if (STRCMP(key, "SKIP") == 0)
5206 {
5207 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5208 {
5209 illegal = TRUE;
5210 break;
5211 }
5212 item = ITEM_SKIP;
5213 }
5214 else
5215 break;
5216 rest = skipwhite(key_end);
5217 if (*rest != '=')
5218 {
5219 rest = NULL;
5220 EMSG2(_("E398: Missing '=': %s"), arg);
5221 break;
5222 }
5223 rest = skipwhite(rest + 1);
5224 if (*rest == NUL)
5225 {
5226 not_enough = TRUE;
5227 break;
5228 }
5229
5230 if (item == ITEM_MATCHGROUP)
5231 {
5232 p = skiptowhite(rest);
5233 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5234 matchgroup_id = 0;
5235 else
5236 {
5237 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5238 if (matchgroup_id == 0)
5239 {
5240 illegal = TRUE;
5241 break;
5242 }
5243 }
5244 rest = skipwhite(p);
5245 }
5246 else
5247 {
5248 /*
5249 * Allocate room for a syn_pattern, and link it in the list of
5250 * syn_patterns for this item, at the start (because the list is
5251 * used from end to start).
5252 */
5253 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5254 if (ppp == NULL)
5255 {
5256 rest = NULL;
5257 break;
5258 }
5259 ppp->pp_next = pat_ptrs[item];
5260 pat_ptrs[item] = ppp;
5261 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5262 if (ppp->pp_synp == NULL)
5263 {
5264 rest = NULL;
5265 break;
5266 }
5267
5268 /*
5269 * Get the syntax pattern and the following offset(s).
5270 */
5271 /* Enable the appropriate \z specials. */
5272 if (item == ITEM_START)
5273 reg_do_extmatch = REX_SET;
5274 else if (item == ITEM_SKIP || item == ITEM_END)
5275 reg_do_extmatch = REX_USE;
5276 rest = get_syn_pattern(rest, ppp->pp_synp);
5277 reg_do_extmatch = 0;
5278 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005279 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5281 ppp->pp_matchgroup_id = matchgroup_id;
5282 ++pat_count;
5283 }
5284 }
5285 vim_free(key);
5286 if (illegal || not_enough)
5287 rest = NULL;
5288
5289 /*
5290 * Must have a "start" and "end" pattern.
5291 */
5292 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5293 pat_ptrs[ITEM_END] == NULL))
5294 {
5295 not_enough = TRUE;
5296 rest = NULL;
5297 }
5298
5299 if (rest != NULL)
5300 {
5301 /*
5302 * Check for trailing garbage or command.
5303 * If OK, add the item.
5304 */
5305 eap->nextcmd = check_nextcmd(rest);
5306 if (!ends_excmd(*rest) || eap->skip)
5307 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005308 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005309 && (syn_id = syn_check_group(arg,
5310 (int)(group_name_end - arg))) != 0)
5311 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005312 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005313 /*
5314 * Store the start/skip/end in the syn_items list
5315 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005316 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005317 for (item = ITEM_START; item <= ITEM_END; ++item)
5318 {
5319 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5320 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005321 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5322 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5323 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005324 (item == ITEM_START) ? SPTYPE_START :
5325 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005326 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5327 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005328 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5329 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005330 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005331 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005332#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005333 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005334#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005335 if (item == ITEM_START)
5336 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005337 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005338 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005339 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005340 syn_opt_arg.cont_in_list;
5341 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005342 curwin->w_s->b_syn_containedin = TRUE;
5343 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005344 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005345 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005346 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005347 ++idx;
5348#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005349 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005350 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351#endif
5352 }
5353 }
5354
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005355 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005356 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357 success = TRUE; /* don't free the progs and patterns now */
5358 }
5359 }
5360
5361 /*
5362 * Free the allocated memory.
5363 */
5364 for (item = ITEM_START; item <= ITEM_END; ++item)
5365 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5366 {
5367 if (!success)
5368 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005369 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005370 vim_free(ppp->pp_synp->sp_pattern);
5371 }
5372 vim_free(ppp->pp_synp);
5373 ppp_next = ppp->pp_next;
5374 vim_free(ppp);
5375 }
5376
5377 if (!success)
5378 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005379 vim_free(syn_opt_arg.cont_list);
5380 vim_free(syn_opt_arg.cont_in_list);
5381 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005382 if (not_enough)
5383 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5384 else if (illegal || rest == NULL)
5385 EMSG2(_(e_invarg2), arg);
5386 }
5387}
5388
5389/*
5390 * A simple syntax group ID comparison function suitable for use in qsort()
5391 */
5392 static int
5393#ifdef __BORLANDC__
5394_RTLENTRYF
5395#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005396syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397{
5398 const short *s1 = v1;
5399 const short *s2 = v2;
5400
5401 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5402}
5403
5404/*
5405 * Combines lists of syntax clusters.
5406 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5407 */
5408 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005409syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410{
5411 int count1 = 0;
5412 int count2 = 0;
5413 short *g1;
5414 short *g2;
5415 short *clstr = NULL;
5416 int count;
5417 int round;
5418
5419 /*
5420 * Handle degenerate cases.
5421 */
5422 if (*clstr2 == NULL)
5423 return;
5424 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5425 {
5426 if (list_op == CLUSTER_REPLACE)
5427 vim_free(*clstr1);
5428 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5429 *clstr1 = *clstr2;
5430 else
5431 vim_free(*clstr2);
5432 return;
5433 }
5434
5435 for (g1 = *clstr1; *g1; g1++)
5436 ++count1;
5437 for (g2 = *clstr2; *g2; g2++)
5438 ++count2;
5439
5440 /*
5441 * For speed purposes, sort both lists.
5442 */
5443 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5444 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5445
5446 /*
5447 * We proceed in two passes; in round 1, we count the elements to place
5448 * in the new list, and in round 2, we allocate and populate the new
5449 * list. For speed, we use a mergesort-like method, adding the smaller
5450 * of the current elements in each list to the new list.
5451 */
5452 for (round = 1; round <= 2; round++)
5453 {
5454 g1 = *clstr1;
5455 g2 = *clstr2;
5456 count = 0;
5457
5458 /*
5459 * First, loop through the lists until one of them is empty.
5460 */
5461 while (*g1 && *g2)
5462 {
5463 /*
5464 * We always want to add from the first list.
5465 */
5466 if (*g1 < *g2)
5467 {
5468 if (round == 2)
5469 clstr[count] = *g1;
5470 count++;
5471 g1++;
5472 continue;
5473 }
5474 /*
5475 * We only want to add from the second list if we're adding the
5476 * lists.
5477 */
5478 if (list_op == CLUSTER_ADD)
5479 {
5480 if (round == 2)
5481 clstr[count] = *g2;
5482 count++;
5483 }
5484 if (*g1 == *g2)
5485 g1++;
5486 g2++;
5487 }
5488
5489 /*
5490 * Now add the leftovers from whichever list didn't get finished
5491 * first. As before, we only want to add from the second list if
5492 * we're adding the lists.
5493 */
5494 for (; *g1; g1++, count++)
5495 if (round == 2)
5496 clstr[count] = *g1;
5497 if (list_op == CLUSTER_ADD)
5498 for (; *g2; g2++, count++)
5499 if (round == 2)
5500 clstr[count] = *g2;
5501
5502 if (round == 1)
5503 {
5504 /*
5505 * If the group ended up empty, we don't need to allocate any
5506 * space for it.
5507 */
5508 if (count == 0)
5509 {
5510 clstr = NULL;
5511 break;
5512 }
5513 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5514 if (clstr == NULL)
5515 break;
5516 clstr[count] = 0;
5517 }
5518 }
5519
5520 /*
5521 * Finally, put the new list in place.
5522 */
5523 vim_free(*clstr1);
5524 vim_free(*clstr2);
5525 *clstr1 = clstr;
5526}
5527
5528/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005529 * Lookup a syntax cluster name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005530 * If it is not found, 0 is returned.
5531 */
5532 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005533syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005534{
5535 int i;
5536 char_u *name_u;
5537
5538 /* Avoid using stricmp() too much, it's slow on some systems */
5539 name_u = vim_strsave_up(name);
5540 if (name_u == NULL)
5541 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005542 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5543 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5544 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005545 break;
5546 vim_free(name_u);
5547 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5548}
5549
5550/*
5551 * Like syn_scl_name2id(), but take a pointer + length argument.
5552 */
5553 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005554syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555{
5556 char_u *name;
5557 int id = 0;
5558
5559 name = vim_strnsave(linep, len);
5560 if (name != NULL)
5561 {
5562 id = syn_scl_name2id(name);
5563 vim_free(name);
5564 }
5565 return id;
5566}
5567
5568/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005569 * Find syntax cluster name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005570 * The argument is a pointer to the name and the length of the name.
5571 * If it doesn't exist yet, a new entry is created.
5572 * Return 0 for failure.
5573 */
5574 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005575syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576{
5577 int id;
5578 char_u *name;
5579
5580 name = vim_strnsave(pp, len);
5581 if (name == NULL)
5582 return 0;
5583
5584 id = syn_scl_name2id(name);
5585 if (id == 0) /* doesn't exist yet */
5586 id = syn_add_cluster(name);
5587 else
5588 vim_free(name);
5589 return id;
5590}
5591
5592/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01005593 * Add new syntax cluster and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594 * "name" must be an allocated string, it will be consumed.
5595 * Return 0 for failure.
5596 */
5597 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005598syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005599{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005600 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601
5602 /*
5603 * First call for this growarray: init growing array.
5604 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005605 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005607 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5608 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005609 }
5610
Bram Moolenaar42431a72011-04-01 14:44:59 +02005611 len = curwin->w_s->b_syn_clusters.ga_len;
5612 if (len >= MAX_CLUSTER_ID)
5613 {
5614 EMSG((char_u *)_("E848: Too many syntax clusters"));
5615 vim_free(name);
5616 return 0;
5617 }
5618
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 /*
5620 * Make room for at least one other cluster entry.
5621 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005622 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005623 {
5624 vim_free(name);
5625 return 0;
5626 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005627
Bram Moolenaar860cae12010-06-05 23:22:07 +02005628 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5629 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5630 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5631 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5632 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633
Bram Moolenaar217ad922005-03-20 22:37:15 +00005634 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005635 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005636 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005637 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005638
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 return len + SYNID_CLUSTER;
5640}
5641
5642/*
5643 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5644 * [add={groupname},..] [remove={groupname},..]".
5645 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005646 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005647syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005648{
5649 char_u *arg = eap->arg;
5650 char_u *group_name_end;
5651 char_u *rest;
5652 int scl_id;
5653 short *clstr_list;
5654 int got_clstr = FALSE;
5655 int opt_len;
5656 int list_op;
5657
5658 eap->nextcmd = find_nextcmd(arg);
5659 if (eap->skip)
5660 return;
5661
5662 rest = get_group_name(arg, &group_name_end);
5663
5664 if (rest != NULL)
5665 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005666 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5667 if (scl_id == 0)
5668 return;
5669 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670
5671 for (;;)
5672 {
5673 if (STRNICMP(rest, "add", 3) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005674 && (VIM_ISWHITE(rest[3]) || rest[3] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 {
5676 opt_len = 3;
5677 list_op = CLUSTER_ADD;
5678 }
5679 else if (STRNICMP(rest, "remove", 6) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005680 && (VIM_ISWHITE(rest[6]) || rest[6] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681 {
5682 opt_len = 6;
5683 list_op = CLUSTER_SUBTRACT;
5684 }
5685 else if (STRNICMP(rest, "contains", 8) == 0
Bram Moolenaar1c465442017-03-12 20:10:05 +01005686 && (VIM_ISWHITE(rest[8]) || rest[8] == '='))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687 {
5688 opt_len = 8;
5689 list_op = CLUSTER_REPLACE;
5690 }
5691 else
5692 break;
5693
5694 clstr_list = NULL;
Bram Moolenaarde318c52017-01-17 16:27:10 +01005695 if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005696 {
5697 EMSG2(_(e_invarg2), rest);
5698 break;
5699 }
Bram Moolenaarde318c52017-01-17 16:27:10 +01005700 if (scl_id >= 0)
5701 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005702 &clstr_list, list_op);
Bram Moolenaard7a96152017-01-22 15:28:55 +01005703 else
5704 vim_free(clstr_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705 got_clstr = TRUE;
5706 }
5707
5708 if (got_clstr)
5709 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005710 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005711 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005712 }
5713 }
5714
5715 if (!got_clstr)
5716 EMSG(_("E400: No cluster specified"));
5717 if (rest == NULL || !ends_excmd(*rest))
5718 EMSG2(_(e_invarg2), arg);
5719}
5720
5721/*
5722 * On first call for current buffer: Init growing array.
5723 */
5724 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005725init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005727 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5728 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005729}
5730
5731/*
5732 * Get one pattern for a ":syntax match" or ":syntax region" command.
5733 * Stores the pattern and program in a synpat_T.
5734 * Returns a pointer to the next argument, or NULL in case of an error.
5735 */
5736 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005737get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738{
5739 char_u *end;
5740 int *p;
5741 int idx;
5742 char_u *cpo_save;
5743
5744 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005745 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746 return NULL;
5747
5748 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5749 if (*end != *arg) /* end delimiter not found */
5750 {
5751 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5752 return NULL;
5753 }
5754 /* store the pattern and compiled regexp program */
5755 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5756 return NULL;
5757
5758 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5759 cpo_save = p_cpo;
5760 p_cpo = (char_u *)"";
5761 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5762 p_cpo = cpo_save;
5763
5764 if (ci->sp_prog == NULL)
5765 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005766 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005767#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005768 syn_clear_time(&ci->sp_time);
5769#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005770
5771 /*
5772 * Check for a match, highlight or region offset.
5773 */
5774 ++end;
5775 do
5776 {
5777 for (idx = SPO_COUNT; --idx >= 0; )
5778 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5779 break;
5780 if (idx >= 0)
5781 {
5782 p = &(ci->sp_offsets[idx]);
5783 if (idx != SPO_LC_OFF)
5784 switch (end[3])
5785 {
5786 case 's': break;
5787 case 'b': break;
5788 case 'e': idx += SPO_COUNT; break;
5789 default: idx = -1; break;
5790 }
5791 if (idx >= 0)
5792 {
5793 ci->sp_off_flags |= (1 << idx);
5794 if (idx == SPO_LC_OFF) /* lc=99 */
5795 {
5796 end += 3;
5797 *p = getdigits(&end);
5798
5799 /* "lc=" offset automatically sets "ms=" offset */
5800 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5801 {
5802 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5803 ci->sp_offsets[SPO_MS_OFF] = *p;
5804 }
5805 }
5806 else /* yy=x+99 */
5807 {
5808 end += 4;
5809 if (*end == '+')
5810 {
5811 ++end;
5812 *p = getdigits(&end); /* positive offset */
5813 }
5814 else if (*end == '-')
5815 {
5816 ++end;
5817 *p = -getdigits(&end); /* negative offset */
5818 }
5819 }
5820 if (*end != ',')
5821 break;
5822 ++end;
5823 }
5824 }
5825 } while (idx >= 0);
5826
Bram Moolenaar1c465442017-03-12 20:10:05 +01005827 if (!ends_excmd(*end) && !VIM_ISWHITE(*end))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005828 {
5829 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5830 return NULL;
5831 }
5832 return skipwhite(end);
5833}
5834
5835/*
5836 * Handle ":syntax sync .." command.
5837 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005838 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005839syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840{
5841 char_u *arg_start = eap->arg;
5842 char_u *arg_end;
5843 char_u *key = NULL;
5844 char_u *next_arg;
5845 int illegal = FALSE;
5846 int finished = FALSE;
5847 long n;
5848 char_u *cpo_save;
5849
5850 if (ends_excmd(*arg_start))
5851 {
5852 syn_cmd_list(eap, TRUE);
5853 return;
5854 }
5855
5856 while (!ends_excmd(*arg_start))
5857 {
5858 arg_end = skiptowhite(arg_start);
5859 next_arg = skipwhite(arg_end);
5860 vim_free(key);
5861 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5862 if (STRCMP(key, "CCOMMENT") == 0)
5863 {
5864 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005865 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866 if (!ends_excmd(*next_arg))
5867 {
5868 arg_end = skiptowhite(next_arg);
5869 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005870 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005871 (int)(arg_end - next_arg));
5872 next_arg = skipwhite(arg_end);
5873 }
5874 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005875 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005876 }
5877 else if ( STRNCMP(key, "LINES", 5) == 0
5878 || STRNCMP(key, "MINLINES", 8) == 0
5879 || STRNCMP(key, "MAXLINES", 8) == 0
5880 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5881 {
5882 if (key[4] == 'S')
5883 arg_end = key + 6;
5884 else if (key[0] == 'L')
5885 arg_end = key + 11;
5886 else
5887 arg_end = key + 9;
5888 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5889 {
5890 illegal = TRUE;
5891 break;
5892 }
5893 n = getdigits(&arg_end);
5894 if (!eap->skip)
5895 {
5896 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005897 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005898 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005899 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005900 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005901 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005902 }
5903 }
5904 else if (STRCMP(key, "FROMSTART") == 0)
5905 {
5906 if (!eap->skip)
5907 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005908 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5909 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005910 }
5911 }
5912 else if (STRCMP(key, "LINECONT") == 0)
5913 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005914 if (*next_arg == NUL) /* missing pattern */
5915 {
5916 illegal = TRUE;
5917 break;
5918 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005919 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005920 {
5921 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5922 finished = TRUE;
5923 break;
5924 }
5925 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5926 if (*arg_end != *next_arg) /* end delimiter not found */
5927 {
5928 illegal = TRUE;
5929 break;
5930 }
5931
5932 if (!eap->skip)
5933 {
5934 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005935 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005936 (int)(arg_end - next_arg - 1))) == NULL)
5937 {
5938 finished = TRUE;
5939 break;
5940 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005941 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005942
5943 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5944 cpo_save = p_cpo;
5945 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005946 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005947 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005948 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005949#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005950 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5951#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005952
Bram Moolenaar860cae12010-06-05 23:22:07 +02005953 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005954 {
Bram Moolenaard23a8232018-02-10 18:45:26 +01005955 VIM_CLEAR(curwin->w_s->b_syn_linecont_pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005956 finished = TRUE;
5957 break;
5958 }
5959 }
5960 next_arg = skipwhite(arg_end + 1);
5961 }
5962 else
5963 {
5964 eap->arg = next_arg;
5965 if (STRCMP(key, "MATCH") == 0)
5966 syn_cmd_match(eap, TRUE);
5967 else if (STRCMP(key, "REGION") == 0)
5968 syn_cmd_region(eap, TRUE);
5969 else if (STRCMP(key, "CLEAR") == 0)
5970 syn_cmd_clear(eap, TRUE);
5971 else
5972 illegal = TRUE;
5973 finished = TRUE;
5974 break;
5975 }
5976 arg_start = next_arg;
5977 }
5978 vim_free(key);
5979 if (illegal)
5980 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5981 else if (!finished)
5982 {
5983 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005984 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005985 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005986 }
5987}
5988
5989/*
5990 * Convert a line of highlight group names into a list of group ID numbers.
5991 * "arg" should point to the "contains" or "nextgroup" keyword.
5992 * "arg" is advanced to after the last group name.
5993 * Careful: the argument is modified (NULs added).
5994 * returns FAIL for some error, OK for success.
5995 */
5996 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005997get_id_list(
5998 char_u **arg,
5999 int keylen, /* length of keyword */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006000 short **list, /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00006001 NULL, the list is silently skipped! */
Bram Moolenaarde318c52017-01-17 16:27:10 +01006002 int skip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006003{
6004 char_u *p = NULL;
6005 char_u *end;
6006 int round;
6007 int count;
6008 int total_count = 0;
6009 short *retval = NULL;
6010 char_u *name;
6011 regmatch_T regmatch;
6012 int id;
6013 int i;
6014 int failed = FALSE;
6015
6016 /*
6017 * We parse the list twice:
6018 * round == 1: count the number of items, allocate the array.
6019 * round == 2: fill the array with the items.
6020 * In round 1 new groups may be added, causing the number of items to
6021 * grow when a regexp is used. In that case round 1 is done once again.
6022 */
6023 for (round = 1; round <= 2; ++round)
6024 {
6025 /*
6026 * skip "contains"
6027 */
6028 p = skipwhite(*arg + keylen);
6029 if (*p != '=')
6030 {
6031 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6032 break;
6033 }
6034 p = skipwhite(p + 1);
6035 if (ends_excmd(*p))
6036 {
6037 EMSG2(_("E406: Empty argument: %s"), *arg);
6038 break;
6039 }
6040
6041 /*
6042 * parse the arguments after "contains"
6043 */
6044 count = 0;
6045 while (!ends_excmd(*p))
6046 {
Bram Moolenaar1c465442017-03-12 20:10:05 +01006047 for (end = p; *end && !VIM_ISWHITE(*end) && *end != ','; ++end)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006048 ;
6049 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6050 if (name == NULL)
6051 {
6052 failed = TRUE;
6053 break;
6054 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006055 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006056 if ( STRCMP(name + 1, "ALLBUT") == 0
6057 || STRCMP(name + 1, "ALL") == 0
6058 || STRCMP(name + 1, "TOP") == 0
6059 || STRCMP(name + 1, "CONTAINED") == 0)
6060 {
6061 if (TOUPPER_ASC(**arg) != 'C')
6062 {
6063 EMSG2(_("E407: %s not allowed here"), name + 1);
6064 failed = TRUE;
6065 vim_free(name);
6066 break;
6067 }
6068 if (count != 0)
6069 {
Bram Moolenaard7a96152017-01-22 15:28:55 +01006070 EMSG2(_("E408: %s must be first in contains list"),
6071 name + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006072 failed = TRUE;
6073 vim_free(name);
6074 break;
6075 }
6076 if (name[1] == 'A')
6077 id = SYNID_ALLBUT;
6078 else if (name[1] == 'T')
6079 id = SYNID_TOP;
6080 else
6081 id = SYNID_CONTAINED;
6082 id += current_syn_inc_tag;
6083 }
6084 else if (name[1] == '@')
6085 {
Bram Moolenaareb46f8f2017-01-17 19:48:53 +01006086 if (skip)
6087 id = -1;
6088 else
Bram Moolenaarde318c52017-01-17 16:27:10 +01006089 id = syn_check_cluster(name + 2, (int)(end - p - 1));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006090 }
6091 else
6092 {
6093 /*
6094 * Handle full group name.
6095 */
6096 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6097 id = syn_check_group(name + 1, (int)(end - p));
6098 else
6099 {
6100 /*
6101 * Handle match of regexp with group names.
6102 */
6103 *name = '^';
6104 STRCAT(name, "$");
6105 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6106 if (regmatch.regprog == NULL)
6107 {
6108 failed = TRUE;
6109 vim_free(name);
6110 break;
6111 }
6112
6113 regmatch.rm_ic = TRUE;
6114 id = 0;
6115 for (i = highlight_ga.ga_len; --i >= 0; )
6116 {
6117 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6118 (colnr_T)0))
6119 {
6120 if (round == 2)
6121 {
6122 /* Got more items than expected; can happen
6123 * when adding items that match:
6124 * "contains=a.*b,axb".
6125 * Go back to first round */
6126 if (count >= total_count)
6127 {
6128 vim_free(retval);
6129 round = 1;
6130 }
6131 else
6132 retval[count] = i + 1;
6133 }
6134 ++count;
6135 id = -1; /* remember that we found one */
6136 }
6137 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006138 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006139 }
6140 }
6141 vim_free(name);
6142 if (id == 0)
6143 {
6144 EMSG2(_("E409: Unknown group name: %s"), p);
6145 failed = TRUE;
6146 break;
6147 }
6148 if (id > 0)
6149 {
6150 if (round == 2)
6151 {
6152 /* Got more items than expected, go back to first round */
6153 if (count >= total_count)
6154 {
6155 vim_free(retval);
6156 round = 1;
6157 }
6158 else
6159 retval[count] = id;
6160 }
6161 ++count;
6162 }
6163 p = skipwhite(end);
6164 if (*p != ',')
6165 break;
6166 p = skipwhite(p + 1); /* skip comma in between arguments */
6167 }
6168 if (failed)
6169 break;
6170 if (round == 1)
6171 {
6172 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6173 if (retval == NULL)
6174 break;
6175 retval[count] = 0; /* zero means end of the list */
6176 total_count = count;
6177 }
6178 }
6179
6180 *arg = p;
6181 if (failed || retval == NULL)
6182 {
6183 vim_free(retval);
6184 return FAIL;
6185 }
6186
6187 if (*list == NULL)
6188 *list = retval;
6189 else
6190 vim_free(retval); /* list already found, don't overwrite it */
6191
6192 return OK;
6193}
6194
6195/*
6196 * Make a copy of an ID list.
6197 */
6198 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006199copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006200{
6201 int len;
6202 int count;
6203 short *retval;
6204
6205 if (list == NULL)
6206 return NULL;
6207
6208 for (count = 0; list[count]; ++count)
6209 ;
6210 len = (count + 1) * sizeof(short);
6211 retval = (short *)alloc((unsigned)len);
6212 if (retval != NULL)
6213 mch_memmove(retval, list, (size_t)len);
6214
6215 return retval;
6216}
6217
6218/*
6219 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6220 * "cur_si" can be NULL if not checking the "containedin" list.
6221 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6222 * the current item.
6223 * This function is called very often, keep it fast!!
6224 */
6225 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006226in_id_list(
6227 stateitem_T *cur_si, /* current item or NULL */
6228 short *list, /* id list */
6229 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6230 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006231{
6232 int retval;
6233 short *scl_list;
6234 short item;
6235 short id = ssp->id;
6236 static int depth = 0;
6237 int r;
6238
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006239 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006240 if (cur_si != NULL && ssp->cont_in_list != NULL
6241 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006242 {
6243 /* Ignore transparent items without a contains argument. Double check
6244 * that we don't go back past the first one. */
6245 while ((cur_si->si_flags & HL_TRANS_CONT)
6246 && cur_si > (stateitem_T *)(current_state.ga_data))
6247 --cur_si;
6248 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6249 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006250 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6251 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252 return TRUE;
6253 }
6254
6255 if (list == NULL)
6256 return FALSE;
6257
6258 /*
6259 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6260 * inside anything. Only allow not-contained groups.
6261 */
6262 if (list == ID_LIST_ALL)
6263 return !contained;
6264
6265 /*
6266 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6267 * contains list. We also require that "id" is at the same ":syn include"
6268 * level as the list.
6269 */
6270 item = *list;
6271 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6272 {
6273 if (item < SYNID_TOP)
6274 {
6275 /* ALL or ALLBUT: accept all groups in the same file */
6276 if (item - SYNID_ALLBUT != ssp->inc_tag)
6277 return FALSE;
6278 }
6279 else if (item < SYNID_CONTAINED)
6280 {
6281 /* TOP: accept all not-contained groups in the same file */
6282 if (item - SYNID_TOP != ssp->inc_tag || contained)
6283 return FALSE;
6284 }
6285 else
6286 {
6287 /* CONTAINED: accept all contained groups in the same file */
6288 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6289 return FALSE;
6290 }
6291 item = *++list;
6292 retval = FALSE;
6293 }
6294 else
6295 retval = TRUE;
6296
6297 /*
6298 * Return "retval" if id is in the contains list.
6299 */
6300 while (item != 0)
6301 {
6302 if (item == id)
6303 return retval;
6304 if (item >= SYNID_CLUSTER)
6305 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006306 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006307 /* restrict recursiveness to 30 to avoid an endless loop for a
6308 * cluster that includes itself (indirectly) */
6309 if (scl_list != NULL && depth < 30)
6310 {
6311 ++depth;
6312 r = in_id_list(NULL, scl_list, ssp, contained);
6313 --depth;
6314 if (r)
6315 return retval;
6316 }
6317 }
6318 item = *++list;
6319 }
6320 return !retval;
6321}
6322
6323struct subcommand
6324{
Bram Moolenaard99df422016-01-29 23:20:40 +01006325 char *name; /* subcommand name */
6326 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327};
6328
6329static struct subcommand subcommands[] =
6330{
6331 {"case", syn_cmd_case},
6332 {"clear", syn_cmd_clear},
6333 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006334 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006335 {"enable", syn_cmd_enable},
6336 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006337 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338 {"keyword", syn_cmd_keyword},
6339 {"list", syn_cmd_list},
6340 {"manual", syn_cmd_manual},
6341 {"match", syn_cmd_match},
6342 {"on", syn_cmd_on},
6343 {"off", syn_cmd_off},
6344 {"region", syn_cmd_region},
6345 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006346 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347 {"sync", syn_cmd_sync},
6348 {"", syn_cmd_list},
6349 {NULL, NULL}
6350};
6351
6352/*
6353 * ":syntax".
6354 * This searches the subcommands[] table for the subcommand name, and calls a
6355 * syntax_subcommand() function to do the rest.
6356 */
6357 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006358ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006359{
6360 char_u *arg = eap->arg;
6361 char_u *subcmd_end;
6362 char_u *subcmd_name;
6363 int i;
6364
6365 syn_cmdlinep = eap->cmdlinep;
6366
6367 /* isolate subcommand name */
6368 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6369 ;
6370 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6371 if (subcmd_name != NULL)
6372 {
6373 if (eap->skip) /* skip error messages for all subcommands */
6374 ++emsg_skip;
6375 for (i = 0; ; ++i)
6376 {
6377 if (subcommands[i].name == NULL)
6378 {
6379 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6380 break;
6381 }
6382 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6383 {
6384 eap->arg = skipwhite(subcmd_end);
6385 (subcommands[i].func)(eap, FALSE);
6386 break;
6387 }
6388 }
6389 vim_free(subcmd_name);
6390 if (eap->skip)
6391 --emsg_skip;
6392 }
6393}
6394
Bram Moolenaar860cae12010-06-05 23:22:07 +02006395 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006396ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006397{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006398 char_u *old_value;
6399 char_u *new_value;
6400
Bram Moolenaar860cae12010-06-05 23:22:07 +02006401 if (curwin->w_s == &curwin->w_buffer->b_s)
6402 {
6403 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6404 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006405 hash_init(&curwin->w_s->b_keywtab);
6406 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006407#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006408 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006409 curwin->w_p_spell = FALSE; /* No spell checking */
6410 clear_string_option(&curwin->w_s->b_p_spc);
6411 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006412 clear_string_option(&curwin->w_s->b_p_spl);
6413#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006414 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006415 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006416
6417 /* save value of b:current_syntax */
6418 old_value = get_var_value((char_u *)"b:current_syntax");
6419 if (old_value != NULL)
6420 old_value = vim_strsave(old_value);
6421
Bram Moolenaard1413d92016-03-02 21:51:56 +01006422#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006423 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6424 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006425 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006426#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006427
6428 /* move value of b:current_syntax to w:current_syntax */
6429 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006430 if (new_value != NULL)
6431 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006432
6433 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006434 if (old_value == NULL)
6435 do_unlet((char_u *)"b:current_syntax", TRUE);
6436 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006437 {
6438 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6439 vim_free(old_value);
6440 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006441}
6442
6443 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006444syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006445{
6446 return (win->w_s->b_syn_patterns.ga_len != 0
6447 || win->w_s->b_syn_clusters.ga_len != 0
6448 || win->w_s->b_keywtab.ht_used > 0
6449 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450}
6451
6452#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6453
6454static enum
6455{
6456 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006457 EXP_CASE, /* expand ":syn case" arguments */
6458 EXP_SPELL, /* expand ":syn spell" arguments */
6459 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460} expand_what;
6461
Bram Moolenaar4f688582007-07-24 12:34:30 +00006462/*
6463 * Reset include_link, include_default, include_none to 0.
6464 * Called when we are done expanding.
6465 */
6466 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006467reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006468{
6469 include_link = include_default = include_none = 0;
6470}
6471
6472/*
6473 * Handle command line completion for :match and :echohl command: Add "None"
6474 * as highlight group.
6475 */
6476 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006477set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006478{
6479 xp->xp_context = EXPAND_HIGHLIGHT;
6480 xp->xp_pattern = arg;
6481 include_none = 1;
6482}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483
6484/*
6485 * Handle command line completion for :syntax command.
6486 */
6487 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006488set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006489{
6490 char_u *p;
6491
6492 /* Default: expand subcommands */
6493 xp->xp_context = EXPAND_SYNTAX;
6494 expand_what = EXP_SUBCMD;
6495 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006496 include_link = 0;
6497 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498
6499 /* (part of) subcommand already typed */
6500 if (*arg != NUL)
6501 {
6502 p = skiptowhite(arg);
6503 if (*p != NUL) /* past first word */
6504 {
6505 xp->xp_pattern = skipwhite(p);
6506 if (*skiptowhite(xp->xp_pattern) != NUL)
6507 xp->xp_context = EXPAND_NOTHING;
6508 else if (STRNICMP(arg, "case", p - arg) == 0)
6509 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006510 else if (STRNICMP(arg, "spell", p - arg) == 0)
6511 expand_what = EXP_SPELL;
6512 else if (STRNICMP(arg, "sync", p - arg) == 0)
6513 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6515 || STRNICMP(arg, "region", p - arg) == 0
6516 || STRNICMP(arg, "match", p - arg) == 0
6517 || STRNICMP(arg, "list", p - arg) == 0)
6518 xp->xp_context = EXPAND_HIGHLIGHT;
6519 else
6520 xp->xp_context = EXPAND_NOTHING;
6521 }
6522 }
6523}
6524
Bram Moolenaar071d4272004-06-13 20:20:40 +00006525/*
6526 * Function given to ExpandGeneric() to obtain the list syntax names for
6527 * expansion.
6528 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006529 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006530get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006531{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006532 switch (expand_what)
6533 {
6534 case EXP_SUBCMD:
6535 return (char_u *)subcommands[idx].name;
6536 case EXP_CASE:
6537 {
6538 static char *case_args[] = {"match", "ignore", NULL};
6539 return (char_u *)case_args[idx];
6540 }
6541 case EXP_SPELL:
6542 {
6543 static char *spell_args[] =
6544 {"toplevel", "notoplevel", "default", NULL};
6545 return (char_u *)spell_args[idx];
6546 }
6547 case EXP_SYNC:
6548 {
6549 static char *sync_args[] =
6550 {"ccomment", "clear", "fromstart",
6551 "linebreaks=", "linecont", "lines=", "match",
6552 "maxlines=", "minlines=", "region", NULL};
6553 return (char_u *)sync_args[idx];
6554 }
6555 }
6556 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557}
6558
6559#endif /* FEAT_CMDL_COMPL */
6560
Bram Moolenaar071d4272004-06-13 20:20:40 +00006561/*
6562 * Function called for expression evaluation: get syntax ID at file position.
6563 */
6564 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006565syn_get_id(
6566 win_T *wp,
6567 long lnum,
6568 colnr_T col,
6569 int trans, /* remove transparency */
6570 int *spellp, /* return: can do spell checking */
6571 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006572{
6573 /* When the position is not after the current position and in the same
6574 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006575 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006576 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006577 || col < current_col)
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006578 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006579 else if (wp->w_buffer == syn_buf
6580 && lnum == current_lnum
6581 && col > current_col)
6582 /* next_match may not be correct when moving around, e.g. with the
6583 * "skip" expression in searchpair() */
6584 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006585
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006586 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006587
6588 return (trans ? current_trans_id : current_id);
6589}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590
Bram Moolenaar860cae12010-06-05 23:22:07 +02006591#if defined(FEAT_CONCEAL) || defined(PROTO)
6592/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006593 * Get extra information about the syntax item. Must be called right after
6594 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006595 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006596 * Returns the current flags.
6597 */
6598 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006599get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006600{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006601 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006602 return current_flags;
6603}
6604
6605/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006606 * Return conceal substitution character
6607 */
6608 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006609syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006610{
6611 return current_sub_char;
6612}
6613#endif
6614
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006615#if defined(FEAT_EVAL) || defined(PROTO)
6616/*
6617 * Return the syntax ID at position "i" in the current stack.
6618 * The caller must have called syn_get_id() before to fill the stack.
6619 * Returns -1 when "i" is out of range.
6620 */
6621 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006622syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006623{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006624 if (i >= current_state.ga_len)
6625 {
6626 /* Need to invalidate the state, because we didn't properly finish it
6627 * for the last character, "keep_state" was TRUE. */
6628 invalidate_current_state();
6629 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006630 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006631 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006632 return CUR_STATE(i).si_id;
6633}
6634#endif
6635
Bram Moolenaar071d4272004-06-13 20:20:40 +00006636#if defined(FEAT_FOLDING) || defined(PROTO)
6637/*
6638 * Function called to get folding level for line "lnum" in window "wp".
6639 */
6640 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006641syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006642{
6643 int level = 0;
6644 int i;
6645
6646 /* Return quickly when there are no fold items at all. */
Bram Moolenaar06f1ed22017-06-18 22:41:03 +02006647 if (wp->w_s->b_syn_folditems != 0
6648 && !wp->w_s->b_syn_error
6649# ifdef SYN_TIME_LIMIT
6650 && !wp->w_s->b_syn_slow
6651# endif
6652 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653 {
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02006654 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006655
6656 for (i = 0; i < current_state.ga_len; ++i)
6657 if (CUR_STATE(i).si_flags & HL_FOLD)
6658 ++level;
6659 }
6660 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006661 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006662 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006663 if (level < 0)
6664 level = 0;
6665 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006666 return level;
6667}
6668#endif
6669
Bram Moolenaar01615492015-02-03 13:00:38 +01006670#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006671/*
6672 * ":syntime".
6673 */
6674 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006675ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006676{
6677 if (STRCMP(eap->arg, "on") == 0)
6678 syn_time_on = TRUE;
6679 else if (STRCMP(eap->arg, "off") == 0)
6680 syn_time_on = FALSE;
6681 else if (STRCMP(eap->arg, "clear") == 0)
6682 syntime_clear();
6683 else if (STRCMP(eap->arg, "report") == 0)
6684 syntime_report();
6685 else
6686 EMSG2(_(e_invarg2), eap->arg);
6687}
6688
6689 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006690syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006691{
6692 profile_zero(&st->total);
6693 profile_zero(&st->slowest);
6694 st->count = 0;
6695 st->match = 0;
6696}
6697
6698/*
6699 * Clear the syntax timing for the current buffer.
6700 */
6701 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006702syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006703{
6704 int idx;
6705 synpat_T *spp;
6706
6707 if (!syntax_present(curwin))
6708 {
6709 MSG(_(msg_no_items));
6710 return;
6711 }
6712 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6713 {
6714 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6715 syn_clear_time(&spp->sp_time);
6716 }
6717}
6718
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006719#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6720/*
6721 * Function given to ExpandGeneric() to obtain the possible arguments of the
6722 * ":syntime {on,off,clear,report}" command.
6723 */
6724 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006725get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006726{
6727 switch (idx)
6728 {
6729 case 0: return (char_u *)"on";
6730 case 1: return (char_u *)"off";
6731 case 2: return (char_u *)"clear";
6732 case 3: return (char_u *)"report";
6733 }
6734 return NULL;
6735}
6736#endif
6737
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006738typedef struct
6739{
6740 proftime_T total;
6741 int count;
6742 int match;
6743 proftime_T slowest;
6744 proftime_T average;
6745 int id;
6746 char_u *pattern;
6747} time_entry_T;
6748
6749 static int
6750#ifdef __BORLANDC__
6751_RTLENTRYF
6752#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006753syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006754{
6755 const time_entry_T *s1 = v1;
6756 const time_entry_T *s2 = v2;
6757
6758 return profile_cmp(&s1->total, &s2->total);
6759}
6760
6761/*
6762 * Clear the syntax timing for the current buffer.
6763 */
6764 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006765syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006766{
6767 int idx;
6768 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006769# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006770 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006771# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006772 int len;
6773 proftime_T total_total;
6774 int total_count = 0;
6775 garray_T ga;
6776 time_entry_T *p;
6777
6778 if (!syntax_present(curwin))
6779 {
6780 MSG(_(msg_no_items));
6781 return;
6782 }
6783
6784 ga_init2(&ga, sizeof(time_entry_T), 50);
6785 profile_zero(&total_total);
6786 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6787 {
6788 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6789 if (spp->sp_time.count > 0)
6790 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006791 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006792 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6793 p->total = spp->sp_time.total;
6794 profile_add(&total_total, &spp->sp_time.total);
6795 p->count = spp->sp_time.count;
6796 p->match = spp->sp_time.match;
6797 total_count += spp->sp_time.count;
6798 p->slowest = spp->sp_time.slowest;
6799# ifdef FEAT_FLOAT
6800 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6801 p->average = tm;
6802# endif
6803 p->id = spp->sp_syn.id;
6804 p->pattern = spp->sp_pattern;
6805 ++ga.ga_len;
6806 }
6807 }
6808
Bram Moolenaara2162552017-01-08 17:46:20 +01006809 /* Sort on total time. Skip if there are no items to avoid passing NULL
6810 * pointer to qsort(). */
6811 if (ga.ga_len > 1)
6812 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006813 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006814
6815 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6816 MSG_PUTS("\n");
6817 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6818 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006819 p = ((time_entry_T *)ga.ga_data) + idx;
6820
6821 MSG_PUTS(profile_msg(&p->total));
6822 MSG_PUTS(" "); /* make sure there is always a separating space */
6823 msg_advance(13);
6824 msg_outnum(p->count);
6825 MSG_PUTS(" ");
6826 msg_advance(20);
6827 msg_outnum(p->match);
6828 MSG_PUTS(" ");
6829 msg_advance(26);
6830 MSG_PUTS(profile_msg(&p->slowest));
6831 MSG_PUTS(" ");
6832 msg_advance(38);
6833# ifdef FEAT_FLOAT
6834 MSG_PUTS(profile_msg(&p->average));
6835 MSG_PUTS(" ");
6836# endif
6837 msg_advance(50);
6838 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6839 MSG_PUTS(" ");
6840
6841 msg_advance(69);
6842 if (Columns < 80)
6843 len = 20; /* will wrap anyway */
6844 else
6845 len = Columns - 70;
6846 if (len > (int)STRLEN(p->pattern))
6847 len = (int)STRLEN(p->pattern);
6848 msg_outtrans_len(p->pattern, len);
6849 MSG_PUTS("\n");
6850 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006851 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006852 if (!got_int)
6853 {
6854 MSG_PUTS("\n");
6855 MSG_PUTS(profile_msg(&total_total));
6856 msg_advance(13);
6857 msg_outnum(total_count);
6858 MSG_PUTS("\n");
6859 }
6860}
6861#endif
6862
Bram Moolenaar071d4272004-06-13 20:20:40 +00006863#endif /* FEAT_SYN_HL */
6864
Bram Moolenaar071d4272004-06-13 20:20:40 +00006865/**************************************
6866 * Highlighting stuff *
6867 **************************************/
6868
6869/*
6870 * The default highlight groups. These are compiled-in for fast startup and
6871 * they still work when the runtime files can't be found.
6872 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006873 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6874 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006875 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006876#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006877# define CENT(a, b) b
6878#else
6879# define CENT(a, b) a
6880#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006881static char *(highlight_init_both[]) = {
6882 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6883 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6884 CENT("IncSearch term=reverse cterm=reverse",
6885 "IncSearch term=reverse cterm=reverse gui=reverse"),
6886 CENT("ModeMsg term=bold cterm=bold",
6887 "ModeMsg term=bold cterm=bold gui=bold"),
6888 CENT("NonText term=bold ctermfg=Blue",
6889 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6890 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6891 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6892 CENT("StatusLineNC term=reverse cterm=reverse",
6893 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
6894 "default link EndOfBuffer NonText",
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006895 CENT("VertSplit term=reverse cterm=reverse",
6896 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006897#ifdef FEAT_CLIPBOARD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006898 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6899 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006900#endif
6901#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006902 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6903 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006904#endif
6905#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006906 CENT("PmenuSbar ctermbg=Grey",
6907 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006908#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006909 CENT("TabLineSel term=bold cterm=bold",
6910 "TabLineSel term=bold cterm=bold gui=bold"),
6911 CENT("TabLineFill term=reverse cterm=reverse",
6912 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00006913#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006914 "Cursor guibg=fg guifg=bg",
6915 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006916#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006917 "default link QuickFixLine Search",
6918 NULL
6919};
Bram Moolenaar071d4272004-06-13 20:20:40 +00006920
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006921/* Default colors only used with a light background. */
6922static char *(highlight_init_light[]) = {
6923 CENT("Directory term=bold ctermfg=DarkBlue",
6924 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6925 CENT("LineNr term=underline ctermfg=Brown",
6926 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6927 CENT("CursorLineNr term=bold ctermfg=Brown",
6928 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
6929 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6930 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6931 CENT("Question term=standout ctermfg=DarkGreen",
6932 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6933 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6934 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006935#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006936 CENT("SpellBad term=reverse ctermbg=LightRed",
6937 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6938 CENT("SpellCap term=reverse ctermbg=LightBlue",
6939 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6940 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6941 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6942 CENT("SpellLocal term=underline ctermbg=Cyan",
6943 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006944#endif
6945#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006946 CENT("PmenuThumb ctermbg=Black",
6947 "PmenuThumb ctermbg=Black guibg=Black"),
6948 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6949 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6950 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6951 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006952#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006953 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6954 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6955 CENT("Title term=bold ctermfg=DarkMagenta",
6956 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6957 CENT("WarningMsg term=standout ctermfg=DarkRed",
6958 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006959#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006960 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6961 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006962#endif
6963#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006964 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6965 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6966 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6967 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006968#endif
6969#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006970 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6971 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006972#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006973 CENT("Visual term=reverse",
6974 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006975#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006976 CENT("DiffAdd term=bold ctermbg=LightBlue",
6977 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6978 CENT("DiffChange term=bold ctermbg=LightMagenta",
6979 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6980 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6981 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006982#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006983 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6984 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006985#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006986 CENT("CursorColumn term=reverse ctermbg=LightGrey",
6987 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
6988 CENT("CursorLine term=underline cterm=underline",
6989 "CursorLine term=underline cterm=underline guibg=Grey90"),
6990 CENT("ColorColumn term=reverse ctermbg=LightRed",
6991 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006992#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006993#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006994 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6995 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02006996#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006997#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02006998 CENT("MatchParen term=reverse ctermbg=Cyan",
6999 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007000#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007001#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007002 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007003#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007004#ifdef FEAT_TERMINAL
7005 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
7006 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
7007 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
7008 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
7009#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007010#ifdef FEAT_MENU
7011 CENT("ToolbarLine term=underline ctermbg=LightGrey",
7012 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
7013 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007014 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007015#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007016 NULL
7017};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007018
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007019/* Default colors only used with a dark background. */
7020static char *(highlight_init_dark[]) = {
7021 CENT("Directory term=bold ctermfg=LightCyan",
7022 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7023 CENT("LineNr term=underline ctermfg=Yellow",
7024 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
7025 CENT("CursorLineNr term=bold ctermfg=Yellow",
7026 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
7027 CENT("MoreMsg term=bold ctermfg=LightGreen",
7028 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7029 CENT("Question term=standout ctermfg=LightGreen",
7030 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7031 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7032 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7033 CENT("SpecialKey term=bold ctermfg=LightBlue",
7034 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007035#ifdef FEAT_SPELL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007036 CENT("SpellBad term=reverse ctermbg=Red",
7037 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7038 CENT("SpellCap term=reverse ctermbg=Blue",
7039 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7040 CENT("SpellRare term=reverse ctermbg=Magenta",
7041 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7042 CENT("SpellLocal term=underline ctermbg=Cyan",
7043 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007044#endif
7045#ifdef FEAT_INS_EXPAND
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007046 CENT("PmenuThumb ctermbg=White",
7047 "PmenuThumb ctermbg=White guibg=White"),
7048 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7049 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
7050 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7051 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007052#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007053 CENT("Title term=bold ctermfg=LightMagenta",
7054 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7055 CENT("WarningMsg term=standout ctermfg=LightRed",
7056 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007057#ifdef FEAT_WILDMENU
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007058 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7059 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007060#endif
7061#ifdef FEAT_FOLDING
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007062 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7063 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7064 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7065 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007066#endif
7067#ifdef FEAT_SIGNS
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007068 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7069 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007070#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007071 CENT("Visual term=reverse",
7072 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007073#ifdef FEAT_DIFF
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007074 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7075 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7076 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7077 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7078 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7079 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007080#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007081 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7082 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007083#ifdef FEAT_SYN_HL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007084 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
7085 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
7086 CENT("CursorLine term=underline cterm=underline",
7087 "CursorLine term=underline cterm=underline guibg=Grey40"),
7088 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7089 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007090#endif
7091#ifdef FEAT_AUTOCMD
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007092 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7093 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007094#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007095#ifdef FEAT_CONCEAL
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007096 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7097 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
Bram Moolenaar860cae12010-06-05 23:22:07 +02007098#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007099#ifdef FEAT_GUI
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007100 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007101#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007102#ifdef FEAT_TERMINAL
7103 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
7104 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
7105 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
7106 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
7107#endif
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007108#ifdef FEAT_MENU
7109 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
Bram Moolenaarf3d769a2017-09-22 13:44:56 +02007110 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02007111 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
7112 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
7113#endif
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02007114 NULL
7115};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007116
7117 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007118init_highlight(
7119 int both, /* include groups where 'bg' doesn't matter */
7120 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121{
7122 int i;
7123 char **pp;
7124 static int had_both = FALSE;
7125#ifdef FEAT_EVAL
7126 char_u *p;
7127
7128 /*
7129 * Try finding the color scheme file. Used when a color file was loaded
7130 * and 'background' or 't_Co' is changed.
7131 */
7132 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007133 if (p != NULL)
7134 {
7135 /* The value of g:colors_name could be freed when sourcing the script,
7136 * making "p" invalid, so copy it. */
7137 char_u *copy_p = vim_strsave(p);
7138 int r;
7139
7140 if (copy_p != NULL)
7141 {
7142 r = load_colors(copy_p);
7143 vim_free(copy_p);
7144 if (r == OK)
7145 return;
7146 }
7147 }
7148
Bram Moolenaar071d4272004-06-13 20:20:40 +00007149#endif
7150
7151 /*
7152 * Didn't use a color file, use the compiled-in colors.
7153 */
7154 if (both)
7155 {
7156 had_both = TRUE;
7157 pp = highlight_init_both;
7158 for (i = 0; pp[i] != NULL; ++i)
7159 do_highlight((char_u *)pp[i], reset, TRUE);
7160 }
7161 else if (!had_both)
7162 /* Don't do anything before the call with both == TRUE from main().
7163 * Not everything has been setup then, and that call will overrule
7164 * everything anyway. */
7165 return;
7166
7167 if (*p_bg == 'l')
7168 pp = highlight_init_light;
7169 else
7170 pp = highlight_init_dark;
7171 for (i = 0; pp[i] != NULL; ++i)
7172 do_highlight((char_u *)pp[i], reset, TRUE);
7173
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007174 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007175 * depend on the number of colors available.
7176 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007177 * to avoid Statement highlighted text disappears.
7178 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007179 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007180 do_highlight((char_u *)(*p_bg == 'l'
7181 ? "Visual cterm=NONE ctermbg=LightGrey"
7182 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007183 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007184 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007185 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7186 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007187 if (*p_bg == 'l')
7188 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7189 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007190
Bram Moolenaar071d4272004-06-13 20:20:40 +00007191#ifdef FEAT_SYN_HL
7192 /*
7193 * If syntax highlighting is enabled load the highlighting for it.
7194 */
7195 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007196 {
7197 static int recursive = 0;
7198
7199 if (recursive >= 5)
7200 EMSG(_("E679: recursive loop loading syncolor.vim"));
7201 else
7202 {
7203 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007204 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007205 --recursive;
7206 }
7207 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208#endif
7209}
7210
7211/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007212 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007213 * Return OK for success, FAIL for failure.
7214 */
7215 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007216load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007217{
7218 char_u *buf;
7219 int retval = FAIL;
7220 static int recursive = FALSE;
7221
7222 /* When being called recursively, this is probably because setting
7223 * 'background' caused the highlighting to be reloaded. This means it is
7224 * working, thus we should return OK. */
7225 if (recursive)
7226 return OK;
7227
7228 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007229 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007230 if (buf != NULL)
7231 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007232 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007233 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007234 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007235#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007236 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007237#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007238 }
7239 recursive = FALSE;
7240
7241 return retval;
7242}
7243
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007244static char *(color_names[28]) = {
7245 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7246 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7247 "Gray", "Grey", "LightGray", "LightGrey",
7248 "DarkGray", "DarkGrey",
7249 "Blue", "LightBlue", "Green", "LightGreen",
7250 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7251 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7252 /* indices:
7253 * 0, 1, 2, 3,
7254 * 4, 5, 6, 7,
7255 * 8, 9, 10, 11,
7256 * 12, 13,
7257 * 14, 15, 16, 17,
7258 * 18, 19, 20, 21, 22,
7259 * 23, 24, 25, 26, 27 */
7260static int color_numbers_16[28] = {0, 1, 2, 3,
7261 4, 5, 6, 6,
7262 7, 7, 7, 7,
7263 8, 8,
7264 9, 9, 10, 10,
7265 11, 11, 12, 12, 13,
7266 13, 14, 14, 15, -1};
7267/* for xterm with 88 colors... */
7268static int color_numbers_88[28] = {0, 4, 2, 6,
7269 1, 5, 32, 72,
7270 84, 84, 7, 7,
7271 82, 82,
7272 12, 43, 10, 61,
7273 14, 63, 9, 74, 13,
7274 75, 11, 78, 15, -1};
7275/* for xterm with 256 colors... */
7276static int color_numbers_256[28] = {0, 4, 2, 6,
7277 1, 5, 130, 130,
7278 248, 248, 7, 7,
7279 242, 242,
7280 12, 81, 10, 121,
7281 14, 159, 9, 224, 13,
7282 225, 11, 229, 15, -1};
7283/* for terminals with less than 16 colors... */
7284static int color_numbers_8[28] = {0, 4, 2, 6,
7285 1, 5, 3, 3,
7286 7, 7, 7, 7,
7287 0+8, 0+8,
7288 4+8, 4+8, 2+8, 2+8,
7289 6+8, 6+8, 1+8, 1+8, 5+8,
7290 5+8, 3+8, 3+8, 7+8, -1};
7291
7292/*
7293 * Lookup the "cterm" value to be used for color with index "idx" in
7294 * color_names[].
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007295 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
7296 * colors, otherwise it will be unchanged.
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007297 */
7298 int
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007299lookup_color(int idx, int foreground, int *boldp)
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007300{
7301 int color = color_numbers_16[idx];
7302 char_u *p;
7303
7304 /* Use the _16 table to check if it's a valid color name. */
7305 if (color < 0)
7306 return -1;
7307
7308 if (t_colors == 8)
7309 {
7310 /* t_Co is 8: use the 8 colors table */
7311#if defined(__QNXNTO__)
7312 color = color_numbers_8_qansi[idx];
7313#else
7314 color = color_numbers_8[idx];
7315#endif
7316 if (foreground)
7317 {
7318 /* set/reset bold attribute to get light foreground
7319 * colors (on some terminals, e.g. "linux") */
7320 if (color & 8)
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007321 *boldp = TRUE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007322 else
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007323 *boldp = FALSE;
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007324 }
7325 color &= 7; /* truncate to 8 colors */
7326 }
7327 else if (t_colors == 16 || t_colors == 88
7328 || t_colors >= 256)
7329 {
7330 /*
7331 * Guess: if the termcap entry ends in 'm', it is
7332 * probably an xterm-like terminal. Use the changed
7333 * order for colors.
7334 */
7335 if (*T_CAF != NUL)
7336 p = T_CAF;
7337 else
7338 p = T_CSF;
7339 if (*p != NUL && (t_colors > 256
7340 || *(p + STRLEN(p) - 1) == 'm'))
7341 {
7342 if (t_colors == 88)
7343 color = color_numbers_88[idx];
7344 else if (t_colors >= 256)
7345 color = color_numbers_256[idx];
7346 else
7347 color = color_numbers_8[idx];
7348 }
Bram Moolenaarc9026092017-10-04 19:35:02 +02007349#ifdef FEAT_TERMRESPONSE
Bram Moolenaara0a6f272017-10-04 18:04:16 +02007350 if (t_colors >= 256 && color == 15 && is_mac_terminal)
7351 /* Terminal.app has a bug: 15 is light grey. Use white
7352 * from the color cube instead. */
7353 color = 231;
Bram Moolenaarc9026092017-10-04 19:35:02 +02007354#endif
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007355 }
7356 return color;
7357}
7358
Bram Moolenaar071d4272004-06-13 20:20:40 +00007359/*
7360 * Handle the ":highlight .." command.
7361 * When using ":hi clear" this is called recursively for each group with
7362 * "forceit" and "init" both TRUE.
7363 */
7364 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007365do_highlight(
7366 char_u *line,
7367 int forceit,
7368 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007369{
7370 char_u *name_end;
7371 char_u *p;
7372 char_u *linep;
7373 char_u *key_start;
7374 char_u *arg_start;
7375 char_u *key = NULL, *arg = NULL;
7376 long i;
7377 int off;
7378 int len;
7379 int attr;
7380 int id;
7381 int idx;
Bram Moolenaar99433292017-09-08 12:37:47 +02007382 struct hl_group item_before;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007383 int did_change = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007384 int dodefault = FALSE;
7385 int doclear = FALSE;
7386 int dolink = FALSE;
7387 int error = FALSE;
7388 int color;
7389 int is_normal_group = FALSE; /* "Normal" group */
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007390#ifdef FEAT_TERMINAL
7391 int is_terminal_group = FALSE; /* "Terminal" group */
7392#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007393#ifdef FEAT_GUI_X11
7394 int is_menu_group = FALSE; /* "Menu" group */
7395 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7396 int is_tooltip_group = FALSE; /* "Tooltip" group */
7397 int do_colors = FALSE; /* need to update colors? */
7398#else
7399# define is_menu_group 0
7400# define is_tooltip_group 0
7401#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007402#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
7403 int did_highlight_changed = FALSE;
7404#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007405
7406 /*
7407 * If no argument, list current highlighting.
7408 */
7409 if (ends_excmd(*line))
7410 {
7411 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7412 /* TODO: only call when the group has attributes set */
7413 highlight_list_one((int)i);
7414 return;
7415 }
7416
7417 /*
7418 * Isolate the name.
7419 */
7420 name_end = skiptowhite(line);
7421 linep = skipwhite(name_end);
7422
7423 /*
7424 * Check for "default" argument.
7425 */
7426 if (STRNCMP(line, "default", name_end - line) == 0)
7427 {
7428 dodefault = TRUE;
7429 line = linep;
7430 name_end = skiptowhite(line);
7431 linep = skipwhite(name_end);
7432 }
7433
7434 /*
7435 * Check for "clear" or "link" argument.
7436 */
7437 if (STRNCMP(line, "clear", name_end - line) == 0)
7438 doclear = TRUE;
7439 if (STRNCMP(line, "link", name_end - line) == 0)
7440 dolink = TRUE;
7441
7442 /*
7443 * ":highlight {group-name}": list highlighting for one group.
7444 */
7445 if (!doclear && !dolink && ends_excmd(*linep))
7446 {
7447 id = syn_namen2id(line, (int)(name_end - line));
7448 if (id == 0)
7449 EMSG2(_("E411: highlight group not found: %s"), line);
7450 else
7451 highlight_list_one(id);
7452 return;
7453 }
7454
7455 /*
7456 * Handle ":highlight link {from} {to}" command.
7457 */
7458 if (dolink)
7459 {
7460 char_u *from_start = linep;
7461 char_u *from_end;
7462 char_u *to_start;
7463 char_u *to_end;
7464 int from_id;
7465 int to_id;
7466
7467 from_end = skiptowhite(from_start);
7468 to_start = skipwhite(from_end);
7469 to_end = skiptowhite(to_start);
7470
7471 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7472 {
7473 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7474 from_start);
7475 return;
7476 }
7477
7478 if (!ends_excmd(*skipwhite(to_end)))
7479 {
7480 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7481 return;
7482 }
7483
7484 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7485 if (STRNCMP(to_start, "NONE", 4) == 0)
7486 to_id = 0;
7487 else
7488 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7489
Bram Moolenaar414168d2017-09-10 15:21:55 +02007490 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007491 {
7492 /*
7493 * Don't allow a link when there already is some highlighting
7494 * for the group, unless '!' is used
7495 */
7496 if (to_id > 0 && !forceit && !init
7497 && hl_has_settings(from_id - 1, dodefault))
7498 {
7499 if (sourcing_name == NULL && !dodefault)
7500 EMSG(_("E414: group has settings, highlight link ignored"));
7501 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02007502 else if (HL_TABLE()[from_id - 1].sg_link != to_id
Bram Moolenaar99433292017-09-08 12:37:47 +02007503#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007504 || HL_TABLE()[from_id - 1].sg_scriptID != current_SID
Bram Moolenaar99433292017-09-08 12:37:47 +02007505#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007506 || HL_TABLE()[from_id - 1].sg_cleared)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007507 {
7508 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007509 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7510 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007511#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02007512 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007513#endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007514 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007515 redraw_all_later(SOME_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02007516
7517 /* Only call highlight_changed() once after multiple changes. */
7518 need_highlight_changed = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519 }
7520 }
7521
Bram Moolenaar071d4272004-06-13 20:20:40 +00007522 return;
7523 }
7524
7525 if (doclear)
7526 {
7527 /*
7528 * ":highlight clear [group]" command.
7529 */
7530 line = linep;
7531 if (ends_excmd(*line))
7532 {
7533#ifdef FEAT_GUI
7534 /* First, we do not destroy the old values, but allocate the new
7535 * ones and update the display. THEN we destroy the old values.
7536 * If we destroy the old values first, then the old values
7537 * (such as GuiFont's or GuiFontset's) will still be displayed but
7538 * invalid because they were free'd.
7539 */
7540 if (gui.in_use)
7541 {
7542# ifdef FEAT_BEVAL_TIP
7543 gui_init_tooltip_font();
7544# endif
7545# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7546 gui_init_menu_font();
7547# endif
7548 }
7549# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7550 gui_mch_def_colors();
7551# endif
7552# ifdef FEAT_GUI_X11
7553# ifdef FEAT_MENU
7554
7555 /* This only needs to be done when there is no Menu highlight
7556 * group defined by default, which IS currently the case.
7557 */
7558 gui_mch_new_menu_colors();
7559# endif
7560 if (gui.in_use)
7561 {
7562 gui_new_scrollbar_colors();
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01007563# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007564 gui_mch_new_tooltip_colors();
7565# endif
7566# ifdef FEAT_MENU
7567 gui_mch_new_menu_font();
7568# endif
7569 }
7570# endif
7571
7572 /* Ok, we're done allocating the new default graphics items.
7573 * The screen should already be refreshed at this point.
7574 * It is now Ok to clear out the old data.
7575 */
7576#endif
7577#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007578 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007579#endif
7580 restore_cterm_colors();
7581
7582 /*
7583 * Clear all default highlight groups and load the defaults.
7584 */
7585 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7586 highlight_clear(idx);
7587 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007588#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007589 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007590 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007591 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00007592#endif
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02007593 highlight_changed();
Bram Moolenaar071d4272004-06-13 20:20:40 +00007594 redraw_later_clear();
7595 return;
7596 }
7597 name_end = skiptowhite(line);
7598 linep = skipwhite(name_end);
7599 }
7600
7601 /*
7602 * Find the group name in the table. If it does not exist yet, add it.
7603 */
7604 id = syn_check_group(line, (int)(name_end - line));
7605 if (id == 0) /* failed (out of memory) */
7606 return;
7607 idx = id - 1; /* index is ID minus one */
7608
7609 /* Return if "default" was used and the group already has settings. */
7610 if (dodefault && hl_has_settings(idx, TRUE))
7611 return;
7612
Bram Moolenaar99433292017-09-08 12:37:47 +02007613 /* Make a copy so we can check if any attribute actually changed. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007614 item_before = HL_TABLE()[idx];
Bram Moolenaar99433292017-09-08 12:37:47 +02007615
Bram Moolenaar414168d2017-09-10 15:21:55 +02007616 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007617 is_normal_group = TRUE;
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01007618#ifdef FEAT_TERMINAL
7619 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
7620 is_terminal_group = TRUE;
7621#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007622#ifdef FEAT_GUI_X11
Bram Moolenaar414168d2017-09-10 15:21:55 +02007623 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007624 is_menu_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007625 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007626 is_scrollbar_group = TRUE;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007627 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007628 is_tooltip_group = TRUE;
7629#endif
7630
7631 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7632 if (doclear || (forceit && init))
7633 {
7634 highlight_clear(idx);
7635 if (!doclear)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007636 HL_TABLE()[idx].sg_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007637 }
7638
7639 if (!doclear)
7640 while (!ends_excmd(*linep))
7641 {
7642 key_start = linep;
7643 if (*linep == '=')
7644 {
7645 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7646 error = TRUE;
7647 break;
7648 }
7649
7650 /*
7651 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7652 * "guibg").
7653 */
Bram Moolenaar1c465442017-03-12 20:10:05 +01007654 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
Bram Moolenaar071d4272004-06-13 20:20:40 +00007655 ++linep;
7656 vim_free(key);
7657 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7658 if (key == NULL)
7659 {
7660 error = TRUE;
7661 break;
7662 }
7663 linep = skipwhite(linep);
7664
7665 if (STRCMP(key, "NONE") == 0)
7666 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007667 if (!init || HL_TABLE()[idx].sg_set == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007668 {
7669 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007670 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007671 highlight_clear(idx);
7672 }
7673 continue;
7674 }
7675
7676 /*
7677 * Check for the equal sign.
7678 */
7679 if (*linep != '=')
7680 {
7681 EMSG2(_("E416: missing equal sign: %s"), key_start);
7682 error = TRUE;
7683 break;
7684 }
7685 ++linep;
7686
7687 /*
7688 * Isolate the argument.
7689 */
7690 linep = skipwhite(linep);
7691 if (*linep == '\'') /* guifg='color name' */
7692 {
7693 arg_start = ++linep;
7694 linep = vim_strchr(linep, '\'');
7695 if (linep == NULL)
7696 {
7697 EMSG2(_(e_invarg2), key_start);
7698 error = TRUE;
7699 break;
7700 }
7701 }
7702 else
7703 {
7704 arg_start = linep;
7705 linep = skiptowhite(linep);
7706 }
7707 if (linep == arg_start)
7708 {
7709 EMSG2(_("E417: missing argument: %s"), key_start);
7710 error = TRUE;
7711 break;
7712 }
7713 vim_free(arg);
7714 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7715 if (arg == NULL)
7716 {
7717 error = TRUE;
7718 break;
7719 }
7720 if (*linep == '\'')
7721 ++linep;
7722
7723 /*
7724 * Store the argument.
7725 */
7726 if ( STRCMP(key, "TERM") == 0
7727 || STRCMP(key, "CTERM") == 0
7728 || STRCMP(key, "GUI") == 0)
7729 {
7730 attr = 0;
7731 off = 0;
7732 while (arg[off] != NUL)
7733 {
7734 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7735 {
7736 len = (int)STRLEN(hl_name_table[i]);
7737 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7738 {
7739 attr |= hl_attr_table[i];
7740 off += len;
7741 break;
7742 }
7743 }
7744 if (i < 0)
7745 {
7746 EMSG2(_("E418: Illegal value: %s"), arg);
7747 error = TRUE;
7748 break;
7749 }
7750 if (arg[off] == ',') /* another one follows */
7751 ++off;
7752 }
7753 if (error)
7754 break;
7755 if (*key == 'T')
7756 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007757 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007758 {
7759 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007760 HL_TABLE()[idx].sg_set |= SG_TERM;
7761 HL_TABLE()[idx].sg_term = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007762 }
7763 }
7764 else if (*key == 'C')
7765 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007766 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007767 {
7768 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007769 HL_TABLE()[idx].sg_set |= SG_CTERM;
7770 HL_TABLE()[idx].sg_cterm = attr;
7771 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007772 }
7773 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007774#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007775 else
7776 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007777 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778 {
7779 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007780 HL_TABLE()[idx].sg_set |= SG_GUI;
7781 HL_TABLE()[idx].sg_gui = attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007782 }
7783 }
7784#endif
7785 }
7786 else if (STRCMP(key, "FONT") == 0)
7787 {
7788 /* in non-GUI fonts are simply ignored */
7789#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02007790 if (HL_TABLE()[idx].sg_font_name != NULL
7791 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
Bram Moolenaar99433292017-09-08 12:37:47 +02007792 {
7793 /* Font name didn't change, ignore. */
7794 }
7795 else if (!gui.shell_created)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007796 {
7797 /* GUI not started yet, always accept the name. */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007798 vim_free(HL_TABLE()[idx].sg_font_name);
7799 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007800 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007801 }
7802 else
7803 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007804 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007806 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007807# endif
7808 /* First, save the current font/fontset.
7809 * Then try to allocate the font/fontset.
Bram Moolenaar414168d2017-09-10 15:21:55 +02007810 * If the allocation fails, HL_TABLE()[idx].sg_font OR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7812 */
7813
Bram Moolenaar414168d2017-09-10 15:21:55 +02007814 HL_TABLE()[idx].sg_font = NOFONT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007816 HL_TABLE()[idx].sg_fontset = NOFONTSET;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007817# endif
7818 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007819 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820
7821# ifdef FEAT_XFONTSET
Bram Moolenaar414168d2017-09-10 15:21:55 +02007822 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007824 /* New fontset was accepted. Free the old one, if there
7825 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007826 gui_mch_free_fontset(temp_sg_fontset);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007827 vim_free(HL_TABLE()[idx].sg_font_name);
7828 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007829 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007830 }
7831 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007832 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833# endif
Bram Moolenaar414168d2017-09-10 15:21:55 +02007834 if (HL_TABLE()[idx].sg_font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007835 {
7836 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007837 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007838 gui_mch_free_font(temp_sg_font);
Bram Moolenaar414168d2017-09-10 15:21:55 +02007839 vim_free(HL_TABLE()[idx].sg_font_name);
7840 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
Bram Moolenaar65ed1362017-09-30 16:00:14 +02007841 did_change = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007842 }
7843 else
Bram Moolenaar414168d2017-09-10 15:21:55 +02007844 HL_TABLE()[idx].sg_font = temp_sg_font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 }
7846#endif
7847 }
7848 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7849 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007850 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007851 {
7852 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007853 HL_TABLE()[idx].sg_set |= SG_CTERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854
7855 /* When setting the foreground color, and previously the "bold"
7856 * flag was set for a light color, reset it now */
Bram Moolenaar414168d2017-09-10 15:21:55 +02007857 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007859 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7860 HL_TABLE()[idx].sg_cterm_bold = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007861 }
7862
7863 if (VIM_ISDIGIT(*arg))
7864 color = atoi((char *)arg);
7865 else if (STRICMP(arg, "fg") == 0)
7866 {
7867 if (cterm_normal_fg_color)
7868 color = cterm_normal_fg_color - 1;
7869 else
7870 {
7871 EMSG(_("E419: FG color unknown"));
7872 error = TRUE;
7873 break;
7874 }
7875 }
7876 else if (STRICMP(arg, "bg") == 0)
7877 {
7878 if (cterm_normal_bg_color > 0)
7879 color = cterm_normal_bg_color - 1;
7880 else
7881 {
7882 EMSG(_("E420: BG color unknown"));
7883 error = TRUE;
7884 break;
7885 }
7886 }
7887 else
7888 {
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007889 int bold = MAYBE;
7890
Bram Moolenaar071d4272004-06-13 20:20:40 +00007891#if defined(__QNXNTO__)
7892 static int *color_numbers_8_qansi = color_numbers_8;
7893 /* On qnx, the 8 & 16 color arrays are the same */
7894 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7895 color_numbers_8_qansi = color_numbers_16;
7896#endif
7897
7898 /* reduce calls to STRICMP a bit, it can be slow */
7899 off = TOUPPER_ASC(*arg);
7900 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7901 if (off == color_names[i][0]
7902 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7903 break;
7904 if (i < 0)
7905 {
7906 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7907 error = TRUE;
7908 break;
7909 }
7910
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007911 color = lookup_color(i, key[5] == 'F', &bold);
7912
7913 /* set/reset bold attribute to get light foreground
7914 * colors (on some terminals, e.g. "linux") */
7915 if (bold == TRUE)
7916 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007917 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7918 HL_TABLE()[idx].sg_cterm_bold = TRUE;
Bram Moolenaar12d853f2017-08-01 18:04:04 +02007919 }
7920 else if (bold == FALSE)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007921 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007922 }
Bram Moolenaarb41bf8e2017-07-28 15:11:38 +02007923
Bram Moolenaarccbab932010-05-13 15:40:30 +02007924 /* Add one to the argument, to avoid zero. Zero is used for
7925 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007926 if (key[5] == 'F')
7927 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007928 HL_TABLE()[idx].sg_cterm_fg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007929 if (is_normal_group)
7930 {
7931 cterm_normal_fg_color = color + 1;
Bram Moolenaar414168d2017-09-10 15:21:55 +02007932 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007933#ifdef FEAT_GUI
7934 /* Don't do this if the GUI is used. */
7935 if (!gui.in_use && !gui.starting)
7936#endif
7937 {
7938 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007939 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007940 term_fg_color(color);
7941 }
7942 }
7943 }
7944 else
7945 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007946 HL_TABLE()[idx].sg_cterm_bg = color + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007947 if (is_normal_group)
7948 {
7949 cterm_normal_bg_color = color + 1;
7950#ifdef FEAT_GUI
7951 /* Don't mess with 'background' if the GUI is used. */
7952 if (!gui.in_use && !gui.starting)
7953#endif
7954 {
7955 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007956 if (color >= 0)
7957 {
Bram Moolenaar1615b362017-06-04 21:06:09 +02007958 int dark = -1;
7959
Bram Moolenaarccbab932010-05-13 15:40:30 +02007960 if (termcap_active)
7961 term_bg_color(color);
7962 if (t_colors < 16)
Bram Moolenaar1615b362017-06-04 21:06:09 +02007963 dark = (color == 0 || color == 4);
7964 /* Limit the heuristic to the standard 16 colors */
7965 else if (color < 16)
7966 dark = (color < 7 || color == 8);
Bram Moolenaarccbab932010-05-13 15:40:30 +02007967 /* Set the 'background' option if the value is
7968 * wrong. */
Bram Moolenaar1615b362017-06-04 21:06:09 +02007969 if (dark != -1
7970 && dark != (*p_bg == 'd')
7971 && !option_was_set((char_u *)"bg"))
7972 {
Bram Moolenaarccbab932010-05-13 15:40:30 +02007973 set_option_value((char_u *)"bg", 0L,
Bram Moolenaar1615b362017-06-04 21:06:09 +02007974 (char_u *)(dark ? "dark" : "light"), 0);
7975 reset_option_was_set((char_u *)"bg");
7976 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007977 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007978 }
7979 }
7980 }
7981 }
7982 }
7983 else if (STRCMP(key, "GUIFG") == 0)
7984 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02007985#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02007986 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
7987
Bram Moolenaar414168d2017-09-10 15:21:55 +02007988 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007989 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007990 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02007991 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007992
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007993# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007994 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007995 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007996 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007997 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02007998 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007999# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008000 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8001 {
8002 vim_free(*namep);
8003 if (STRCMP(arg, "NONE") != 0)
8004 *namep = vim_strsave(arg);
8005 else
8006 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008007 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008008 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008009# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008010# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008011 if (is_menu_group && gui.menu_fg_pixel != i)
8012 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008013 gui.menu_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008014 do_colors = TRUE;
8015 }
8016 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
8017 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008018 gui.scroll_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008019 do_colors = TRUE;
8020 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008021# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008022 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
8023 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008024 gui.tooltip_fg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008025 do_colors = TRUE;
8026 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008027# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008028# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008029 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008030# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008031 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008032#endif
8033 }
8034 else if (STRCMP(key, "GUIBG") == 0)
8035 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008036#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008037 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
8038
Bram Moolenaar414168d2017-09-10 15:21:55 +02008039 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008040 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008041 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008042 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008043
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008044# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008045 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008046 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008047 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008048 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008049 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008050# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008051 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8052 {
8053 vim_free(*namep);
8054 if (STRCMP(arg, "NONE") != 0)
8055 *namep = vim_strsave(arg);
8056 else
8057 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008058 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008059 }
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008060# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008061# ifdef FEAT_GUI_X11
Bram Moolenaar452030e2017-09-25 22:57:27 +02008062 if (is_menu_group && gui.menu_bg_pixel != i)
8063 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008064 gui.menu_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008065 do_colors = TRUE;
8066 }
8067 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
8068 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008069 gui.scroll_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008070 do_colors = TRUE;
8071 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008072# ifdef FEAT_BEVAL_GUI
Bram Moolenaar452030e2017-09-25 22:57:27 +02008073 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
8074 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008075 gui.tooltip_bg_pixel = i;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008076 do_colors = TRUE;
8077 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008078# endif
Bram Moolenaar61623362010-07-14 22:04:22 +02008079# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008080 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008081# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008082 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008083#endif
8084 }
8085 else if (STRCMP(key, "GUISP") == 0)
8086 {
Bram Moolenaar7c456a42017-09-26 11:15:53 +02008087#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar452030e2017-09-25 22:57:27 +02008088 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
8089
Bram Moolenaar414168d2017-09-10 15:21:55 +02008090 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008091 {
8092 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008093 HL_TABLE()[idx].sg_set |= SG_GUI;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008094
Bram Moolenaar61623362010-07-14 22:04:22 +02008095# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008096 i = color_name2handle(arg);
8097 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
8098 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008099 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02008100# endif
Bram Moolenaar452030e2017-09-25 22:57:27 +02008101 if (*namep == NULL || STRCMP(*namep, arg) != 0)
8102 {
8103 vim_free(*namep);
8104 if (STRCMP(arg, "NONE") != 0)
8105 *namep = vim_strsave(arg);
8106 else
8107 *namep = NULL;
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008108 did_change = TRUE;
Bram Moolenaar452030e2017-09-25 22:57:27 +02008109 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008110# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008111 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008112# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008113 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114#endif
8115 }
8116 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
8117 {
8118 char_u buf[100];
8119 char_u *tname;
8120
8121 if (!init)
Bram Moolenaar414168d2017-09-10 15:21:55 +02008122 HL_TABLE()[idx].sg_set |= SG_TERM;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008123
8124 /*
8125 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008126 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00008127 */
8128 if (STRNCMP(arg, "t_", 2) == 0)
8129 {
8130 off = 0;
8131 buf[0] = 0;
8132 while (arg[off] != NUL)
8133 {
8134 /* Isolate one termcap name */
8135 for (len = 0; arg[off + len] &&
8136 arg[off + len] != ','; ++len)
8137 ;
8138 tname = vim_strnsave(arg + off, len);
8139 if (tname == NULL) /* out of memory */
8140 {
8141 error = TRUE;
8142 break;
8143 }
8144 /* lookup the escape sequence for the item */
8145 p = get_term_code(tname);
8146 vim_free(tname);
8147 if (p == NULL) /* ignore non-existing things */
8148 p = (char_u *)"";
8149
8150 /* Append it to the already found stuff */
8151 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8152 {
8153 EMSG2(_("E422: terminal code too long: %s"), arg);
8154 error = TRUE;
8155 break;
8156 }
8157 STRCAT(buf, p);
8158
8159 /* Advance to the next item */
8160 off += len;
8161 if (arg[off] == ',') /* another one follows */
8162 ++off;
8163 }
8164 }
8165 else
8166 {
8167 /*
8168 * Copy characters from arg[] to buf[], translating <> codes.
8169 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008170 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008171 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02008172 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008173 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174 off += len;
8175 else /* copy as normal char */
8176 buf[off++] = *p++;
8177 }
8178 buf[off] = NUL;
8179 }
8180 if (error)
8181 break;
8182
8183 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8184 p = NULL;
8185 else
8186 p = vim_strsave(buf);
8187 if (key[2] == 'A')
8188 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008189 vim_free(HL_TABLE()[idx].sg_start);
8190 HL_TABLE()[idx].sg_start = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008191 }
8192 else
8193 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008194 vim_free(HL_TABLE()[idx].sg_stop);
8195 HL_TABLE()[idx].sg_stop = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008196 }
8197 }
8198 else
8199 {
8200 EMSG2(_("E423: Illegal argument: %s"), key_start);
8201 error = TRUE;
8202 break;
8203 }
Bram Moolenaar414168d2017-09-10 15:21:55 +02008204 HL_TABLE()[idx].sg_cleared = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008205
8206 /*
8207 * When highlighting has been given for a group, don't link it.
8208 */
Bram Moolenaar414168d2017-09-10 15:21:55 +02008209 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8210 HL_TABLE()[idx].sg_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211
8212 /*
8213 * Continue with next argument.
8214 */
8215 linep = skipwhite(linep);
8216 }
8217
8218 /*
8219 * If there is an error, and it's a new entry, remove it from the table.
8220 */
8221 if (error && idx == highlight_ga.ga_len)
8222 syn_unadd_group();
8223 else
8224 {
8225 if (is_normal_group)
8226 {
Bram Moolenaar414168d2017-09-10 15:21:55 +02008227 HL_TABLE()[idx].sg_term_attr = 0;
8228 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008229#ifdef FEAT_GUI
Bram Moolenaar414168d2017-09-10 15:21:55 +02008230 HL_TABLE()[idx].sg_gui_attr = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008231 /*
8232 * Need to update all groups, because they might be using "bg"
8233 * and/or "fg", which have been changed now.
8234 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008235#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008236#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008237 if (USE_24BIT)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008238 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008239 highlight_gui_started();
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008240 did_highlight_changed = TRUE;
8241 redraw_all_later(NOT_VALID);
8242 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008243#endif
8244 }
Bram Moolenaara7c54cf2017-12-01 21:07:20 +01008245#ifdef FEAT_TERMINAL
8246 else if (is_terminal_group)
8247 set_terminal_default_colors(
8248 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
8249#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008250#ifdef FEAT_GUI_X11
8251# ifdef FEAT_MENU
8252 else if (is_menu_group)
8253 {
8254 if (gui.in_use && do_colors)
8255 gui_mch_new_menu_colors();
8256 }
8257# endif
8258 else if (is_scrollbar_group)
8259 {
8260 if (gui.in_use && do_colors)
8261 gui_new_scrollbar_colors();
8262 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008263# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008264 else if (is_tooltip_group)
8265 {
8266 if (gui.in_use && do_colors)
8267 gui_mch_new_tooltip_colors();
8268 }
8269# endif
8270#endif
8271 else
8272 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008273#ifdef FEAT_EVAL
Bram Moolenaar414168d2017-09-10 15:21:55 +02008274 HL_TABLE()[idx].sg_scriptID = current_SID;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008275#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008276 }
Bram Moolenaar99433292017-09-08 12:37:47 +02008277
Bram Moolenaar071d4272004-06-13 20:20:40 +00008278 vim_free(key);
8279 vim_free(arg);
8280
Bram Moolenaar99433292017-09-08 12:37:47 +02008281 /* Only call highlight_changed() once, after a sequence of highlight
8282 * commands, and only if an attribute actually changed. */
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008283 if ((did_change
8284 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
Bram Moolenaarb4ea1912017-09-09 15:28:14 +02008285#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
8286 && !did_highlight_changed
8287#endif
8288 )
Bram Moolenaar99433292017-09-08 12:37:47 +02008289 {
Bram Moolenaar65ed1362017-09-30 16:00:14 +02008290 /* Do not trigger a redraw when highlighting is changed while
8291 * redrawing. This may happen when evaluating 'statusline' changes the
8292 * StatusLine group. */
8293 if (!updating_screen)
8294 redraw_all_later(NOT_VALID);
Bram Moolenaar99433292017-09-08 12:37:47 +02008295 need_highlight_changed = TRUE;
8296 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297}
8298
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008299#if defined(EXITFREE) || defined(PROTO)
8300 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008301free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008302{
8303 int i;
8304
8305 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008306 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008307 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008308 vim_free(HL_TABLE()[i].sg_name);
8309 vim_free(HL_TABLE()[i].sg_name_u);
8310 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008311 ga_clear(&highlight_ga);
8312}
8313#endif
8314
Bram Moolenaar071d4272004-06-13 20:20:40 +00008315/*
8316 * Reset the cterm colors to what they were before Vim was started, if
8317 * possible. Otherwise reset them to zero.
8318 */
8319 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008320restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008321{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008322#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008323 /* Since t_me has been set, this probably means that the user
8324 * wants to use this as default colors. Need to reset default
8325 * background/foreground colors. */
8326 mch_set_normal_colors();
8327#else
8328 cterm_normal_fg_color = 0;
8329 cterm_normal_fg_bold = 0;
8330 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008331# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008332 cterm_normal_fg_gui_color = INVALCOLOR;
8333 cterm_normal_bg_gui_color = INVALCOLOR;
8334# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008335#endif
8336}
8337
8338/*
8339 * Return TRUE if highlight group "idx" has any settings.
8340 * When "check_link" is TRUE also check for an existing link.
8341 */
8342 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008343hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008344{
8345 return ( HL_TABLE()[idx].sg_term_attr != 0
8346 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008347 || HL_TABLE()[idx].sg_cterm_fg != 0
8348 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008349#ifdef FEAT_GUI
8350 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008351 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8352 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8353 || HL_TABLE()[idx].sg_gui_sp_name != NULL
Bram Moolenaar87748452017-03-12 17:10:33 +01008354 || HL_TABLE()[idx].sg_font_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008355#endif
8356 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8357}
8358
8359/*
8360 * Clear highlighting for one group.
8361 */
8362 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008363highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364{
Bram Moolenaard61e8aa2017-01-17 17:44:46 +01008365 HL_TABLE()[idx].sg_cleared = TRUE;
8366
Bram Moolenaar071d4272004-06-13 20:20:40 +00008367 HL_TABLE()[idx].sg_term = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008368 VIM_CLEAR(HL_TABLE()[idx].sg_start);
8369 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008370 HL_TABLE()[idx].sg_term_attr = 0;
8371 HL_TABLE()[idx].sg_cterm = 0;
8372 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8373 HL_TABLE()[idx].sg_cterm_fg = 0;
8374 HL_TABLE()[idx].sg_cterm_bg = 0;
8375 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008376#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008377 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +01008378 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
8379 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
8380 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
Bram Moolenaar61623362010-07-14 22:04:22 +02008381#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008382#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008383 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8384 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008385#endif
8386#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008387 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008388 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8389 HL_TABLE()[idx].sg_font = NOFONT;
8390# ifdef FEAT_XFONTSET
8391 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8392 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8393# endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01008394 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008395 HL_TABLE()[idx].sg_gui_attr = 0;
8396#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008397#ifdef FEAT_EVAL
8398 /* Clear the script ID only when there is no link, since that is not
8399 * cleared. */
8400 if (HL_TABLE()[idx].sg_link == 0)
8401 HL_TABLE()[idx].sg_scriptID = 0;
8402#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008403}
8404
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008405#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008406/*
8407 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008408 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008409 * "Tooltip" colors.
8410 */
8411 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008412set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008413{
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008414# ifdef FEAT_GUI
8415# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008416 if (gui.in_use)
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008417# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008418 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008419 if (set_group_colors((char_u *)"Normal",
8420 &gui.norm_pixel, &gui.back_pixel,
8421 FALSE, TRUE, FALSE))
8422 {
8423 gui_mch_new_colors();
8424 must_redraw = CLEAR;
8425 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008426# ifdef FEAT_GUI_X11
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008427 if (set_group_colors((char_u *)"Menu",
8428 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8429 TRUE, FALSE, FALSE))
8430 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008431# ifdef FEAT_MENU
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008432 gui_mch_new_menu_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008433# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008434 must_redraw = CLEAR;
8435 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008436# ifdef FEAT_BEVAL_GUI
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008437 if (set_group_colors((char_u *)"Tooltip",
8438 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8439 FALSE, FALSE, TRUE))
8440 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008441# ifdef FEAT_TOOLBAR
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008442 gui_mch_new_tooltip_colors();
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008443# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008444 must_redraw = CLEAR;
8445 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008446# endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008447 if (set_group_colors((char_u *)"Scrollbar",
8448 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8449 FALSE, FALSE, FALSE))
8450 {
8451 gui_new_scrollbar_colors();
8452 must_redraw = CLEAR;
8453 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008454# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008455 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008456# endif
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008457# ifdef FEAT_TERMGUICOLORS
8458# ifdef FEAT_GUI
8459 else
8460# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008461 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008462 int idx;
8463
8464 idx = syn_name2id((char_u *)"Normal") - 1;
8465 if (idx >= 0)
8466 {
8467 gui_do_one_color(idx, FALSE, FALSE);
8468
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008469 /* If the normal fg or bg color changed a complete redraw is
8470 * required. */
8471 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
8472 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008473 {
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008474 /* if the GUI color is INVALCOLOR then we use the default cterm
8475 * color */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008476 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008477 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8478 must_redraw = CLEAR;
8479 }
8480 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008481 }
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01008482# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008484#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008485
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008486#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008487/*
8488 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8489 */
8490 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008491set_group_colors(
8492 char_u *name,
8493 guicolor_T *fgp,
8494 guicolor_T *bgp,
8495 int do_menu,
8496 int use_norm,
8497 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008498{
8499 int idx;
8500
8501 idx = syn_name2id(name) - 1;
8502 if (idx >= 0)
8503 {
8504 gui_do_one_color(idx, do_menu, do_tooltip);
8505
8506 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8507 *fgp = HL_TABLE()[idx].sg_gui_fg;
8508 else if (use_norm)
8509 *fgp = gui.def_norm_pixel;
8510 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8511 *bgp = HL_TABLE()[idx].sg_gui_bg;
8512 else if (use_norm)
8513 *bgp = gui.def_back_pixel;
8514 return TRUE;
8515 }
8516 return FALSE;
8517}
8518
8519/*
8520 * Get the font of the "Normal" group.
8521 * Returns "" when it's not found or not set.
8522 */
8523 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008524hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008525{
8526 int id;
8527 char_u *s;
8528
8529 id = syn_name2id((char_u *)"Normal");
8530 if (id > 0)
8531 {
8532 s = HL_TABLE()[id - 1].sg_font_name;
8533 if (s != NULL)
8534 return s;
8535 }
8536 return (char_u *)"";
8537}
8538
8539/*
8540 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8541 * actually chosen to be used.
8542 */
8543 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008544hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008545{
8546 int id;
8547
8548 id = syn_name2id((char_u *)"Normal");
8549 if (id > 0)
8550 {
8551 vim_free(HL_TABLE()[id - 1].sg_font_name);
8552 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8553 }
8554}
8555
8556/*
8557 * Set background color for "Normal" group. Called by gui_set_bg_color()
8558 * when the color is known.
8559 */
8560 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008561hl_set_bg_color_name(
8562 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008563{
8564 int id;
8565
8566 if (name != NULL)
8567 {
8568 id = syn_name2id((char_u *)"Normal");
8569 if (id > 0)
8570 {
8571 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8572 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8573 }
8574 }
8575}
8576
8577/*
8578 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8579 * when the color is known.
8580 */
8581 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008582hl_set_fg_color_name(
8583 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008584{
8585 int id;
8586
8587 if (name != NULL)
8588 {
8589 id = syn_name2id((char_u *)"Normal");
8590 if (id > 0)
8591 {
8592 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8593 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8594 }
8595 }
8596}
8597
8598/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008599 * Return the handle for a font name.
8600 * Returns NOFONT when failed.
8601 */
8602 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008603font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008604{
8605 if (STRCMP(name, "NONE") == 0)
8606 return NOFONT;
8607
8608 return gui_mch_get_font(name, TRUE);
8609}
8610
8611# ifdef FEAT_XFONTSET
8612/*
8613 * Return the handle for a fontset name.
8614 * Returns NOFONTSET when failed.
8615 */
8616 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008617fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008618{
8619 if (STRCMP(name, "NONE") == 0)
8620 return NOFONTSET;
8621
8622 return gui_mch_get_fontset(name, TRUE, fixed_width);
8623}
8624# endif
8625
8626/*
8627 * Get the font or fontset for one highlight group.
8628 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008629 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008630hl_do_font(
8631 int idx,
8632 char_u *arg,
8633 int do_normal, /* set normal font */
8634 int do_menu UNUSED, /* set menu font */
8635 int do_tooltip UNUSED, /* set tooltip font */
8636 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008637{
8638# ifdef FEAT_XFONTSET
8639 /* If 'guifontset' is not empty, first try using the name as a
8640 * fontset. If that doesn't work, use it as a font name. */
8641 if (*p_guifontset != NUL
8642# ifdef FONTSET_ALWAYS
8643 || do_menu
8644# endif
8645# ifdef FEAT_BEVAL_TIP
8646 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8647 || do_tooltip
8648# endif
8649 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008650 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008651 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008652 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008653 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8654# ifdef FONTSET_ALWAYS
8655 || do_menu
8656# endif
8657# ifdef FEAT_BEVAL_TIP
8658 || do_tooltip
8659# endif
8660 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008661 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8663 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008664 /* If it worked and it's the Normal group, use it as the normal
8665 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008666 if (do_normal)
8667 gui_init_font(arg, TRUE);
8668# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8669 if (do_menu)
8670 {
8671# ifdef FONTSET_ALWAYS
8672 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8673# else
8674 /* YIKES! This is a bug waiting to crash the program */
8675 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8676# endif
8677 gui_mch_new_menu_font();
8678 }
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01008679# ifdef FEAT_BEVAL_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00008680 if (do_tooltip)
8681 {
8682 /* The Athena widget set cannot currently handle switching between
8683 * displaying a single font and a fontset.
8684 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008685 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008686 * XFontStruct is used.
8687 */
8688 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8689 gui_mch_new_tooltip_font();
8690 }
8691# endif
8692# endif
8693 }
8694 else
8695# endif
8696 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008697 if (free_font)
8698 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008699 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8700 /* If it worked and it's the Normal group, use it as the
8701 * normal font. Same for the Menu group. */
8702 if (HL_TABLE()[idx].sg_font != NOFONT)
8703 {
8704 if (do_normal)
8705 gui_init_font(arg, FALSE);
8706#ifndef FONTSET_ALWAYS
8707# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8708 if (do_menu)
8709 {
8710 gui.menu_font = HL_TABLE()[idx].sg_font;
8711 gui_mch_new_menu_font();
8712 }
8713# endif
8714#endif
8715 }
8716 }
8717}
8718
8719#endif /* FEAT_GUI */
8720
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008721#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008722/*
8723 * Return the handle for a color name.
8724 * Returns INVALCOLOR when failed.
8725 */
Bram Moolenaar3d9bdfe2017-08-12 22:55:58 +02008726 guicolor_T
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008727color_name2handle(char_u *name)
8728{
8729 if (STRCMP(name, "NONE") == 0)
8730 return INVALCOLOR;
8731
8732 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8733 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008734#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008735 if (gui.in_use)
8736#endif
8737#ifdef FEAT_GUI
8738 return gui.norm_pixel;
8739#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008740#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008741 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008742 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008743 /* Guess that the foreground is black or white. */
8744 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008745#endif
8746 }
8747 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8748 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008749#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008750 if (gui.in_use)
8751#endif
8752#ifdef FEAT_GUI
8753 return gui.back_pixel;
8754#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008755#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008756 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008757 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008758 /* Guess that the background is white or black. */
8759 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008760#endif
8761 }
8762
8763 return GUI_GET_COLOR(name);
8764}
8765#endif
8766
Bram Moolenaar071d4272004-06-13 20:20:40 +00008767/*
8768 * Table with the specifications for an attribute number.
8769 * Note that this table is used by ALL buffers. This is required because the
8770 * GUI can redraw at any time for any buffer.
8771 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008772static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008773
8774#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8775
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008776static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008777
8778#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8779
8780#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008781static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008782
8783#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8784#endif
8785
8786/*
8787 * Return the attr number for a set of colors and font.
8788 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8789 * if the combination is new.
8790 * Return 0 for error (no more room).
8791 */
8792 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008793get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008794{
8795 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008796 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008797 static int recursive = FALSE;
8798
8799 /*
8800 * Init the table, in case it wasn't done yet.
8801 */
8802 table->ga_itemsize = sizeof(attrentry_T);
8803 table->ga_growsize = 7;
8804
8805 /*
8806 * Try to find an entry with the same specifications.
8807 */
8808 for (i = 0; i < table->ga_len; ++i)
8809 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008810 taep = &(((attrentry_T *)table->ga_data)[i]);
8811 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008812 && (
8813#ifdef FEAT_GUI
8814 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008815 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8816 && aep->ae_u.gui.bg_color
8817 == taep->ae_u.gui.bg_color
8818 && aep->ae_u.gui.sp_color
8819 == taep->ae_u.gui.sp_color
8820 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008821# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008822 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008823# endif
8824 ))
8825 ||
8826#endif
8827 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008828 && (aep->ae_u.term.start == NULL)
8829 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008830 && (aep->ae_u.term.start == NULL
8831 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008832 taep->ae_u.term.start) == 0)
8833 && (aep->ae_u.term.stop == NULL)
8834 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008835 && (aep->ae_u.term.stop == NULL
8836 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008837 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008838 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008839 && aep->ae_u.cterm.fg_color
8840 == taep->ae_u.cterm.fg_color
8841 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008842 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008843#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008844 && aep->ae_u.cterm.fg_rgb
8845 == taep->ae_u.cterm.fg_rgb
8846 && aep->ae_u.cterm.bg_rgb
8847 == taep->ae_u.cterm.bg_rgb
8848#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008849 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008850
8851 return i + ATTR_OFF;
8852 }
8853
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008854 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008855 {
8856 /*
8857 * Running out of attribute entries! remove all attributes, and
8858 * compute new ones for all groups.
8859 * When called recursively, we are really out of numbers.
8860 */
8861 if (recursive)
8862 {
8863 EMSG(_("E424: Too many different highlighting attributes in use"));
8864 return 0;
8865 }
8866 recursive = TRUE;
8867
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008868 clear_hl_tables();
8869
Bram Moolenaar071d4272004-06-13 20:20:40 +00008870 must_redraw = CLEAR;
8871
8872 for (i = 0; i < highlight_ga.ga_len; ++i)
8873 set_hl_attr(i);
8874
8875 recursive = FALSE;
8876 }
8877
8878 /*
8879 * This is a new combination of colors and font, add an entry.
8880 */
8881 if (ga_grow(table, 1) == FAIL)
8882 return 0;
8883
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008884 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8885 vim_memset(taep, 0, sizeof(attrentry_T));
8886 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008887#ifdef FEAT_GUI
8888 if (table == &gui_attr_table)
8889 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008890 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8891 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8892 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8893 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008894# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008895 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008896# endif
8897 }
8898#endif
8899 if (table == &term_attr_table)
8900 {
8901 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008902 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008903 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008904 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008905 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008906 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008907 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008908 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008909 }
8910 else if (table == &cterm_attr_table)
8911 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008912 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8913 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008914#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008915 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8916 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8917#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008918 }
8919 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008920 return (table->ga_len - 1 + ATTR_OFF);
8921}
8922
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008923/*
Bram Moolenaareeac6772017-07-23 15:48:37 +02008924 * Get an attribute index for a cterm entry.
8925 * Uses an existing entry when possible or adds one when needed.
8926 */
8927 int
8928get_cterm_attr_idx(int attr, int fg, int bg)
8929{
8930 attrentry_T at_en;
8931
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008932 vim_memset(&at_en, 0, sizeof(attrentry_T));
Bram Moolenaarcafafb32018-02-22 21:07:09 +01008933#ifdef FEAT_TERMGUICOLORS
8934 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8935 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8936#endif
Bram Moolenaareeac6772017-07-23 15:48:37 +02008937 at_en.ae_attr = attr;
8938 at_en.ae_u.cterm.fg_color = fg;
8939 at_en.ae_u.cterm.bg_color = bg;
8940 return get_attr_entry(&cterm_attr_table, &at_en);
8941}
8942
Bram Moolenaar065f41c2017-07-23 18:07:56 +02008943#if defined(FEAT_TERMGUICOLORS) || defined(PROTO)
8944/*
8945 * Get an attribute index for a 'termguicolors' entry.
8946 * Uses an existing entry when possible or adds one when needed.
8947 */
8948 int
8949get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8950{
8951 attrentry_T at_en;
8952
8953 vim_memset(&at_en, 0, sizeof(attrentry_T));
8954 at_en.ae_attr = attr;
8955 at_en.ae_u.cterm.fg_rgb = fg;
8956 at_en.ae_u.cterm.bg_rgb = bg;
8957 return get_attr_entry(&cterm_attr_table, &at_en);
8958}
8959#endif
8960
Bram Moolenaar26af85d2017-07-23 16:45:10 +02008961#if defined(FEAT_GUI) || defined(PROTO)
8962/*
8963 * Get an attribute index for a cterm entry.
8964 * Uses an existing entry when possible or adds one when needed.
8965 */
8966 int
8967get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
8968{
8969 attrentry_T at_en;
8970
8971 vim_memset(&at_en, 0, sizeof(attrentry_T));
8972 at_en.ae_attr = attr;
8973 at_en.ae_u.gui.fg_color = fg;
8974 at_en.ae_u.gui.bg_color = bg;
8975 return get_attr_entry(&gui_attr_table, &at_en);
8976}
8977#endif
8978
Bram Moolenaareeac6772017-07-23 15:48:37 +02008979/*
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008980 * Clear all highlight tables.
8981 */
8982 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008983clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008984{
8985 int i;
8986 attrentry_T *taep;
8987
8988#ifdef FEAT_GUI
8989 ga_clear(&gui_attr_table);
8990#endif
8991 for (i = 0; i < term_attr_table.ga_len; ++i)
8992 {
8993 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8994 vim_free(taep->ae_u.term.start);
8995 vim_free(taep->ae_u.term.stop);
8996 }
8997 ga_clear(&term_attr_table);
8998 ga_clear(&cterm_attr_table);
8999}
9000
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009001#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009002/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00009003 * Combine special attributes (e.g., for spelling) with other attributes
9004 * (e.g., for syntax highlighting).
9005 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00009006 * This creates a new group when required.
9007 * Since we expect there to be few spelling mistakes we don't cache the
9008 * result.
9009 * Return the resulting attributes.
9010 */
9011 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009012hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009013{
9014 attrentry_T *char_aep = NULL;
9015 attrentry_T *spell_aep;
9016 attrentry_T new_en;
9017
9018 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00009019 return prim_attr;
9020 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009021 return ATTR_COMBINE(char_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009022#ifdef FEAT_GUI
9023 if (gui.in_use)
9024 {
9025 if (char_attr > HL_ALL)
9026 char_aep = syn_gui_attr2entry(char_attr);
9027 if (char_aep != NULL)
9028 new_en = *char_aep;
9029 else
9030 {
9031 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00009032 new_en.ae_u.gui.fg_color = INVALCOLOR;
9033 new_en.ae_u.gui.bg_color = INVALCOLOR;
9034 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00009035 if (char_attr <= HL_ALL)
9036 new_en.ae_attr = char_attr;
9037 }
9038
Bram Moolenaar30abd282005-06-22 22:35:10 +00009039 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009040 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009041 else
9042 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009043 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009044 if (spell_aep != NULL)
9045 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009046 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9047 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009048 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
9049 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
9050 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
9051 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
9052 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
9053 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
9054 if (spell_aep->ae_u.gui.font != NOFONT)
9055 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
9056# ifdef FEAT_XFONTSET
9057 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
9058 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
9059# endif
9060 }
9061 }
9062 return get_attr_entry(&gui_attr_table, &new_en);
9063 }
9064#endif
9065
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009066 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00009067 {
9068 if (char_attr > HL_ALL)
9069 char_aep = syn_cterm_attr2entry(char_attr);
9070 if (char_aep != NULL)
9071 new_en = *char_aep;
9072 else
9073 {
9074 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01009075#ifdef FEAT_TERMGUICOLORS
9076 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
9077 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
9078#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009079 if (char_attr <= HL_ALL)
9080 new_en.ae_attr = char_attr;
9081 }
9082
Bram Moolenaar30abd282005-06-22 22:35:10 +00009083 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009084 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009085 else
9086 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00009087 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009088 if (spell_aep != NULL)
9089 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009090 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
9091 spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009092 if (spell_aep->ae_u.cterm.fg_color > 0)
9093 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
9094 if (spell_aep->ae_u.cterm.bg_color > 0)
9095 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009096#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009097 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009098 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009099 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009100 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
9101#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00009102 }
9103 }
9104 return get_attr_entry(&cterm_attr_table, &new_en);
9105 }
9106
9107 if (char_attr > HL_ALL)
9108 char_aep = syn_term_attr2entry(char_attr);
9109 if (char_aep != NULL)
9110 new_en = *char_aep;
9111 else
9112 {
9113 vim_memset(&new_en, 0, sizeof(new_en));
9114 if (char_attr <= HL_ALL)
9115 new_en.ae_attr = char_attr;
9116 }
9117
Bram Moolenaar30abd282005-06-22 22:35:10 +00009118 if (prim_attr <= HL_ALL)
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009119 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009120 else
9121 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00009122 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009123 if (spell_aep != NULL)
9124 {
Bram Moolenaar0cd2a942017-08-12 15:12:30 +02009125 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00009126 if (spell_aep->ae_u.term.start != NULL)
9127 {
9128 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
9129 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
9130 }
9131 }
9132 }
9133 return get_attr_entry(&term_attr_table, &new_en);
9134}
9135#endif
9136
Bram Moolenaar071d4272004-06-13 20:20:40 +00009137#ifdef FEAT_GUI
9138
9139 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009140syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009141{
9142 attr -= ATTR_OFF;
9143 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
9144 return NULL;
9145 return &(GUI_ATTR_ENTRY(attr));
9146}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009147#endif /* FEAT_GUI */
9148
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009149/*
9150 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
9151 * Only to be used when "attr" > HL_ALL.
9152 */
9153 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009154syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009155{
9156 attrentry_T *aep;
9157
9158#ifdef FEAT_GUI
9159 if (gui.in_use)
9160 aep = syn_gui_attr2entry(attr);
9161 else
9162#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009163 if (IS_CTERM)
9164 aep = syn_cterm_attr2entry(attr);
9165 else
9166 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00009167
9168 if (aep == NULL) /* highlighting not set */
9169 return 0;
9170 return aep->ae_attr;
9171}
9172
9173
Bram Moolenaar071d4272004-06-13 20:20:40 +00009174 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009175syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009176{
9177 attr -= ATTR_OFF;
9178 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
9179 return NULL;
9180 return &(TERM_ATTR_ENTRY(attr));
9181}
9182
9183 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009184syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009185{
9186 attr -= ATTR_OFF;
9187 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
9188 return NULL;
9189 return &(CTERM_ATTR_ENTRY(attr));
9190}
9191
9192#define LIST_ATTR 1
9193#define LIST_STRING 2
9194#define LIST_INT 3
9195
9196 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009197highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009198{
9199 struct hl_group *sgp;
9200 int didh = FALSE;
9201
9202 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
9203
9204 didh = highlight_list_arg(id, didh, LIST_ATTR,
9205 sgp->sg_term, NULL, "term");
9206 didh = highlight_list_arg(id, didh, LIST_STRING,
9207 0, sgp->sg_start, "start");
9208 didh = highlight_list_arg(id, didh, LIST_STRING,
9209 0, sgp->sg_stop, "stop");
9210
9211 didh = highlight_list_arg(id, didh, LIST_ATTR,
9212 sgp->sg_cterm, NULL, "cterm");
9213 didh = highlight_list_arg(id, didh, LIST_INT,
9214 sgp->sg_cterm_fg, NULL, "ctermfg");
9215 didh = highlight_list_arg(id, didh, LIST_INT,
9216 sgp->sg_cterm_bg, NULL, "ctermbg");
9217
Bram Moolenaar61623362010-07-14 22:04:22 +02009218#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009219 didh = highlight_list_arg(id, didh, LIST_ATTR,
9220 sgp->sg_gui, NULL, "gui");
9221 didh = highlight_list_arg(id, didh, LIST_STRING,
9222 0, sgp->sg_gui_fg_name, "guifg");
9223 didh = highlight_list_arg(id, didh, LIST_STRING,
9224 0, sgp->sg_gui_bg_name, "guibg");
9225 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009226 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02009227#endif
9228#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00009229 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00009230 0, sgp->sg_font_name, "font");
9231#endif
9232
Bram Moolenaar661b1822005-07-28 22:36:45 +00009233 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009234 {
9235 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00009236 didh = TRUE;
Bram Moolenaar8820b482017-03-16 17:23:31 +01009237 msg_puts_attr((char_u *)"links to", HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009238 msg_putchar(' ');
9239 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
9240 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009241
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009242 if (!didh)
9243 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00009244#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009245 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009246 last_set_msg(sgp->sg_scriptID);
9247#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009248}
9249
9250 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009251highlight_list_arg(
9252 int id,
9253 int didh,
9254 int type,
9255 int iarg,
9256 char_u *sarg,
9257 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009258{
9259 char_u buf[100];
9260 char_u *ts;
9261 int i;
9262
Bram Moolenaar661b1822005-07-28 22:36:45 +00009263 if (got_int)
9264 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009265 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
9266 {
9267 ts = buf;
9268 if (type == LIST_INT)
9269 sprintf((char *)buf, "%d", iarg - 1);
9270 else if (type == LIST_STRING)
9271 ts = sarg;
9272 else /* type == LIST_ATTR */
9273 {
9274 buf[0] = NUL;
9275 for (i = 0; hl_attr_table[i] != 0; ++i)
9276 {
9277 if (iarg & hl_attr_table[i])
9278 {
9279 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009280 vim_strcat(buf, (char_u *)",", 100);
9281 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009282 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9283 }
9284 }
9285 }
9286
9287 (void)syn_list_header(didh,
9288 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9289 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009290 if (!got_int)
9291 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009292 if (*name != NUL)
9293 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01009294 MSG_PUTS_ATTR(name, HL_ATTR(HLF_D));
9295 MSG_PUTS_ATTR("=", HL_ATTR(HLF_D));
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009296 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009297 msg_outtrans(ts);
9298 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009299 }
9300 return didh;
9301}
9302
9303#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9304/*
9305 * Return "1" if highlight group "id" has attribute "flag".
9306 * Return NULL otherwise.
9307 */
9308 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009309highlight_has_attr(
9310 int id,
9311 int flag,
9312 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009313{
9314 int attr;
9315
9316 if (id <= 0 || id > highlight_ga.ga_len)
9317 return NULL;
9318
Bram Moolenaar61623362010-07-14 22:04:22 +02009319#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009320 if (modec == 'g')
9321 attr = HL_TABLE()[id - 1].sg_gui;
9322 else
9323#endif
9324 if (modec == 'c')
9325 attr = HL_TABLE()[id - 1].sg_cterm;
9326 else
9327 attr = HL_TABLE()[id - 1].sg_term;
9328
9329 if (attr & flag)
9330 return (char_u *)"1";
9331 return NULL;
9332}
9333#endif
9334
9335#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9336/*
9337 * Return color name of highlight group "id".
9338 */
9339 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009340highlight_color(
9341 int id,
9342 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9343 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009344{
9345 static char_u name[20];
9346 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009347 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009348 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009349 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009350
9351 if (id <= 0 || id > highlight_ga.ga_len)
9352 return NULL;
9353
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009354 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009356 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009357 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009358 font = TRUE;
9359 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009360 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009361 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9362 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009363 if (modec == 'g')
9364 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009365# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009366# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009367 /* return font name */
9368 if (font)
9369 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009370# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009371
Bram Moolenaar071d4272004-06-13 20:20:40 +00009372 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009373 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009374 {
9375 guicolor_T color;
9376 long_u rgb;
9377 static char_u buf[10];
9378
9379 if (fg)
9380 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009381 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009382# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009383 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009384# else
9385 color = INVALCOLOR;
9386# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009387 else
9388 color = HL_TABLE()[id - 1].sg_gui_bg;
9389 if (color == INVALCOLOR)
9390 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009391 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009392 sprintf((char *)buf, "#%02x%02x%02x",
9393 (unsigned)(rgb >> 16),
9394 (unsigned)(rgb >> 8) & 255,
9395 (unsigned)rgb & 255);
9396 return buf;
9397 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009398# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009399 if (fg)
9400 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009401 if (sp)
9402 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009403 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9404 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009405 if (font || sp)
9406 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407 if (modec == 'c')
9408 {
9409 if (fg)
9410 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9411 else
9412 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009413 if (n < 0)
9414 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009415 sprintf((char *)name, "%d", n);
9416 return name;
9417 }
9418 /* term doesn't have color */
9419 return NULL;
9420}
9421#endif
9422
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009423#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009424 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009425 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009426/*
9427 * Return color name of highlight group "id" as RGB value.
9428 */
9429 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009430highlight_gui_color_rgb(
9431 int id,
9432 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009433{
9434 guicolor_T color;
9435
9436 if (id <= 0 || id > highlight_ga.ga_len)
9437 return 0L;
9438
9439 if (fg)
9440 color = HL_TABLE()[id - 1].sg_gui_fg;
9441 else
9442 color = HL_TABLE()[id - 1].sg_gui_bg;
9443
9444 if (color == INVALCOLOR)
9445 return 0L;
9446
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009447 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009448}
9449#endif
9450
9451/*
9452 * Output the syntax list header.
9453 * Return TRUE when started a new line.
9454 */
9455 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009456syn_list_header(
9457 int did_header, /* did header already */
9458 int outlen, /* length of string that comes */
9459 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009460{
9461 int endcol = 19;
9462 int newline = TRUE;
9463
9464 if (!did_header)
9465 {
9466 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009467 if (got_int)
9468 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009469 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9470 endcol = 15;
9471 }
9472 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009473 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009474 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009475 if (got_int)
9476 return TRUE;
9477 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009478 else
9479 {
9480 if (msg_col >= endcol) /* wrap around is like starting a new line */
9481 newline = FALSE;
9482 }
9483
9484 if (msg_col >= endcol) /* output at least one space */
9485 endcol = msg_col + 1;
9486 if (Columns <= endcol) /* avoid hang for tiny window */
9487 endcol = Columns - 1;
9488
9489 msg_advance(endcol);
9490
9491 /* Show "xxx" with the attributes. */
9492 if (!did_header)
9493 {
9494 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9495 msg_putchar(' ');
9496 }
9497
9498 return newline;
9499}
9500
9501/*
9502 * Set the attribute numbers for a highlight group.
9503 * Called after one of the attributes has changed.
9504 */
9505 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009506set_hl_attr(
9507 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009508{
9509 attrentry_T at_en;
9510 struct hl_group *sgp = HL_TABLE() + idx;
9511
9512 /* The "Normal" group doesn't need an attribute number */
9513 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9514 return;
9515
9516#ifdef FEAT_GUI
9517 /*
9518 * For the GUI mode: If there are other than "normal" highlighting
9519 * attributes, need to allocate an attr number.
9520 */
9521 if (sgp->sg_gui_fg == INVALCOLOR
9522 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009523 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009524 && sgp->sg_font == NOFONT
9525# ifdef FEAT_XFONTSET
9526 && sgp->sg_fontset == NOFONTSET
9527# endif
9528 )
9529 {
9530 sgp->sg_gui_attr = sgp->sg_gui;
9531 }
9532 else
9533 {
9534 at_en.ae_attr = sgp->sg_gui;
9535 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9536 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009537 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009538 at_en.ae_u.gui.font = sgp->sg_font;
9539# ifdef FEAT_XFONTSET
9540 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9541# endif
9542 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9543 }
9544#endif
9545 /*
9546 * For the term mode: If there are other than "normal" highlighting
9547 * attributes, need to allocate an attr number.
9548 */
9549 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9550 sgp->sg_term_attr = sgp->sg_term;
9551 else
9552 {
9553 at_en.ae_attr = sgp->sg_term;
9554 at_en.ae_u.term.start = sgp->sg_start;
9555 at_en.ae_u.term.stop = sgp->sg_stop;
9556 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9557 }
9558
9559 /*
9560 * For the color term mode: If there are other than "normal"
9561 * highlighting attributes, need to allocate an attr number.
9562 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009563 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009564# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009565 && sgp->sg_gui_fg == INVALCOLOR
9566 && sgp->sg_gui_bg == INVALCOLOR
9567# endif
9568 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009569 sgp->sg_cterm_attr = sgp->sg_cterm;
9570 else
9571 {
9572 at_en.ae_attr = sgp->sg_cterm;
9573 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9574 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009575# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarcafafb32018-02-22 21:07:09 +01009576# ifdef WIN3264
9577 {
9578 int id;
9579 guicolor_T fg, bg;
9580
9581 id = syn_name2id((char_u *)"Normal");
9582 if (id > 0)
9583 {
9584 syn_id2colors(id, &fg, &bg);
9585 if (sgp->sg_gui_fg == INVALCOLOR)
9586 sgp->sg_gui_fg = fg;
9587 if (sgp->sg_gui_bg == INVALCOLOR)
9588 sgp->sg_gui_bg = bg;
9589 }
9590
9591 }
9592# endif
Bram Moolenaar187147a2016-05-01 13:09:57 +02009593 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9594 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009595# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009596 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9597 }
9598}
9599
9600/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009601 * Lookup a highlight group name and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009602 * If it is not found, 0 is returned.
9603 */
9604 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009605syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009606{
9607 int i;
9608 char_u name_u[200];
9609
9610 /* Avoid using stricmp() too much, it's slow on some systems */
9611 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9612 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009613 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009614 vim_strup(name_u);
9615 for (i = highlight_ga.ga_len; --i >= 0; )
9616 if (HL_TABLE()[i].sg_name_u != NULL
9617 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9618 break;
9619 return i + 1;
9620}
9621
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009622/*
9623 * Lookup a highlight group name and return its attributes.
9624 * Return zero if not found.
9625 */
9626 int
9627syn_name2attr(char_u *name)
9628{
9629 int id = syn_name2id(name);
9630
9631 if (id != 0)
Bram Moolenaar76301952017-09-22 13:53:37 +02009632 return syn_id2attr(id);
Bram Moolenaar1b9645d2017-09-17 23:03:31 +02009633 return 0;
9634}
9635
Bram Moolenaar071d4272004-06-13 20:20:40 +00009636#if defined(FEAT_EVAL) || defined(PROTO)
9637/*
9638 * Return TRUE if highlight group "name" exists.
9639 */
9640 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009641highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009642{
9643 return (syn_name2id(name) > 0);
9644}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009645
9646# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9647/*
9648 * Return the name of highlight group "id".
9649 * When not a valid ID return an empty string.
9650 */
9651 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009652syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009653{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009654 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009655 return (char_u *)"";
9656 return HL_TABLE()[id - 1].sg_name;
9657}
9658# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009659#endif
9660
9661/*
9662 * Like syn_name2id(), but take a pointer + length argument.
9663 */
9664 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009665syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009666{
9667 char_u *name;
9668 int id = 0;
9669
9670 name = vim_strnsave(linep, len);
9671 if (name != NULL)
9672 {
9673 id = syn_name2id(name);
9674 vim_free(name);
9675 }
9676 return id;
9677}
9678
9679/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009680 * Find highlight group name in the table and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009681 * The argument is a pointer to the name and the length of the name.
9682 * If it doesn't exist yet, a new entry is created.
9683 * Return 0 for failure.
9684 */
9685 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009686syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009687{
9688 int id;
9689 char_u *name;
9690
9691 name = vim_strnsave(pp, len);
9692 if (name == NULL)
9693 return 0;
9694
9695 id = syn_name2id(name);
9696 if (id == 0) /* doesn't exist yet */
9697 id = syn_add_group(name);
9698 else
9699 vim_free(name);
9700 return id;
9701}
9702
9703/*
Bram Moolenaaraab93b12017-03-18 21:37:28 +01009704 * Add new highlight group and return its ID.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009705 * "name" must be an allocated string, it will be consumed.
9706 * Return 0 for failure.
9707 */
9708 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009709syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009710{
9711 char_u *p;
9712
9713 /* Check that the name is ASCII letters, digits and underscore. */
9714 for (p = name; *p != NUL; ++p)
9715 {
9716 if (!vim_isprintc(*p))
9717 {
9718 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009719 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009720 return 0;
9721 }
9722 else if (!ASCII_ISALNUM(*p) && *p != '_')
9723 {
9724 /* This is an error, but since there previously was no check only
9725 * give a warning. */
Bram Moolenaar8820b482017-03-16 17:23:31 +01009726 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009727 MSG(_("W18: Invalid character in group name"));
9728 break;
9729 }
9730 }
9731
9732 /*
9733 * First call for this growarray: init growing array.
9734 */
9735 if (highlight_ga.ga_data == NULL)
9736 {
9737 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9738 highlight_ga.ga_growsize = 10;
9739 }
9740
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009741 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009742 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009743 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009744 vim_free(name);
9745 return 0;
9746 }
9747
Bram Moolenaar071d4272004-06-13 20:20:40 +00009748 /*
9749 * Make room for at least one other syntax_highlight entry.
9750 */
9751 if (ga_grow(&highlight_ga, 1) == FAIL)
9752 {
9753 vim_free(name);
9754 return 0;
9755 }
9756
9757 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9758 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9759 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009760#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009761 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9762 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009763# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009764 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009765# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009766#endif
9767 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009768
9769 return highlight_ga.ga_len; /* ID is index plus one */
9770}
9771
9772/*
9773 * When, just after calling syn_add_group(), an error is discovered, this
9774 * function deletes the new name.
9775 */
9776 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009777syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009778{
9779 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009780 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9781 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9782}
9783
9784/*
9785 * Translate a group ID to highlight attributes.
9786 */
9787 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009788syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009789{
9790 int attr;
9791 struct hl_group *sgp;
9792
9793 hl_id = syn_get_final_id(hl_id);
9794 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9795
9796#ifdef FEAT_GUI
9797 /*
9798 * Only use GUI attr when the GUI is being used.
9799 */
9800 if (gui.in_use)
9801 attr = sgp->sg_gui_attr;
9802 else
9803#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009804 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009805 attr = sgp->sg_cterm_attr;
9806 else
9807 attr = sgp->sg_term_attr;
9808
9809 return attr;
9810}
9811
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009812#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009813/*
9814 * Get the GUI colors and attributes for a group ID.
9815 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9816 */
9817 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009818syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009819{
9820 struct hl_group *sgp;
9821
9822 hl_id = syn_get_final_id(hl_id);
9823 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9824
9825 *fgp = sgp->sg_gui_fg;
9826 *bgp = sgp->sg_gui_bg;
9827 return sgp->sg_gui;
9828}
9829#endif
9830
Bram Moolenaard371bbe2017-09-28 22:35:25 +02009831#if defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarc71053c2017-09-14 00:00:44 +02009832 void
9833syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
9834{
9835 struct hl_group *sgp;
9836
9837 hl_id = syn_get_final_id(hl_id);
9838 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9839 *fgp = sgp->sg_cterm_fg - 1;
9840 *bgp = sgp->sg_cterm_bg - 1;
9841}
9842#endif
9843
Bram Moolenaar071d4272004-06-13 20:20:40 +00009844/*
9845 * Translate a group ID to the final group ID (following links).
9846 */
9847 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009848syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009849{
9850 int count;
9851 struct hl_group *sgp;
9852
9853 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9854 return 0; /* Can be called from eval!! */
9855
9856 /*
9857 * Follow links until there is no more.
9858 * Look out for loops! Break after 100 links.
9859 */
9860 for (count = 100; --count >= 0; )
9861 {
9862 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9863 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9864 break;
9865 hl_id = sgp->sg_link;
9866 }
9867
9868 return hl_id;
9869}
9870
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009871#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009872/*
9873 * Call this function just after the GUI has started.
Bram Moolenaar33ef5bb2018-02-27 13:04:59 +01009874 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
Bram Moolenaar071d4272004-06-13 20:20:40 +00009875 * It finds the font and color handles for the highlighting groups.
9876 */
9877 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009878highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009879{
9880 int idx;
9881
9882 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009883 if (USE_24BIT)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009884 set_normal_colors();
Bram Moolenaar071d4272004-06-13 20:20:40 +00009885
9886 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9887 gui_do_one_color(idx, FALSE, FALSE);
9888
9889 highlight_changed();
9890}
9891
9892 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009893gui_do_one_color(
9894 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009895 int do_menu UNUSED, /* TRUE: might set the menu font */
9896 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009897{
9898 int didit = FALSE;
9899
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009900# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009901# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009902 if (gui.in_use)
9903# endif
9904 if (HL_TABLE()[idx].sg_font_name != NULL)
9905 {
9906 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009907 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009908 didit = TRUE;
9909 }
9910# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009911 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9912 {
9913 HL_TABLE()[idx].sg_gui_fg =
9914 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9915 didit = TRUE;
9916 }
9917 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9918 {
9919 HL_TABLE()[idx].sg_gui_bg =
9920 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9921 didit = TRUE;
9922 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009923# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009924 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9925 {
9926 HL_TABLE()[idx].sg_gui_sp =
9927 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9928 didit = TRUE;
9929 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009930# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009931 if (didit) /* need to get a new attr number */
9932 set_hl_attr(idx);
9933}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009934#endif
9935
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009936#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
9937/*
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +02009938 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
Bram Moolenaarbce4f622017-08-13 21:37:43 +02009939 */
9940 static void
9941combine_stl_hlt(
9942 int id,
9943 int id_S,
9944 int id_alt,
9945 int hlcnt,
9946 int i,
9947 int hlf,
9948 int *table)
9949{
9950 struct hl_group *hlt = HL_TABLE();
9951
9952 if (id_alt == 0)
9953 {
9954 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
9955 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
9956 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
9957# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9958 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
9959# endif
9960 }
9961 else
9962 mch_memmove(&hlt[hlcnt + i],
9963 &hlt[id_alt - 1],
9964 sizeof(struct hl_group));
9965 hlt[hlcnt + i].sg_link = 0;
9966
9967 hlt[hlcnt + i].sg_term ^=
9968 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9969 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9970 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9971 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9972 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9973 hlt[hlcnt + i].sg_cterm ^=
9974 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9975 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9976 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9977 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9978 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9979# if defined(FEAT_GUI) || defined(FEAT_EVAL)
9980 hlt[hlcnt + i].sg_gui ^=
9981 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9982# endif
9983# ifdef FEAT_GUI
9984 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9985 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9986 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9987 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
9988 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9989 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
9990 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9991 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9992# ifdef FEAT_XFONTSET
9993 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9994 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9995# endif
9996# endif
9997 highlight_ga.ga_len = hlcnt + i + 1;
9998 set_hl_attr(hlcnt + i); /* At long last we can apply */
9999 table[i] = syn_id2attr(hlcnt + i + 1);
10000}
10001#endif
10002
Bram Moolenaar071d4272004-06-13 20:20:40 +000010003/*
10004 * Translate the 'highlight' option into attributes in highlight_attr[] and
10005 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
10006 * corresponding highlights to use on top of HLF_SNC is computed.
10007 * Called only when the 'highlight' option has been changed and upon first
10008 * screen redraw after any :highlight command.
10009 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
10010 */
10011 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010012highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010013{
10014 int hlf;
10015 int i;
10016 char_u *p;
10017 int attr;
10018 char_u *end;
10019 int id;
10020#ifdef USER_HIGHLIGHT
10021 char_u userhl[10];
10022# ifdef FEAT_STL_OPT
10023 int id_SNC = -1;
10024 int id_S = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010025# ifdef FEAT_TERMINAL
10026 int id_ST = -1;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010027 int id_STNC = -1;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010028# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010029 int hlcnt;
10030# endif
10031#endif
10032 static int hl_flags[HLF_COUNT] = HL_FLAGS;
10033
10034 need_highlight_changed = FALSE;
10035
10036 /*
10037 * Clear all attributes.
10038 */
10039 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10040 highlight_attr[hlf] = 0;
10041
10042 /*
10043 * First set all attributes to their default value.
10044 * Then use the attributes from the 'highlight' option.
10045 */
10046 for (i = 0; i < 2; ++i)
10047 {
10048 if (i)
10049 p = p_hl;
10050 else
10051 p = get_highlight_default();
10052 if (p == NULL) /* just in case */
10053 continue;
10054
10055 while (*p)
10056 {
10057 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
10058 if (hl_flags[hlf] == *p)
10059 break;
10060 ++p;
10061 if (hlf == (int)HLF_COUNT || *p == NUL)
10062 return FAIL;
10063
10064 /*
10065 * Allow several hl_flags to be combined, like "bu" for
10066 * bold-underlined.
10067 */
10068 attr = 0;
10069 for ( ; *p && *p != ','; ++p) /* parse upto comma */
10070 {
Bram Moolenaar1c465442017-03-12 20:10:05 +010010071 if (VIM_ISWHITE(*p)) /* ignore white space */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010072 continue;
10073
10074 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
10075 return FAIL;
10076
10077 switch (*p)
10078 {
10079 case 'b': attr |= HL_BOLD;
10080 break;
10081 case 'i': attr |= HL_ITALIC;
10082 break;
10083 case '-':
10084 case 'n': /* no highlighting */
10085 break;
10086 case 'r': attr |= HL_INVERSE;
10087 break;
10088 case 's': attr |= HL_STANDOUT;
10089 break;
10090 case 'u': attr |= HL_UNDERLINE;
10091 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +000010092 case 'c': attr |= HL_UNDERCURL;
10093 break;
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +020010094 case 't': attr |= HL_STRIKETHROUGH;
10095 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010096 case ':': ++p; /* highlight group name */
10097 if (attr || *p == NUL) /* no combinations */
10098 return FAIL;
10099 end = vim_strchr(p, ',');
10100 if (end == NULL)
10101 end = p + STRLEN(p);
10102 id = syn_check_group(p, (int)(end - p));
10103 if (id == 0)
10104 return FAIL;
10105 attr = syn_id2attr(id);
10106 p = end - 1;
10107#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
10108 if (hlf == (int)HLF_SNC)
10109 id_SNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010110# ifdef FEAT_TERMINAL
10111 else if (hlf == (int)HLF_ST)
10112 id_ST = syn_get_final_id(id);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010113 else if (hlf == (int)HLF_STNC)
10114 id_STNC = syn_get_final_id(id);
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010115# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010116 else if (hlf == (int)HLF_S)
10117 id_S = syn_get_final_id(id);
10118#endif
10119 break;
10120 default: return FAIL;
10121 }
10122 }
10123 highlight_attr[hlf] = attr;
10124
10125 p = skip_to_option_part(p); /* skip comma and spaces */
10126 }
10127 }
10128
10129#ifdef USER_HIGHLIGHT
10130 /* Setup the user highlights
10131 *
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010132 * Temporarily utilize 28 more hl entries:
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010133 * 9 for User1-User9 combined with StatusLineNC
10134 * 9 for User1-User9 combined with StatusLineTerm
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010135 * 9 for User1-User9 combined with StatusLineTermNC
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010136 * 1 for StatusLine default
10137 * Have to be in there simultaneously in case of table overflows in
10138 * get_attr_entry()
Bram Moolenaar071d4272004-06-13 20:20:40 +000010139 */
10140# ifdef FEAT_STL_OPT
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010141 if (ga_grow(&highlight_ga, 28) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010142 return FAIL;
10143 hlcnt = highlight_ga.ga_len;
Bram Moolenaard6a7b3e2017-08-19 21:35:35 +020010144 if (id_S == -1)
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010145 {
10146 /* Make sure id_S is always valid to simplify code below. Use the last
10147 * entry. */
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010148 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(struct hl_group));
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010149 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
10150 id_S = hlcnt + 19;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010151 }
10152# endif
10153 for (i = 0; i < 9; i++)
10154 {
10155 sprintf((char *)userhl, "User%d", i + 1);
10156 id = syn_name2id(userhl);
10157 if (id == 0)
10158 {
10159 highlight_user[i] = 0;
10160# ifdef FEAT_STL_OPT
10161 highlight_stlnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010162# ifdef FEAT_TERMINAL
10163 highlight_stlterm[i] = 0;
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010164 highlight_stltermnc[i] = 0;
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010165# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010166# endif
10167 }
10168 else
10169 {
Bram Moolenaar071d4272004-06-13 20:20:40 +000010170 highlight_user[i] = syn_id2attr(id);
10171# ifdef FEAT_STL_OPT
Bram Moolenaarbce4f622017-08-13 21:37:43 +020010172 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
10173 HLF_SNC, highlight_stlnc);
10174# ifdef FEAT_TERMINAL
10175 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
10176 HLF_ST, highlight_stlterm);
Bram Moolenaar05fbfdc2017-08-14 22:35:08 +020010177 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
10178 HLF_STNC, highlight_stltermnc);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010179# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000010180# endif
10181 }
10182 }
10183# ifdef FEAT_STL_OPT
10184 highlight_ga.ga_len = hlcnt;
10185# endif
10186
10187#endif /* USER_HIGHLIGHT */
10188
10189 return OK;
10190}
10191
Bram Moolenaar4f688582007-07-24 12:34:30 +000010192#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010193
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010010194static void highlight_list(void);
10195static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010196
10197/*
10198 * Handle command line completion for :highlight command.
10199 */
10200 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010201set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010202{
10203 char_u *p;
10204
10205 /* Default: expand group names */
10206 xp->xp_context = EXPAND_HIGHLIGHT;
10207 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +000010208 include_link = 2;
10209 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010210
10211 /* (part of) subcommand already typed */
10212 if (*arg != NUL)
10213 {
10214 p = skiptowhite(arg);
10215 if (*p != NUL) /* past "default" or group name */
10216 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010217 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010218 if (STRNCMP("default", arg, p - arg) == 0)
10219 {
10220 arg = skipwhite(p);
10221 xp->xp_pattern = arg;
10222 p = skiptowhite(arg);
10223 }
10224 if (*p != NUL) /* past group name */
10225 {
Bram Moolenaar4f688582007-07-24 12:34:30 +000010226 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +000010227 if (arg[1] == 'i' && arg[0] == 'N')
10228 highlight_list();
10229 if (STRNCMP("link", arg, p - arg) == 0
10230 || STRNCMP("clear", arg, p - arg) == 0)
10231 {
10232 xp->xp_pattern = skipwhite(p);
10233 p = skiptowhite(xp->xp_pattern);
10234 if (*p != NUL) /* past first group name */
10235 {
10236 xp->xp_pattern = skipwhite(p);
10237 p = skiptowhite(xp->xp_pattern);
10238 }
10239 }
10240 if (*p != NUL) /* past group name(s) */
10241 xp->xp_context = EXPAND_NOTHING;
10242 }
10243 }
10244 }
10245}
10246
10247/*
10248 * List highlighting matches in a nice way.
10249 */
10250 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010251highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010252{
10253 int i;
10254
10255 for (i = 10; --i >= 0; )
Bram Moolenaar8820b482017-03-16 17:23:31 +010010256 highlight_list_two(i, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +000010257 for (i = 40; --i >= 0; )
10258 highlight_list_two(99, 0);
10259}
10260
10261 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010262highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010263{
Bram Moolenaar112f3182012-06-01 13:18:53 +020010264 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +000010265 msg_clr_eos();
10266 out_flush();
10267 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
10268}
10269
10270#endif /* FEAT_CMDL_COMPL */
10271
10272#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
10273 || defined(FEAT_SIGNS) || defined(PROTO)
10274/*
10275 * Function given to ExpandGeneric() to obtain the list of group names.
Bram Moolenaar071d4272004-06-13 20:20:40 +000010276 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000010277 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010278get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010279{
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010280 return get_highlight_name_ext(xp, idx, TRUE);
10281}
10282
10283/*
10284 * Obtain a highlight group name.
10285 * When "skip_cleared" is TRUE don't return a cleared entry.
10286 */
10287 char_u *
10288get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
10289{
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010290 if (idx < 0)
10291 return NULL;
Bram Moolenaarc96272e2017-03-26 13:50:09 +020010292
10293 /* Items are never removed from the table, skip the ones that were
10294 * cleared. */
10295 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
10296 return (char_u *)"";
Bram Moolenaar15eedf12017-01-22 19:25:33 +010010297
Bram Moolenaar071d4272004-06-13 20:20:40 +000010298#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000010299 if (idx == highlight_ga.ga_len && include_none != 0)
10300 return (char_u *)"none";
10301 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010302 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +000010303 if (idx == highlight_ga.ga_len + include_none + include_default
10304 && include_link != 0)
10305 return (char_u *)"link";
10306 if (idx == highlight_ga.ga_len + include_none + include_default + 1
10307 && include_link != 0)
10308 return (char_u *)"clear";
10309#endif
Bram Moolenaard61e8aa2017-01-17 17:44:46 +010010310 if (idx >= highlight_ga.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010311 return NULL;
10312 return HL_TABLE()[idx].sg_name;
10313}
10314#endif
10315
Bram Moolenaar4f688582007-07-24 12:34:30 +000010316#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010317/*
10318 * Free all the highlight group fonts.
10319 * Used when quitting for systems which need it.
10320 */
10321 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +010010322free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +000010323{
10324 int idx;
10325
10326 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
10327 {
10328 gui_mch_free_font(HL_TABLE()[idx].sg_font);
10329 HL_TABLE()[idx].sg_font = NOFONT;
10330# ifdef FEAT_XFONTSET
10331 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
10332 HL_TABLE()[idx].sg_fontset = NOFONTSET;
10333# endif
10334 }
10335
10336 gui_mch_free_font(gui.norm_font);
10337# ifdef FEAT_XFONTSET
10338 gui_mch_free_fontset(gui.fontset);
10339# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +020010340# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +000010341 gui_mch_free_font(gui.bold_font);
10342 gui_mch_free_font(gui.ital_font);
10343 gui_mch_free_font(gui.boldital_font);
10344# endif
10345}
10346#endif
10347
10348/**************************************
10349 * End of Highlighting stuff *
10350 **************************************/