blob: bc5c9b74ff9605b437ed7e3e74ea1184560038b9 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
Bram Moolenaar071d4272004-06-13 20:20:40 +000036/* for when using the GUI */
Bram Moolenaar61be73b2016-04-29 22:59:22 +020037#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaar8a633e32016-04-21 21:10:14 +020040#endif
41#ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +000042 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 GuiFont sg_font; /* GUI font handle */
44#ifdef FEAT_XFONTSET
45 GuiFontset sg_fontset; /* GUI fontset handle */
46#endif
47 char_u *sg_font_name; /* GUI font or fontset name */
48 int sg_gui_attr; /* Screen attr for GUI mode */
49#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020050#if defined(FEAT_GUI) || defined(FEAT_EVAL)
51/* Store the sp color name for the GUI or synIDattr() */
52 int sg_gui; /* "gui=" highlighting attributes */
53 char_u *sg_gui_fg_name;/* GUI foreground color name */
54 char_u *sg_gui_bg_name;/* GUI background color name */
55 char_u *sg_gui_sp_name;/* GUI special color name */
56#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000057 int sg_link; /* link to this highlight group ID */
58 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000059#ifdef FEAT_EVAL
60 scid_T sg_scriptID; /* script in which the group was last set */
61#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000062};
63
64#define SG_TERM 1 /* term has been set */
65#define SG_CTERM 2 /* cterm has been set */
66#define SG_GUI 4 /* gui has been set */
67#define SG_LINK 8 /* link has been set */
68
69static garray_T highlight_ga; /* highlight groups for 'highlight' option */
70
71#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
72
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020073#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
74
Bram Moolenaar071d4272004-06-13 20:20:40 +000075#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000076/* Flags to indicate an additional string for highlight name completion. */
77static int include_none = 0; /* when 1 include "None" */
78static int include_default = 0; /* when 1 include "default" */
79static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000080#endif
81
82/*
83 * The "term", "cterm" and "gui" arguments can be any combination of the
84 * following names, separated by commas (but no spaces!).
85 */
86static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000087 {"bold", "standout", "underline", "undercurl",
88 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000089static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000090 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000091
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010092static int get_attr_entry(garray_T *table, attrentry_T *aep);
93static void syn_unadd_group(void);
94static void set_hl_attr(int idx);
95static void highlight_list_one(int id);
96static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
97static int syn_add_group(char_u *name);
98static int syn_list_header(int did_header, int outlen, int id);
99static int hl_has_settings(int idx, int check_link);
100static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101
Bram Moolenaar61be73b2016-04-29 22:59:22 +0200102#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100103static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100104static guicolor_T color_name2handle(char_u *name);
Bram Moolenaar8a633e32016-04-21 21:10:14 +0200105#endif
106#ifdef FEAT_GUI
107static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100108static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100110static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000111# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100112static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000113#endif
114
115/*
116 * An attribute number is the index in attr_table plus ATTR_OFF.
117 */
118#define ATTR_OFF (HL_ALL + 1)
119
120#if defined(FEAT_SYN_HL) || defined(PROTO)
121
122#define SYN_NAMELEN 50 /* maximum length of a syntax name */
123
124/* different types of offsets that are possible */
125#define SPO_MS_OFF 0 /* match start offset */
126#define SPO_ME_OFF 1 /* match end offset */
127#define SPO_HS_OFF 2 /* highl. start offset */
128#define SPO_HE_OFF 3 /* highl. end offset */
129#define SPO_RS_OFF 4 /* region start offset */
130#define SPO_RE_OFF 5 /* region end offset */
131#define SPO_LC_OFF 6 /* leading context offset */
132#define SPO_COUNT 7
133
134static char *(spo_name_tab[SPO_COUNT]) =
135 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
136
137/*
138 * The patterns that are being searched for are stored in a syn_pattern.
139 * A match item consists of one pattern.
140 * A start/end item consists of n start patterns and m end patterns.
141 * A start/skip/end item consists of n start patterns, one skip pattern and m
142 * end patterns.
143 * For the latter two, the patterns are always consecutive: start-skip-end.
144 *
145 * A character offset can be given for the matched text (_m_start and _m_end)
146 * and for the actually highlighted text (_h_start and _h_end).
147 */
148typedef struct syn_pattern
149{
150 char sp_type; /* see SPTYPE_ defines below */
151 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200152 int sp_flags; /* see HL_ defines below */
153#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200154 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200155#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156 struct sp_syn sp_syn; /* struct passed to in_id_list() */
157 short sp_syn_match_id; /* highlight group ID of pattern */
158 char_u *sp_pattern; /* regexp to match, pattern */
159 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200160#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200161 syn_time_T sp_time;
162#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163 int sp_ic; /* ignore-case flag for sp_prog */
164 short sp_off_flags; /* see below */
165 int sp_offsets[SPO_COUNT]; /* offsets */
166 short *sp_cont_list; /* cont. group IDs, if non-zero */
167 short *sp_next_list; /* next group IDs, if non-zero */
168 int sp_sync_idx; /* sync item index (syncing only) */
169 int sp_line_id; /* ID of last line where tried */
170 int sp_startcol; /* next match in sp_line_id line */
171} synpat_T;
172
173/* The sp_off_flags are computed like this:
174 * offset from the start of the matched text: (1 << SPO_XX_OFF)
175 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
176 * When both are present, only one is used.
177 */
178
179#define SPTYPE_MATCH 1 /* match keyword with this group ID */
180#define SPTYPE_START 2 /* match a regexp, start of item */
181#define SPTYPE_END 3 /* match a regexp, end of item */
182#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
183
Bram Moolenaar071d4272004-06-13 20:20:40 +0000184
185#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
186
187#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
188
189/*
190 * Flags for b_syn_sync_flags:
191 */
192#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
193#define SF_MATCH 0x02 /* sync by matching a pattern */
194
195#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
196
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197#define MAXKEYWLEN 80 /* maximum length of a keyword */
198
199/*
200 * The attributes of the syntax item that has been recognized.
201 */
202static int current_attr = 0; /* attr of current syntax word */
203#ifdef FEAT_EVAL
204static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000205static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200207#ifdef FEAT_CONCEAL
208static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200209static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200210static int current_sub_char = 0;
211#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212
Bram Moolenaar217ad922005-03-20 22:37:15 +0000213typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000214{
215 char_u *scl_name; /* syntax cluster name */
216 char_u *scl_name_u; /* uppercase of scl_name */
217 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000218} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * Methods of combining two clusters
222 */
223#define CLUSTER_REPLACE 1 /* replace first list with second */
224#define CLUSTER_ADD 2 /* add second list to first */
225#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
226
Bram Moolenaar217ad922005-03-20 22:37:15 +0000227#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228
229/*
230 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200231 * 0 - 19999 normal syntax groups
232 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
233 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
234 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
235 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200237#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200238#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
239#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
240#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
241
Bram Moolenaar42431a72011-04-01 14:44:59 +0200242#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
243#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244
245/*
246 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
247 * expand_filename(). Most of the other syntax commands don't need it, so
248 * instead of passing it to them, we stow it here.
249 */
250static char_u **syn_cmdlinep;
251
252/*
253 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200254 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 * rules in each ":syn include"'d file.
256 */
257static int current_syn_inc_tag = 0;
258static int running_syn_inc_tag = 0;
259
260/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000261 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
262 * This avoids adding a pointer to the hashtable item.
263 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
264 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
265 * HI2KE() converts a hashitem pointer to a var pointer.
266 */
267static keyentry_T dumkey;
268#define KE2HIKEY(kp) ((kp)->keyword)
269#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
270#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
271
272/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000273 * To reduce the time spent in keepend(), remember at which level in the state
274 * stack the first item with "keepend" is present. When "-1", there is no
275 * "keepend" on the stack.
276 */
277static int keepend_level = -1;
278
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200279static char msg_no_items[] = N_("No Syntax items defined for this buffer");
280
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281/*
282 * For the current state we need to remember more than just the idx.
283 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
284 * (The end positions have the column number of the next char)
285 */
286typedef struct state_item
287{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000288 int si_idx; /* index of syntax pattern or
289 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000291 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000292 int si_m_lnum; /* lnum of the match */
293 int si_m_startcol; /* starting column of the match */
294 lpos_T si_m_endpos; /* just after end posn of the match */
295 lpos_T si_h_startpos; /* start position of the highlighting */
296 lpos_T si_h_endpos; /* end position of the highlighting */
297 lpos_T si_eoe_pos; /* end position of end pattern */
298 int si_end_idx; /* group ID for end pattern or zero */
299 int si_ends; /* if match ends before si_m_endpos */
300 int si_attr; /* attributes in this state */
301 long si_flags; /* HL_HAS_EOL flag in this state, and
302 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200303#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200304 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200305 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200306#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 short *si_cont_list; /* list of contained groups */
308 short *si_next_list; /* nextgroup IDs after this item ends */
309 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
310 * pattern */
311} stateitem_T;
312
313#define KEYWORD_IDX -1 /* value of si_idx for keywords */
314#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
315 but contained groups */
316
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200317#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100318static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200319#endif
320
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000322 * Struct to reduce the number of arguments to get_syn_options(), it's used
323 * very often.
324 */
325typedef struct
326{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000327 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000328 int keyword; /* TRUE for ":syn keyword" */
329 int *sync_idx; /* syntax item for "grouphere" argument, NULL
330 if not allowed */
331 char has_cont_list; /* TRUE if "cont_list" can be used */
332 short *cont_list; /* group IDs for "contains" argument */
333 short *cont_in_list; /* group IDs for "containedin" argument */
334 short *next_list; /* group IDs for "nextgroup" argument */
335} syn_opt_arg_T;
336
337/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000338 * The next possible match in the current line for any pattern is remembered,
339 * to avoid having to try for a match in each column.
340 * If next_match_idx == -1, not tried (in this line) yet.
341 * If next_match_col == MAXCOL, no match found in this line.
342 * (All end positions have the column of the char after the end)
343 */
344static int next_match_col; /* column for start of next match */
345static lpos_T next_match_m_endpos; /* position for end of next match */
346static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
347static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
348static int next_match_idx; /* index of matched item */
349static long next_match_flags; /* flags for next match */
350static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
351static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
352static int next_match_end_idx; /* ID of group for end pattn or zero */
353static reg_extmatch_T *next_match_extmatch = NULL;
354
355/*
356 * A state stack is an array of integers or stateitem_T, stored in a
357 * garray_T. A state stack is invalid if it's itemsize entry is zero.
358 */
359#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
360#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
361
362/*
363 * The current state (within the line) of the recognition engine.
364 * When current_state.ga_itemsize is 0 the current state is invalid.
365 */
366static win_T *syn_win; /* current window for highlighting */
367static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200368static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369static linenr_T current_lnum = 0; /* lnum of current state */
370static colnr_T current_col = 0; /* column of current state */
371static int current_state_stored = 0; /* TRUE if stored current state
372 * after setting current_finished */
373static int current_finished = 0; /* current line has been finished */
374static garray_T current_state /* current stack of state_items */
375 = {0, 0, 0, 0, NULL};
376static short *current_next_list = NULL; /* when non-zero, nextgroup list */
377static int current_next_flags = 0; /* flags for current_next_list */
378static int current_line_id = 0; /* unique number for current line */
379
380#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
381
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100382static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100383static void save_chartab(char_u *chartab);
384static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100385static int syn_match_linecont(linenr_T lnum);
386static void syn_start_line(void);
387static void syn_update_ends(int startofline);
388static void syn_stack_alloc(void);
389static int syn_stack_cleanup(void);
390static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
391static synstate_T *syn_stack_find_entry(linenr_T lnum);
392static synstate_T *store_current_state(void);
393static void load_current_state(synstate_T *from);
394static void invalidate_current_state(void);
395static int syn_stack_equal(synstate_T *sp);
396static void validate_current_state(void);
397static int syn_finish_line(int syncing);
398static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
399static int did_match_already(int idx, garray_T *gap);
400static stateitem_T *push_next_match(stateitem_T *cur_si);
401static void check_state_ends(void);
402static void update_si_attr(int idx);
403static void check_keepend(void);
404static void update_si_end(stateitem_T *sip, int startcol, int force);
405static short *copy_id_list(short *list);
406static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
407static int push_current_state(int idx);
408static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200409#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100410static void syn_clear_time(syn_time_T *tt);
411static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200412#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100413static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200414#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100415static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200416#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100417static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200418static int syn_time_on = FALSE;
419# define IF_SYN_TIME(p) (p)
420#else
421# define IF_SYN_TIME(p) NULL
422typedef int syn_time_T;
423#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100425static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
426static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext);
427static void clear_syn_state(synstate_T *p);
428static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100430static void limit_pos(lpos_T *pos, lpos_T *limit);
431static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
432static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
433static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
434static char_u *syn_getcurline(void);
435static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
436static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
437static void syn_cmd_case(exarg_T *eap, int syncing);
438static void syn_cmd_spell(exarg_T *eap, int syncing);
439static void syntax_sync_clear(void);
440static void syn_remove_pattern(synblock_T *block, int idx);
441static void syn_clear_pattern(synblock_T *block, int i);
442static void syn_clear_cluster(synblock_T *block, int i);
443static void syn_cmd_clear(exarg_T *eap, int syncing);
444static void syn_cmd_conceal(exarg_T *eap, int syncing);
445static void syn_clear_one(int id, int syncing);
446static void syn_cmd_on(exarg_T *eap, int syncing);
447static void syn_cmd_enable(exarg_T *eap, int syncing);
448static void syn_cmd_reset(exarg_T *eap, int syncing);
449static void syn_cmd_manual(exarg_T *eap, int syncing);
450static void syn_cmd_off(exarg_T *eap, int syncing);
451static void syn_cmd_onoff(exarg_T *eap, char *name);
452static void syn_cmd_list(exarg_T *eap, int syncing);
453static void syn_lines_msg(void);
454static void syn_match_msg(void);
455static void syn_stack_free_block(synblock_T *block);
456static void syn_list_one(int id, int syncing, int link_only);
457static void syn_list_cluster(int id);
458static void put_id_list(char_u *name, short *list, int attr);
459static void put_pattern(char *s, int c, synpat_T *spp, int attr);
460static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
461static void syn_clear_keyword(int id, hashtab_T *ht);
462static void clear_keywtab(hashtab_T *ht);
463static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
464static char_u *get_group_name(char_u *arg, char_u **name_end);
465static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char);
466static void syn_cmd_include(exarg_T *eap, int syncing);
467static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
468static void syn_cmd_keyword(exarg_T *eap, int syncing);
469static void syn_cmd_match(exarg_T *eap, int syncing);
470static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100472static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000473#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100474static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100476static void syn_cmd_cluster(exarg_T *eap, int syncing);
477static int syn_scl_name2id(char_u *name);
478static int syn_scl_namen2id(char_u *linep, int len);
479static int syn_check_cluster(char_u *pp, int len);
480static int syn_add_cluster(char_u *name);
481static void init_syn_patterns(void);
482static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
483static void syn_cmd_sync(exarg_T *eap, int syncing);
484static int get_id_list(char_u **arg, int keylen, short **list);
485static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
486static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487
488/*
489 * Start the syntax recognition for a line. This function is normally called
490 * from the screen updating, once for each displayed line.
491 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
492 * it. Careful: curbuf and curwin are likely to point to another buffer and
493 * window.
494 */
495 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100496syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497{
498 synstate_T *p;
499 synstate_T *last_valid = NULL;
500 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000501 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 linenr_T parsed_lnum;
503 linenr_T first_stored;
504 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000505 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200507#ifdef FEAT_CONCEAL
508 current_sub_char = NUL;
509#endif
510
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511 /*
512 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000513 * Also do this when a change was made, the current state may be invalid
514 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 */
Bram Moolenaarb681be12016-03-31 23:02:16 +0200516 if (syn_block != wp->w_s
517 || syn_buf != wp->w_buffer
518 || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 {
520 invalidate_current_state();
521 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200522 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000524 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525 syn_win = wp;
526
527 /*
528 * Allocate syntax stack when needed.
529 */
530 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200531 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000532 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200533 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534
535 /*
536 * If the state of the end of the previous line is useful, store it.
537 */
538 if (VALID_STATE(&current_state)
539 && current_lnum < lnum
540 && current_lnum < syn_buf->b_ml.ml_line_count)
541 {
542 (void)syn_finish_line(FALSE);
543 if (!current_state_stored)
544 {
545 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000546 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547 }
548
549 /*
550 * If the current_lnum is now the same as "lnum", keep the current
551 * state (this happens very often!). Otherwise invalidate
552 * current_state and figure it out below.
553 */
554 if (current_lnum != lnum)
555 invalidate_current_state();
556 }
557 else
558 invalidate_current_state();
559
560 /*
561 * Try to synchronize from a saved state in b_sst_array[].
562 * Only do this if lnum is not before and not to far beyond a saved state.
563 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200564 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565 {
566 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200567 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 {
569 if (p->sst_lnum > lnum)
570 break;
571 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
572 {
573 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 last_min_valid = p;
576 }
577 }
578 if (last_min_valid != NULL)
579 load_current_state(last_min_valid);
580 }
581
582 /*
583 * If "lnum" is before or far beyond a line with a saved state, need to
584 * re-synchronize.
585 */
586 if (INVALID_STATE(&current_state))
587 {
588 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200589 if (current_lnum == 1)
590 /* First line is always valid, no matter "minlines". */
591 first_stored = 1;
592 else
593 /* Need to parse "minlines" lines before state can be considered
594 * valid to store. */
595 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 }
597 else
598 first_stored = current_lnum;
599
600 /*
601 * Advance from the sync point or saved state until the current line.
602 * Save some entries for syncing with later on.
603 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200604 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000605 dist = 999999;
606 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200607 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000608 while (current_lnum < lnum)
609 {
610 syn_start_line();
611 (void)syn_finish_line(FALSE);
612 ++current_lnum;
613
614 /* If we parsed at least "minlines" lines or started at a valid
615 * state, the current state is considered valid. */
616 if (current_lnum >= first_stored)
617 {
618 /* Check if the saved state entry is for the current line and is
619 * equal to the current state. If so, then validate all saved
620 * states that depended on a change before the parsed line. */
621 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000622 prev = syn_stack_find_entry(current_lnum - 1);
623 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200624 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000626 sp = prev;
627 while (sp != NULL && sp->sst_lnum < current_lnum)
628 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629 if (sp != NULL
630 && sp->sst_lnum == current_lnum
631 && syn_stack_equal(sp))
632 {
633 parsed_lnum = current_lnum;
634 prev = sp;
635 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
636 {
637 if (sp->sst_lnum <= lnum)
638 /* valid state before desired line, use this one */
639 prev = sp;
640 else if (sp->sst_change_lnum == 0)
641 /* past saved states depending on change, break here. */
642 break;
643 sp->sst_change_lnum = 0;
644 sp = sp->sst_next;
645 }
646 load_current_state(prev);
647 }
648 /* Store the state at this line when it's the first one, the line
649 * where we start parsing, or some distance from the previously
650 * saved state. But only when parsed at least 'minlines'. */
651 else if (prev == NULL
652 || current_lnum == lnum
653 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000654 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 }
656
657 /* This can take a long time: break when CTRL-C pressed. The current
658 * state will be wrong then. */
659 line_breakcheck();
660 if (got_int)
661 {
662 current_lnum = lnum;
663 break;
664 }
665 }
666
667 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668}
669
670/*
671 * We cannot simply discard growarrays full of state_items or buf_states; we
672 * have to manually release their extmatch pointers first.
673 */
674 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100675clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000676{
677 int i;
678 garray_T *gap;
679
680 if (p->sst_stacksize > SST_FIX_STATES)
681 {
682 gap = &(p->sst_union.sst_ga);
683 for (i = 0; i < gap->ga_len; i++)
684 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
685 ga_clear(gap);
686 }
687 else
688 {
689 for (i = 0; i < p->sst_stacksize; i++)
690 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
691 }
692}
693
694/*
695 * Cleanup the current_state stack.
696 */
697 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100698clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000699{
700 int i;
701 stateitem_T *sip;
702
703 sip = (stateitem_T *)(current_state.ga_data);
704 for (i = 0; i < current_state.ga_len; i++)
705 unref_extmatch(sip[i].si_extmatch);
706 ga_clear(&current_state);
707}
708
709/*
710 * Try to find a synchronisation point for line "lnum".
711 *
712 * This sets current_lnum and the current state. One of three methods is
713 * used:
714 * 1. Search backwards for the end of a C-comment.
715 * 2. Search backwards for given sync patterns.
716 * 3. Simply start on a given number of lines above "lnum".
717 */
718 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100719syn_sync(
720 win_T *wp,
721 linenr_T start_lnum,
722 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723{
724 buf_T *curbuf_save;
725 win_T *curwin_save;
726 pos_T cursor_save;
727 int idx;
728 linenr_T lnum;
729 linenr_T end_lnum;
730 linenr_T break_lnum;
731 int had_sync_point;
732 stateitem_T *cur_si;
733 synpat_T *spp;
734 char_u *line;
735 int found_flags = 0;
736 int found_match_idx = 0;
737 linenr_T found_current_lnum = 0;
738 int found_current_col= 0;
739 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000740 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000741
742 /*
743 * Clear any current state that might be hanging around.
744 */
745 invalidate_current_state();
746
747 /*
748 * Start at least "minlines" back. Default starting point for parsing is
749 * there.
750 * Start further back, to avoid that scrolling backwards will result in
751 * resyncing for every line. Now it resyncs only one out of N lines,
752 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
753 * Watch out for overflow when minlines is MAXLNUM.
754 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200755 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756 start_lnum = 1;
757 else
758 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200759 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 else if (syn_block->b_syn_sync_minlines < 10)
762 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000763 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200764 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
765 if (syn_block->b_syn_sync_maxlines != 0
766 && lnum > syn_block->b_syn_sync_maxlines)
767 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000768 if (lnum >= start_lnum)
769 start_lnum = 1;
770 else
771 start_lnum -= lnum;
772 }
773 current_lnum = start_lnum;
774
775 /*
776 * 1. Search backwards for the end of a C-style comment.
777 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200778 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 {
780 /* Need to make syn_buf the current buffer for a moment, to be able to
781 * use find_start_comment(). */
782 curwin_save = curwin;
783 curwin = wp;
784 curbuf_save = curbuf;
785 curbuf = syn_buf;
786
787 /*
788 * Skip lines that end in a backslash.
789 */
790 for ( ; start_lnum > 1; --start_lnum)
791 {
792 line = ml_get(start_lnum - 1);
793 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
794 break;
795 }
796 current_lnum = start_lnum;
797
798 /* set cursor to start of search */
799 cursor_save = wp->w_cursor;
800 wp->w_cursor.lnum = start_lnum;
801 wp->w_cursor.col = 0;
802
803 /*
804 * If the line is inside a comment, need to find the syntax item that
805 * defines the comment.
806 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
807 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200808 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200810 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
811 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
812 == syn_block->b_syn_sync_id
813 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000814 {
815 validate_current_state();
816 if (push_current_state(idx) == OK)
817 update_si_attr(current_state.ga_len - 1);
818 break;
819 }
820 }
821
822 /* restore cursor and buffer */
823 wp->w_cursor = cursor_save;
824 curwin = curwin_save;
825 curbuf = curbuf_save;
826 }
827
828 /*
829 * 2. Search backwards for given sync patterns.
830 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200831 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200833 if (syn_block->b_syn_sync_maxlines != 0
834 && start_lnum > syn_block->b_syn_sync_maxlines)
835 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000836 else
837 break_lnum = 0;
838
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000839 found_m_endpos.lnum = 0;
840 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000841 end_lnum = start_lnum;
842 lnum = start_lnum;
843 while (--lnum > break_lnum)
844 {
845 /* This can take a long time: break when CTRL-C pressed. */
846 line_breakcheck();
847 if (got_int)
848 {
849 invalidate_current_state();
850 current_lnum = start_lnum;
851 break;
852 }
853
854 /* Check if we have run into a valid saved state stack now. */
855 if (last_valid != NULL && lnum == last_valid->sst_lnum)
856 {
857 load_current_state(last_valid);
858 break;
859 }
860
861 /*
862 * Check if the previous line has the line-continuation pattern.
863 */
864 if (lnum > 1 && syn_match_linecont(lnum - 1))
865 continue;
866
867 /*
868 * Start with nothing on the state stack
869 */
870 validate_current_state();
871
872 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
873 {
874 syn_start_line();
875 for (;;)
876 {
877 had_sync_point = syn_finish_line(TRUE);
878 /*
879 * When a sync point has been found, remember where, and
880 * continue to look for another one, further on in the line.
881 */
882 if (had_sync_point && current_state.ga_len)
883 {
884 cur_si = &CUR_STATE(current_state.ga_len - 1);
885 if (cur_si->si_m_endpos.lnum > start_lnum)
886 {
887 /* ignore match that goes to after where started */
888 current_lnum = end_lnum;
889 break;
890 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000891 if (cur_si->si_idx < 0)
892 {
893 /* Cannot happen? */
894 found_flags = 0;
895 found_match_idx = KEYWORD_IDX;
896 }
897 else
898 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200899 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000900 found_flags = spp->sp_flags;
901 found_match_idx = spp->sp_sync_idx;
902 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 found_current_lnum = current_lnum;
904 found_current_col = current_col;
905 found_m_endpos = cur_si->si_m_endpos;
906 /*
907 * Continue after the match (be aware of a zero-length
908 * match).
909 */
910 if (found_m_endpos.lnum > current_lnum)
911 {
912 current_lnum = found_m_endpos.lnum;
913 current_col = found_m_endpos.col;
914 if (current_lnum >= end_lnum)
915 break;
916 }
917 else if (found_m_endpos.col > current_col)
918 current_col = found_m_endpos.col;
919 else
920 ++current_col;
921
922 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000923 * an item that ends here, need to do that now. Be
924 * careful not to go past the NUL. */
925 prev_current_col = current_col;
926 if (syn_getcurline()[current_col] != NUL)
927 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000929 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 }
931 else
932 break;
933 }
934 }
935
936 /*
937 * If a sync point was encountered, break here.
938 */
939 if (found_flags)
940 {
941 /*
942 * Put the item that was specified by the sync point on the
943 * state stack. If there was no item specified, make the
944 * state stack empty.
945 */
946 clear_current_state();
947 if (found_match_idx >= 0
948 && push_current_state(found_match_idx) == OK)
949 update_si_attr(current_state.ga_len - 1);
950
951 /*
952 * When using "grouphere", continue from the sync point
953 * match, until the end of the line. Parsing starts at
954 * the next line.
955 * For "groupthere" the parsing starts at start_lnum.
956 */
957 if (found_flags & HL_SYNC_HERE)
958 {
959 if (current_state.ga_len)
960 {
961 cur_si = &CUR_STATE(current_state.ga_len - 1);
962 cur_si->si_h_startpos.lnum = found_current_lnum;
963 cur_si->si_h_startpos.col = found_current_col;
964 update_si_end(cur_si, (int)current_col, TRUE);
965 check_keepend();
966 }
967 current_col = found_m_endpos.col;
968 current_lnum = found_m_endpos.lnum;
969 (void)syn_finish_line(FALSE);
970 ++current_lnum;
971 }
972 else
973 current_lnum = start_lnum;
974
975 break;
976 }
977
978 end_lnum = lnum;
979 invalidate_current_state();
980 }
981
982 /* Ran into start of the file or exceeded maximum number of lines */
983 if (lnum <= break_lnum)
984 {
985 invalidate_current_state();
986 current_lnum = break_lnum + 1;
987 }
988 }
989
990 validate_current_state();
991}
992
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100993 static void
994save_chartab(char_u *chartab)
995{
996 if (syn_block->b_syn_isk != empty_option)
997 {
998 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
999 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
1000 (size_t)32);
1001 }
1002}
1003
1004 static void
1005restore_chartab(char_u *chartab)
1006{
1007 if (syn_win->w_s->b_syn_isk != empty_option)
1008 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1009}
1010
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011/*
1012 * Return TRUE if the line-continuation pattern matches in line "lnum".
1013 */
1014 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001015syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016{
1017 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001018 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001019 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020
Bram Moolenaar860cae12010-06-05 23:22:07 +02001021 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001023 /* use syntax iskeyword option */
1024 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001025 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1026 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001027 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001028 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001029 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001030 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001031 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032 }
1033 return FALSE;
1034}
1035
1036/*
1037 * Prepare the current state for the start of a line.
1038 */
1039 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001040syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001041{
1042 current_finished = FALSE;
1043 current_col = 0;
1044
1045 /*
1046 * Need to update the end of a start/skip/end that continues from the
1047 * previous line and regions that have "keepend".
1048 */
1049 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001050 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001051 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001052 check_state_ends();
1053 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054
1055 next_match_idx = -1;
1056 ++current_line_id;
1057}
1058
1059/*
1060 * Check for items in the stack that need their end updated.
1061 * When "startofline" is TRUE the last item is always updated.
1062 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1063 */
1064 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001065syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066{
1067 stateitem_T *cur_si;
1068 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001069 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070
1071 if (startofline)
1072 {
1073 /* Check for a match carried over from a previous line with a
1074 * contained region. The match ends as soon as the region ends. */
1075 for (i = 0; i < current_state.ga_len; ++i)
1076 {
1077 cur_si = &CUR_STATE(i);
1078 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001079 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 == SPTYPE_MATCH
1081 && cur_si->si_m_endpos.lnum < current_lnum)
1082 {
1083 cur_si->si_flags |= HL_MATCHCONT;
1084 cur_si->si_m_endpos.lnum = 0;
1085 cur_si->si_m_endpos.col = 0;
1086 cur_si->si_h_endpos = cur_si->si_m_endpos;
1087 cur_si->si_ends = TRUE;
1088 }
1089 }
1090 }
1091
1092 /*
1093 * Need to update the end of a start/skip/end that continues from the
1094 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001095 * influence contained items. If we've just removed "extend"
1096 * (startofline == 0) then we should update ends of normal regions
1097 * contained inside "keepend" because "extend" could have extended
1098 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 * Then check for items ending in column 0.
1100 */
1101 i = current_state.ga_len - 1;
1102 if (keepend_level >= 0)
1103 for ( ; i > keepend_level; --i)
1104 if (CUR_STATE(i).si_flags & HL_EXTEND)
1105 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001106
1107 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 for ( ; i < current_state.ga_len; ++i)
1109 {
1110 cur_si = &CUR_STATE(i);
1111 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001112 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 || (i == current_state.ga_len - 1 && startofline))
1114 {
1115 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1116 cur_si->si_h_startpos.lnum = current_lnum;
1117
1118 if (!(cur_si->si_flags & HL_MATCHCONT))
1119 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001120
1121 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1122 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123 }
1124 }
1125 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001126}
1127
1128/****************************************
1129 * Handling of the state stack cache.
1130 */
1131
1132/*
1133 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1134 *
1135 * To speed up syntax highlighting, the state stack for the start of some
1136 * lines is cached. These entries can be used to start parsing at that point.
1137 *
1138 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1139 * valid entries. b_sst_first points to the first one, then follow sst_next.
1140 * The entries are sorted on line number. The first entry is often for line 2
1141 * (line 1 always starts with an empty stack).
1142 * There is also a list for free entries. This construction is used to avoid
1143 * having to allocate and free memory blocks too often.
1144 *
1145 * When making changes to the buffer, this is logged in b_mod_*. When calling
1146 * update_screen() to update the display, it will call
1147 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1148 * entries. The entries which are inside the changed area are removed,
1149 * because they must be recomputed. Entries below the changed have their line
1150 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1151 * set to indicate that a check must be made if the changed lines would change
1152 * the cached entry.
1153 *
1154 * When later displaying lines, an entry is stored for each line. Displayed
1155 * lines are likely to be displayed again, in which case the state at the
1156 * start of the line is needed.
1157 * For not displayed lines, an entry is stored for every so many lines. These
1158 * entries will be used e.g., when scrolling backwards. The distance between
1159 * entries depends on the number of lines in the buffer. For small buffers
1160 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1161 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1162 */
1163
Bram Moolenaar860cae12010-06-05 23:22:07 +02001164 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001165syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001166{
1167 synstate_T *p;
1168
1169 if (block->b_sst_array != NULL)
1170 {
1171 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1172 clear_syn_state(p);
1173 vim_free(block->b_sst_array);
1174 block->b_sst_array = NULL;
1175 block->b_sst_len = 0;
1176 }
1177}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178/*
1179 * Free b_sst_array[] for buffer "buf".
1180 * Used when syntax items changed to force resyncing everywhere.
1181 */
1182 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001183syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 win_T *wp;
1186
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 syn_stack_free_block(block);
1188
1189
Bram Moolenaar071d4272004-06-13 20:20:40 +00001190#ifdef FEAT_FOLDING
1191 /* When using "syntax" fold method, must update all folds. */
1192 FOR_ALL_WINDOWS(wp)
1193 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001194 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 foldUpdateAll(wp);
1196 }
1197#endif
1198}
1199
1200/*
1201 * Allocate the syntax state stack for syn_buf when needed.
1202 * If the number of entries in b_sst_array[] is much too big or a bit too
1203 * small, reallocate it.
1204 * Also used to allocate b_sst_array[] for the first time.
1205 */
1206 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001207syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208{
1209 long len;
1210 synstate_T *to, *from;
1211 synstate_T *sstp;
1212
1213 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1214 if (len < SST_MIN_ENTRIES)
1215 len = SST_MIN_ENTRIES;
1216 else if (len > SST_MAX_ENTRIES)
1217 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001218 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219 {
1220 /* Allocate 50% too much, to avoid reallocating too often. */
1221 len = syn_buf->b_ml.ml_line_count;
1222 len = (len + len / 2) / SST_DIST + Rows * 2;
1223 if (len < SST_MIN_ENTRIES)
1224 len = SST_MIN_ENTRIES;
1225 else if (len > SST_MAX_ENTRIES)
1226 len = SST_MAX_ENTRIES;
1227
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229 {
1230 /* When shrinking the array, cleanup the existing stack.
1231 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001232 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 && syn_stack_cleanup())
1234 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001235 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1236 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 }
1238
1239 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1240 if (sstp == NULL) /* out of memory! */
1241 return;
1242
1243 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001244 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 {
1246 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 from = from->sst_next)
1249 {
1250 ++to;
1251 *to = *from;
1252 to->sst_next = to + 1;
1253 }
1254 }
1255 if (to != sstp - 1)
1256 {
1257 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 syn_block->b_sst_first = sstp;
1259 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260 }
1261 else
1262 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001263 syn_block->b_sst_first = NULL;
1264 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 }
1266
1267 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 while (++to < sstp + len)
1270 to->sst_next = to + 1;
1271 (sstp + len - 1)->sst_next = NULL;
1272
Bram Moolenaar860cae12010-06-05 23:22:07 +02001273 vim_free(syn_block->b_sst_array);
1274 syn_block->b_sst_array = sstp;
1275 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001276 }
1277}
1278
1279/*
1280 * Check for changes in a buffer to affect stored syntax states. Uses the
1281 * b_mod_* fields.
1282 * Called from update_screen(), before screen is being updated, once for each
1283 * displayed buffer.
1284 */
1285 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001286syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001288 win_T *wp;
1289
1290 syn_stack_apply_changes_block(&buf->b_s, buf);
1291
1292 FOR_ALL_WINDOWS(wp)
1293 {
1294 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1295 syn_stack_apply_changes_block(wp->w_s, buf);
1296 }
1297}
1298
1299 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001300syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001301{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302 synstate_T *p, *prev, *np;
1303 linenr_T n;
1304
Bram Moolenaar860cae12010-06-05 23:22:07 +02001305 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 return;
1307
1308 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001312 {
1313 n = p->sst_lnum + buf->b_mod_xlines;
1314 if (n <= buf->b_mod_bot)
1315 {
1316 /* this state is inside the changed area, remove it */
1317 np = p->sst_next;
1318 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001319 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 else
1321 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 p = np;
1324 continue;
1325 }
1326 /* This state is below the changed area. Remember the line
1327 * that needs to be parsed before this entry can be made valid
1328 * again. */
1329 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1330 {
1331 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1332 p->sst_change_lnum += buf->b_mod_xlines;
1333 else
1334 p->sst_change_lnum = buf->b_mod_top;
1335 }
1336 if (p->sst_change_lnum == 0
1337 || p->sst_change_lnum < buf->b_mod_bot)
1338 p->sst_change_lnum = buf->b_mod_bot;
1339
1340 p->sst_lnum = n;
1341 }
1342 prev = p;
1343 p = p->sst_next;
1344 }
1345}
1346
1347/*
1348 * Reduce the number of entries in the state stack for syn_buf.
1349 * Returns TRUE if at least one entry was freed.
1350 */
1351 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001352syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353{
1354 synstate_T *p, *prev;
1355 disptick_T tick;
1356 int above;
1357 int dist;
1358 int retval = FALSE;
1359
Bram Moolenaar860cae12010-06-05 23:22:07 +02001360 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 return retval;
1362
1363 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001364 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001365 dist = 999999;
1366 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001367 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001368
1369 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001370 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371 * be removed. Set "above" when the "tick" for the oldest entry is above
1372 * "b_sst_lasttick" (the display tick wraps around).
1373 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001374 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1378 {
1379 if (prev->sst_lnum + dist > p->sst_lnum)
1380 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001381 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382 {
1383 if (!above || p->sst_tick < tick)
1384 tick = p->sst_tick;
1385 above = TRUE;
1386 }
1387 else if (!above && p->sst_tick < tick)
1388 tick = p->sst_tick;
1389 }
1390 }
1391
1392 /*
1393 * Go through the list to make the entries for the oldest tick at an
1394 * interval of several lines.
1395 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001396 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1398 {
1399 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1400 {
1401 /* Move this entry from used list to free list */
1402 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001403 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001404 p = prev;
1405 retval = TRUE;
1406 }
1407 }
1408 return retval;
1409}
1410
1411/*
1412 * Free the allocated memory for a syn_state item.
1413 * Move the entry into the free list.
1414 */
1415 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001416syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001417{
1418 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001419 p->sst_next = block->b_sst_firstfree;
1420 block->b_sst_firstfree = p;
1421 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422}
1423
1424/*
1425 * Find an entry in the list of state stacks at or before "lnum".
1426 * Returns NULL when there is no entry or the first entry is after "lnum".
1427 */
1428 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001429syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430{
1431 synstate_T *p, *prev;
1432
1433 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001434 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435 {
1436 if (p->sst_lnum == lnum)
1437 return p;
1438 if (p->sst_lnum > lnum)
1439 break;
1440 }
1441 return prev;
1442}
1443
1444/*
1445 * Try saving the current state in b_sst_array[].
1446 * The current state must be valid for the start of the current_lnum line!
1447 */
1448 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001449store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450{
1451 int i;
1452 synstate_T *p;
1453 bufstate_T *bp;
1454 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001455 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456
1457 /*
1458 * If the current state contains a start or end pattern that continues
1459 * from the previous line, we can't use it. Don't store it then.
1460 */
1461 for (i = current_state.ga_len - 1; i >= 0; --i)
1462 {
1463 cur_si = &CUR_STATE(i);
1464 if (cur_si->si_h_startpos.lnum >= current_lnum
1465 || cur_si->si_m_endpos.lnum >= current_lnum
1466 || cur_si->si_h_endpos.lnum >= current_lnum
1467 || (cur_si->si_end_idx
1468 && cur_si->si_eoe_pos.lnum >= current_lnum))
1469 break;
1470 }
1471 if (i >= 0)
1472 {
1473 if (sp != NULL)
1474 {
1475 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001476 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001478 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 else
1480 {
1481 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001482 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 if (p->sst_next == sp)
1484 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001485 if (p != NULL) /* just in case */
1486 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001488 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 sp = NULL;
1490 }
1491 }
1492 else if (sp == NULL || sp->sst_lnum != current_lnum)
1493 {
1494 /*
1495 * Add a new entry
1496 */
1497 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001498 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 {
1500 (void)syn_stack_cleanup();
1501 /* "sp" may have been moved to the freelist now */
1502 sp = syn_stack_find_entry(current_lnum);
1503 }
1504 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001505 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 sp = NULL;
1507 else
1508 {
1509 /* Take the first item from the free list and put it in the used
1510 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001511 p = syn_block->b_sst_firstfree;
1512 syn_block->b_sst_firstfree = p->sst_next;
1513 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001514 if (sp == NULL)
1515 {
1516 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001517 p->sst_next = syn_block->b_sst_first;
1518 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001519 }
1520 else
1521 {
1522 /* insert in list after *sp */
1523 p->sst_next = sp->sst_next;
1524 sp->sst_next = p;
1525 }
1526 sp = p;
1527 sp->sst_stacksize = 0;
1528 sp->sst_lnum = current_lnum;
1529 }
1530 }
1531 if (sp != NULL)
1532 {
1533 /* When overwriting an existing state stack, clear it first */
1534 clear_syn_state(sp);
1535 sp->sst_stacksize = current_state.ga_len;
1536 if (current_state.ga_len > SST_FIX_STATES)
1537 {
1538 /* Need to clear it, might be something remaining from when the
1539 * length was less than SST_FIX_STATES. */
1540 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1541 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1542 sp->sst_stacksize = 0;
1543 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001544 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1546 }
1547 else
1548 bp = sp->sst_union.sst_stack;
1549 for (i = 0; i < sp->sst_stacksize; ++i)
1550 {
1551 bp[i].bs_idx = CUR_STATE(i).si_idx;
1552 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001553#ifdef FEAT_CONCEAL
1554 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1555 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1556#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001557 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1558 }
1559 sp->sst_next_flags = current_next_flags;
1560 sp->sst_next_list = current_next_list;
1561 sp->sst_tick = display_tick;
1562 sp->sst_change_lnum = 0;
1563 }
1564 current_state_stored = TRUE;
1565 return sp;
1566}
1567
1568/*
1569 * Copy a state stack from "from" in b_sst_array[] to current_state;
1570 */
1571 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001572load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573{
1574 int i;
1575 bufstate_T *bp;
1576
1577 clear_current_state();
1578 validate_current_state();
1579 keepend_level = -1;
1580 if (from->sst_stacksize
1581 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1582 {
1583 if (from->sst_stacksize > SST_FIX_STATES)
1584 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1585 else
1586 bp = from->sst_union.sst_stack;
1587 for (i = 0; i < from->sst_stacksize; ++i)
1588 {
1589 CUR_STATE(i).si_idx = bp[i].bs_idx;
1590 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001591#ifdef FEAT_CONCEAL
1592 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1593 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1594#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001595 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1596 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1597 keepend_level = i;
1598 CUR_STATE(i).si_ends = FALSE;
1599 CUR_STATE(i).si_m_lnum = 0;
1600 if (CUR_STATE(i).si_idx >= 0)
1601 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001602 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 else
1604 CUR_STATE(i).si_next_list = NULL;
1605 update_si_attr(i);
1606 }
1607 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608 }
1609 current_next_list = from->sst_next_list;
1610 current_next_flags = from->sst_next_flags;
1611 current_lnum = from->sst_lnum;
1612}
1613
1614/*
1615 * Compare saved state stack "*sp" with the current state.
1616 * Return TRUE when they are equal.
1617 */
1618 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001619syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001620{
1621 int i, j;
1622 bufstate_T *bp;
1623 reg_extmatch_T *six, *bsx;
1624
1625 /* First a quick check if the stacks have the same size end nextlist. */
1626 if (sp->sst_stacksize == current_state.ga_len
1627 && sp->sst_next_list == current_next_list)
1628 {
1629 /* Need to compare all states on both stacks. */
1630 if (sp->sst_stacksize > SST_FIX_STATES)
1631 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1632 else
1633 bp = sp->sst_union.sst_stack;
1634
1635 for (i = current_state.ga_len; --i >= 0; )
1636 {
1637 /* If the item has another index the state is different. */
1638 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1639 break;
1640 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1641 {
1642 /* When the extmatch pointers are different, the strings in
1643 * them can still be the same. Check if the extmatch
1644 * references are equal. */
1645 bsx = bp[i].bs_extmatch;
1646 six = CUR_STATE(i).si_extmatch;
1647 /* If one of the extmatch pointers is NULL the states are
1648 * different. */
1649 if (bsx == NULL || six == NULL)
1650 break;
1651 for (j = 0; j < NSUBEXP; ++j)
1652 {
1653 /* Check each referenced match string. They must all be
1654 * equal. */
1655 if (bsx->matches[j] != six->matches[j])
1656 {
1657 /* If the pointer is different it can still be the
1658 * same text. Compare the strings, ignore case when
1659 * the start item has the sp_ic flag set. */
1660 if (bsx->matches[j] == NULL
1661 || six->matches[j] == NULL)
1662 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001663 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 ? MB_STRICMP(bsx->matches[j],
1665 six->matches[j]) != 0
1666 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1667 break;
1668 }
1669 }
1670 if (j != NSUBEXP)
1671 break;
1672 }
1673 }
1674 if (i < 0)
1675 return TRUE;
1676 }
1677 return FALSE;
1678}
1679
1680/*
1681 * We stop parsing syntax above line "lnum". If the stored state at or below
1682 * this line depended on a change before it, it now depends on the line below
1683 * the last parsed line.
1684 * The window looks like this:
1685 * line which changed
1686 * displayed line
1687 * displayed line
1688 * lnum -> line below window
1689 */
1690 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001691syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692{
1693 synstate_T *sp;
1694
1695 sp = syn_stack_find_entry(lnum);
1696 if (sp != NULL && sp->sst_lnum < lnum)
1697 sp = sp->sst_next;
1698
1699 if (sp != NULL && sp->sst_change_lnum != 0)
1700 sp->sst_change_lnum = lnum;
1701}
1702
1703/*
1704 * End of handling of the state stack.
1705 ****************************************/
1706
1707 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001708invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001709{
1710 clear_current_state();
1711 current_state.ga_itemsize = 0; /* mark current_state invalid */
1712 current_next_list = NULL;
1713 keepend_level = -1;
1714}
1715
1716 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001717validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718{
1719 current_state.ga_itemsize = sizeof(stateitem_T);
1720 current_state.ga_growsize = 3;
1721}
1722
1723/*
1724 * Return TRUE if the syntax at start of lnum changed since last time.
1725 * This will only be called just after get_syntax_attr() for the previous
1726 * line, to check if the next line needs to be redrawn too.
1727 */
1728 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001729syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730{
1731 int retval = TRUE;
1732 synstate_T *sp;
1733
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734 /*
1735 * Check the state stack when:
1736 * - lnum is just below the previously syntaxed line.
1737 * - lnum is not before the lines with saved states.
1738 * - lnum is not past the lines with saved states.
1739 * - lnum is at or before the last changed line.
1740 */
1741 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1742 {
1743 sp = syn_stack_find_entry(lnum);
1744 if (sp != NULL && sp->sst_lnum == lnum)
1745 {
1746 /*
1747 * finish the previous line (needed when not all of the line was
1748 * drawn)
1749 */
1750 (void)syn_finish_line(FALSE);
1751
1752 /*
1753 * Compare the current state with the previously saved state of
1754 * the line.
1755 */
1756 if (syn_stack_equal(sp))
1757 retval = FALSE;
1758
1759 /*
1760 * Store the current state in b_sst_array[] for later use.
1761 */
1762 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001763 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 }
1765 }
1766
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 return retval;
1768}
1769
1770/*
1771 * Finish the current line.
1772 * This doesn't return any attributes, it only gets the state at the end of
1773 * the line. It can start anywhere in the line, as long as the current state
1774 * is valid.
1775 */
1776 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001777syn_finish_line(
1778 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779{
1780 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001781 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782
1783 if (!current_finished)
1784 {
1785 while (!current_finished)
1786 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001787 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001788 /*
1789 * When syncing, and found some item, need to check the item.
1790 */
1791 if (syncing && current_state.ga_len)
1792 {
1793 /*
1794 * Check for match with sync item.
1795 */
1796 cur_si = &CUR_STATE(current_state.ga_len - 1);
1797 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001798 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1800 return TRUE;
1801
1802 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001803 * that ends here, need to do that now. Be careful not to go
1804 * past the NUL. */
1805 prev_current_col = current_col;
1806 if (syn_getcurline()[current_col] != NUL)
1807 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001809 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 }
1811 ++current_col;
1812 }
1813 }
1814 return FALSE;
1815}
1816
1817/*
1818 * Return highlight attributes for next character.
1819 * Must first call syntax_start() once for the line.
1820 * "col" is normally 0 for the first use in a line, and increments by one each
1821 * time. It's allowed to skip characters and to stop before the end of the
1822 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001823 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1824 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825 */
1826 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001827get_syntax_attr(
1828 colnr_T col,
1829 int *can_spell,
1830 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001831{
1832 int attr = 0;
1833
Bram Moolenaar349955a2007-08-14 21:07:36 +00001834 if (can_spell != NULL)
1835 /* Default: Only do spelling when there is no @Spell cluster or when
1836 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001837 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1838 ? (syn_block->b_spell_cluster_id == 0)
1839 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001840
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001842 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 return 0;
1844
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001845 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001846 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001847 {
1848 clear_current_state();
1849#ifdef FEAT_EVAL
1850 current_id = 0;
1851 current_trans_id = 0;
1852#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001853#ifdef FEAT_CONCEAL
1854 current_flags = 0;
1855#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001856 return 0;
1857 }
1858
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 /* Make sure current_state is valid */
1860 if (INVALID_STATE(&current_state))
1861 validate_current_state();
1862
1863 /*
1864 * Skip from the current column to "col", get the attributes for "col".
1865 */
1866 while (current_col <= col)
1867 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001868 attr = syn_current_attr(FALSE, TRUE, can_spell,
1869 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 ++current_col;
1871 }
1872
Bram Moolenaar071d4272004-06-13 20:20:40 +00001873 return attr;
1874}
1875
1876/*
1877 * Get syntax attributes for current_lnum, current_col.
1878 */
1879 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001880syn_current_attr(
1881 int syncing, /* When 1: called for syncing */
1882 int displaying, /* result will be displayed */
1883 int *can_spell, /* return: do spell checking */
1884 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885{
1886 int syn_id;
1887 lpos_T endpos; /* was: char_u *endp; */
1888 lpos_T hl_startpos; /* was: int hl_startcol; */
1889 lpos_T hl_endpos;
1890 lpos_T eos_pos; /* end-of-start match (start region) */
1891 lpos_T eoe_pos; /* end-of-end pattern */
1892 int end_idx; /* group ID for end pattern */
1893 int idx;
1894 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001895 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 int startcol;
1897 int endcol;
1898 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001899 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001900 short *next_list;
1901 int found_match; /* found usable match */
1902 static int try_next_column = FALSE; /* must try in next col */
1903 int do_keywords;
1904 regmmatch_T regmatch;
1905 lpos_T pos;
1906 int lc_col;
1907 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001908 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001909 char_u *line; /* current line. NOTE: becomes invalid after
1910 looking for a pattern match! */
1911
1912 /* variables for zero-width matches that have a "nextgroup" argument */
1913 int keep_next_list;
1914 int zero_width_next_list = FALSE;
1915 garray_T zero_width_next_ga;
1916
1917 /*
1918 * No character, no attributes! Past end of line?
1919 * Do try matching with an empty line (could be the start of a region).
1920 */
1921 line = syn_getcurline();
1922 if (line[current_col] == NUL && current_col != 0)
1923 {
1924 /*
1925 * If we found a match after the last column, use it.
1926 */
1927 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1928 && next_match_col != MAXCOL)
1929 (void)push_next_match(NULL);
1930
1931 current_finished = TRUE;
1932 current_state_stored = FALSE;
1933 return 0;
1934 }
1935
1936 /* if the current or next character is NUL, we will finish the line now */
1937 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1938 {
1939 current_finished = TRUE;
1940 current_state_stored = FALSE;
1941 }
1942
1943 /*
1944 * When in the previous column there was a match but it could not be used
1945 * (empty match or already matched in this column) need to try again in
1946 * the next column.
1947 */
1948 if (try_next_column)
1949 {
1950 next_match_idx = -1;
1951 try_next_column = FALSE;
1952 }
1953
1954 /* Only check for keywords when not syncing and there are some. */
1955 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001956 && (syn_block->b_keywtab.ht_used > 0
1957 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958
1959 /* Init the list of zero-width matches with a nextlist. This is used to
1960 * avoid matching the same item in the same position twice. */
1961 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1962
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001963 /* use syntax iskeyword option */
1964 save_chartab(buf_chartab);
1965
Bram Moolenaar071d4272004-06-13 20:20:40 +00001966 /*
1967 * Repeat matching keywords and patterns, to find contained items at the
1968 * same column. This stops when there are no extra matches at the current
1969 * column.
1970 */
1971 do
1972 {
1973 found_match = FALSE;
1974 keep_next_list = FALSE;
1975 syn_id = 0;
1976
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001977
Bram Moolenaar071d4272004-06-13 20:20:40 +00001978 /*
1979 * 1. Check for a current state.
1980 * Only when there is no current state, or if the current state may
1981 * contain other things, we need to check for keywords and patterns.
1982 * Always need to check for contained items if some item has the
1983 * "containedin" argument (takes extra time!).
1984 */
1985 if (current_state.ga_len)
1986 cur_si = &CUR_STATE(current_state.ga_len - 1);
1987 else
1988 cur_si = NULL;
1989
Bram Moolenaar860cae12010-06-05 23:22:07 +02001990 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 || cur_si->si_cont_list != NULL)
1992 {
1993 /*
1994 * 2. Check for keywords, if on a keyword char after a non-keyword
1995 * char. Don't do this when syncing.
1996 */
1997 if (do_keywords)
1998 {
1999 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002000 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002002 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003#ifdef FEAT_MBYTE
2004 - (has_mbyte
2005 ? (*mb_head_off)(line, line + current_col - 1)
2006 : 0)
2007#endif
2008 , syn_buf)))
2009 {
2010 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002011 &endcol, &flags, &next_list, cur_si,
2012 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002013 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 {
2015 if (push_current_state(KEYWORD_IDX) == OK)
2016 {
2017 cur_si = &CUR_STATE(current_state.ga_len - 1);
2018 cur_si->si_m_startcol = current_col;
2019 cur_si->si_h_startpos.lnum = current_lnum;
2020 cur_si->si_h_startpos.col = 0; /* starts right away */
2021 cur_si->si_m_endpos.lnum = current_lnum;
2022 cur_si->si_m_endpos.col = endcol;
2023 cur_si->si_h_endpos.lnum = current_lnum;
2024 cur_si->si_h_endpos.col = endcol;
2025 cur_si->si_ends = TRUE;
2026 cur_si->si_end_idx = 0;
2027 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002028#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002029 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002030 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002031 if (current_state.ga_len > 1)
2032 cur_si->si_flags |=
2033 CUR_STATE(current_state.ga_len - 2).si_flags
2034 & HL_CONCEAL;
2035#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 cur_si->si_id = syn_id;
2037 cur_si->si_trans_id = syn_id;
2038 if (flags & HL_TRANSP)
2039 {
2040 if (current_state.ga_len < 2)
2041 {
2042 cur_si->si_attr = 0;
2043 cur_si->si_trans_id = 0;
2044 }
2045 else
2046 {
2047 cur_si->si_attr = CUR_STATE(
2048 current_state.ga_len - 2).si_attr;
2049 cur_si->si_trans_id = CUR_STATE(
2050 current_state.ga_len - 2).si_trans_id;
2051 }
2052 }
2053 else
2054 cur_si->si_attr = syn_id2attr(syn_id);
2055 cur_si->si_cont_list = NULL;
2056 cur_si->si_next_list = next_list;
2057 check_keepend();
2058 }
2059 else
2060 vim_free(next_list);
2061 }
2062 }
2063 }
2064
2065 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002066 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002067 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002068 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 {
2070 /*
2071 * If we didn't check for a match yet, or we are past it, check
2072 * for any match with a pattern.
2073 */
2074 if (next_match_idx < 0 || next_match_col < (int)current_col)
2075 {
2076 /*
2077 * Check all relevant patterns for a match at this
2078 * position. This is complicated, because matching with a
2079 * pattern takes quite a bit of time, thus we want to
2080 * avoid doing it when it's not needed.
2081 */
2082 next_match_idx = 0; /* no match in this line yet */
2083 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002084 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002086 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 if ( spp->sp_syncing == syncing
2088 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2089 && (spp->sp_type == SPTYPE_MATCH
2090 || spp->sp_type == SPTYPE_START)
2091 && (current_next_list != NULL
2092 ? in_id_list(NULL, current_next_list,
2093 &spp->sp_syn, 0)
2094 : (cur_si == NULL
2095 ? !(spp->sp_flags & HL_CONTAINED)
2096 : in_id_list(cur_si,
2097 cur_si->si_cont_list, &spp->sp_syn,
2098 spp->sp_flags & HL_CONTAINED))))
2099 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002100 int r;
2101
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102 /* If we already tried matching in this line, and
2103 * there isn't a match before next_match_col, skip
2104 * this item. */
2105 if (spp->sp_line_id == current_line_id
2106 && spp->sp_startcol >= next_match_col)
2107 continue;
2108 spp->sp_line_id = current_line_id;
2109
2110 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2111 if (lc_col < 0)
2112 lc_col = 0;
2113
2114 regmatch.rmm_ic = spp->sp_ic;
2115 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002116 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002117 current_lnum,
2118 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002119 IF_SYN_TIME(&spp->sp_time));
2120 spp->sp_prog = regmatch.regprog;
2121 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002122 {
2123 /* no match in this line, try another one */
2124 spp->sp_startcol = MAXCOL;
2125 continue;
2126 }
2127
2128 /*
2129 * Compute the first column of the match.
2130 */
2131 syn_add_start_off(&pos, &regmatch,
2132 spp, SPO_MS_OFF, -1);
2133 if (pos.lnum > current_lnum)
2134 {
2135 /* must have used end of match in a next line,
2136 * we can't handle that */
2137 spp->sp_startcol = MAXCOL;
2138 continue;
2139 }
2140 startcol = pos.col;
2141
2142 /* remember the next column where this pattern
2143 * matches in the current line */
2144 spp->sp_startcol = startcol;
2145
2146 /*
2147 * If a previously found match starts at a lower
2148 * column number, don't use this one.
2149 */
2150 if (startcol >= next_match_col)
2151 continue;
2152
2153 /*
2154 * If we matched this pattern at this position
2155 * before, skip it. Must retry in the next
2156 * column, because it may match from there.
2157 */
2158 if (did_match_already(idx, &zero_width_next_ga))
2159 {
2160 try_next_column = TRUE;
2161 continue;
2162 }
2163
2164 endpos.lnum = regmatch.endpos[0].lnum;
2165 endpos.col = regmatch.endpos[0].col;
2166
2167 /* Compute the highlight start. */
2168 syn_add_start_off(&hl_startpos, &regmatch,
2169 spp, SPO_HS_OFF, -1);
2170
2171 /* Compute the region start. */
2172 /* Default is to use the end of the match. */
2173 syn_add_end_off(&eos_pos, &regmatch,
2174 spp, SPO_RS_OFF, 0);
2175
2176 /*
2177 * Grab the external submatches before they get
2178 * overwritten. Reference count doesn't change.
2179 */
2180 unref_extmatch(cur_extmatch);
2181 cur_extmatch = re_extmatch_out;
2182 re_extmatch_out = NULL;
2183
2184 flags = 0;
2185 eoe_pos.lnum = 0; /* avoid warning */
2186 eoe_pos.col = 0;
2187 end_idx = 0;
2188 hl_endpos.lnum = 0;
2189
2190 /*
2191 * For a "oneline" the end must be found in the
2192 * same line too. Search for it after the end of
2193 * the match with the start pattern. Set the
2194 * resulting end positions at the same time.
2195 */
2196 if (spp->sp_type == SPTYPE_START
2197 && (spp->sp_flags & HL_ONELINE))
2198 {
2199 lpos_T startpos;
2200
2201 startpos = endpos;
2202 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2203 &flags, &eoe_pos, &end_idx, cur_extmatch);
2204 if (endpos.lnum == 0)
2205 continue; /* not found */
2206 }
2207
2208 /*
2209 * For a "match" the size must be > 0 after the
2210 * end offset needs has been added. Except when
2211 * syncing.
2212 */
2213 else if (spp->sp_type == SPTYPE_MATCH)
2214 {
2215 syn_add_end_off(&hl_endpos, &regmatch, spp,
2216 SPO_HE_OFF, 0);
2217 syn_add_end_off(&endpos, &regmatch, spp,
2218 SPO_ME_OFF, 0);
2219 if (endpos.lnum == current_lnum
2220 && (int)endpos.col + syncing < startcol)
2221 {
2222 /*
2223 * If an empty string is matched, may need
2224 * to try matching again at next column.
2225 */
2226 if (regmatch.startpos[0].col
2227 == regmatch.endpos[0].col)
2228 try_next_column = TRUE;
2229 continue;
2230 }
2231 }
2232
2233 /*
2234 * keep the best match so far in next_match_*
2235 */
2236 /* Highlighting must start after startpos and end
2237 * before endpos. */
2238 if (hl_startpos.lnum == current_lnum
2239 && (int)hl_startpos.col < startcol)
2240 hl_startpos.col = startcol;
2241 limit_pos_zero(&hl_endpos, &endpos);
2242
2243 next_match_idx = idx;
2244 next_match_col = startcol;
2245 next_match_m_endpos = endpos;
2246 next_match_h_endpos = hl_endpos;
2247 next_match_h_startpos = hl_startpos;
2248 next_match_flags = flags;
2249 next_match_eos_pos = eos_pos;
2250 next_match_eoe_pos = eoe_pos;
2251 next_match_end_idx = end_idx;
2252 unref_extmatch(next_match_extmatch);
2253 next_match_extmatch = cur_extmatch;
2254 cur_extmatch = NULL;
2255 }
2256 }
2257 }
2258
2259 /*
2260 * If we found a match at the current column, use it.
2261 */
2262 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2263 {
2264 synpat_T *lspp;
2265
2266 /* When a zero-width item matched which has a nextgroup,
2267 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002268 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269 if (next_match_m_endpos.lnum == current_lnum
2270 && next_match_m_endpos.col == current_col
2271 && lspp->sp_next_list != NULL)
2272 {
2273 current_next_list = lspp->sp_next_list;
2274 current_next_flags = lspp->sp_flags;
2275 keep_next_list = TRUE;
2276 zero_width_next_list = TRUE;
2277
2278 /* Add the index to a list, so that we can check
2279 * later that we don't match it again (and cause an
2280 * endless loop). */
2281 if (ga_grow(&zero_width_next_ga, 1) == OK)
2282 {
2283 ((int *)(zero_width_next_ga.ga_data))
2284 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 }
2286 next_match_idx = -1;
2287 }
2288 else
2289 cur_si = push_next_match(cur_si);
2290 found_match = TRUE;
2291 }
2292 }
2293 }
2294
2295 /*
2296 * Handle searching for nextgroup match.
2297 */
2298 if (current_next_list != NULL && !keep_next_list)
2299 {
2300 /*
2301 * If a nextgroup was not found, continue looking for one if:
2302 * - this is an empty line and the "skipempty" option was given
2303 * - we are on white space and the "skipwhite" option was given
2304 */
2305 if (!found_match)
2306 {
2307 line = syn_getcurline();
2308 if (((current_next_flags & HL_SKIPWHITE)
2309 && vim_iswhite(line[current_col]))
2310 || ((current_next_flags & HL_SKIPEMPTY)
2311 && *line == NUL))
2312 break;
2313 }
2314
2315 /*
2316 * If a nextgroup was found: Use it, and continue looking for
2317 * contained matches.
2318 * If a nextgroup was not found: Continue looking for a normal
2319 * match.
2320 * When did set current_next_list for a zero-width item and no
2321 * match was found don't loop (would get stuck).
2322 */
2323 current_next_list = NULL;
2324 next_match_idx = -1;
2325 if (!zero_width_next_list)
2326 found_match = TRUE;
2327 }
2328
2329 } while (found_match);
2330
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002331 restore_chartab(buf_chartab);
2332
Bram Moolenaar071d4272004-06-13 20:20:40 +00002333 /*
2334 * Use attributes from the current state, if within its highlighting.
2335 * If not, use attributes from the current-but-one state, etc.
2336 */
2337 current_attr = 0;
2338#ifdef FEAT_EVAL
2339 current_id = 0;
2340 current_trans_id = 0;
2341#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002342#ifdef FEAT_CONCEAL
2343 current_flags = 0;
2344#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345 if (cur_si != NULL)
2346 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002347#ifndef FEAT_EVAL
2348 int current_trans_id = 0;
2349#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2351 {
2352 sip = &CUR_STATE(idx);
2353 if ((current_lnum > sip->si_h_startpos.lnum
2354 || (current_lnum == sip->si_h_startpos.lnum
2355 && current_col >= sip->si_h_startpos.col))
2356 && (sip->si_h_endpos.lnum == 0
2357 || current_lnum < sip->si_h_endpos.lnum
2358 || (current_lnum == sip->si_h_endpos.lnum
2359 && current_col < sip->si_h_endpos.col)))
2360 {
2361 current_attr = sip->si_attr;
2362#ifdef FEAT_EVAL
2363 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002365 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002366#ifdef FEAT_CONCEAL
2367 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002368 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002369 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002370#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371 break;
2372 }
2373 }
2374
Bram Moolenaar217ad922005-03-20 22:37:15 +00002375 if (can_spell != NULL)
2376 {
2377 struct sp_syn sps;
2378
2379 /*
2380 * set "can_spell" to TRUE if spell checking is supposed to be
2381 * done in the current item.
2382 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002383 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002384 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002385 /* There is no @Spell cluster: Do spelling for items without
2386 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002387 if (syn_block->b_nospell_cluster_id == 0
2388 || current_trans_id == 0)
2389 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002390 else
2391 {
2392 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002393 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002394 sps.cont_in_list = NULL;
2395 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2396 }
2397 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002398 else
2399 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002400 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002401 * the @Spell cluster. But not when @NoSpell is also there.
2402 * At the toplevel only spell check when ":syn spell toplevel"
2403 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002404 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002405 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002406 else
2407 {
2408 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002409 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002410 sps.cont_in_list = NULL;
2411 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2412
Bram Moolenaar860cae12010-06-05 23:22:07 +02002413 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002414 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002415 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002416 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2417 *can_spell = FALSE;
2418 }
2419 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002420 }
2421 }
2422
2423
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 /*
2425 * Check for end of current state (and the states before it) at the
2426 * next column. Don't do this for syncing, because we would miss a
2427 * single character match.
2428 * First check if the current state ends at the current column. It
2429 * may be for an empty match and a containing item might end in the
2430 * current column.
2431 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002432 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 {
2434 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002435 if (current_state.ga_len > 0
2436 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002437 {
2438 ++current_col;
2439 check_state_ends();
2440 --current_col;
2441 }
2442 }
2443 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002444 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002445 /* Default: Only do spelling when there is no @Spell cluster or when
2446 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002447 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2448 ? (syn_block->b_spell_cluster_id == 0)
2449 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002451 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 if (current_next_list != NULL
2453 && syn_getcurline()[current_col + 1] == NUL
2454 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2455 current_next_list = NULL;
2456
2457 if (zero_width_next_ga.ga_len > 0)
2458 ga_clear(&zero_width_next_ga);
2459
2460 /* No longer need external matches. But keep next_match_extmatch. */
2461 unref_extmatch(re_extmatch_out);
2462 re_extmatch_out = NULL;
2463 unref_extmatch(cur_extmatch);
2464
2465 return current_attr;
2466}
2467
2468
2469/*
2470 * Check if we already matched pattern "idx" at the current column.
2471 */
2472 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002473did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002474{
2475 int i;
2476
2477 for (i = current_state.ga_len; --i >= 0; )
2478 if (CUR_STATE(i).si_m_startcol == (int)current_col
2479 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2480 && CUR_STATE(i).si_idx == idx)
2481 return TRUE;
2482
2483 /* Zero-width matches with a nextgroup argument are not put on the syntax
2484 * stack, and can only be matched once anyway. */
2485 for (i = gap->ga_len; --i >= 0; )
2486 if (((int *)(gap->ga_data))[i] == idx)
2487 return TRUE;
2488
2489 return FALSE;
2490}
2491
2492/*
2493 * Push the next match onto the stack.
2494 */
2495 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002496push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497{
2498 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002499#ifdef FEAT_CONCEAL
2500 int save_flags;
2501#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002502
Bram Moolenaar860cae12010-06-05 23:22:07 +02002503 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504
2505 /*
2506 * Push the item in current_state stack;
2507 */
2508 if (push_current_state(next_match_idx) == OK)
2509 {
2510 /*
2511 * If it's a start-skip-end type that crosses lines, figure out how
2512 * much it continues in this line. Otherwise just fill in the length.
2513 */
2514 cur_si = &CUR_STATE(current_state.ga_len - 1);
2515 cur_si->si_h_startpos = next_match_h_startpos;
2516 cur_si->si_m_startcol = current_col;
2517 cur_si->si_m_lnum = current_lnum;
2518 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002519#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002520 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002521 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002522 if (current_state.ga_len > 1)
2523 cur_si->si_flags |=
2524 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2525#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 cur_si->si_next_list = spp->sp_next_list;
2527 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2528 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2529 {
2530 /* Try to find the end pattern in the current line */
2531 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2532 check_keepend();
2533 }
2534 else
2535 {
2536 cur_si->si_m_endpos = next_match_m_endpos;
2537 cur_si->si_h_endpos = next_match_h_endpos;
2538 cur_si->si_ends = TRUE;
2539 cur_si->si_flags |= next_match_flags;
2540 cur_si->si_eoe_pos = next_match_eoe_pos;
2541 cur_si->si_end_idx = next_match_end_idx;
2542 }
2543 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2544 keepend_level = current_state.ga_len - 1;
2545 check_keepend();
2546 update_si_attr(current_state.ga_len - 1);
2547
Bram Moolenaar860cae12010-06-05 23:22:07 +02002548#ifdef FEAT_CONCEAL
2549 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2550#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002551 /*
2552 * If the start pattern has another highlight group, push another item
2553 * on the stack for the start pattern.
2554 */
2555 if ( spp->sp_type == SPTYPE_START
2556 && spp->sp_syn_match_id != 0
2557 && push_current_state(next_match_idx) == OK)
2558 {
2559 cur_si = &CUR_STATE(current_state.ga_len - 1);
2560 cur_si->si_h_startpos = next_match_h_startpos;
2561 cur_si->si_m_startcol = current_col;
2562 cur_si->si_m_lnum = current_lnum;
2563 cur_si->si_m_endpos = next_match_eos_pos;
2564 cur_si->si_h_endpos = next_match_eos_pos;
2565 cur_si->si_ends = TRUE;
2566 cur_si->si_end_idx = 0;
2567 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002568#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002569 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002570 cur_si->si_flags |= save_flags;
2571 if (cur_si->si_flags & HL_CONCEALENDS)
2572 cur_si->si_flags |= HL_CONCEAL;
2573#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574 cur_si->si_next_list = NULL;
2575 check_keepend();
2576 update_si_attr(current_state.ga_len - 1);
2577 }
2578 }
2579
2580 next_match_idx = -1; /* try other match next time */
2581
2582 return cur_si;
2583}
2584
2585/*
2586 * Check for end of current state (and the states before it).
2587 */
2588 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002589check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590{
2591 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002592 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593
2594 cur_si = &CUR_STATE(current_state.ga_len - 1);
2595 for (;;)
2596 {
2597 if (cur_si->si_ends
2598 && (cur_si->si_m_endpos.lnum < current_lnum
2599 || (cur_si->si_m_endpos.lnum == current_lnum
2600 && cur_si->si_m_endpos.col <= current_col)))
2601 {
2602 /*
2603 * If there is an end pattern group ID, highlight the end pattern
2604 * now. No need to pop the current item from the stack.
2605 * Only do this if the end pattern continues beyond the current
2606 * position.
2607 */
2608 if (cur_si->si_end_idx
2609 && (cur_si->si_eoe_pos.lnum > current_lnum
2610 || (cur_si->si_eoe_pos.lnum == current_lnum
2611 && cur_si->si_eoe_pos.col > current_col)))
2612 {
2613 cur_si->si_idx = cur_si->si_end_idx;
2614 cur_si->si_end_idx = 0;
2615 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2616 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2617 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002618#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002619 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002620 if (cur_si->si_flags & HL_CONCEALENDS)
2621 cur_si->si_flags |= HL_CONCEAL;
2622#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002624
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002625 /* nextgroup= should not match in the end pattern */
2626 current_next_list = NULL;
2627
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002628 /* what matches next may be different now, clear it */
2629 next_match_idx = 0;
2630 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002631 break;
2632 }
2633 else
2634 {
2635 /* handle next_list, unless at end of line and no "skipnl" or
2636 * "skipempty" */
2637 current_next_list = cur_si->si_next_list;
2638 current_next_flags = cur_si->si_flags;
2639 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2640 && syn_getcurline()[current_col] == NUL)
2641 current_next_list = NULL;
2642
2643 /* When the ended item has "extend", another item with
2644 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002645 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646
2647 pop_current_state();
2648
2649 if (current_state.ga_len == 0)
2650 break;
2651
Bram Moolenaar81993f42008-01-11 20:27:45 +00002652 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 {
2654 syn_update_ends(FALSE);
2655 if (current_state.ga_len == 0)
2656 break;
2657 }
2658
2659 cur_si = &CUR_STATE(current_state.ga_len - 1);
2660
2661 /*
2662 * Only for a region the search for the end continues after
2663 * the end of the contained item. If the contained match
2664 * included the end-of-line, break here, the region continues.
2665 * Don't do this when:
2666 * - "keepend" is used for the contained item
2667 * - not at the end of the line (could be end="x$"me=e-1).
2668 * - "excludenl" is used (HL_HAS_EOL won't be set)
2669 */
2670 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002671 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672 == SPTYPE_START
2673 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2674 {
2675 update_si_end(cur_si, (int)current_col, TRUE);
2676 check_keepend();
2677 if ((current_next_flags & HL_HAS_EOL)
2678 && keepend_level < 0
2679 && syn_getcurline()[current_col] == NUL)
2680 break;
2681 }
2682 }
2683 }
2684 else
2685 break;
2686 }
2687}
2688
2689/*
2690 * Update an entry in the current_state stack for a match or region. This
2691 * fills in si_attr, si_next_list and si_cont_list.
2692 */
2693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002694update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695{
2696 stateitem_T *sip = &CUR_STATE(idx);
2697 synpat_T *spp;
2698
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002699 /* This should not happen... */
2700 if (sip->si_idx < 0)
2701 return;
2702
Bram Moolenaar860cae12010-06-05 23:22:07 +02002703 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 if (sip->si_flags & HL_MATCH)
2705 sip->si_id = spp->sp_syn_match_id;
2706 else
2707 sip->si_id = spp->sp_syn.id;
2708 sip->si_attr = syn_id2attr(sip->si_id);
2709 sip->si_trans_id = sip->si_id;
2710 if (sip->si_flags & HL_MATCH)
2711 sip->si_cont_list = NULL;
2712 else
2713 sip->si_cont_list = spp->sp_cont_list;
2714
2715 /*
2716 * For transparent items, take attr from outer item.
2717 * Also take cont_list, if there is none.
2718 * Don't do this for the matchgroup of a start or end pattern.
2719 */
2720 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2721 {
2722 if (idx == 0)
2723 {
2724 sip->si_attr = 0;
2725 sip->si_trans_id = 0;
2726 if (sip->si_cont_list == NULL)
2727 sip->si_cont_list = ID_LIST_ALL;
2728 }
2729 else
2730 {
2731 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2732 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002733 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2734 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 if (sip->si_cont_list == NULL)
2736 {
2737 sip->si_flags |= HL_TRANS_CONT;
2738 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2739 }
2740 }
2741 }
2742}
2743
2744/*
2745 * Check the current stack for patterns with "keepend" flag.
2746 * Propagate the match-end to contained items, until a "skipend" item is found.
2747 */
2748 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002749check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750{
2751 int i;
2752 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002753 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002754 stateitem_T *sip;
2755
2756 /*
2757 * This check can consume a lot of time; only do it from the level where
2758 * there really is a keepend.
2759 */
2760 if (keepend_level < 0)
2761 return;
2762
2763 /*
2764 * Find the last index of an "extend" item. "keepend" items before that
2765 * won't do anything. If there is no "extend" item "i" will be
2766 * "keepend_level" and all "keepend" items will work normally.
2767 */
2768 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2769 if (CUR_STATE(i).si_flags & HL_EXTEND)
2770 break;
2771
2772 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002773 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002774 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002775 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002776 for ( ; i < current_state.ga_len; ++i)
2777 {
2778 sip = &CUR_STATE(i);
2779 if (maxpos.lnum != 0)
2780 {
2781 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002782 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2784 sip->si_ends = TRUE;
2785 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002786 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2787 {
2788 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789 || maxpos.lnum > sip->si_m_endpos.lnum
2790 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002791 && maxpos.col > sip->si_m_endpos.col))
2792 maxpos = sip->si_m_endpos;
2793 if (maxpos_h.lnum == 0
2794 || maxpos_h.lnum > sip->si_h_endpos.lnum
2795 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2796 && maxpos_h.col > sip->si_h_endpos.col))
2797 maxpos_h = sip->si_h_endpos;
2798 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002799 }
2800}
2801
2802/*
2803 * Update an entry in the current_state stack for a start-skip-end pattern.
2804 * This finds the end of the current item, if it's in the current line.
2805 *
2806 * Return the flags for the matched END.
2807 */
2808 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002809update_si_end(
2810 stateitem_T *sip,
2811 int startcol, /* where to start searching for the end */
2812 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002813{
2814 lpos_T startpos;
2815 lpos_T endpos;
2816 lpos_T hl_endpos;
2817 lpos_T end_endpos;
2818 int end_idx;
2819
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002820 /* return quickly for a keyword */
2821 if (sip->si_idx < 0)
2822 return;
2823
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824 /* Don't update when it's already done. Can be a match of an end pattern
2825 * that started in a previous line. Watch out: can also be a "keepend"
2826 * from a containing item. */
2827 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2828 return;
2829
2830 /*
2831 * We need to find the end of the region. It may continue in the next
2832 * line.
2833 */
2834 end_idx = 0;
2835 startpos.lnum = current_lnum;
2836 startpos.col = startcol;
2837 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2838 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2839
2840 if (endpos.lnum == 0)
2841 {
2842 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002843 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002844 {
2845 /* a "oneline" never continues in the next line */
2846 sip->si_ends = TRUE;
2847 sip->si_m_endpos.lnum = current_lnum;
2848 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2849 }
2850 else
2851 {
2852 /* continues in the next line */
2853 sip->si_ends = FALSE;
2854 sip->si_m_endpos.lnum = 0;
2855 }
2856 sip->si_h_endpos = sip->si_m_endpos;
2857 }
2858 else
2859 {
2860 /* match within this line */
2861 sip->si_m_endpos = endpos;
2862 sip->si_h_endpos = hl_endpos;
2863 sip->si_eoe_pos = end_endpos;
2864 sip->si_ends = TRUE;
2865 sip->si_end_idx = end_idx;
2866 }
2867}
2868
2869/*
2870 * Add a new state to the current state stack.
2871 * It is cleared and the index set to "idx".
2872 * Return FAIL if it's not possible (out of memory).
2873 */
2874 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002875push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876{
2877 if (ga_grow(&current_state, 1) == FAIL)
2878 return FAIL;
2879 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2880 CUR_STATE(current_state.ga_len).si_idx = idx;
2881 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002882 return OK;
2883}
2884
2885/*
2886 * Remove a state from the current_state stack.
2887 */
2888 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002889pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002890{
2891 if (current_state.ga_len)
2892 {
2893 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2894 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002895 }
2896 /* after the end of a pattern, try matching a keyword or pattern */
2897 next_match_idx = -1;
2898
2899 /* if first state with "keepend" is popped, reset keepend_level */
2900 if (keepend_level >= current_state.ga_len)
2901 keepend_level = -1;
2902}
2903
2904/*
2905 * Find the end of a start/skip/end syntax region after "startpos".
2906 * Only checks one line.
2907 * Also handles a match item that continued from a previous line.
2908 * If not found, the syntax item continues in the next line. m_endpos->lnum
2909 * will be 0.
2910 * If found, the end of the region and the end of the highlighting is
2911 * computed.
2912 */
2913 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002914find_endpos(
2915 int idx, /* index of the pattern */
2916 lpos_T *startpos, /* where to start looking for an END match */
2917 lpos_T *m_endpos, /* return: end of match */
2918 lpos_T *hl_endpos, /* return: end of highlighting */
2919 long *flagsp, /* return: flags of matching END */
2920 lpos_T *end_endpos, /* return: end of end pattern match */
2921 int *end_idx, /* return: group ID for end pat. match, or 0 */
2922 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923{
2924 colnr_T matchcol;
2925 synpat_T *spp, *spp_skip;
2926 int start_idx;
2927 int best_idx;
2928 regmmatch_T regmatch;
2929 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2930 lpos_T pos;
2931 char_u *line;
2932 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002933 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002935 /* just in case we are invoked for a keyword */
2936 if (idx < 0)
2937 return;
2938
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 /*
2940 * Check for being called with a START pattern.
2941 * Can happen with a match that continues to the next line, because it
2942 * contained a region.
2943 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002944 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945 if (spp->sp_type != SPTYPE_START)
2946 {
2947 *hl_endpos = *startpos;
2948 return;
2949 }
2950
2951 /*
2952 * Find the SKIP or first END pattern after the last START pattern.
2953 */
2954 for (;;)
2955 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002956 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002957 if (spp->sp_type != SPTYPE_START)
2958 break;
2959 ++idx;
2960 }
2961
2962 /*
2963 * Lookup the SKIP pattern (if present)
2964 */
2965 if (spp->sp_type == SPTYPE_SKIP)
2966 {
2967 spp_skip = spp;
2968 ++idx;
2969 }
2970 else
2971 spp_skip = NULL;
2972
2973 /* Setup external matches for syn_regexec(). */
2974 unref_extmatch(re_extmatch_in);
2975 re_extmatch_in = ref_extmatch(start_ext);
2976
2977 matchcol = startpos->col; /* start looking for a match at sstart */
2978 start_idx = idx; /* remember the first END pattern. */
2979 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002980
2981 /* use syntax iskeyword option */
2982 save_chartab(buf_chartab);
2983
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 for (;;)
2985 {
2986 /*
2987 * Find end pattern that matches first after "matchcol".
2988 */
2989 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002990 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991 {
2992 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002993 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994
Bram Moolenaar860cae12010-06-05 23:22:07 +02002995 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002996 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2997 break;
2998 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2999 if (lc_col < 0)
3000 lc_col = 0;
3001
3002 regmatch.rmm_ic = spp->sp_ic;
3003 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003004 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3005 IF_SYN_TIME(&spp->sp_time));
3006 spp->sp_prog = regmatch.regprog;
3007 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 {
3009 if (best_idx == -1 || regmatch.startpos[0].col
3010 < best_regmatch.startpos[0].col)
3011 {
3012 best_idx = idx;
3013 best_regmatch.startpos[0] = regmatch.startpos[0];
3014 best_regmatch.endpos[0] = regmatch.endpos[0];
3015 }
3016 }
3017 }
3018
3019 /*
3020 * If all end patterns have been tried, and there is no match, the
3021 * item continues until end-of-line.
3022 */
3023 if (best_idx == -1)
3024 break;
3025
3026 /*
3027 * If the skip pattern matches before the end pattern,
3028 * continue searching after the skip pattern.
3029 */
3030 if (spp_skip != NULL)
3031 {
3032 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003033 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003034
3035 if (lc_col < 0)
3036 lc_col = 0;
3037 regmatch.rmm_ic = spp_skip->sp_ic;
3038 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003039 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3040 IF_SYN_TIME(&spp_skip->sp_time));
3041 spp_skip->sp_prog = regmatch.regprog;
3042 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 <= best_regmatch.startpos[0].col)
3044 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003045 int line_len;
3046
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047 /* Add offset to skip pattern match */
3048 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3049
3050 /* If the skip pattern goes on to the next line, there is no
3051 * match with an end pattern in this line. */
3052 if (pos.lnum > startpos->lnum)
3053 break;
3054
3055 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003056 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003057
3058 /* take care of an empty match or negative offset */
3059 if (pos.col <= matchcol)
3060 ++matchcol;
3061 else if (pos.col <= regmatch.endpos[0].col)
3062 matchcol = pos.col;
3063 else
3064 /* Be careful not to jump over the NUL at the end-of-line */
3065 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003066 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 ++matchcol)
3068 ;
3069
3070 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003071 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 break;
3073
3074 continue; /* start with first end pattern again */
3075 }
3076 }
3077
3078 /*
3079 * Match from start pattern to end pattern.
3080 * Correct for match and highlight offset of end pattern.
3081 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003082 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003083 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3084 /* can't end before the start */
3085 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3086 m_endpos->col = startpos->col;
3087
3088 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3089 /* can't end before the start */
3090 if (end_endpos->lnum == startpos->lnum
3091 && end_endpos->col < startpos->col)
3092 end_endpos->col = startpos->col;
3093 /* can't end after the match */
3094 limit_pos(end_endpos, m_endpos);
3095
3096 /*
3097 * If the end group is highlighted differently, adjust the pointers.
3098 */
3099 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3100 {
3101 *end_idx = best_idx;
3102 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3103 {
3104 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3105 hl_endpos->col = best_regmatch.endpos[0].col;
3106 }
3107 else
3108 {
3109 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3110 hl_endpos->col = best_regmatch.startpos[0].col;
3111 }
3112 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3113
3114 /* can't end before the start */
3115 if (hl_endpos->lnum == startpos->lnum
3116 && hl_endpos->col < startpos->col)
3117 hl_endpos->col = startpos->col;
3118 limit_pos(hl_endpos, m_endpos);
3119
3120 /* now the match ends where the highlighting ends, it is turned
3121 * into the matchgroup for the end */
3122 *m_endpos = *hl_endpos;
3123 }
3124 else
3125 {
3126 *end_idx = 0;
3127 *hl_endpos = *end_endpos;
3128 }
3129
3130 *flagsp = spp->sp_flags;
3131
3132 had_match = TRUE;
3133 break;
3134 }
3135
3136 /* no match for an END pattern in this line */
3137 if (!had_match)
3138 m_endpos->lnum = 0;
3139
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003140 restore_chartab(buf_chartab);
3141
Bram Moolenaar071d4272004-06-13 20:20:40 +00003142 /* Remove external matches. */
3143 unref_extmatch(re_extmatch_in);
3144 re_extmatch_in = NULL;
3145}
3146
3147/*
3148 * Limit "pos" not to be after "limit".
3149 */
3150 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003151limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152{
3153 if (pos->lnum > limit->lnum)
3154 *pos = *limit;
3155 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3156 pos->col = limit->col;
3157}
3158
3159/*
3160 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3161 */
3162 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003163limit_pos_zero(
3164 lpos_T *pos,
3165 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003166{
3167 if (pos->lnum == 0)
3168 *pos = *limit;
3169 else
3170 limit_pos(pos, limit);
3171}
3172
3173/*
3174 * Add offset to matched text for end of match or highlight.
3175 */
3176 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003177syn_add_end_off(
3178 lpos_T *result, /* returned position */
3179 regmmatch_T *regmatch, /* start/end of match */
3180 synpat_T *spp, /* matched pattern */
3181 int idx, /* index of offset */
3182 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003183{
3184 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003185 int off;
3186 char_u *base;
3187 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188
3189 if (spp->sp_off_flags & (1 << idx))
3190 {
3191 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003192 col = regmatch->startpos[0].col;
3193 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 }
3195 else
3196 {
3197 result->lnum = regmatch->endpos[0].lnum;
3198 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003199 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003200 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003201 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3202 * is a matchgroup. Watch out for match with last NL in the buffer. */
3203 if (result->lnum > syn_buf->b_ml.ml_line_count)
3204 col = 0;
3205 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003206 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003207 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3208 p = base + col;
3209 if (off > 0)
3210 {
3211 while (off-- > 0 && *p != NUL)
3212 mb_ptr_adv(p);
3213 }
3214 else if (off < 0)
3215 {
3216 while (off++ < 0 && base < p)
3217 mb_ptr_back(base, p);
3218 }
3219 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003220 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003221 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222}
3223
3224/*
3225 * Add offset to matched text for start of match or highlight.
3226 * Avoid resulting column to become negative.
3227 */
3228 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003229syn_add_start_off(
3230 lpos_T *result, /* returned position */
3231 regmmatch_T *regmatch, /* start/end of match */
3232 synpat_T *spp,
3233 int idx,
3234 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235{
3236 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003237 int off;
3238 char_u *base;
3239 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240
3241 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3242 {
3243 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003244 col = regmatch->endpos[0].col;
3245 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 }
3247 else
3248 {
3249 result->lnum = regmatch->startpos[0].lnum;
3250 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003251 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003253 if (result->lnum > syn_buf->b_ml.ml_line_count)
3254 {
3255 /* a "\n" at the end of the pattern may take us below the last line */
3256 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003257 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003258 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003259 if (off != 0)
3260 {
3261 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3262 p = base + col;
3263 if (off > 0)
3264 {
3265 while (off-- && *p != NUL)
3266 mb_ptr_adv(p);
3267 }
3268 else if (off < 0)
3269 {
3270 while (off++ && base < p)
3271 mb_ptr_back(base, p);
3272 }
3273 col = (int)(p - base);
3274 }
3275 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276}
3277
3278/*
3279 * Get current line in syntax buffer.
3280 */
3281 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003282syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283{
3284 return ml_get_buf(syn_buf, current_lnum, FALSE);
3285}
3286
3287/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003288 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289 * Returns TRUE when there is a match.
3290 */
3291 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003292syn_regexec(
3293 regmmatch_T *rmp,
3294 linenr_T lnum,
3295 colnr_T col,
3296 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003298 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003299#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003300 proftime_T pt;
3301
3302 if (syn_time_on)
3303 profile_start(&pt);
3304#endif
3305
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003306 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003307 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3308
Bram Moolenaarf7512552013-06-06 14:55:19 +02003309#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003310 if (syn_time_on)
3311 {
3312 profile_end(&pt);
3313 profile_add(&st->total, &pt);
3314 if (profile_cmp(&pt, &st->slowest) < 0)
3315 st->slowest = pt;
3316 ++st->count;
3317 if (r > 0)
3318 ++st->match;
3319 }
3320#endif
3321
3322 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323 {
3324 rmp->startpos[0].lnum += lnum;
3325 rmp->endpos[0].lnum += lnum;
3326 return TRUE;
3327 }
3328 return FALSE;
3329}
3330
3331/*
3332 * Check one position in a line for a matching keyword.
3333 * The caller must check if a keyword can start at startcol.
3334 * Return it's ID if found, 0 otherwise.
3335 */
3336 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003337check_keyword_id(
3338 char_u *line,
3339 int startcol, /* position in line to check for keyword */
3340 int *endcolp, /* return: character after found keyword */
3341 long *flagsp, /* return: flags of matching keyword */
3342 short **next_listp, /* return: next_list of matching keyword */
3343 stateitem_T *cur_si, /* item at the top of the stack */
3344 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003346 keyentry_T *kp;
3347 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003349 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003351 hashtab_T *ht;
3352 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353
3354 /* Find first character after the keyword. First character was already
3355 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003356 kwp = line + startcol;
3357 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 do
3359 {
3360#ifdef FEAT_MBYTE
3361 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003362 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363 else
3364#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003365 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003367 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003368
Bram Moolenaardad6b692005-01-25 22:14:34 +00003369 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 return 0;
3371
3372 /*
3373 * Must make a copy of the keyword, so we can add a NUL and make it
3374 * lowercase.
3375 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003376 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003377
3378 /*
3379 * Try twice:
3380 * 1. matching case
3381 * 2. ignoring case
3382 */
3383 for (round = 1; round <= 2; ++round)
3384 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003385 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003386 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 if (round == 2) /* ignore case */
3389 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390
3391 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 * Find keywords that match. There can be several with different
3393 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394 * When current_next_list is non-zero accept only that group, otherwise:
3395 * Accept a not-contained keyword at toplevel.
3396 * Accept a keyword at other levels only if it is in the contains list.
3397 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003398 hi = hash_find(ht, keyword);
3399 if (!HASHITEM_EMPTY(hi))
3400 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003401 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003402 if (current_next_list != 0
3403 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3404 : (cur_si == NULL
3405 ? !(kp->flags & HL_CONTAINED)
3406 : in_id_list(cur_si, cur_si->si_cont_list,
3407 &kp->k_syn, kp->flags & HL_CONTAINED)))
3408 {
3409 *endcolp = startcol + kwlen;
3410 *flagsp = kp->flags;
3411 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003412#ifdef FEAT_CONCEAL
3413 *ccharp = kp->k_char;
3414#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003415 return kp->k_syn.id;
3416 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417 }
3418 }
3419 return 0;
3420}
3421
3422/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423 * Handle ":syntax conceal" command.
3424 */
3425 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003426syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003427{
3428#ifdef FEAT_CONCEAL
3429 char_u *arg = eap->arg;
3430 char_u *next;
3431
3432 eap->nextcmd = find_nextcmd(arg);
3433 if (eap->skip)
3434 return;
3435
3436 next = skiptowhite(arg);
3437 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3438 curwin->w_s->b_syn_conceal = TRUE;
3439 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3440 curwin->w_s->b_syn_conceal = FALSE;
3441 else
3442 EMSG2(_("E390: Illegal argument: %s"), arg);
3443#endif
3444}
3445
3446/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447 * Handle ":syntax case" command.
3448 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003450syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451{
3452 char_u *arg = eap->arg;
3453 char_u *next;
3454
3455 eap->nextcmd = find_nextcmd(arg);
3456 if (eap->skip)
3457 return;
3458
3459 next = skiptowhite(arg);
3460 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003461 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003462 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003463 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003464 else
3465 EMSG2(_("E390: Illegal argument: %s"), arg);
3466}
3467
3468/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003469 * Handle ":syntax spell" command.
3470 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003471 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003472syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003473{
3474 char_u *arg = eap->arg;
3475 char_u *next;
3476
3477 eap->nextcmd = find_nextcmd(arg);
3478 if (eap->skip)
3479 return;
3480
3481 next = skiptowhite(arg);
3482 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003483 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003484 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003485 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003486 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003487 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003488 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003489 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003490 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003491 return;
3492 }
3493
3494 /* assume spell checking changed, force a redraw */
3495 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003496}
3497
3498/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003499 * Handle ":syntax iskeyword" command.
3500 */
3501 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003502syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003503{
3504 char_u *arg = eap->arg;
3505 char_u save_chartab[32];
3506 char_u *save_isk;
3507
3508 if (eap->skip)
3509 return;
3510
3511 arg = skipwhite(arg);
3512 if (*arg == NUL)
3513 {
3514 MSG_PUTS("\n");
3515 MSG_PUTS(_("syntax iskeyword "));
3516 if (curwin->w_s->b_syn_isk != empty_option)
3517 msg_outtrans(curwin->w_s->b_syn_isk);
3518 else
3519 msg_outtrans((char_u *)"not set");
3520 }
3521 else
3522 {
3523 if (STRNICMP(arg, "clear", 5) == 0)
3524 {
3525 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3526 (size_t)32);
3527 clear_string_option(&curwin->w_s->b_syn_isk);
3528 }
3529 else
3530 {
3531 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3532 save_isk = curbuf->b_p_isk;
3533 curbuf->b_p_isk = vim_strsave(arg);
3534
3535 buf_init_chartab(curbuf, FALSE);
3536 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3537 (size_t)32);
3538 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3539 clear_string_option(&curwin->w_s->b_syn_isk);
3540 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3541 curbuf->b_p_isk = save_isk;
3542 }
3543 }
3544 redraw_win_later(curwin, NOT_VALID);
3545}
3546
3547/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548 * Clear all syntax info for one buffer.
3549 */
3550 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003551syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552{
3553 int i;
3554
Bram Moolenaar860cae12010-06-05 23:22:07 +02003555 block->b_syn_error = FALSE; /* clear previous error */
3556 block->b_syn_ic = FALSE; /* Use case, by default */
3557 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3558 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559
3560 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003561 clear_keywtab(&block->b_keywtab);
3562 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563
3564 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003565 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3566 syn_clear_pattern(block, i);
3567 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003568
3569 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003570 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3571 syn_clear_cluster(block, i);
3572 ga_clear(&block->b_syn_clusters);
3573 block->b_spell_cluster_id = 0;
3574 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575
Bram Moolenaar860cae12010-06-05 23:22:07 +02003576 block->b_syn_sync_flags = 0;
3577 block->b_syn_sync_minlines = 0;
3578 block->b_syn_sync_maxlines = 0;
3579 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580
Bram Moolenaar473de612013-06-08 18:19:48 +02003581 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003582 block->b_syn_linecont_prog = NULL;
3583 vim_free(block->b_syn_linecont_pat);
3584 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003585#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003586 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003588 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589
3590 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003591 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003593
3594 /* Reset the counter for ":syn include" */
3595 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596}
3597
3598/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003599 * Get rid of ownsyntax for window "wp".
3600 */
3601 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003602reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003603{
3604 if (wp->w_s != &wp->w_buffer->b_s)
3605 {
3606 syntax_clear(wp->w_s);
3607 vim_free(wp->w_s);
3608 wp->w_s = &wp->w_buffer->b_s;
3609 }
3610}
3611
3612/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613 * Clear syncing info for one buffer.
3614 */
3615 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003616syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617{
3618 int i;
3619
3620 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003621 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3622 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3623 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624
Bram Moolenaar860cae12010-06-05 23:22:07 +02003625 curwin->w_s->b_syn_sync_flags = 0;
3626 curwin->w_s->b_syn_sync_minlines = 0;
3627 curwin->w_s->b_syn_sync_maxlines = 0;
3628 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629
Bram Moolenaar473de612013-06-08 18:19:48 +02003630 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003631 curwin->w_s->b_syn_linecont_prog = NULL;
3632 vim_free(curwin->w_s->b_syn_linecont_pat);
3633 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003634 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003636 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637}
3638
3639/*
3640 * Remove one pattern from the buffer's pattern list.
3641 */
3642 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003643syn_remove_pattern(
3644 synblock_T *block,
3645 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646{
3647 synpat_T *spp;
3648
Bram Moolenaar860cae12010-06-05 23:22:07 +02003649 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650#ifdef FEAT_FOLDING
3651 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003654 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003655 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003656 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3657 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658}
3659
3660/*
3661 * Clear and free one syntax pattern. When clearing all, must be called from
3662 * last to first!
3663 */
3664 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003665syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003667 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003668 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003670 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003672 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3673 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3674 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 }
3676}
3677
3678/*
3679 * Clear and free one syntax cluster.
3680 */
3681 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003682syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003684 vim_free(SYN_CLSTR(block)[i].scl_name);
3685 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3686 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687}
3688
3689/*
3690 * Handle ":syntax clear" command.
3691 */
3692 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003693syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694{
3695 char_u *arg = eap->arg;
3696 char_u *arg_end;
3697 int id;
3698
3699 eap->nextcmd = find_nextcmd(arg);
3700 if (eap->skip)
3701 return;
3702
3703 /*
3704 * We have to disable this within ":syn include @group filename",
3705 * because otherwise @group would get deleted.
3706 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3707 * clear".
3708 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003709 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710 return;
3711
3712 if (ends_excmd(*arg))
3713 {
3714 /*
3715 * No argument: Clear all syntax items.
3716 */
3717 if (syncing)
3718 syntax_sync_clear();
3719 else
3720 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003721 syntax_clear(curwin->w_s);
3722 if (curwin->w_s == &curwin->w_buffer->b_s)
3723 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003724 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003725 }
3726 }
3727 else
3728 {
3729 /*
3730 * Clear the group IDs that are in the argument.
3731 */
3732 while (!ends_excmd(*arg))
3733 {
3734 arg_end = skiptowhite(arg);
3735 if (*arg == '@')
3736 {
3737 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3738 if (id == 0)
3739 {
3740 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3741 break;
3742 }
3743 else
3744 {
3745 /*
3746 * We can't physically delete a cluster without changing
3747 * the IDs of other clusters, so we do the next best thing
3748 * and make it empty.
3749 */
3750 short scl_id = id - SYNID_CLUSTER;
3751
Bram Moolenaar860cae12010-06-05 23:22:07 +02003752 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3753 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003754 }
3755 }
3756 else
3757 {
3758 id = syn_namen2id(arg, (int)(arg_end - arg));
3759 if (id == 0)
3760 {
3761 EMSG2(_(e_nogroup), arg);
3762 break;
3763 }
3764 else
3765 syn_clear_one(id, syncing);
3766 }
3767 arg = skipwhite(arg_end);
3768 }
3769 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003770 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003771 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772}
3773
3774/*
3775 * Clear one syntax group for the current buffer.
3776 */
3777 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003778syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779{
3780 synpat_T *spp;
3781 int idx;
3782
3783 /* Clear keywords only when not ":syn sync clear group-name" */
3784 if (!syncing)
3785 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003786 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3787 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 }
3789
3790 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003791 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003793 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003794 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3795 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003796 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797 }
3798}
3799
3800/*
3801 * Handle ":syntax on" command.
3802 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003803 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003804syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805{
3806 syn_cmd_onoff(eap, "syntax");
3807}
3808
3809/*
3810 * Handle ":syntax enable" command.
3811 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003812 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003813syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814{
3815 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3816 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003817 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818}
3819
3820/*
3821 * Handle ":syntax reset" command.
Bram Moolenaar8bc189e2016-04-02 19:01:58 +02003822 * It actually resets highlighting, not syntax.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003825syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826{
3827 eap->nextcmd = check_nextcmd(eap->arg);
3828 if (!eap->skip)
3829 {
3830 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3831 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003832 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 }
3834}
3835
3836/*
3837 * Handle ":syntax manual" command.
3838 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003839 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003840syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841{
3842 syn_cmd_onoff(eap, "manual");
3843}
3844
3845/*
3846 * Handle ":syntax off" command.
3847 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003849syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850{
3851 syn_cmd_onoff(eap, "nosyntax");
3852}
3853
3854 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003855syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856{
3857 char_u buf[100];
3858
3859 eap->nextcmd = check_nextcmd(eap->arg);
3860 if (!eap->skip)
3861 {
3862 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003863 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 do_cmdline_cmd(buf);
3865 }
3866}
3867
3868/*
3869 * Handle ":syntax [list]" command: list current syntax words.
3870 */
3871 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003872syn_cmd_list(
3873 exarg_T *eap,
3874 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003875{
3876 char_u *arg = eap->arg;
3877 int id;
3878 char_u *arg_end;
3879
3880 eap->nextcmd = find_nextcmd(arg);
3881 if (eap->skip)
3882 return;
3883
Bram Moolenaar860cae12010-06-05 23:22:07 +02003884 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003886 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003887 return;
3888 }
3889
3890 if (syncing)
3891 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003892 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 {
3894 MSG_PUTS(_("syncing on C-style comments"));
3895 syn_lines_msg();
3896 syn_match_msg();
3897 return;
3898 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003899 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003901 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 MSG_PUTS(_("no syncing"));
3903 else
3904 {
3905 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003906 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 MSG_PUTS(_(" lines before top line"));
3908 syn_match_msg();
3909 }
3910 return;
3911 }
3912 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003913 if (curwin->w_s->b_syn_sync_minlines > 0
3914 || curwin->w_s->b_syn_sync_maxlines > 0
3915 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916 {
3917 MSG_PUTS(_("\nsyncing on items"));
3918 syn_lines_msg();
3919 syn_match_msg();
3920 }
3921 }
3922 else
3923 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3924 if (ends_excmd(*arg))
3925 {
3926 /*
3927 * No argument: List all group IDs and all syntax clusters.
3928 */
3929 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3930 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003931 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 syn_list_cluster(id);
3933 }
3934 else
3935 {
3936 /*
3937 * List the group IDs and syntax clusters that are in the argument.
3938 */
3939 while (!ends_excmd(*arg) && !got_int)
3940 {
3941 arg_end = skiptowhite(arg);
3942 if (*arg == '@')
3943 {
3944 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3945 if (id == 0)
3946 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3947 else
3948 syn_list_cluster(id - SYNID_CLUSTER);
3949 }
3950 else
3951 {
3952 id = syn_namen2id(arg, (int)(arg_end - arg));
3953 if (id == 0)
3954 EMSG2(_(e_nogroup), arg);
3955 else
3956 syn_list_one(id, syncing, TRUE);
3957 }
3958 arg = skipwhite(arg_end);
3959 }
3960 }
3961 eap->nextcmd = check_nextcmd(arg);
3962}
3963
3964 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003965syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003966{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003967 if (curwin->w_s->b_syn_sync_maxlines > 0
3968 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003969 {
3970 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003971 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003972 {
3973 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003974 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3975 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 MSG_PUTS(", ");
3977 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003978 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979 {
3980 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003981 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 }
3983 MSG_PUTS(_(" lines before top line"));
3984 }
3985}
3986
3987 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003988syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003989{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003990 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 {
3992 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003993 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994 MSG_PUTS(_(" line breaks"));
3995 }
3996}
3997
3998static int last_matchgroup;
3999
4000struct name_list
4001{
4002 int flag;
4003 char *name;
4004};
4005
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004006static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007
4008/*
4009 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4010 */
4011 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004012syn_list_one(
4013 int id,
4014 int syncing, /* when TRUE: list syncing items */
4015 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016{
4017 int attr;
4018 int idx;
4019 int did_header = FALSE;
4020 synpat_T *spp;
4021 static struct name_list namelist1[] =
4022 {
4023 {HL_DISPLAY, "display"},
4024 {HL_CONTAINED, "contained"},
4025 {HL_ONELINE, "oneline"},
4026 {HL_KEEPEND, "keepend"},
4027 {HL_EXTEND, "extend"},
4028 {HL_EXCLUDENL, "excludenl"},
4029 {HL_TRANSP, "transparent"},
4030 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004031#ifdef FEAT_CONCEAL
4032 {HL_CONCEAL, "conceal"},
4033 {HL_CONCEALENDS, "concealends"},
4034#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 {0, NULL}
4036 };
4037 static struct name_list namelist2[] =
4038 {
4039 {HL_SKIPWHITE, "skipwhite"},
4040 {HL_SKIPNL, "skipnl"},
4041 {HL_SKIPEMPTY, "skipempty"},
4042 {0, NULL}
4043 };
4044
4045 attr = hl_attr(HLF_D); /* highlight like directories */
4046
4047 /* list the keywords for "id" */
4048 if (!syncing)
4049 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004050 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4051 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052 did_header, attr);
4053 }
4054
4055 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004056 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004057 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004058 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4060 continue;
4061
4062 (void)syn_list_header(did_header, 999, id);
4063 did_header = TRUE;
4064 last_matchgroup = 0;
4065 if (spp->sp_type == SPTYPE_MATCH)
4066 {
4067 put_pattern("match", ' ', spp, attr);
4068 msg_putchar(' ');
4069 }
4070 else if (spp->sp_type == SPTYPE_START)
4071 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004072 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4073 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4074 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4075 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4076 while (idx < curwin->w_s->b_syn_patterns.ga_len
4077 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4078 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079 --idx;
4080 msg_putchar(' ');
4081 }
4082 syn_list_flags(namelist1, spp->sp_flags, attr);
4083
4084 if (spp->sp_cont_list != NULL)
4085 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4086
4087 if (spp->sp_syn.cont_in_list != NULL)
4088 put_id_list((char_u *)"containedin",
4089 spp->sp_syn.cont_in_list, attr);
4090
4091 if (spp->sp_next_list != NULL)
4092 {
4093 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4094 syn_list_flags(namelist2, spp->sp_flags, attr);
4095 }
4096 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4097 {
4098 if (spp->sp_flags & HL_SYNC_HERE)
4099 msg_puts_attr((char_u *)"grouphere", attr);
4100 else
4101 msg_puts_attr((char_u *)"groupthere", attr);
4102 msg_putchar(' ');
4103 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004104 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4106 else
4107 MSG_PUTS("NONE");
4108 msg_putchar(' ');
4109 }
4110 }
4111
4112 /* list the link, if there is one */
4113 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4114 {
4115 (void)syn_list_header(did_header, 999, id);
4116 msg_puts_attr((char_u *)"links to", attr);
4117 msg_putchar(' ');
4118 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4119 }
4120}
4121
4122 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004123syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124{
4125 int i;
4126
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004127 for (i = 0; nlist[i].flag != 0; ++i)
4128 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004129 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004130 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004131 msg_putchar(' ');
4132 }
4133}
4134
4135/*
4136 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4137 */
4138 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004139syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140{
4141 int endcol = 15;
4142
4143 /* slight hack: roughly duplicate the guts of syn_list_header() */
4144 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004145 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146
4147 if (msg_col >= endcol) /* output at least one space */
4148 endcol = msg_col + 1;
4149 if (Columns <= endcol) /* avoid hang for tiny window */
4150 endcol = Columns - 1;
4151
4152 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004153 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004155 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 hl_attr(HLF_D));
4157 }
4158 else
4159 {
4160 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4161 msg_puts((char_u *)"=NONE");
4162 }
4163}
4164
4165 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004166put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167{
4168 short *p;
4169
4170 msg_puts_attr(name, attr);
4171 msg_putchar('=');
4172 for (p = list; *p; ++p)
4173 {
4174 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4175 {
4176 if (p[1])
4177 MSG_PUTS("ALLBUT");
4178 else
4179 MSG_PUTS("ALL");
4180 }
4181 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4182 {
4183 MSG_PUTS("TOP");
4184 }
4185 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4186 {
4187 MSG_PUTS("CONTAINED");
4188 }
4189 else if (*p >= SYNID_CLUSTER)
4190 {
4191 short scl_id = *p - SYNID_CLUSTER;
4192
4193 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004194 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 }
4196 else
4197 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4198 if (p[1])
4199 msg_putchar(',');
4200 }
4201 msg_putchar(' ');
4202}
4203
4204 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004205put_pattern(
4206 char *s,
4207 int c,
4208 synpat_T *spp,
4209 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210{
4211 long n;
4212 int mask;
4213 int first;
4214 static char *sepchars = "/+=-#@\"|'^&";
4215 int i;
4216
4217 /* May have to write "matchgroup=group" */
4218 if (last_matchgroup != spp->sp_syn_match_id)
4219 {
4220 last_matchgroup = spp->sp_syn_match_id;
4221 msg_puts_attr((char_u *)"matchgroup", attr);
4222 msg_putchar('=');
4223 if (last_matchgroup == 0)
4224 msg_outtrans((char_u *)"NONE");
4225 else
4226 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4227 msg_putchar(' ');
4228 }
4229
4230 /* output the name of the pattern and an '=' or ' ' */
4231 msg_puts_attr((char_u *)s, attr);
4232 msg_putchar(c);
4233
4234 /* output the pattern, in between a char that is not in the pattern */
4235 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4236 if (sepchars[++i] == NUL)
4237 {
4238 i = 0; /* no good char found, just use the first one */
4239 break;
4240 }
4241 msg_putchar(sepchars[i]);
4242 msg_outtrans(spp->sp_pattern);
4243 msg_putchar(sepchars[i]);
4244
4245 /* output any pattern options */
4246 first = TRUE;
4247 for (i = 0; i < SPO_COUNT; ++i)
4248 {
4249 mask = (1 << i);
4250 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4251 {
4252 if (!first)
4253 msg_putchar(','); /* separate with commas */
4254 msg_puts((char_u *)spo_name_tab[i]);
4255 n = spp->sp_offsets[i];
4256 if (i != SPO_LC_OFF)
4257 {
4258 if (spp->sp_off_flags & mask)
4259 msg_putchar('s');
4260 else
4261 msg_putchar('e');
4262 if (n > 0)
4263 msg_putchar('+');
4264 }
4265 if (n || i == SPO_LC_OFF)
4266 msg_outnum(n);
4267 first = FALSE;
4268 }
4269 }
4270 msg_putchar(' ');
4271}
4272
4273/*
4274 * List or clear the keywords for one syntax group.
4275 * Return TRUE if the header has been printed.
4276 */
4277 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004278syn_list_keywords(
4279 int id,
4280 hashtab_T *ht,
4281 int did_header, /* header has already been printed */
4282 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004285 hashitem_T *hi;
4286 keyentry_T *kp;
4287 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288 int prev_contained = 0;
4289 short *prev_next_list = NULL;
4290 short *prev_cont_in_list = NULL;
4291 int prev_skipnl = 0;
4292 int prev_skipwhite = 0;
4293 int prev_skipempty = 0;
4294
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 /*
4296 * Unfortunately, this list of keywords is not sorted on alphabet but on
4297 * hash value...
4298 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004299 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004302 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004304 --todo;
4305 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004309 if (prev_contained != (kp->flags & HL_CONTAINED)
4310 || prev_skipnl != (kp->flags & HL_SKIPNL)
4311 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4312 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4313 || prev_cont_in_list != kp->k_syn.cont_in_list
4314 || prev_next_list != kp->next_list)
4315 outlen = 9999;
4316 else
4317 outlen = (int)STRLEN(kp->keyword);
4318 /* output "contained" and "nextgroup" on each line */
4319 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321 prev_contained = 0;
4322 prev_next_list = NULL;
4323 prev_cont_in_list = NULL;
4324 prev_skipnl = 0;
4325 prev_skipwhite = 0;
4326 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 did_header = TRUE;
4329 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 put_id_list((char_u *)"containedin",
4338 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004340 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004342 if (kp->next_list != prev_next_list)
4343 {
4344 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4345 msg_putchar(' ');
4346 prev_next_list = kp->next_list;
4347 if (kp->flags & HL_SKIPNL)
4348 {
4349 msg_puts_attr((char_u *)"skipnl", attr);
4350 msg_putchar(' ');
4351 prev_skipnl = (kp->flags & HL_SKIPNL);
4352 }
4353 if (kp->flags & HL_SKIPWHITE)
4354 {
4355 msg_puts_attr((char_u *)"skipwhite", attr);
4356 msg_putchar(' ');
4357 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4358 }
4359 if (kp->flags & HL_SKIPEMPTY)
4360 {
4361 msg_puts_attr((char_u *)"skipempty", attr);
4362 msg_putchar(' ');
4363 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4364 }
4365 }
4366 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 }
4369 }
4370 }
4371
4372 return did_header;
4373}
4374
4375 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004376syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 hashitem_T *hi;
4379 keyentry_T *kp;
4380 keyentry_T *kp_prev;
4381 keyentry_T *kp_next;
4382 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004385 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004386 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 --todo;
4391 kp_prev = NULL;
4392 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 if (kp->k_syn.id == id)
4395 {
4396 kp_next = kp->ke_next;
4397 if (kp_prev == NULL)
4398 {
4399 if (kp_next == NULL)
4400 hash_remove(ht, hi);
4401 else
4402 hi->hi_key = KE2HIKEY(kp_next);
4403 }
4404 else
4405 kp_prev->ke_next = kp_next;
4406 vim_free(kp->next_list);
4407 vim_free(kp->k_syn.cont_in_list);
4408 vim_free(kp);
4409 kp = kp_next;
4410 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004412 {
4413 kp_prev = kp;
4414 kp = kp->ke_next;
4415 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 }
4417 }
4418 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420}
4421
4422/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004423 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004424 */
4425 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004426clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004427{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004428 hashitem_T *hi;
4429 int todo;
4430 keyentry_T *kp;
4431 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004433 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004434 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004436 if (!HASHITEM_EMPTY(hi))
4437 {
4438 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004440 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004441 kp_next = kp->ke_next;
4442 vim_free(kp->next_list);
4443 vim_free(kp->k_syn.cont_in_list);
4444 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004448 hash_clear(ht);
4449 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450}
4451
4452/*
4453 * Add a keyword to the list of keywords.
4454 */
4455 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004456add_keyword(
4457 char_u *name, /* name of keyword */
4458 int id, /* group ID for this keyword */
4459 int flags, /* flags for this keyword */
4460 short *cont_in_list, /* containedin for this keyword */
4461 short *next_list, /* nextgroup for this keyword */
4462 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 keyentry_T *kp;
4465 hashtab_T *ht;
4466 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004467 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004468 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004469 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470
Bram Moolenaar860cae12010-06-05 23:22:07 +02004471 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004472 name_ic = str_foldcase(name, (int)STRLEN(name),
4473 name_folded, MAXKEYWLEN + 1);
4474 else
4475 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004476 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4477 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004479 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004480 kp->k_syn.id = id;
4481 kp->k_syn.inc_tag = current_syn_inc_tag;
4482 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004483 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004484 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004486 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004487 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488
Bram Moolenaar860cae12010-06-05 23:22:07 +02004489 if (curwin->w_s->b_syn_ic)
4490 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004492 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 hash = hash_hash(kp->keyword);
4495 hi = hash_lookup(ht, kp->keyword, hash);
4496 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 /* new keyword, add to hashtable */
4499 kp->ke_next = NULL;
4500 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004502 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004504 /* keyword already exists, prepend to list */
4505 kp->ke_next = HI2KE(hi);
4506 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004507 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004508}
4509
4510/*
4511 * Get the start and end of the group name argument.
4512 * Return a pointer to the first argument.
4513 * Return NULL if the end of the command was found instead of further args.
4514 */
4515 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004516get_group_name(
4517 char_u *arg, /* start of the argument */
4518 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519{
4520 char_u *rest;
4521
4522 *name_end = skiptowhite(arg);
4523 rest = skipwhite(*name_end);
4524
4525 /*
4526 * Check if there are enough arguments. The first argument may be a
4527 * pattern, where '|' is allowed, so only check for NUL.
4528 */
4529 if (ends_excmd(*arg) || *rest == NUL)
4530 return NULL;
4531 return rest;
4532}
4533
4534/*
4535 * Check for syntax command option arguments.
4536 * This can be called at any place in the list of arguments, and just picks
4537 * out the arguments that are known. Can be called several times in a row to
4538 * collect all options in between other arguments.
4539 * Return a pointer to the next argument (which isn't an option).
4540 * Return NULL for any error;
4541 */
4542 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004543get_syn_options(
4544 char_u *arg, /* next argument to be checked */
4545 syn_opt_arg_T *opt, /* various things */
4546 int *conceal_char UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548 char_u *gname_start, *gname;
4549 int syn_id;
4550 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004551 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552 int i;
4553 int fidx;
4554 static struct flag
4555 {
4556 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004557 int argtype;
4558 int flags;
4559 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4560 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4561 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4562 {"eExXtTeEnNdD", 0, HL_EXTEND},
4563 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4564 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4565 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4566 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4567 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4568 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4569 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4570 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4571 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004572 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4573 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4574 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004575 {"cCoOnNtTaAiInNsS", 1, 0},
4576 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4577 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004579 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580
4581 if (arg == NULL) /* already detected error */
4582 return NULL;
4583
Bram Moolenaar860cae12010-06-05 23:22:07 +02004584#ifdef FEAT_CONCEAL
4585 if (curwin->w_s->b_syn_conceal)
4586 opt->flags |= HL_CONCEAL;
4587#endif
4588
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 for (;;)
4590 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004591 /*
4592 * This is used very often when a large number of keywords is defined.
4593 * Need to skip quickly when no option name is found.
4594 * Also avoid tolower(), it's slow.
4595 */
4596 if (strchr(first_letters, *arg) == NULL)
4597 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598
4599 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4600 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004601 p = flagtab[fidx].name;
4602 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4603 if (arg[len] != p[i] && arg[len] != p[i + 1])
4604 break;
4605 if (p[i] == NUL && (vim_iswhite(arg[len])
4606 || (flagtab[fidx].argtype > 0
4607 ? arg[len] == '='
4608 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 if (opt->keyword
4611 && (flagtab[fidx].flags == HL_DISPLAY
4612 || flagtab[fidx].flags == HL_FOLD
4613 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004614 /* treat "display", "fold" and "extend" as a keyword */
4615 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004616 break;
4617 }
4618 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004619 if (fidx < 0) /* no match found */
4620 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004621
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004622 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004623 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004624 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625 {
4626 EMSG(_("E395: contains argument not accepted here"));
4627 return NULL;
4628 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004629 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004630 return NULL;
4631 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004632 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004634 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004635 return NULL;
4636 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004637 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004639 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640 return NULL;
4641 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004642 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4643 {
4644#ifdef FEAT_MBYTE
4645 /* cchar=? */
4646 if (has_mbyte)
4647 {
4648# ifdef FEAT_CONCEAL
4649 *conceal_char = mb_ptr2char(arg + 6);
4650# endif
4651 arg += mb_ptr2len(arg + 6) - 1;
4652 }
4653 else
4654#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004655 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004656#ifdef FEAT_CONCEAL
4657 *conceal_char = arg[6];
4658#else
4659 ;
4660#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004661 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004662#ifdef FEAT_CONCEAL
4663 if (!vim_isprintc_strict(*conceal_char))
4664 {
4665 EMSG(_("E844: invalid cchar value"));
4666 return NULL;
4667 }
4668#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004669 arg = skipwhite(arg + 7);
4670 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 {
4673 opt->flags |= flagtab[fidx].flags;
4674 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004676 if (flagtab[fidx].flags == HL_SYNC_HERE
4677 || flagtab[fidx].flags == HL_SYNC_THERE)
4678 {
4679 if (opt->sync_idx == NULL)
4680 {
4681 EMSG(_("E393: group[t]here not accepted here"));
4682 return NULL;
4683 }
4684 gname_start = arg;
4685 arg = skiptowhite(arg);
4686 if (gname_start == arg)
4687 return NULL;
4688 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4689 if (gname == NULL)
4690 return NULL;
4691 if (STRCMP(gname, "NONE") == 0)
4692 *opt->sync_idx = NONE_IDX;
4693 else
4694 {
4695 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004696 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4697 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4698 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004699 {
4700 *opt->sync_idx = i;
4701 break;
4702 }
4703 if (i < 0)
4704 {
4705 EMSG2(_("E394: Didn't find region item for %s"), gname);
4706 vim_free(gname);
4707 return NULL;
4708 }
4709 }
4710
4711 vim_free(gname);
4712 arg = skipwhite(arg);
4713 }
4714#ifdef FEAT_FOLDING
4715 else if (flagtab[fidx].flags == HL_FOLD
4716 && foldmethodIsSyntax(curwin))
4717 /* Need to update folds later. */
4718 foldUpdateAll(curwin);
4719#endif
4720 }
4721 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722
4723 return arg;
4724}
4725
4726/*
4727 * Adjustments to syntax item when declared in a ":syn include"'d file.
4728 * Set the contained flag, and if the item is not already contained, add it
4729 * to the specified top-level group, if any.
4730 */
4731 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004732syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004734 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735 return;
4736 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004737 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738 {
4739 /* We have to alloc this, because syn_combine_list() will free it. */
4740 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004741 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742
4743 if (grp_list != NULL)
4744 {
4745 grp_list[0] = id;
4746 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004747 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748 CLUSTER_ADD);
4749 }
4750 }
4751}
4752
4753/*
4754 * Handle ":syntax include [@{group-name}] filename" command.
4755 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004756 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004757syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758{
4759 char_u *arg = eap->arg;
4760 int sgl_id = 1;
4761 char_u *group_name_end;
4762 char_u *rest;
4763 char_u *errormsg = NULL;
4764 int prev_toplvl_grp;
4765 int prev_syn_inc_tag;
4766 int source = FALSE;
4767
4768 eap->nextcmd = find_nextcmd(arg);
4769 if (eap->skip)
4770 return;
4771
4772 if (arg[0] == '@')
4773 {
4774 ++arg;
4775 rest = get_group_name(arg, &group_name_end);
4776 if (rest == NULL)
4777 {
4778 EMSG((char_u *)_("E397: Filename required"));
4779 return;
4780 }
4781 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004782 if (sgl_id == 0)
4783 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 /* separate_nextcmd() and expand_filename() depend on this */
4785 eap->arg = rest;
4786 }
4787
4788 /*
4789 * Everything that's left, up to the next command, should be the
4790 * filename to include.
4791 */
4792 eap->argt |= (XFILE | NOSPC);
4793 separate_nextcmd(eap);
4794 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4795 {
4796 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4797 * file. Need to expand the file name first. In other cases
4798 * ":runtime!" is used. */
4799 source = TRUE;
4800 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4801 {
4802 if (errormsg != NULL)
4803 EMSG(errormsg);
4804 return;
4805 }
4806 }
4807
4808 /*
4809 * Save and restore the existing top-level grouplist id and ":syn
4810 * include" tag around the actual inclusion.
4811 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004812 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4813 {
4814 EMSG((char_u *)_("E847: Too many syntax includes"));
4815 return;
4816 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 prev_syn_inc_tag = current_syn_inc_tag;
4818 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004819 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4820 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004821 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004822 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004824 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825 current_syn_inc_tag = prev_syn_inc_tag;
4826}
4827
4828/*
4829 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4830 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004832syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833{
4834 char_u *arg = eap->arg;
4835 char_u *group_name_end;
4836 int syn_id;
4837 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004838 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004840 char_u *kw;
4841 syn_opt_arg_T syn_opt_arg;
4842 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004843 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004844
4845 rest = get_group_name(arg, &group_name_end);
4846
4847 if (rest != NULL)
4848 {
4849 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004850 if (syn_id != 0)
4851 /* allocate a buffer, for removing backslashes in the keyword */
4852 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853 if (keyword_copy != NULL)
4854 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004855 syn_opt_arg.flags = 0;
4856 syn_opt_arg.keyword = TRUE;
4857 syn_opt_arg.sync_idx = NULL;
4858 syn_opt_arg.has_cont_list = FALSE;
4859 syn_opt_arg.cont_in_list = NULL;
4860 syn_opt_arg.next_list = NULL;
4861
Bram Moolenaar071d4272004-06-13 20:20:40 +00004862 /*
4863 * The options given apply to ALL keywords, so all options must be
4864 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004865 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004866 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004867 cnt = 0;
4868 p = keyword_copy;
4869 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004871 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004872 if (rest == NULL || ends_excmd(*rest))
4873 break;
4874 /* Copy the keyword, removing backslashes, and add a NUL. */
4875 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004877 if (*rest == '\\' && rest[1] != NUL)
4878 ++rest;
4879 *p++ = *rest++;
4880 }
4881 *p++ = NUL;
4882 ++cnt;
4883 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004885 if (!eap->skip)
4886 {
4887 /* Adjust flags for use of ":syn include". */
4888 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4889
4890 /*
4891 * 2: Add an entry for each keyword.
4892 */
4893 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4894 {
4895 for (p = vim_strchr(kw, '['); ; )
4896 {
4897 if (p != NULL)
4898 *p = NUL;
4899 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004900 syn_opt_arg.cont_in_list,
4901 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004902 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004903 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004904 if (p[1] == NUL)
4905 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004906 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004907 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004908 }
4909 if (p[1] == ']')
4910 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004911 if (p[2] != NUL)
4912 {
4913 EMSG3(_("E890: trailing char after ']': %s]%s"),
4914 kw, &p[2]);
4915 goto error;
4916 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004917 kw = p + 1; /* skip over the "]" */
4918 break;
4919 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004920#ifdef FEAT_MBYTE
4921 if (has_mbyte)
4922 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004923 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004924
4925 mch_memmove(p, p + 1, l);
4926 p += l;
4927 }
4928 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 {
4931 p[0] = p[1];
4932 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004933 }
4934 }
4935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004936 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004937error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004939 vim_free(syn_opt_arg.cont_in_list);
4940 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 }
4942 }
4943
4944 if (rest != NULL)
4945 eap->nextcmd = check_nextcmd(rest);
4946 else
4947 EMSG2(_(e_invarg2), arg);
4948
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004949 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004950 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004951}
4952
4953/*
4954 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4955 *
4956 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4957 */
4958 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004959syn_cmd_match(
4960 exarg_T *eap,
4961 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004962{
4963 char_u *arg = eap->arg;
4964 char_u *group_name_end;
4965 char_u *rest;
4966 synpat_T item; /* the item found in the line */
4967 int syn_id;
4968 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004969 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004971 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972
4973 /* Isolate the group name, check for validity */
4974 rest = get_group_name(arg, &group_name_end);
4975
4976 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004977 syn_opt_arg.flags = 0;
4978 syn_opt_arg.keyword = FALSE;
4979 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4980 syn_opt_arg.has_cont_list = TRUE;
4981 syn_opt_arg.cont_list = NULL;
4982 syn_opt_arg.cont_in_list = NULL;
4983 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004984 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004985
4986 /* get the pattern. */
4987 init_syn_patterns();
4988 vim_memset(&item, 0, sizeof(item));
4989 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004990 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4991 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004992
4993 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004994 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995
4996 if (rest != NULL) /* all arguments are valid */
4997 {
4998 /*
4999 * Check for trailing command and illegal trailing arguments.
5000 */
5001 eap->nextcmd = check_nextcmd(rest);
5002 if (!ends_excmd(*rest) || eap->skip)
5003 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005004 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005 && (syn_id = syn_check_group(arg,
5006 (int)(group_name_end - arg))) != 0)
5007 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005008 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009 /*
5010 * Store the pattern in the syn_items list
5011 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005012 idx = curwin->w_s->b_syn_patterns.ga_len;
5013 SYN_ITEMS(curwin->w_s)[idx] = item;
5014 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5015 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5016 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5017 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5018 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5019 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5020 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5021 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005022 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005023#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005024 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005025#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005026 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005027 curwin->w_s->b_syn_containedin = TRUE;
5028 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5029 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030
5031 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005032 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005033 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005034#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005035 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005036 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037#endif
5038
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005039 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005040 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005041 return; /* don't free the progs and patterns now */
5042 }
5043 }
5044
5045 /*
5046 * Something failed, free the allocated memory.
5047 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005048 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005050 vim_free(syn_opt_arg.cont_list);
5051 vim_free(syn_opt_arg.cont_in_list);
5052 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005053
5054 if (rest == NULL)
5055 EMSG2(_(e_invarg2), arg);
5056}
5057
5058/*
5059 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5060 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5061 */
5062 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005063syn_cmd_region(
5064 exarg_T *eap,
5065 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005066{
5067 char_u *arg = eap->arg;
5068 char_u *group_name_end;
5069 char_u *rest; /* next arg, NULL on error */
5070 char_u *key_end;
5071 char_u *key = NULL;
5072 char_u *p;
5073 int item;
5074#define ITEM_START 0
5075#define ITEM_SKIP 1
5076#define ITEM_END 2
5077#define ITEM_MATCHGROUP 3
5078 struct pat_ptr
5079 {
5080 synpat_T *pp_synp; /* pointer to syn_pattern */
5081 int pp_matchgroup_id; /* matchgroup ID */
5082 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5083 } *(pat_ptrs[3]);
5084 /* patterns found in the line */
5085 struct pat_ptr *ppp;
5086 struct pat_ptr *ppp_next;
5087 int pat_count = 0; /* nr of syn_patterns found */
5088 int syn_id;
5089 int matchgroup_id = 0;
5090 int not_enough = FALSE; /* not enough arguments */
5091 int illegal = FALSE; /* illegal arguments */
5092 int success = FALSE;
5093 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005094 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005095 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005096
5097 /* Isolate the group name, check for validity */
5098 rest = get_group_name(arg, &group_name_end);
5099
5100 pat_ptrs[0] = NULL;
5101 pat_ptrs[1] = NULL;
5102 pat_ptrs[2] = NULL;
5103
5104 init_syn_patterns();
5105
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005106 syn_opt_arg.flags = 0;
5107 syn_opt_arg.keyword = FALSE;
5108 syn_opt_arg.sync_idx = NULL;
5109 syn_opt_arg.has_cont_list = TRUE;
5110 syn_opt_arg.cont_list = NULL;
5111 syn_opt_arg.cont_in_list = NULL;
5112 syn_opt_arg.next_list = NULL;
5113
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 /*
5115 * get the options, patterns and matchgroup.
5116 */
5117 while (rest != NULL && !ends_excmd(*rest))
5118 {
5119 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005120 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121 if (rest == NULL || ends_excmd(*rest))
5122 break;
5123
5124 /* must be a pattern or matchgroup then */
5125 key_end = rest;
5126 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5127 ++key_end;
5128 vim_free(key);
5129 key = vim_strnsave_up(rest, (int)(key_end - rest));
5130 if (key == NULL) /* out of memory */
5131 {
5132 rest = NULL;
5133 break;
5134 }
5135 if (STRCMP(key, "MATCHGROUP") == 0)
5136 item = ITEM_MATCHGROUP;
5137 else if (STRCMP(key, "START") == 0)
5138 item = ITEM_START;
5139 else if (STRCMP(key, "END") == 0)
5140 item = ITEM_END;
5141 else if (STRCMP(key, "SKIP") == 0)
5142 {
5143 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5144 {
5145 illegal = TRUE;
5146 break;
5147 }
5148 item = ITEM_SKIP;
5149 }
5150 else
5151 break;
5152 rest = skipwhite(key_end);
5153 if (*rest != '=')
5154 {
5155 rest = NULL;
5156 EMSG2(_("E398: Missing '=': %s"), arg);
5157 break;
5158 }
5159 rest = skipwhite(rest + 1);
5160 if (*rest == NUL)
5161 {
5162 not_enough = TRUE;
5163 break;
5164 }
5165
5166 if (item == ITEM_MATCHGROUP)
5167 {
5168 p = skiptowhite(rest);
5169 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5170 matchgroup_id = 0;
5171 else
5172 {
5173 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5174 if (matchgroup_id == 0)
5175 {
5176 illegal = TRUE;
5177 break;
5178 }
5179 }
5180 rest = skipwhite(p);
5181 }
5182 else
5183 {
5184 /*
5185 * Allocate room for a syn_pattern, and link it in the list of
5186 * syn_patterns for this item, at the start (because the list is
5187 * used from end to start).
5188 */
5189 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5190 if (ppp == NULL)
5191 {
5192 rest = NULL;
5193 break;
5194 }
5195 ppp->pp_next = pat_ptrs[item];
5196 pat_ptrs[item] = ppp;
5197 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5198 if (ppp->pp_synp == NULL)
5199 {
5200 rest = NULL;
5201 break;
5202 }
5203
5204 /*
5205 * Get the syntax pattern and the following offset(s).
5206 */
5207 /* Enable the appropriate \z specials. */
5208 if (item == ITEM_START)
5209 reg_do_extmatch = REX_SET;
5210 else if (item == ITEM_SKIP || item == ITEM_END)
5211 reg_do_extmatch = REX_USE;
5212 rest = get_syn_pattern(rest, ppp->pp_synp);
5213 reg_do_extmatch = 0;
5214 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005215 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005216 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5217 ppp->pp_matchgroup_id = matchgroup_id;
5218 ++pat_count;
5219 }
5220 }
5221 vim_free(key);
5222 if (illegal || not_enough)
5223 rest = NULL;
5224
5225 /*
5226 * Must have a "start" and "end" pattern.
5227 */
5228 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5229 pat_ptrs[ITEM_END] == NULL))
5230 {
5231 not_enough = TRUE;
5232 rest = NULL;
5233 }
5234
5235 if (rest != NULL)
5236 {
5237 /*
5238 * Check for trailing garbage or command.
5239 * If OK, add the item.
5240 */
5241 eap->nextcmd = check_nextcmd(rest);
5242 if (!ends_excmd(*rest) || eap->skip)
5243 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005244 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005245 && (syn_id = syn_check_group(arg,
5246 (int)(group_name_end - arg))) != 0)
5247 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005248 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 /*
5250 * Store the start/skip/end in the syn_items list
5251 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005252 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005253 for (item = ITEM_START; item <= ITEM_END; ++item)
5254 {
5255 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5256 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005257 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5258 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5259 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005260 (item == ITEM_START) ? SPTYPE_START :
5261 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005262 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5263 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005264 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5265 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005266 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005268#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005269 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005270#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005271 if (item == ITEM_START)
5272 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005273 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005274 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005275 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005276 syn_opt_arg.cont_in_list;
5277 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005278 curwin->w_s->b_syn_containedin = TRUE;
5279 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005280 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005281 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005282 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005283 ++idx;
5284#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005285 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005286 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287#endif
5288 }
5289 }
5290
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005291 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005292 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005293 success = TRUE; /* don't free the progs and patterns now */
5294 }
5295 }
5296
5297 /*
5298 * Free the allocated memory.
5299 */
5300 for (item = ITEM_START; item <= ITEM_END; ++item)
5301 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5302 {
5303 if (!success)
5304 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005305 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306 vim_free(ppp->pp_synp->sp_pattern);
5307 }
5308 vim_free(ppp->pp_synp);
5309 ppp_next = ppp->pp_next;
5310 vim_free(ppp);
5311 }
5312
5313 if (!success)
5314 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005315 vim_free(syn_opt_arg.cont_list);
5316 vim_free(syn_opt_arg.cont_in_list);
5317 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318 if (not_enough)
5319 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5320 else if (illegal || rest == NULL)
5321 EMSG2(_(e_invarg2), arg);
5322 }
5323}
5324
5325/*
5326 * A simple syntax group ID comparison function suitable for use in qsort()
5327 */
5328 static int
5329#ifdef __BORLANDC__
5330_RTLENTRYF
5331#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005332syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005333{
5334 const short *s1 = v1;
5335 const short *s2 = v2;
5336
5337 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5338}
5339
5340/*
5341 * Combines lists of syntax clusters.
5342 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5343 */
5344 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005345syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005346{
5347 int count1 = 0;
5348 int count2 = 0;
5349 short *g1;
5350 short *g2;
5351 short *clstr = NULL;
5352 int count;
5353 int round;
5354
5355 /*
5356 * Handle degenerate cases.
5357 */
5358 if (*clstr2 == NULL)
5359 return;
5360 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5361 {
5362 if (list_op == CLUSTER_REPLACE)
5363 vim_free(*clstr1);
5364 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5365 *clstr1 = *clstr2;
5366 else
5367 vim_free(*clstr2);
5368 return;
5369 }
5370
5371 for (g1 = *clstr1; *g1; g1++)
5372 ++count1;
5373 for (g2 = *clstr2; *g2; g2++)
5374 ++count2;
5375
5376 /*
5377 * For speed purposes, sort both lists.
5378 */
5379 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5380 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5381
5382 /*
5383 * We proceed in two passes; in round 1, we count the elements to place
5384 * in the new list, and in round 2, we allocate and populate the new
5385 * list. For speed, we use a mergesort-like method, adding the smaller
5386 * of the current elements in each list to the new list.
5387 */
5388 for (round = 1; round <= 2; round++)
5389 {
5390 g1 = *clstr1;
5391 g2 = *clstr2;
5392 count = 0;
5393
5394 /*
5395 * First, loop through the lists until one of them is empty.
5396 */
5397 while (*g1 && *g2)
5398 {
5399 /*
5400 * We always want to add from the first list.
5401 */
5402 if (*g1 < *g2)
5403 {
5404 if (round == 2)
5405 clstr[count] = *g1;
5406 count++;
5407 g1++;
5408 continue;
5409 }
5410 /*
5411 * We only want to add from the second list if we're adding the
5412 * lists.
5413 */
5414 if (list_op == CLUSTER_ADD)
5415 {
5416 if (round == 2)
5417 clstr[count] = *g2;
5418 count++;
5419 }
5420 if (*g1 == *g2)
5421 g1++;
5422 g2++;
5423 }
5424
5425 /*
5426 * Now add the leftovers from whichever list didn't get finished
5427 * first. As before, we only want to add from the second list if
5428 * we're adding the lists.
5429 */
5430 for (; *g1; g1++, count++)
5431 if (round == 2)
5432 clstr[count] = *g1;
5433 if (list_op == CLUSTER_ADD)
5434 for (; *g2; g2++, count++)
5435 if (round == 2)
5436 clstr[count] = *g2;
5437
5438 if (round == 1)
5439 {
5440 /*
5441 * If the group ended up empty, we don't need to allocate any
5442 * space for it.
5443 */
5444 if (count == 0)
5445 {
5446 clstr = NULL;
5447 break;
5448 }
5449 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5450 if (clstr == NULL)
5451 break;
5452 clstr[count] = 0;
5453 }
5454 }
5455
5456 /*
5457 * Finally, put the new list in place.
5458 */
5459 vim_free(*clstr1);
5460 vim_free(*clstr2);
5461 *clstr1 = clstr;
5462}
5463
5464/*
5465 * Lookup a syntax cluster name and return it's ID.
5466 * If it is not found, 0 is returned.
5467 */
5468 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005469syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005470{
5471 int i;
5472 char_u *name_u;
5473
5474 /* Avoid using stricmp() too much, it's slow on some systems */
5475 name_u = vim_strsave_up(name);
5476 if (name_u == NULL)
5477 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005478 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5479 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5480 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481 break;
5482 vim_free(name_u);
5483 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5484}
5485
5486/*
5487 * Like syn_scl_name2id(), but take a pointer + length argument.
5488 */
5489 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005490syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005491{
5492 char_u *name;
5493 int id = 0;
5494
5495 name = vim_strnsave(linep, len);
5496 if (name != NULL)
5497 {
5498 id = syn_scl_name2id(name);
5499 vim_free(name);
5500 }
5501 return id;
5502}
5503
5504/*
5505 * Find syntax cluster name in the table and return it's ID.
5506 * The argument is a pointer to the name and the length of the name.
5507 * If it doesn't exist yet, a new entry is created.
5508 * Return 0 for failure.
5509 */
5510 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005511syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512{
5513 int id;
5514 char_u *name;
5515
5516 name = vim_strnsave(pp, len);
5517 if (name == NULL)
5518 return 0;
5519
5520 id = syn_scl_name2id(name);
5521 if (id == 0) /* doesn't exist yet */
5522 id = syn_add_cluster(name);
5523 else
5524 vim_free(name);
5525 return id;
5526}
5527
5528/*
5529 * Add new syntax cluster and return it's ID.
5530 * "name" must be an allocated string, it will be consumed.
5531 * Return 0 for failure.
5532 */
5533 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005534syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005535{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005536 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005537
5538 /*
5539 * First call for this growarray: init growing array.
5540 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005541 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005543 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5544 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005545 }
5546
Bram Moolenaar42431a72011-04-01 14:44:59 +02005547 len = curwin->w_s->b_syn_clusters.ga_len;
5548 if (len >= MAX_CLUSTER_ID)
5549 {
5550 EMSG((char_u *)_("E848: Too many syntax clusters"));
5551 vim_free(name);
5552 return 0;
5553 }
5554
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 /*
5556 * Make room for at least one other cluster entry.
5557 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005558 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559 {
5560 vim_free(name);
5561 return 0;
5562 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563
Bram Moolenaar860cae12010-06-05 23:22:07 +02005564 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5565 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5566 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5567 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5568 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569
Bram Moolenaar217ad922005-03-20 22:37:15 +00005570 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005571 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005572 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005573 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005574
Bram Moolenaar071d4272004-06-13 20:20:40 +00005575 return len + SYNID_CLUSTER;
5576}
5577
5578/*
5579 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5580 * [add={groupname},..] [remove={groupname},..]".
5581 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005582 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005583syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005584{
5585 char_u *arg = eap->arg;
5586 char_u *group_name_end;
5587 char_u *rest;
5588 int scl_id;
5589 short *clstr_list;
5590 int got_clstr = FALSE;
5591 int opt_len;
5592 int list_op;
5593
5594 eap->nextcmd = find_nextcmd(arg);
5595 if (eap->skip)
5596 return;
5597
5598 rest = get_group_name(arg, &group_name_end);
5599
5600 if (rest != NULL)
5601 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005602 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5603 if (scl_id == 0)
5604 return;
5605 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606
5607 for (;;)
5608 {
5609 if (STRNICMP(rest, "add", 3) == 0
5610 && (vim_iswhite(rest[3]) || rest[3] == '='))
5611 {
5612 opt_len = 3;
5613 list_op = CLUSTER_ADD;
5614 }
5615 else if (STRNICMP(rest, "remove", 6) == 0
5616 && (vim_iswhite(rest[6]) || rest[6] == '='))
5617 {
5618 opt_len = 6;
5619 list_op = CLUSTER_SUBTRACT;
5620 }
5621 else if (STRNICMP(rest, "contains", 8) == 0
5622 && (vim_iswhite(rest[8]) || rest[8] == '='))
5623 {
5624 opt_len = 8;
5625 list_op = CLUSTER_REPLACE;
5626 }
5627 else
5628 break;
5629
5630 clstr_list = NULL;
5631 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5632 {
5633 EMSG2(_(e_invarg2), rest);
5634 break;
5635 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005636 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005637 &clstr_list, list_op);
5638 got_clstr = TRUE;
5639 }
5640
5641 if (got_clstr)
5642 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005643 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005644 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005645 }
5646 }
5647
5648 if (!got_clstr)
5649 EMSG(_("E400: No cluster specified"));
5650 if (rest == NULL || !ends_excmd(*rest))
5651 EMSG2(_(e_invarg2), arg);
5652}
5653
5654/*
5655 * On first call for current buffer: Init growing array.
5656 */
5657 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005658init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005660 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5661 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005662}
5663
5664/*
5665 * Get one pattern for a ":syntax match" or ":syntax region" command.
5666 * Stores the pattern and program in a synpat_T.
5667 * Returns a pointer to the next argument, or NULL in case of an error.
5668 */
5669 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005670get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005671{
5672 char_u *end;
5673 int *p;
5674 int idx;
5675 char_u *cpo_save;
5676
5677 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005678 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005679 return NULL;
5680
5681 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5682 if (*end != *arg) /* end delimiter not found */
5683 {
5684 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5685 return NULL;
5686 }
5687 /* store the pattern and compiled regexp program */
5688 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5689 return NULL;
5690
5691 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5692 cpo_save = p_cpo;
5693 p_cpo = (char_u *)"";
5694 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5695 p_cpo = cpo_save;
5696
5697 if (ci->sp_prog == NULL)
5698 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005699 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005700#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005701 syn_clear_time(&ci->sp_time);
5702#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703
5704 /*
5705 * Check for a match, highlight or region offset.
5706 */
5707 ++end;
5708 do
5709 {
5710 for (idx = SPO_COUNT; --idx >= 0; )
5711 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5712 break;
5713 if (idx >= 0)
5714 {
5715 p = &(ci->sp_offsets[idx]);
5716 if (idx != SPO_LC_OFF)
5717 switch (end[3])
5718 {
5719 case 's': break;
5720 case 'b': break;
5721 case 'e': idx += SPO_COUNT; break;
5722 default: idx = -1; break;
5723 }
5724 if (idx >= 0)
5725 {
5726 ci->sp_off_flags |= (1 << idx);
5727 if (idx == SPO_LC_OFF) /* lc=99 */
5728 {
5729 end += 3;
5730 *p = getdigits(&end);
5731
5732 /* "lc=" offset automatically sets "ms=" offset */
5733 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5734 {
5735 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5736 ci->sp_offsets[SPO_MS_OFF] = *p;
5737 }
5738 }
5739 else /* yy=x+99 */
5740 {
5741 end += 4;
5742 if (*end == '+')
5743 {
5744 ++end;
5745 *p = getdigits(&end); /* positive offset */
5746 }
5747 else if (*end == '-')
5748 {
5749 ++end;
5750 *p = -getdigits(&end); /* negative offset */
5751 }
5752 }
5753 if (*end != ',')
5754 break;
5755 ++end;
5756 }
5757 }
5758 } while (idx >= 0);
5759
5760 if (!ends_excmd(*end) && !vim_iswhite(*end))
5761 {
5762 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5763 return NULL;
5764 }
5765 return skipwhite(end);
5766}
5767
5768/*
5769 * Handle ":syntax sync .." command.
5770 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005772syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005773{
5774 char_u *arg_start = eap->arg;
5775 char_u *arg_end;
5776 char_u *key = NULL;
5777 char_u *next_arg;
5778 int illegal = FALSE;
5779 int finished = FALSE;
5780 long n;
5781 char_u *cpo_save;
5782
5783 if (ends_excmd(*arg_start))
5784 {
5785 syn_cmd_list(eap, TRUE);
5786 return;
5787 }
5788
5789 while (!ends_excmd(*arg_start))
5790 {
5791 arg_end = skiptowhite(arg_start);
5792 next_arg = skipwhite(arg_end);
5793 vim_free(key);
5794 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5795 if (STRCMP(key, "CCOMMENT") == 0)
5796 {
5797 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005798 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005799 if (!ends_excmd(*next_arg))
5800 {
5801 arg_end = skiptowhite(next_arg);
5802 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005803 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005804 (int)(arg_end - next_arg));
5805 next_arg = skipwhite(arg_end);
5806 }
5807 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005808 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005809 }
5810 else if ( STRNCMP(key, "LINES", 5) == 0
5811 || STRNCMP(key, "MINLINES", 8) == 0
5812 || STRNCMP(key, "MAXLINES", 8) == 0
5813 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5814 {
5815 if (key[4] == 'S')
5816 arg_end = key + 6;
5817 else if (key[0] == 'L')
5818 arg_end = key + 11;
5819 else
5820 arg_end = key + 9;
5821 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5822 {
5823 illegal = TRUE;
5824 break;
5825 }
5826 n = getdigits(&arg_end);
5827 if (!eap->skip)
5828 {
5829 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005830 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005832 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005834 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835 }
5836 }
5837 else if (STRCMP(key, "FROMSTART") == 0)
5838 {
5839 if (!eap->skip)
5840 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005841 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5842 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843 }
5844 }
5845 else if (STRCMP(key, "LINECONT") == 0)
5846 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005847 if (*next_arg == NUL) /* missing pattern */
5848 {
5849 illegal = TRUE;
5850 break;
5851 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005852 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005853 {
5854 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5855 finished = TRUE;
5856 break;
5857 }
5858 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5859 if (*arg_end != *next_arg) /* end delimiter not found */
5860 {
5861 illegal = TRUE;
5862 break;
5863 }
5864
5865 if (!eap->skip)
5866 {
5867 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869 (int)(arg_end - next_arg - 1))) == NULL)
5870 {
5871 finished = TRUE;
5872 break;
5873 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005874 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005875
5876 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5877 cpo_save = p_cpo;
5878 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005879 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005880 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005882#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005883 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5884#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005885
Bram Moolenaar860cae12010-06-05 23:22:07 +02005886 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005887 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005888 vim_free(curwin->w_s->b_syn_linecont_pat);
5889 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005890 finished = TRUE;
5891 break;
5892 }
5893 }
5894 next_arg = skipwhite(arg_end + 1);
5895 }
5896 else
5897 {
5898 eap->arg = next_arg;
5899 if (STRCMP(key, "MATCH") == 0)
5900 syn_cmd_match(eap, TRUE);
5901 else if (STRCMP(key, "REGION") == 0)
5902 syn_cmd_region(eap, TRUE);
5903 else if (STRCMP(key, "CLEAR") == 0)
5904 syn_cmd_clear(eap, TRUE);
5905 else
5906 illegal = TRUE;
5907 finished = TRUE;
5908 break;
5909 }
5910 arg_start = next_arg;
5911 }
5912 vim_free(key);
5913 if (illegal)
5914 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5915 else if (!finished)
5916 {
5917 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005918 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005919 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005920 }
5921}
5922
5923/*
5924 * Convert a line of highlight group names into a list of group ID numbers.
5925 * "arg" should point to the "contains" or "nextgroup" keyword.
5926 * "arg" is advanced to after the last group name.
5927 * Careful: the argument is modified (NULs added).
5928 * returns FAIL for some error, OK for success.
5929 */
5930 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005931get_id_list(
5932 char_u **arg,
5933 int keylen, /* length of keyword */
5934 short **list) /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005935 NULL, the list is silently skipped! */
5936{
5937 char_u *p = NULL;
5938 char_u *end;
5939 int round;
5940 int count;
5941 int total_count = 0;
5942 short *retval = NULL;
5943 char_u *name;
5944 regmatch_T regmatch;
5945 int id;
5946 int i;
5947 int failed = FALSE;
5948
5949 /*
5950 * We parse the list twice:
5951 * round == 1: count the number of items, allocate the array.
5952 * round == 2: fill the array with the items.
5953 * In round 1 new groups may be added, causing the number of items to
5954 * grow when a regexp is used. In that case round 1 is done once again.
5955 */
5956 for (round = 1; round <= 2; ++round)
5957 {
5958 /*
5959 * skip "contains"
5960 */
5961 p = skipwhite(*arg + keylen);
5962 if (*p != '=')
5963 {
5964 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5965 break;
5966 }
5967 p = skipwhite(p + 1);
5968 if (ends_excmd(*p))
5969 {
5970 EMSG2(_("E406: Empty argument: %s"), *arg);
5971 break;
5972 }
5973
5974 /*
5975 * parse the arguments after "contains"
5976 */
5977 count = 0;
5978 while (!ends_excmd(*p))
5979 {
5980 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5981 ;
5982 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5983 if (name == NULL)
5984 {
5985 failed = TRUE;
5986 break;
5987 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005988 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005989 if ( STRCMP(name + 1, "ALLBUT") == 0
5990 || STRCMP(name + 1, "ALL") == 0
5991 || STRCMP(name + 1, "TOP") == 0
5992 || STRCMP(name + 1, "CONTAINED") == 0)
5993 {
5994 if (TOUPPER_ASC(**arg) != 'C')
5995 {
5996 EMSG2(_("E407: %s not allowed here"), name + 1);
5997 failed = TRUE;
5998 vim_free(name);
5999 break;
6000 }
6001 if (count != 0)
6002 {
6003 EMSG2(_("E408: %s must be first in contains list"), name + 1);
6004 failed = TRUE;
6005 vim_free(name);
6006 break;
6007 }
6008 if (name[1] == 'A')
6009 id = SYNID_ALLBUT;
6010 else if (name[1] == 'T')
6011 id = SYNID_TOP;
6012 else
6013 id = SYNID_CONTAINED;
6014 id += current_syn_inc_tag;
6015 }
6016 else if (name[1] == '@')
6017 {
6018 id = syn_check_cluster(name + 2, (int)(end - p - 1));
6019 }
6020 else
6021 {
6022 /*
6023 * Handle full group name.
6024 */
6025 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6026 id = syn_check_group(name + 1, (int)(end - p));
6027 else
6028 {
6029 /*
6030 * Handle match of regexp with group names.
6031 */
6032 *name = '^';
6033 STRCAT(name, "$");
6034 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6035 if (regmatch.regprog == NULL)
6036 {
6037 failed = TRUE;
6038 vim_free(name);
6039 break;
6040 }
6041
6042 regmatch.rm_ic = TRUE;
6043 id = 0;
6044 for (i = highlight_ga.ga_len; --i >= 0; )
6045 {
6046 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6047 (colnr_T)0))
6048 {
6049 if (round == 2)
6050 {
6051 /* Got more items than expected; can happen
6052 * when adding items that match:
6053 * "contains=a.*b,axb".
6054 * Go back to first round */
6055 if (count >= total_count)
6056 {
6057 vim_free(retval);
6058 round = 1;
6059 }
6060 else
6061 retval[count] = i + 1;
6062 }
6063 ++count;
6064 id = -1; /* remember that we found one */
6065 }
6066 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006067 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006068 }
6069 }
6070 vim_free(name);
6071 if (id == 0)
6072 {
6073 EMSG2(_("E409: Unknown group name: %s"), p);
6074 failed = TRUE;
6075 break;
6076 }
6077 if (id > 0)
6078 {
6079 if (round == 2)
6080 {
6081 /* Got more items than expected, go back to first round */
6082 if (count >= total_count)
6083 {
6084 vim_free(retval);
6085 round = 1;
6086 }
6087 else
6088 retval[count] = id;
6089 }
6090 ++count;
6091 }
6092 p = skipwhite(end);
6093 if (*p != ',')
6094 break;
6095 p = skipwhite(p + 1); /* skip comma in between arguments */
6096 }
6097 if (failed)
6098 break;
6099 if (round == 1)
6100 {
6101 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6102 if (retval == NULL)
6103 break;
6104 retval[count] = 0; /* zero means end of the list */
6105 total_count = count;
6106 }
6107 }
6108
6109 *arg = p;
6110 if (failed || retval == NULL)
6111 {
6112 vim_free(retval);
6113 return FAIL;
6114 }
6115
6116 if (*list == NULL)
6117 *list = retval;
6118 else
6119 vim_free(retval); /* list already found, don't overwrite it */
6120
6121 return OK;
6122}
6123
6124/*
6125 * Make a copy of an ID list.
6126 */
6127 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006128copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006129{
6130 int len;
6131 int count;
6132 short *retval;
6133
6134 if (list == NULL)
6135 return NULL;
6136
6137 for (count = 0; list[count]; ++count)
6138 ;
6139 len = (count + 1) * sizeof(short);
6140 retval = (short *)alloc((unsigned)len);
6141 if (retval != NULL)
6142 mch_memmove(retval, list, (size_t)len);
6143
6144 return retval;
6145}
6146
6147/*
6148 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6149 * "cur_si" can be NULL if not checking the "containedin" list.
6150 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6151 * the current item.
6152 * This function is called very often, keep it fast!!
6153 */
6154 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006155in_id_list(
6156 stateitem_T *cur_si, /* current item or NULL */
6157 short *list, /* id list */
6158 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6159 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006160{
6161 int retval;
6162 short *scl_list;
6163 short item;
6164 short id = ssp->id;
6165 static int depth = 0;
6166 int r;
6167
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006168 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006169 if (cur_si != NULL && ssp->cont_in_list != NULL
6170 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006171 {
6172 /* Ignore transparent items without a contains argument. Double check
6173 * that we don't go back past the first one. */
6174 while ((cur_si->si_flags & HL_TRANS_CONT)
6175 && cur_si > (stateitem_T *)(current_state.ga_data))
6176 --cur_si;
6177 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6178 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006179 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6180 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006181 return TRUE;
6182 }
6183
6184 if (list == NULL)
6185 return FALSE;
6186
6187 /*
6188 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6189 * inside anything. Only allow not-contained groups.
6190 */
6191 if (list == ID_LIST_ALL)
6192 return !contained;
6193
6194 /*
6195 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6196 * contains list. We also require that "id" is at the same ":syn include"
6197 * level as the list.
6198 */
6199 item = *list;
6200 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6201 {
6202 if (item < SYNID_TOP)
6203 {
6204 /* ALL or ALLBUT: accept all groups in the same file */
6205 if (item - SYNID_ALLBUT != ssp->inc_tag)
6206 return FALSE;
6207 }
6208 else if (item < SYNID_CONTAINED)
6209 {
6210 /* TOP: accept all not-contained groups in the same file */
6211 if (item - SYNID_TOP != ssp->inc_tag || contained)
6212 return FALSE;
6213 }
6214 else
6215 {
6216 /* CONTAINED: accept all contained groups in the same file */
6217 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6218 return FALSE;
6219 }
6220 item = *++list;
6221 retval = FALSE;
6222 }
6223 else
6224 retval = TRUE;
6225
6226 /*
6227 * Return "retval" if id is in the contains list.
6228 */
6229 while (item != 0)
6230 {
6231 if (item == id)
6232 return retval;
6233 if (item >= SYNID_CLUSTER)
6234 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006235 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006236 /* restrict recursiveness to 30 to avoid an endless loop for a
6237 * cluster that includes itself (indirectly) */
6238 if (scl_list != NULL && depth < 30)
6239 {
6240 ++depth;
6241 r = in_id_list(NULL, scl_list, ssp, contained);
6242 --depth;
6243 if (r)
6244 return retval;
6245 }
6246 }
6247 item = *++list;
6248 }
6249 return !retval;
6250}
6251
6252struct subcommand
6253{
Bram Moolenaard99df422016-01-29 23:20:40 +01006254 char *name; /* subcommand name */
6255 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006256};
6257
6258static struct subcommand subcommands[] =
6259{
6260 {"case", syn_cmd_case},
6261 {"clear", syn_cmd_clear},
6262 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006263 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 {"enable", syn_cmd_enable},
6265 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006266 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006267 {"keyword", syn_cmd_keyword},
6268 {"list", syn_cmd_list},
6269 {"manual", syn_cmd_manual},
6270 {"match", syn_cmd_match},
6271 {"on", syn_cmd_on},
6272 {"off", syn_cmd_off},
6273 {"region", syn_cmd_region},
6274 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006275 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006276 {"sync", syn_cmd_sync},
6277 {"", syn_cmd_list},
6278 {NULL, NULL}
6279};
6280
6281/*
6282 * ":syntax".
6283 * This searches the subcommands[] table for the subcommand name, and calls a
6284 * syntax_subcommand() function to do the rest.
6285 */
6286 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006287ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006288{
6289 char_u *arg = eap->arg;
6290 char_u *subcmd_end;
6291 char_u *subcmd_name;
6292 int i;
6293
6294 syn_cmdlinep = eap->cmdlinep;
6295
6296 /* isolate subcommand name */
6297 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6298 ;
6299 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6300 if (subcmd_name != NULL)
6301 {
6302 if (eap->skip) /* skip error messages for all subcommands */
6303 ++emsg_skip;
6304 for (i = 0; ; ++i)
6305 {
6306 if (subcommands[i].name == NULL)
6307 {
6308 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6309 break;
6310 }
6311 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6312 {
6313 eap->arg = skipwhite(subcmd_end);
6314 (subcommands[i].func)(eap, FALSE);
6315 break;
6316 }
6317 }
6318 vim_free(subcmd_name);
6319 if (eap->skip)
6320 --emsg_skip;
6321 }
6322}
6323
Bram Moolenaar860cae12010-06-05 23:22:07 +02006324 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006325ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006327 char_u *old_value;
6328 char_u *new_value;
6329
Bram Moolenaar860cae12010-06-05 23:22:07 +02006330 if (curwin->w_s == &curwin->w_buffer->b_s)
6331 {
6332 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6333 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006334 hash_init(&curwin->w_s->b_keywtab);
6335 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006336#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006337 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006338 curwin->w_p_spell = FALSE; /* No spell checking */
6339 clear_string_option(&curwin->w_s->b_p_spc);
6340 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006341 clear_string_option(&curwin->w_s->b_p_spl);
6342#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006343 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006344 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006345
6346 /* save value of b:current_syntax */
6347 old_value = get_var_value((char_u *)"b:current_syntax");
6348 if (old_value != NULL)
6349 old_value = vim_strsave(old_value);
6350
Bram Moolenaard1413d92016-03-02 21:51:56 +01006351#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006352 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6353 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006354 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006355#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006356
6357 /* move value of b:current_syntax to w:current_syntax */
6358 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006359 if (new_value != NULL)
6360 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006361
6362 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006363 if (old_value == NULL)
6364 do_unlet((char_u *)"b:current_syntax", TRUE);
6365 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006366 {
6367 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6368 vim_free(old_value);
6369 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006370}
6371
6372 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006373syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006374{
6375 return (win->w_s->b_syn_patterns.ga_len != 0
6376 || win->w_s->b_syn_clusters.ga_len != 0
6377 || win->w_s->b_keywtab.ht_used > 0
6378 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006379}
6380
6381#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6382
6383static enum
6384{
6385 EXP_SUBCMD, /* expand ":syn" sub-commands */
Bram Moolenaar2d028392017-01-08 18:28:22 +01006386 EXP_CASE, /* expand ":syn case" arguments */
6387 EXP_SPELL, /* expand ":syn spell" arguments */
6388 EXP_SYNC /* expand ":syn sync" arguments */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389} expand_what;
6390
Bram Moolenaar4f688582007-07-24 12:34:30 +00006391/*
6392 * Reset include_link, include_default, include_none to 0.
6393 * Called when we are done expanding.
6394 */
6395 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006396reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006397{
6398 include_link = include_default = include_none = 0;
6399}
6400
6401/*
6402 * Handle command line completion for :match and :echohl command: Add "None"
6403 * as highlight group.
6404 */
6405 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006406set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006407{
6408 xp->xp_context = EXPAND_HIGHLIGHT;
6409 xp->xp_pattern = arg;
6410 include_none = 1;
6411}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006412
6413/*
6414 * Handle command line completion for :syntax command.
6415 */
6416 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006417set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418{
6419 char_u *p;
6420
6421 /* Default: expand subcommands */
6422 xp->xp_context = EXPAND_SYNTAX;
6423 expand_what = EXP_SUBCMD;
6424 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006425 include_link = 0;
6426 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006427
6428 /* (part of) subcommand already typed */
6429 if (*arg != NUL)
6430 {
6431 p = skiptowhite(arg);
6432 if (*p != NUL) /* past first word */
6433 {
6434 xp->xp_pattern = skipwhite(p);
6435 if (*skiptowhite(xp->xp_pattern) != NUL)
6436 xp->xp_context = EXPAND_NOTHING;
6437 else if (STRNICMP(arg, "case", p - arg) == 0)
6438 expand_what = EXP_CASE;
Bram Moolenaar2d028392017-01-08 18:28:22 +01006439 else if (STRNICMP(arg, "spell", p - arg) == 0)
6440 expand_what = EXP_SPELL;
6441 else if (STRNICMP(arg, "sync", p - arg) == 0)
6442 expand_what = EXP_SYNC;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006443 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6444 || STRNICMP(arg, "region", p - arg) == 0
6445 || STRNICMP(arg, "match", p - arg) == 0
6446 || STRNICMP(arg, "list", p - arg) == 0)
6447 xp->xp_context = EXPAND_HIGHLIGHT;
6448 else
6449 xp->xp_context = EXPAND_NOTHING;
6450 }
6451 }
6452}
6453
Bram Moolenaar071d4272004-06-13 20:20:40 +00006454/*
6455 * Function given to ExpandGeneric() to obtain the list syntax names for
6456 * expansion.
6457 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006458 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006459get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460{
Bram Moolenaar2d028392017-01-08 18:28:22 +01006461 switch (expand_what)
6462 {
6463 case EXP_SUBCMD:
6464 return (char_u *)subcommands[idx].name;
6465 case EXP_CASE:
6466 {
6467 static char *case_args[] = {"match", "ignore", NULL};
6468 return (char_u *)case_args[idx];
6469 }
6470 case EXP_SPELL:
6471 {
6472 static char *spell_args[] =
6473 {"toplevel", "notoplevel", "default", NULL};
6474 return (char_u *)spell_args[idx];
6475 }
6476 case EXP_SYNC:
6477 {
6478 static char *sync_args[] =
6479 {"ccomment", "clear", "fromstart",
6480 "linebreaks=", "linecont", "lines=", "match",
6481 "maxlines=", "minlines=", "region", NULL};
6482 return (char_u *)sync_args[idx];
6483 }
6484 }
6485 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486}
6487
6488#endif /* FEAT_CMDL_COMPL */
6489
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490/*
6491 * Function called for expression evaluation: get syntax ID at file position.
6492 */
6493 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006494syn_get_id(
6495 win_T *wp,
6496 long lnum,
6497 colnr_T col,
6498 int trans, /* remove transparency */
6499 int *spellp, /* return: can do spell checking */
6500 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006501{
6502 /* When the position is not after the current position and in the same
6503 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006504 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006505 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006506 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006507 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006508 else if (wp->w_buffer == syn_buf
6509 && lnum == current_lnum
6510 && col > current_col)
6511 /* next_match may not be correct when moving around, e.g. with the
6512 * "skip" expression in searchpair() */
6513 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006514
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006515 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006516
6517 return (trans ? current_trans_id : current_id);
6518}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006519
Bram Moolenaar860cae12010-06-05 23:22:07 +02006520#if defined(FEAT_CONCEAL) || defined(PROTO)
6521/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006522 * Get extra information about the syntax item. Must be called right after
6523 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006524 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006525 * Returns the current flags.
6526 */
6527 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006528get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006529{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006530 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006531 return current_flags;
6532}
6533
6534/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006535 * Return conceal substitution character
6536 */
6537 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006538syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006539{
6540 return current_sub_char;
6541}
6542#endif
6543
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006544#if defined(FEAT_EVAL) || defined(PROTO)
6545/*
6546 * Return the syntax ID at position "i" in the current stack.
6547 * The caller must have called syn_get_id() before to fill the stack.
6548 * Returns -1 when "i" is out of range.
6549 */
6550 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006551syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006552{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006553 if (i >= current_state.ga_len)
6554 {
6555 /* Need to invalidate the state, because we didn't properly finish it
6556 * for the last character, "keep_state" was TRUE. */
6557 invalidate_current_state();
6558 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006559 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006560 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006561 return CUR_STATE(i).si_id;
6562}
6563#endif
6564
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565#if defined(FEAT_FOLDING) || defined(PROTO)
6566/*
6567 * Function called to get folding level for line "lnum" in window "wp".
6568 */
6569 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006570syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006571{
6572 int level = 0;
6573 int i;
6574
6575 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006576 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006577 {
6578 syntax_start(wp, lnum);
6579
6580 for (i = 0; i < current_state.ga_len; ++i)
6581 if (CUR_STATE(i).si_flags & HL_FOLD)
6582 ++level;
6583 }
6584 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006585 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006586 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006587 if (level < 0)
6588 level = 0;
6589 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590 return level;
6591}
6592#endif
6593
Bram Moolenaar01615492015-02-03 13:00:38 +01006594#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006595/*
6596 * ":syntime".
6597 */
6598 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006599ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006600{
6601 if (STRCMP(eap->arg, "on") == 0)
6602 syn_time_on = TRUE;
6603 else if (STRCMP(eap->arg, "off") == 0)
6604 syn_time_on = FALSE;
6605 else if (STRCMP(eap->arg, "clear") == 0)
6606 syntime_clear();
6607 else if (STRCMP(eap->arg, "report") == 0)
6608 syntime_report();
6609 else
6610 EMSG2(_(e_invarg2), eap->arg);
6611}
6612
6613 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006614syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006615{
6616 profile_zero(&st->total);
6617 profile_zero(&st->slowest);
6618 st->count = 0;
6619 st->match = 0;
6620}
6621
6622/*
6623 * Clear the syntax timing for the current buffer.
6624 */
6625 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006626syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006627{
6628 int idx;
6629 synpat_T *spp;
6630
6631 if (!syntax_present(curwin))
6632 {
6633 MSG(_(msg_no_items));
6634 return;
6635 }
6636 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6637 {
6638 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6639 syn_clear_time(&spp->sp_time);
6640 }
6641}
6642
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006643#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6644/*
6645 * Function given to ExpandGeneric() to obtain the possible arguments of the
6646 * ":syntime {on,off,clear,report}" command.
6647 */
6648 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006649get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006650{
6651 switch (idx)
6652 {
6653 case 0: return (char_u *)"on";
6654 case 1: return (char_u *)"off";
6655 case 2: return (char_u *)"clear";
6656 case 3: return (char_u *)"report";
6657 }
6658 return NULL;
6659}
6660#endif
6661
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006662typedef struct
6663{
6664 proftime_T total;
6665 int count;
6666 int match;
6667 proftime_T slowest;
6668 proftime_T average;
6669 int id;
6670 char_u *pattern;
6671} time_entry_T;
6672
6673 static int
6674#ifdef __BORLANDC__
6675_RTLENTRYF
6676#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006677syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006678{
6679 const time_entry_T *s1 = v1;
6680 const time_entry_T *s2 = v2;
6681
6682 return profile_cmp(&s1->total, &s2->total);
6683}
6684
6685/*
6686 * Clear the syntax timing for the current buffer.
6687 */
6688 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006689syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006690{
6691 int idx;
6692 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006693# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006694 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006695# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006696 int len;
6697 proftime_T total_total;
6698 int total_count = 0;
6699 garray_T ga;
6700 time_entry_T *p;
6701
6702 if (!syntax_present(curwin))
6703 {
6704 MSG(_(msg_no_items));
6705 return;
6706 }
6707
6708 ga_init2(&ga, sizeof(time_entry_T), 50);
6709 profile_zero(&total_total);
6710 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6711 {
6712 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6713 if (spp->sp_time.count > 0)
6714 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006715 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006716 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6717 p->total = spp->sp_time.total;
6718 profile_add(&total_total, &spp->sp_time.total);
6719 p->count = spp->sp_time.count;
6720 p->match = spp->sp_time.match;
6721 total_count += spp->sp_time.count;
6722 p->slowest = spp->sp_time.slowest;
6723# ifdef FEAT_FLOAT
6724 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6725 p->average = tm;
6726# endif
6727 p->id = spp->sp_syn.id;
6728 p->pattern = spp->sp_pattern;
6729 ++ga.ga_len;
6730 }
6731 }
6732
Bram Moolenaara2162552017-01-08 17:46:20 +01006733 /* Sort on total time. Skip if there are no items to avoid passing NULL
6734 * pointer to qsort(). */
6735 if (ga.ga_len > 1)
6736 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
Bram Moolenaar4e312962013-06-06 21:19:51 +02006737 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006738
6739 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6740 MSG_PUTS("\n");
6741 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6742 {
6743 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6744 p = ((time_entry_T *)ga.ga_data) + idx;
6745
6746 MSG_PUTS(profile_msg(&p->total));
6747 MSG_PUTS(" "); /* make sure there is always a separating space */
6748 msg_advance(13);
6749 msg_outnum(p->count);
6750 MSG_PUTS(" ");
6751 msg_advance(20);
6752 msg_outnum(p->match);
6753 MSG_PUTS(" ");
6754 msg_advance(26);
6755 MSG_PUTS(profile_msg(&p->slowest));
6756 MSG_PUTS(" ");
6757 msg_advance(38);
6758# ifdef FEAT_FLOAT
6759 MSG_PUTS(profile_msg(&p->average));
6760 MSG_PUTS(" ");
6761# endif
6762 msg_advance(50);
6763 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6764 MSG_PUTS(" ");
6765
6766 msg_advance(69);
6767 if (Columns < 80)
6768 len = 20; /* will wrap anyway */
6769 else
6770 len = Columns - 70;
6771 if (len > (int)STRLEN(p->pattern))
6772 len = (int)STRLEN(p->pattern);
6773 msg_outtrans_len(p->pattern, len);
6774 MSG_PUTS("\n");
6775 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006776 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006777 if (!got_int)
6778 {
6779 MSG_PUTS("\n");
6780 MSG_PUTS(profile_msg(&total_total));
6781 msg_advance(13);
6782 msg_outnum(total_count);
6783 MSG_PUTS("\n");
6784 }
6785}
6786#endif
6787
Bram Moolenaar071d4272004-06-13 20:20:40 +00006788#endif /* FEAT_SYN_HL */
6789
Bram Moolenaar071d4272004-06-13 20:20:40 +00006790/**************************************
6791 * Highlighting stuff *
6792 **************************************/
6793
6794/*
6795 * The default highlight groups. These are compiled-in for fast startup and
6796 * they still work when the runtime files can't be found.
6797 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006798 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6799 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006800 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006801#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006802# define CENT(a, b) b
6803#else
6804# define CENT(a, b) a
6805#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006806static char *(highlight_init_both[]) =
6807 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006808 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6809 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6810 CENT("IncSearch term=reverse cterm=reverse",
6811 "IncSearch term=reverse cterm=reverse gui=reverse"),
6812 CENT("ModeMsg term=bold cterm=bold",
6813 "ModeMsg term=bold cterm=bold gui=bold"),
6814 CENT("NonText term=bold ctermfg=Blue",
6815 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6816 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6817 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6818 CENT("StatusLineNC term=reverse cterm=reverse",
6819 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar58b85342016-08-14 19:54:54 +02006820 "default link EndOfBuffer NonText",
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006821#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006822 CENT("VertSplit term=reverse cterm=reverse",
6823 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006824#endif
6825#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006826 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6827 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006828#endif
6829#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006830 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6831 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006832#endif
6833#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006834 CENT("PmenuSbar ctermbg=Grey",
6835 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006836#endif
6837#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006838 CENT("TabLineSel term=bold cterm=bold",
6839 "TabLineSel term=bold cterm=bold gui=bold"),
6840 CENT("TabLineFill term=reverse cterm=reverse",
6841 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006842#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006843#ifdef FEAT_GUI
6844 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006845 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006846#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006847 NULL
6848 };
6849
6850static char *(highlight_init_light[]) =
6851 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006852 CENT("Directory term=bold ctermfg=DarkBlue",
6853 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6854 CENT("LineNr term=underline ctermfg=Brown",
6855 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006856 CENT("CursorLineNr term=bold ctermfg=Brown",
6857 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006858 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6859 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6860 CENT("Question term=standout ctermfg=DarkGreen",
6861 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6862 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6863 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006864#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006865 CENT("SpellBad term=reverse ctermbg=LightRed",
6866 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6867 CENT("SpellCap term=reverse ctermbg=LightBlue",
6868 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6869 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6870 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6871 CENT("SpellLocal term=underline ctermbg=Cyan",
6872 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006873#endif
6874#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006875 CENT("PmenuThumb ctermbg=Black",
6876 "PmenuThumb ctermbg=Black guibg=Black"),
6877 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6878 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6879 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6880 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006881#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006882 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6883 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6884 CENT("Title term=bold ctermfg=DarkMagenta",
6885 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6886 CENT("WarningMsg term=standout ctermfg=DarkRed",
6887 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006888#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006889 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6890 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006891#endif
6892#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006893 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6894 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6895 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6896 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006897#endif
6898#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006899 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6900 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006902 CENT("Visual term=reverse",
6903 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006904#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006905 CENT("DiffAdd term=bold ctermbg=LightBlue",
6906 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6907 CENT("DiffChange term=bold ctermbg=LightMagenta",
6908 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6909 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6910 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006911#endif
6912#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006913 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6914 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006915#endif
6916#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006917 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006918 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006919 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006920 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006921 CENT("ColorColumn term=reverse ctermbg=LightRed",
6922 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006923#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006924#ifdef FEAT_CONCEAL
6925 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6926 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6927#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006928#ifdef FEAT_AUTOCMD
6929 CENT("MatchParen term=reverse ctermbg=Cyan",
6930 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6931#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006932#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006933 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006934#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006935 NULL
6936 };
6937
6938static char *(highlight_init_dark[]) =
6939 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006940 CENT("Directory term=bold ctermfg=LightCyan",
6941 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6942 CENT("LineNr term=underline ctermfg=Yellow",
6943 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006944 CENT("CursorLineNr term=bold ctermfg=Yellow",
6945 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006946 CENT("MoreMsg term=bold ctermfg=LightGreen",
6947 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6948 CENT("Question term=standout ctermfg=LightGreen",
6949 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6950 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6951 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6952 CENT("SpecialKey term=bold ctermfg=LightBlue",
6953 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006954#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006955 CENT("SpellBad term=reverse ctermbg=Red",
6956 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6957 CENT("SpellCap term=reverse ctermbg=Blue",
6958 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6959 CENT("SpellRare term=reverse ctermbg=Magenta",
6960 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6961 CENT("SpellLocal term=underline ctermbg=Cyan",
6962 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006963#endif
6964#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006965 CENT("PmenuThumb ctermbg=White",
6966 "PmenuThumb ctermbg=White guibg=White"),
6967 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6968 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006969 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6970 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006971#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006972 CENT("Title term=bold ctermfg=LightMagenta",
6973 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6974 CENT("WarningMsg term=standout ctermfg=LightRed",
6975 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006976#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006977 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6978 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006979#endif
6980#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006981 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6982 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6983 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6984 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006985#endif
6986#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006987 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6988 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006989#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006990 CENT("Visual term=reverse",
6991 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006992#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006993 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6994 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6995 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6996 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6997 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6998 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006999#endif
7000#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007001 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7002 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007003#endif
7004#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007005 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007006 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007007 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007008 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007009 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7010 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007011#endif
7012#ifdef FEAT_AUTOCMD
7013 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7014 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007015#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007016#ifdef FEAT_CONCEAL
7017 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7018 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7019#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007020#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007021 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007022#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007023 NULL
7024 };
7025
7026 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007027init_highlight(
7028 int both, /* include groups where 'bg' doesn't matter */
7029 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007030{
7031 int i;
7032 char **pp;
7033 static int had_both = FALSE;
7034#ifdef FEAT_EVAL
7035 char_u *p;
7036
7037 /*
7038 * Try finding the color scheme file. Used when a color file was loaded
7039 * and 'background' or 't_Co' is changed.
7040 */
7041 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007042 if (p != NULL)
7043 {
7044 /* The value of g:colors_name could be freed when sourcing the script,
7045 * making "p" invalid, so copy it. */
7046 char_u *copy_p = vim_strsave(p);
7047 int r;
7048
7049 if (copy_p != NULL)
7050 {
7051 r = load_colors(copy_p);
7052 vim_free(copy_p);
7053 if (r == OK)
7054 return;
7055 }
7056 }
7057
Bram Moolenaar071d4272004-06-13 20:20:40 +00007058#endif
7059
7060 /*
7061 * Didn't use a color file, use the compiled-in colors.
7062 */
7063 if (both)
7064 {
7065 had_both = TRUE;
7066 pp = highlight_init_both;
7067 for (i = 0; pp[i] != NULL; ++i)
7068 do_highlight((char_u *)pp[i], reset, TRUE);
7069 }
7070 else if (!had_both)
7071 /* Don't do anything before the call with both == TRUE from main().
7072 * Not everything has been setup then, and that call will overrule
7073 * everything anyway. */
7074 return;
7075
7076 if (*p_bg == 'l')
7077 pp = highlight_init_light;
7078 else
7079 pp = highlight_init_dark;
7080 for (i = 0; pp[i] != NULL; ++i)
7081 do_highlight((char_u *)pp[i], reset, TRUE);
7082
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007083 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007084 * depend on the number of colors available.
7085 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007086 * to avoid Statement highlighted text disappears.
7087 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007088 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007089 do_highlight((char_u *)(*p_bg == 'l'
7090 ? "Visual cterm=NONE ctermbg=LightGrey"
7091 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007092 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007093 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007094 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7095 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007096 if (*p_bg == 'l')
7097 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7098 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007099
Bram Moolenaar071d4272004-06-13 20:20:40 +00007100#ifdef FEAT_SYN_HL
7101 /*
7102 * If syntax highlighting is enabled load the highlighting for it.
7103 */
7104 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007105 {
7106 static int recursive = 0;
7107
7108 if (recursive >= 5)
7109 EMSG(_("E679: recursive loop loading syncolor.vim"));
7110 else
7111 {
7112 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007113 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007114 --recursive;
7115 }
7116 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007117#endif
7118}
7119
7120/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007121 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007122 * Return OK for success, FAIL for failure.
7123 */
7124 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007125load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007126{
7127 char_u *buf;
7128 int retval = FAIL;
7129 static int recursive = FALSE;
7130
7131 /* When being called recursively, this is probably because setting
7132 * 'background' caused the highlighting to be reloaded. This means it is
7133 * working, thus we should return OK. */
7134 if (recursive)
7135 return OK;
7136
7137 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007138 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007139 if (buf != NULL)
7140 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007141 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007142 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007143 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007144#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007145 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007146#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007147 }
7148 recursive = FALSE;
7149
7150 return retval;
7151}
7152
7153/*
7154 * Handle the ":highlight .." command.
7155 * When using ":hi clear" this is called recursively for each group with
7156 * "forceit" and "init" both TRUE.
7157 */
7158 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007159do_highlight(
7160 char_u *line,
7161 int forceit,
7162 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007163{
7164 char_u *name_end;
7165 char_u *p;
7166 char_u *linep;
7167 char_u *key_start;
7168 char_u *arg_start;
7169 char_u *key = NULL, *arg = NULL;
7170 long i;
7171 int off;
7172 int len;
7173 int attr;
7174 int id;
7175 int idx;
7176 int dodefault = FALSE;
7177 int doclear = FALSE;
7178 int dolink = FALSE;
7179 int error = FALSE;
7180 int color;
7181 int is_normal_group = FALSE; /* "Normal" group */
7182#ifdef FEAT_GUI_X11
7183 int is_menu_group = FALSE; /* "Menu" group */
7184 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7185 int is_tooltip_group = FALSE; /* "Tooltip" group */
7186 int do_colors = FALSE; /* need to update colors? */
7187#else
7188# define is_menu_group 0
7189# define is_tooltip_group 0
7190#endif
7191
7192 /*
7193 * If no argument, list current highlighting.
7194 */
7195 if (ends_excmd(*line))
7196 {
7197 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7198 /* TODO: only call when the group has attributes set */
7199 highlight_list_one((int)i);
7200 return;
7201 }
7202
7203 /*
7204 * Isolate the name.
7205 */
7206 name_end = skiptowhite(line);
7207 linep = skipwhite(name_end);
7208
7209 /*
7210 * Check for "default" argument.
7211 */
7212 if (STRNCMP(line, "default", name_end - line) == 0)
7213 {
7214 dodefault = TRUE;
7215 line = linep;
7216 name_end = skiptowhite(line);
7217 linep = skipwhite(name_end);
7218 }
7219
7220 /*
7221 * Check for "clear" or "link" argument.
7222 */
7223 if (STRNCMP(line, "clear", name_end - line) == 0)
7224 doclear = TRUE;
7225 if (STRNCMP(line, "link", name_end - line) == 0)
7226 dolink = TRUE;
7227
7228 /*
7229 * ":highlight {group-name}": list highlighting for one group.
7230 */
7231 if (!doclear && !dolink && ends_excmd(*linep))
7232 {
7233 id = syn_namen2id(line, (int)(name_end - line));
7234 if (id == 0)
7235 EMSG2(_("E411: highlight group not found: %s"), line);
7236 else
7237 highlight_list_one(id);
7238 return;
7239 }
7240
7241 /*
7242 * Handle ":highlight link {from} {to}" command.
7243 */
7244 if (dolink)
7245 {
7246 char_u *from_start = linep;
7247 char_u *from_end;
7248 char_u *to_start;
7249 char_u *to_end;
7250 int from_id;
7251 int to_id;
7252
7253 from_end = skiptowhite(from_start);
7254 to_start = skipwhite(from_end);
7255 to_end = skiptowhite(to_start);
7256
7257 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7258 {
7259 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7260 from_start);
7261 return;
7262 }
7263
7264 if (!ends_excmd(*skipwhite(to_end)))
7265 {
7266 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7267 return;
7268 }
7269
7270 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7271 if (STRNCMP(to_start, "NONE", 4) == 0)
7272 to_id = 0;
7273 else
7274 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7275
7276 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7277 {
7278 /*
7279 * Don't allow a link when there already is some highlighting
7280 * for the group, unless '!' is used
7281 */
7282 if (to_id > 0 && !forceit && !init
7283 && hl_has_settings(from_id - 1, dodefault))
7284 {
7285 if (sourcing_name == NULL && !dodefault)
7286 EMSG(_("E414: group has settings, highlight link ignored"));
7287 }
7288 else
7289 {
7290 if (!init)
7291 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7292 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007293#ifdef FEAT_EVAL
7294 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7295#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007296 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007297 }
7298 }
7299
7300 /* Only call highlight_changed() once, after sourcing a syntax file */
7301 need_highlight_changed = TRUE;
7302
7303 return;
7304 }
7305
7306 if (doclear)
7307 {
7308 /*
7309 * ":highlight clear [group]" command.
7310 */
7311 line = linep;
7312 if (ends_excmd(*line))
7313 {
7314#ifdef FEAT_GUI
7315 /* First, we do not destroy the old values, but allocate the new
7316 * ones and update the display. THEN we destroy the old values.
7317 * If we destroy the old values first, then the old values
7318 * (such as GuiFont's or GuiFontset's) will still be displayed but
7319 * invalid because they were free'd.
7320 */
7321 if (gui.in_use)
7322 {
7323# ifdef FEAT_BEVAL_TIP
7324 gui_init_tooltip_font();
7325# endif
7326# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7327 gui_init_menu_font();
7328# endif
7329 }
7330# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7331 gui_mch_def_colors();
7332# endif
7333# ifdef FEAT_GUI_X11
7334# ifdef FEAT_MENU
7335
7336 /* This only needs to be done when there is no Menu highlight
7337 * group defined by default, which IS currently the case.
7338 */
7339 gui_mch_new_menu_colors();
7340# endif
7341 if (gui.in_use)
7342 {
7343 gui_new_scrollbar_colors();
7344# ifdef FEAT_BEVAL
7345 gui_mch_new_tooltip_colors();
7346# endif
7347# ifdef FEAT_MENU
7348 gui_mch_new_menu_font();
7349# endif
7350 }
7351# endif
7352
7353 /* Ok, we're done allocating the new default graphics items.
7354 * The screen should already be refreshed at this point.
7355 * It is now Ok to clear out the old data.
7356 */
7357#endif
7358#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007359 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007360#endif
7361 restore_cterm_colors();
7362
7363 /*
7364 * Clear all default highlight groups and load the defaults.
7365 */
7366 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7367 highlight_clear(idx);
7368 init_highlight(TRUE, TRUE);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007369#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007370 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007371 highlight_gui_started();
7372#endif
7373 highlight_changed();
7374 redraw_later_clear();
7375 return;
7376 }
7377 name_end = skiptowhite(line);
7378 linep = skipwhite(name_end);
7379 }
7380
7381 /*
7382 * Find the group name in the table. If it does not exist yet, add it.
7383 */
7384 id = syn_check_group(line, (int)(name_end - line));
7385 if (id == 0) /* failed (out of memory) */
7386 return;
7387 idx = id - 1; /* index is ID minus one */
7388
7389 /* Return if "default" was used and the group already has settings. */
7390 if (dodefault && hl_has_settings(idx, TRUE))
7391 return;
7392
7393 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7394 is_normal_group = TRUE;
7395#ifdef FEAT_GUI_X11
7396 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7397 is_menu_group = TRUE;
7398 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7399 is_scrollbar_group = TRUE;
7400 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7401 is_tooltip_group = TRUE;
7402#endif
7403
7404 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7405 if (doclear || (forceit && init))
7406 {
7407 highlight_clear(idx);
7408 if (!doclear)
7409 HL_TABLE()[idx].sg_set = 0;
7410 }
7411
7412 if (!doclear)
7413 while (!ends_excmd(*linep))
7414 {
7415 key_start = linep;
7416 if (*linep == '=')
7417 {
7418 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7419 error = TRUE;
7420 break;
7421 }
7422
7423 /*
7424 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7425 * "guibg").
7426 */
7427 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7428 ++linep;
7429 vim_free(key);
7430 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7431 if (key == NULL)
7432 {
7433 error = TRUE;
7434 break;
7435 }
7436 linep = skipwhite(linep);
7437
7438 if (STRCMP(key, "NONE") == 0)
7439 {
7440 if (!init || HL_TABLE()[idx].sg_set == 0)
7441 {
7442 if (!init)
7443 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7444 highlight_clear(idx);
7445 }
7446 continue;
7447 }
7448
7449 /*
7450 * Check for the equal sign.
7451 */
7452 if (*linep != '=')
7453 {
7454 EMSG2(_("E416: missing equal sign: %s"), key_start);
7455 error = TRUE;
7456 break;
7457 }
7458 ++linep;
7459
7460 /*
7461 * Isolate the argument.
7462 */
7463 linep = skipwhite(linep);
7464 if (*linep == '\'') /* guifg='color name' */
7465 {
7466 arg_start = ++linep;
7467 linep = vim_strchr(linep, '\'');
7468 if (linep == NULL)
7469 {
7470 EMSG2(_(e_invarg2), key_start);
7471 error = TRUE;
7472 break;
7473 }
7474 }
7475 else
7476 {
7477 arg_start = linep;
7478 linep = skiptowhite(linep);
7479 }
7480 if (linep == arg_start)
7481 {
7482 EMSG2(_("E417: missing argument: %s"), key_start);
7483 error = TRUE;
7484 break;
7485 }
7486 vim_free(arg);
7487 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7488 if (arg == NULL)
7489 {
7490 error = TRUE;
7491 break;
7492 }
7493 if (*linep == '\'')
7494 ++linep;
7495
7496 /*
7497 * Store the argument.
7498 */
7499 if ( STRCMP(key, "TERM") == 0
7500 || STRCMP(key, "CTERM") == 0
7501 || STRCMP(key, "GUI") == 0)
7502 {
7503 attr = 0;
7504 off = 0;
7505 while (arg[off] != NUL)
7506 {
7507 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7508 {
7509 len = (int)STRLEN(hl_name_table[i]);
7510 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7511 {
7512 attr |= hl_attr_table[i];
7513 off += len;
7514 break;
7515 }
7516 }
7517 if (i < 0)
7518 {
7519 EMSG2(_("E418: Illegal value: %s"), arg);
7520 error = TRUE;
7521 break;
7522 }
7523 if (arg[off] == ',') /* another one follows */
7524 ++off;
7525 }
7526 if (error)
7527 break;
7528 if (*key == 'T')
7529 {
7530 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7531 {
7532 if (!init)
7533 HL_TABLE()[idx].sg_set |= SG_TERM;
7534 HL_TABLE()[idx].sg_term = attr;
7535 }
7536 }
7537 else if (*key == 'C')
7538 {
7539 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7540 {
7541 if (!init)
7542 HL_TABLE()[idx].sg_set |= SG_CTERM;
7543 HL_TABLE()[idx].sg_cterm = attr;
7544 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7545 }
7546 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007547#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007548 else
7549 {
7550 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7551 {
7552 if (!init)
7553 HL_TABLE()[idx].sg_set |= SG_GUI;
7554 HL_TABLE()[idx].sg_gui = attr;
7555 }
7556 }
7557#endif
7558 }
7559 else if (STRCMP(key, "FONT") == 0)
7560 {
7561 /* in non-GUI fonts are simply ignored */
7562#ifdef FEAT_GUI
7563 if (!gui.shell_created)
7564 {
7565 /* GUI not started yet, always accept the name. */
7566 vim_free(HL_TABLE()[idx].sg_font_name);
7567 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7568 }
7569 else
7570 {
7571 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7572# ifdef FEAT_XFONTSET
7573 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7574# endif
7575 /* First, save the current font/fontset.
7576 * Then try to allocate the font/fontset.
7577 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7578 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7579 */
7580
7581 HL_TABLE()[idx].sg_font = NOFONT;
7582# ifdef FEAT_XFONTSET
7583 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7584# endif
7585 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007586 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007587
7588# ifdef FEAT_XFONTSET
7589 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7590 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007591 /* New fontset was accepted. Free the old one, if there
7592 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007593 gui_mch_free_fontset(temp_sg_fontset);
7594 vim_free(HL_TABLE()[idx].sg_font_name);
7595 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7596 }
7597 else
7598 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7599# endif
7600 if (HL_TABLE()[idx].sg_font != NOFONT)
7601 {
7602 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007603 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007604 gui_mch_free_font(temp_sg_font);
7605 vim_free(HL_TABLE()[idx].sg_font_name);
7606 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7607 }
7608 else
7609 HL_TABLE()[idx].sg_font = temp_sg_font;
7610 }
7611#endif
7612 }
7613 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7614 {
7615 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7616 {
7617 if (!init)
7618 HL_TABLE()[idx].sg_set |= SG_CTERM;
7619
7620 /* When setting the foreground color, and previously the "bold"
7621 * flag was set for a light color, reset it now */
7622 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7623 {
7624 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7625 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7626 }
7627
7628 if (VIM_ISDIGIT(*arg))
7629 color = atoi((char *)arg);
7630 else if (STRICMP(arg, "fg") == 0)
7631 {
7632 if (cterm_normal_fg_color)
7633 color = cterm_normal_fg_color - 1;
7634 else
7635 {
7636 EMSG(_("E419: FG color unknown"));
7637 error = TRUE;
7638 break;
7639 }
7640 }
7641 else if (STRICMP(arg, "bg") == 0)
7642 {
7643 if (cterm_normal_bg_color > 0)
7644 color = cterm_normal_bg_color - 1;
7645 else
7646 {
7647 EMSG(_("E420: BG color unknown"));
7648 error = TRUE;
7649 break;
7650 }
7651 }
7652 else
7653 {
7654 static char *(color_names[28]) = {
7655 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7656 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7657 "Gray", "Grey",
7658 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7659 "Blue", "LightBlue", "Green", "LightGreen",
7660 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7661 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7662 static int color_numbers_16[28] = {0, 1, 2, 3,
7663 4, 5, 6, 6,
7664 7, 7,
7665 7, 7, 8, 8,
7666 9, 9, 10, 10,
7667 11, 11, 12, 12, 13,
7668 13, 14, 14, 15, -1};
7669 /* for xterm with 88 colors... */
7670 static int color_numbers_88[28] = {0, 4, 2, 6,
7671 1, 5, 32, 72,
7672 84, 84,
7673 7, 7, 82, 82,
7674 12, 43, 10, 61,
7675 14, 63, 9, 74, 13,
7676 75, 11, 78, 15, -1};
7677 /* for xterm with 256 colors... */
7678 static int color_numbers_256[28] = {0, 4, 2, 6,
7679 1, 5, 130, 130,
7680 248, 248,
7681 7, 7, 242, 242,
7682 12, 81, 10, 121,
7683 14, 159, 9, 224, 13,
7684 225, 11, 229, 15, -1};
7685 /* for terminals with less than 16 colors... */
7686 static int color_numbers_8[28] = {0, 4, 2, 6,
7687 1, 5, 3, 3,
7688 7, 7,
7689 7, 7, 0+8, 0+8,
7690 4+8, 4+8, 2+8, 2+8,
7691 6+8, 6+8, 1+8, 1+8, 5+8,
7692 5+8, 3+8, 3+8, 7+8, -1};
7693#if defined(__QNXNTO__)
7694 static int *color_numbers_8_qansi = color_numbers_8;
7695 /* On qnx, the 8 & 16 color arrays are the same */
7696 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7697 color_numbers_8_qansi = color_numbers_16;
7698#endif
7699
7700 /* reduce calls to STRICMP a bit, it can be slow */
7701 off = TOUPPER_ASC(*arg);
7702 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7703 if (off == color_names[i][0]
7704 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7705 break;
7706 if (i < 0)
7707 {
7708 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7709 error = TRUE;
7710 break;
7711 }
7712
7713 /* Use the _16 table to check if its a valid color name. */
7714 color = color_numbers_16[i];
7715 if (color >= 0)
7716 {
7717 if (t_colors == 8)
7718 {
7719 /* t_Co is 8: use the 8 colors table */
7720#if defined(__QNXNTO__)
7721 color = color_numbers_8_qansi[i];
7722#else
7723 color = color_numbers_8[i];
7724#endif
7725 if (key[5] == 'F')
7726 {
7727 /* set/reset bold attribute to get light foreground
7728 * colors (on some terminals, e.g. "linux") */
7729 if (color & 8)
7730 {
7731 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7732 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7733 }
7734 else
7735 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7736 }
7737 color &= 7; /* truncate to 8 colors */
7738 }
7739 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007740 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007741 {
7742 /*
7743 * Guess: if the termcap entry ends in 'm', it is
7744 * probably an xterm-like terminal. Use the changed
7745 * order for colors.
7746 */
7747 if (*T_CAF != NUL)
7748 p = T_CAF;
7749 else
7750 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007751 if (*p != NUL && (t_colors > 256
7752 || *(p + STRLEN(p) - 1) == 'm'))
7753 {
7754 if (t_colors == 88)
7755 color = color_numbers_88[i];
7756 else if (t_colors >= 256)
7757 color = color_numbers_256[i];
7758 else
7759 color = color_numbers_8[i];
7760 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007761 }
7762 }
7763 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007764 /* Add one to the argument, to avoid zero. Zero is used for
7765 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007766 if (key[5] == 'F')
7767 {
7768 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7769 if (is_normal_group)
7770 {
7771 cterm_normal_fg_color = color + 1;
7772 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7773#ifdef FEAT_GUI
7774 /* Don't do this if the GUI is used. */
7775 if (!gui.in_use && !gui.starting)
7776#endif
7777 {
7778 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007779 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780 term_fg_color(color);
7781 }
7782 }
7783 }
7784 else
7785 {
7786 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7787 if (is_normal_group)
7788 {
7789 cterm_normal_bg_color = color + 1;
7790#ifdef FEAT_GUI
7791 /* Don't mess with 'background' if the GUI is used. */
7792 if (!gui.in_use && !gui.starting)
7793#endif
7794 {
7795 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007796 if (color >= 0)
7797 {
7798 if (termcap_active)
7799 term_bg_color(color);
7800 if (t_colors < 16)
7801 i = (color == 0 || color == 4);
7802 else
7803 i = (color < 7 || color == 8);
7804 /* Set the 'background' option if the value is
7805 * wrong. */
7806 if (i != (*p_bg == 'd'))
7807 set_option_value((char_u *)"bg", 0L,
7808 i ? (char_u *)"dark"
7809 : (char_u *)"light", 0);
7810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 }
7812 }
7813 }
7814 }
7815 }
7816 else if (STRCMP(key, "GUIFG") == 0)
7817 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007818#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007819 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007821 if (!init)
7822 HL_TABLE()[idx].sg_set |= SG_GUI;
7823
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007824# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007825 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007826 i = color_name2handle(arg);
Bram Moolenaar63122562016-04-30 12:28:15 +02007827 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007828 {
7829 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007830# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007831 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7832 if (STRCMP(arg, "NONE"))
7833 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7834 else
7835 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007836# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007837# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007838 if (is_menu_group)
7839 gui.menu_fg_pixel = i;
7840 if (is_scrollbar_group)
7841 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007842# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007843 if (is_tooltip_group)
7844 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007845# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007846 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007847# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007848 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007849# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007851#endif
7852 }
7853 else if (STRCMP(key, "GUIBG") == 0)
7854 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007855#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007856 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007857 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007858 if (!init)
7859 HL_TABLE()[idx].sg_set |= SG_GUI;
7860
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007861# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007862 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007863 i = color_name2handle(arg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02007864 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007865 {
7866 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007867# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007868 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7869 if (STRCMP(arg, "NONE") != 0)
7870 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7871 else
7872 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02007873# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02007874# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007875 if (is_menu_group)
7876 gui.menu_bg_pixel = i;
7877 if (is_scrollbar_group)
7878 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007879# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007880 if (is_tooltip_group)
7881 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007882# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007883 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007884# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007885 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007886# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007887 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007888#endif
7889 }
7890 else if (STRCMP(key, "GUISP") == 0)
7891 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007892#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007893 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7894 {
7895 if (!init)
7896 HL_TABLE()[idx].sg_set |= SG_GUI;
7897
Bram Moolenaar61623362010-07-14 22:04:22 +02007898# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007899 i = color_name2handle(arg);
7900 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7901 {
7902 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007903# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007904 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7905 if (STRCMP(arg, "NONE") != 0)
7906 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7907 else
7908 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007909# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007910 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007911# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007912 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007913#endif
7914 }
7915 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7916 {
7917 char_u buf[100];
7918 char_u *tname;
7919
7920 if (!init)
7921 HL_TABLE()[idx].sg_set |= SG_TERM;
7922
7923 /*
7924 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007925 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007926 */
7927 if (STRNCMP(arg, "t_", 2) == 0)
7928 {
7929 off = 0;
7930 buf[0] = 0;
7931 while (arg[off] != NUL)
7932 {
7933 /* Isolate one termcap name */
7934 for (len = 0; arg[off + len] &&
7935 arg[off + len] != ','; ++len)
7936 ;
7937 tname = vim_strnsave(arg + off, len);
7938 if (tname == NULL) /* out of memory */
7939 {
7940 error = TRUE;
7941 break;
7942 }
7943 /* lookup the escape sequence for the item */
7944 p = get_term_code(tname);
7945 vim_free(tname);
7946 if (p == NULL) /* ignore non-existing things */
7947 p = (char_u *)"";
7948
7949 /* Append it to the already found stuff */
7950 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7951 {
7952 EMSG2(_("E422: terminal code too long: %s"), arg);
7953 error = TRUE;
7954 break;
7955 }
7956 STRCAT(buf, p);
7957
7958 /* Advance to the next item */
7959 off += len;
7960 if (arg[off] == ',') /* another one follows */
7961 ++off;
7962 }
7963 }
7964 else
7965 {
7966 /*
7967 * Copy characters from arg[] to buf[], translating <> codes.
7968 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007969 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007970 {
Bram Moolenaar35a4cfa2016-08-14 16:07:48 +02007971 len = trans_special(&p, buf + off, FALSE, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007972 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007973 off += len;
7974 else /* copy as normal char */
7975 buf[off++] = *p++;
7976 }
7977 buf[off] = NUL;
7978 }
7979 if (error)
7980 break;
7981
7982 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7983 p = NULL;
7984 else
7985 p = vim_strsave(buf);
7986 if (key[2] == 'A')
7987 {
7988 vim_free(HL_TABLE()[idx].sg_start);
7989 HL_TABLE()[idx].sg_start = p;
7990 }
7991 else
7992 {
7993 vim_free(HL_TABLE()[idx].sg_stop);
7994 HL_TABLE()[idx].sg_stop = p;
7995 }
7996 }
7997 else
7998 {
7999 EMSG2(_("E423: Illegal argument: %s"), key_start);
8000 error = TRUE;
8001 break;
8002 }
8003
8004 /*
8005 * When highlighting has been given for a group, don't link it.
8006 */
8007 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8008 HL_TABLE()[idx].sg_link = 0;
8009
8010 /*
8011 * Continue with next argument.
8012 */
8013 linep = skipwhite(linep);
8014 }
8015
8016 /*
8017 * If there is an error, and it's a new entry, remove it from the table.
8018 */
8019 if (error && idx == highlight_ga.ga_len)
8020 syn_unadd_group();
8021 else
8022 {
8023 if (is_normal_group)
8024 {
8025 HL_TABLE()[idx].sg_term_attr = 0;
8026 HL_TABLE()[idx].sg_cterm_attr = 0;
8027#ifdef FEAT_GUI
8028 HL_TABLE()[idx].sg_gui_attr = 0;
8029 /*
8030 * Need to update all groups, because they might be using "bg"
8031 * and/or "fg", which have been changed now.
8032 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008033#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008034#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008035 if (USE_24BIT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008036 highlight_gui_started();
8037#endif
8038 }
8039#ifdef FEAT_GUI_X11
8040# ifdef FEAT_MENU
8041 else if (is_menu_group)
8042 {
8043 if (gui.in_use && do_colors)
8044 gui_mch_new_menu_colors();
8045 }
8046# endif
8047 else if (is_scrollbar_group)
8048 {
8049 if (gui.in_use && do_colors)
8050 gui_new_scrollbar_colors();
8051 }
8052# ifdef FEAT_BEVAL
8053 else if (is_tooltip_group)
8054 {
8055 if (gui.in_use && do_colors)
8056 gui_mch_new_tooltip_colors();
8057 }
8058# endif
8059#endif
8060 else
8061 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008062#ifdef FEAT_EVAL
8063 HL_TABLE()[idx].sg_scriptID = current_SID;
8064#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008065 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008066 }
8067 vim_free(key);
8068 vim_free(arg);
8069
8070 /* Only call highlight_changed() once, after sourcing a syntax file */
8071 need_highlight_changed = TRUE;
8072}
8073
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008074#if defined(EXITFREE) || defined(PROTO)
8075 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008076free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008077{
8078 int i;
8079
8080 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008081 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008082 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008083 vim_free(HL_TABLE()[i].sg_name);
8084 vim_free(HL_TABLE()[i].sg_name_u);
8085 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008086 ga_clear(&highlight_ga);
8087}
8088#endif
8089
Bram Moolenaar071d4272004-06-13 20:20:40 +00008090/*
8091 * Reset the cterm colors to what they were before Vim was started, if
8092 * possible. Otherwise reset them to zero.
8093 */
8094 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008095restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008096{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008097#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008098 /* Since t_me has been set, this probably means that the user
8099 * wants to use this as default colors. Need to reset default
8100 * background/foreground colors. */
8101 mch_set_normal_colors();
8102#else
8103 cterm_normal_fg_color = 0;
8104 cterm_normal_fg_bold = 0;
8105 cterm_normal_bg_color = 0;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008106# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008107 cterm_normal_fg_gui_color = INVALCOLOR;
8108 cterm_normal_bg_gui_color = INVALCOLOR;
8109# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008110#endif
8111}
8112
8113/*
8114 * Return TRUE if highlight group "idx" has any settings.
8115 * When "check_link" is TRUE also check for an existing link.
8116 */
8117 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008118hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008119{
8120 return ( HL_TABLE()[idx].sg_term_attr != 0
8121 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008122 || HL_TABLE()[idx].sg_cterm_fg != 0
8123 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008124#ifdef FEAT_GUI
8125 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008126 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8127 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8128 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8129 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008130#endif
8131 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8132}
8133
8134/*
8135 * Clear highlighting for one group.
8136 */
8137 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008138highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008139{
8140 HL_TABLE()[idx].sg_term = 0;
8141 vim_free(HL_TABLE()[idx].sg_start);
8142 HL_TABLE()[idx].sg_start = NULL;
8143 vim_free(HL_TABLE()[idx].sg_stop);
8144 HL_TABLE()[idx].sg_stop = NULL;
8145 HL_TABLE()[idx].sg_term_attr = 0;
8146 HL_TABLE()[idx].sg_cterm = 0;
8147 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8148 HL_TABLE()[idx].sg_cterm_fg = 0;
8149 HL_TABLE()[idx].sg_cterm_bg = 0;
8150 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008151#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008152 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008153 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8154 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008155 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8156 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008157 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8158 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008159#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008160#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar61623362010-07-14 22:04:22 +02008161 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8162 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008163#endif
8164#ifdef FEAT_GUI
Bram Moolenaar61623362010-07-14 22:04:22 +02008165 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008166 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8167 HL_TABLE()[idx].sg_font = NOFONT;
8168# ifdef FEAT_XFONTSET
8169 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8170 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8171# endif
8172 vim_free(HL_TABLE()[idx].sg_font_name);
8173 HL_TABLE()[idx].sg_font_name = NULL;
8174 HL_TABLE()[idx].sg_gui_attr = 0;
8175#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008176#ifdef FEAT_EVAL
8177 /* Clear the script ID only when there is no link, since that is not
8178 * cleared. */
8179 if (HL_TABLE()[idx].sg_link == 0)
8180 HL_TABLE()[idx].sg_scriptID = 0;
8181#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182}
8183
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008184#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008185/*
8186 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008187 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008188 * "Tooltip" colors.
8189 */
8190 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008191set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008192{
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008193#ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008194# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008195 if (gui.in_use)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008196# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008198 if (set_group_colors((char_u *)"Normal",
8199 &gui.norm_pixel, &gui.back_pixel,
8200 FALSE, TRUE, FALSE))
8201 {
8202 gui_mch_new_colors();
8203 must_redraw = CLEAR;
8204 }
8205# ifdef FEAT_GUI_X11
8206 if (set_group_colors((char_u *)"Menu",
8207 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8208 TRUE, FALSE, FALSE))
8209 {
8210# ifdef FEAT_MENU
8211 gui_mch_new_menu_colors();
8212# endif
8213 must_redraw = CLEAR;
8214 }
8215# ifdef FEAT_BEVAL
8216 if (set_group_colors((char_u *)"Tooltip",
8217 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8218 FALSE, FALSE, TRUE))
8219 {
8220# ifdef FEAT_TOOLBAR
8221 gui_mch_new_tooltip_colors();
8222# endif
8223 must_redraw = CLEAR;
8224 }
8225# endif
8226 if (set_group_colors((char_u *)"Scrollbar",
8227 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8228 FALSE, FALSE, FALSE))
8229 {
8230 gui_new_scrollbar_colors();
8231 must_redraw = CLEAR;
8232 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008234 }
8235#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008236#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008237# ifdef FEAT_GUI
8238 else
8239# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008240 {
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008241 int idx;
8242
8243 idx = syn_name2id((char_u *)"Normal") - 1;
8244 if (idx >= 0)
8245 {
8246 gui_do_one_color(idx, FALSE, FALSE);
8247
8248 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8249 {
8250 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
8251 must_redraw = CLEAR;
8252 }
8253 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8254 {
8255 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
8256 must_redraw = CLEAR;
8257 }
8258 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008259 }
8260#endif
8261}
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008262#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008263
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008264#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008265/*
8266 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8267 */
8268 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008269set_group_colors(
8270 char_u *name,
8271 guicolor_T *fgp,
8272 guicolor_T *bgp,
8273 int do_menu,
8274 int use_norm,
8275 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008276{
8277 int idx;
8278
8279 idx = syn_name2id(name) - 1;
8280 if (idx >= 0)
8281 {
8282 gui_do_one_color(idx, do_menu, do_tooltip);
8283
8284 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8285 *fgp = HL_TABLE()[idx].sg_gui_fg;
8286 else if (use_norm)
8287 *fgp = gui.def_norm_pixel;
8288 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8289 *bgp = HL_TABLE()[idx].sg_gui_bg;
8290 else if (use_norm)
8291 *bgp = gui.def_back_pixel;
8292 return TRUE;
8293 }
8294 return FALSE;
8295}
8296
8297/*
8298 * Get the font of the "Normal" group.
8299 * Returns "" when it's not found or not set.
8300 */
8301 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008302hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008303{
8304 int id;
8305 char_u *s;
8306
8307 id = syn_name2id((char_u *)"Normal");
8308 if (id > 0)
8309 {
8310 s = HL_TABLE()[id - 1].sg_font_name;
8311 if (s != NULL)
8312 return s;
8313 }
8314 return (char_u *)"";
8315}
8316
8317/*
8318 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8319 * actually chosen to be used.
8320 */
8321 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008322hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008323{
8324 int id;
8325
8326 id = syn_name2id((char_u *)"Normal");
8327 if (id > 0)
8328 {
8329 vim_free(HL_TABLE()[id - 1].sg_font_name);
8330 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8331 }
8332}
8333
8334/*
8335 * Set background color for "Normal" group. Called by gui_set_bg_color()
8336 * when the color is known.
8337 */
8338 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008339hl_set_bg_color_name(
8340 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008341{
8342 int id;
8343
8344 if (name != NULL)
8345 {
8346 id = syn_name2id((char_u *)"Normal");
8347 if (id > 0)
8348 {
8349 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8350 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8351 }
8352 }
8353}
8354
8355/*
8356 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8357 * when the color is known.
8358 */
8359 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008360hl_set_fg_color_name(
8361 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362{
8363 int id;
8364
8365 if (name != NULL)
8366 {
8367 id = syn_name2id((char_u *)"Normal");
8368 if (id > 0)
8369 {
8370 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8371 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8372 }
8373 }
8374}
8375
8376/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00008377 * Return the handle for a font name.
8378 * Returns NOFONT when failed.
8379 */
8380 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008381font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008382{
8383 if (STRCMP(name, "NONE") == 0)
8384 return NOFONT;
8385
8386 return gui_mch_get_font(name, TRUE);
8387}
8388
8389# ifdef FEAT_XFONTSET
8390/*
8391 * Return the handle for a fontset name.
8392 * Returns NOFONTSET when failed.
8393 */
8394 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008395fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008396{
8397 if (STRCMP(name, "NONE") == 0)
8398 return NOFONTSET;
8399
8400 return gui_mch_get_fontset(name, TRUE, fixed_width);
8401}
8402# endif
8403
8404/*
8405 * Get the font or fontset for one highlight group.
8406 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008407 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008408hl_do_font(
8409 int idx,
8410 char_u *arg,
8411 int do_normal, /* set normal font */
8412 int do_menu UNUSED, /* set menu font */
8413 int do_tooltip UNUSED, /* set tooltip font */
8414 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415{
8416# ifdef FEAT_XFONTSET
8417 /* If 'guifontset' is not empty, first try using the name as a
8418 * fontset. If that doesn't work, use it as a font name. */
8419 if (*p_guifontset != NUL
8420# ifdef FONTSET_ALWAYS
8421 || do_menu
8422# endif
8423# ifdef FEAT_BEVAL_TIP
8424 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8425 || do_tooltip
8426# endif
8427 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008428 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008429 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008430 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008431 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8432# ifdef FONTSET_ALWAYS
8433 || do_menu
8434# endif
8435# ifdef FEAT_BEVAL_TIP
8436 || do_tooltip
8437# endif
8438 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008439 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008440 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8441 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008442 /* If it worked and it's the Normal group, use it as the normal
8443 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008444 if (do_normal)
8445 gui_init_font(arg, TRUE);
8446# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8447 if (do_menu)
8448 {
8449# ifdef FONTSET_ALWAYS
8450 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8451# else
8452 /* YIKES! This is a bug waiting to crash the program */
8453 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8454# endif
8455 gui_mch_new_menu_font();
8456 }
8457# ifdef FEAT_BEVAL
8458 if (do_tooltip)
8459 {
8460 /* The Athena widget set cannot currently handle switching between
8461 * displaying a single font and a fontset.
8462 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008463 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008464 * XFontStruct is used.
8465 */
8466 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8467 gui_mch_new_tooltip_font();
8468 }
8469# endif
8470# endif
8471 }
8472 else
8473# endif
8474 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008475 if (free_font)
8476 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008477 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8478 /* If it worked and it's the Normal group, use it as the
8479 * normal font. Same for the Menu group. */
8480 if (HL_TABLE()[idx].sg_font != NOFONT)
8481 {
8482 if (do_normal)
8483 gui_init_font(arg, FALSE);
8484#ifndef FONTSET_ALWAYS
8485# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8486 if (do_menu)
8487 {
8488 gui.menu_font = HL_TABLE()[idx].sg_font;
8489 gui_mch_new_menu_font();
8490 }
8491# endif
8492#endif
8493 }
8494 }
8495}
8496
8497#endif /* FEAT_GUI */
8498
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008499#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008500/*
8501 * Return the handle for a color name.
8502 * Returns INVALCOLOR when failed.
8503 */
8504 static guicolor_T
8505color_name2handle(char_u *name)
8506{
8507 if (STRCMP(name, "NONE") == 0)
8508 return INVALCOLOR;
8509
8510 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8511 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008512#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008513 if (gui.in_use)
8514#endif
8515#ifdef FEAT_GUI
8516 return gui.norm_pixel;
8517#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008518#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008519 if (cterm_normal_fg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008520 return cterm_normal_fg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008521 /* Guess that the foreground is black or white. */
8522 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008523#endif
8524 }
8525 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8526 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008527#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008528 if (gui.in_use)
8529#endif
8530#ifdef FEAT_GUI
8531 return gui.back_pixel;
8532#endif
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008533#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008534 if (cterm_normal_bg_gui_color != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008535 return cterm_normal_bg_gui_color;
Bram Moolenaard80629c2016-05-28 15:53:53 +02008536 /* Guess that the background is white or black. */
8537 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008538#endif
8539 }
8540
8541 return GUI_GET_COLOR(name);
8542}
8543#endif
8544
Bram Moolenaar071d4272004-06-13 20:20:40 +00008545/*
8546 * Table with the specifications for an attribute number.
8547 * Note that this table is used by ALL buffers. This is required because the
8548 * GUI can redraw at any time for any buffer.
8549 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008550static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008551
8552#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8553
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008554static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008555
8556#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8557
8558#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008559static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008560
8561#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8562#endif
8563
8564/*
8565 * Return the attr number for a set of colors and font.
8566 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8567 * if the combination is new.
8568 * Return 0 for error (no more room).
8569 */
8570 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008571get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008572{
8573 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008574 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008575 static int recursive = FALSE;
8576
8577 /*
8578 * Init the table, in case it wasn't done yet.
8579 */
8580 table->ga_itemsize = sizeof(attrentry_T);
8581 table->ga_growsize = 7;
8582
8583 /*
8584 * Try to find an entry with the same specifications.
8585 */
8586 for (i = 0; i < table->ga_len; ++i)
8587 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008588 taep = &(((attrentry_T *)table->ga_data)[i]);
8589 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008590 && (
8591#ifdef FEAT_GUI
8592 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008593 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8594 && aep->ae_u.gui.bg_color
8595 == taep->ae_u.gui.bg_color
8596 && aep->ae_u.gui.sp_color
8597 == taep->ae_u.gui.sp_color
8598 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008599# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008600 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601# endif
8602 ))
8603 ||
8604#endif
8605 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008606 && (aep->ae_u.term.start == NULL)
8607 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008608 && (aep->ae_u.term.start == NULL
8609 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008610 taep->ae_u.term.start) == 0)
8611 && (aep->ae_u.term.stop == NULL)
8612 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613 && (aep->ae_u.term.stop == NULL
8614 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008615 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008616 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008617 && aep->ae_u.cterm.fg_color
8618 == taep->ae_u.cterm.fg_color
8619 && aep->ae_u.cterm.bg_color
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008620 == taep->ae_u.cterm.bg_color
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008621#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008622 && aep->ae_u.cterm.fg_rgb
8623 == taep->ae_u.cterm.fg_rgb
8624 && aep->ae_u.cterm.bg_rgb
8625 == taep->ae_u.cterm.bg_rgb
8626#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008627 )))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628
8629 return i + ATTR_OFF;
8630 }
8631
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008632 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008633 {
8634 /*
8635 * Running out of attribute entries! remove all attributes, and
8636 * compute new ones for all groups.
8637 * When called recursively, we are really out of numbers.
8638 */
8639 if (recursive)
8640 {
8641 EMSG(_("E424: Too many different highlighting attributes in use"));
8642 return 0;
8643 }
8644 recursive = TRUE;
8645
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008646 clear_hl_tables();
8647
Bram Moolenaar071d4272004-06-13 20:20:40 +00008648 must_redraw = CLEAR;
8649
8650 for (i = 0; i < highlight_ga.ga_len; ++i)
8651 set_hl_attr(i);
8652
8653 recursive = FALSE;
8654 }
8655
8656 /*
8657 * This is a new combination of colors and font, add an entry.
8658 */
8659 if (ga_grow(table, 1) == FAIL)
8660 return 0;
8661
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008662 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8663 vim_memset(taep, 0, sizeof(attrentry_T));
8664 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665#ifdef FEAT_GUI
8666 if (table == &gui_attr_table)
8667 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008668 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8669 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8670 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8671 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008672# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008673 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008674# endif
8675 }
8676#endif
8677 if (table == &term_attr_table)
8678 {
8679 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008680 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008682 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008683 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008684 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008685 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008686 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008687 }
8688 else if (table == &cterm_attr_table)
8689 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008690 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8691 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008692#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008693 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
8694 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
8695#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 }
8697 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008698 return (table->ga_len - 1 + ATTR_OFF);
8699}
8700
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008701/*
8702 * Clear all highlight tables.
8703 */
8704 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008705clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008706{
8707 int i;
8708 attrentry_T *taep;
8709
8710#ifdef FEAT_GUI
8711 ga_clear(&gui_attr_table);
8712#endif
8713 for (i = 0; i < term_attr_table.ga_len; ++i)
8714 {
8715 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8716 vim_free(taep->ae_u.term.start);
8717 vim_free(taep->ae_u.term.stop);
8718 }
8719 ga_clear(&term_attr_table);
8720 ga_clear(&cterm_attr_table);
8721}
8722
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008723#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008724/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008725 * Combine special attributes (e.g., for spelling) with other attributes
8726 * (e.g., for syntax highlighting).
8727 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008728 * This creates a new group when required.
8729 * Since we expect there to be few spelling mistakes we don't cache the
8730 * result.
8731 * Return the resulting attributes.
8732 */
8733 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008734hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008735{
8736 attrentry_T *char_aep = NULL;
8737 attrentry_T *spell_aep;
8738 attrentry_T new_en;
8739
8740 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008741 return prim_attr;
8742 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8743 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008744#ifdef FEAT_GUI
8745 if (gui.in_use)
8746 {
8747 if (char_attr > HL_ALL)
8748 char_aep = syn_gui_attr2entry(char_attr);
8749 if (char_aep != NULL)
8750 new_en = *char_aep;
8751 else
8752 {
8753 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008754 new_en.ae_u.gui.fg_color = INVALCOLOR;
8755 new_en.ae_u.gui.bg_color = INVALCOLOR;
8756 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008757 if (char_attr <= HL_ALL)
8758 new_en.ae_attr = char_attr;
8759 }
8760
Bram Moolenaar30abd282005-06-22 22:35:10 +00008761 if (prim_attr <= HL_ALL)
8762 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008763 else
8764 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008765 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008766 if (spell_aep != NULL)
8767 {
8768 new_en.ae_attr |= spell_aep->ae_attr;
8769 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8770 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8771 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8772 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8773 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8774 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8775 if (spell_aep->ae_u.gui.font != NOFONT)
8776 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8777# ifdef FEAT_XFONTSET
8778 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8779 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8780# endif
8781 }
8782 }
8783 return get_attr_entry(&gui_attr_table, &new_en);
8784 }
8785#endif
8786
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008787 if (IS_CTERM)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008788 {
8789 if (char_attr > HL_ALL)
8790 char_aep = syn_cterm_attr2entry(char_attr);
8791 if (char_aep != NULL)
8792 new_en = *char_aep;
8793 else
8794 {
8795 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaar0cdb72a2017-01-02 21:37:40 +01008796#ifdef FEAT_TERMGUICOLORS
8797 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
8798 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
8799#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008800 if (char_attr <= HL_ALL)
8801 new_en.ae_attr = char_attr;
8802 }
8803
Bram Moolenaar30abd282005-06-22 22:35:10 +00008804 if (prim_attr <= HL_ALL)
8805 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008806 else
8807 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008808 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008809 if (spell_aep != NULL)
8810 {
8811 new_en.ae_attr |= spell_aep->ae_attr;
8812 if (spell_aep->ae_u.cterm.fg_color > 0)
8813 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8814 if (spell_aep->ae_u.cterm.bg_color > 0)
8815 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02008816#ifdef FEAT_TERMGUICOLORS
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008817 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008818 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02008819 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008820 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
8821#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00008822 }
8823 }
8824 return get_attr_entry(&cterm_attr_table, &new_en);
8825 }
8826
8827 if (char_attr > HL_ALL)
8828 char_aep = syn_term_attr2entry(char_attr);
8829 if (char_aep != NULL)
8830 new_en = *char_aep;
8831 else
8832 {
8833 vim_memset(&new_en, 0, sizeof(new_en));
8834 if (char_attr <= HL_ALL)
8835 new_en.ae_attr = char_attr;
8836 }
8837
Bram Moolenaar30abd282005-06-22 22:35:10 +00008838 if (prim_attr <= HL_ALL)
8839 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008840 else
8841 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008842 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008843 if (spell_aep != NULL)
8844 {
8845 new_en.ae_attr |= spell_aep->ae_attr;
8846 if (spell_aep->ae_u.term.start != NULL)
8847 {
8848 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8849 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8850 }
8851 }
8852 }
8853 return get_attr_entry(&term_attr_table, &new_en);
8854}
8855#endif
8856
Bram Moolenaar071d4272004-06-13 20:20:40 +00008857#ifdef FEAT_GUI
8858
8859 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008860syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008861{
8862 attr -= ATTR_OFF;
8863 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8864 return NULL;
8865 return &(GUI_ATTR_ENTRY(attr));
8866}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008867#endif /* FEAT_GUI */
8868
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008869/*
8870 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8871 * Only to be used when "attr" > HL_ALL.
8872 */
8873 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008874syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008875{
8876 attrentry_T *aep;
8877
8878#ifdef FEAT_GUI
8879 if (gui.in_use)
8880 aep = syn_gui_attr2entry(attr);
8881 else
8882#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02008883 if (IS_CTERM)
8884 aep = syn_cterm_attr2entry(attr);
8885 else
8886 aep = syn_term_attr2entry(attr);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008887
8888 if (aep == NULL) /* highlighting not set */
8889 return 0;
8890 return aep->ae_attr;
8891}
8892
8893
Bram Moolenaar071d4272004-06-13 20:20:40 +00008894 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008895syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008896{
8897 attr -= ATTR_OFF;
8898 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8899 return NULL;
8900 return &(TERM_ATTR_ENTRY(attr));
8901}
8902
8903 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008904syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008905{
8906 attr -= ATTR_OFF;
8907 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8908 return NULL;
8909 return &(CTERM_ATTR_ENTRY(attr));
8910}
8911
8912#define LIST_ATTR 1
8913#define LIST_STRING 2
8914#define LIST_INT 3
8915
8916 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008917highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008918{
8919 struct hl_group *sgp;
8920 int didh = FALSE;
8921
8922 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8923
8924 didh = highlight_list_arg(id, didh, LIST_ATTR,
8925 sgp->sg_term, NULL, "term");
8926 didh = highlight_list_arg(id, didh, LIST_STRING,
8927 0, sgp->sg_start, "start");
8928 didh = highlight_list_arg(id, didh, LIST_STRING,
8929 0, sgp->sg_stop, "stop");
8930
8931 didh = highlight_list_arg(id, didh, LIST_ATTR,
8932 sgp->sg_cterm, NULL, "cterm");
8933 didh = highlight_list_arg(id, didh, LIST_INT,
8934 sgp->sg_cterm_fg, NULL, "ctermfg");
8935 didh = highlight_list_arg(id, didh, LIST_INT,
8936 sgp->sg_cterm_bg, NULL, "ctermbg");
8937
Bram Moolenaar61623362010-07-14 22:04:22 +02008938#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008939 didh = highlight_list_arg(id, didh, LIST_ATTR,
8940 sgp->sg_gui, NULL, "gui");
8941 didh = highlight_list_arg(id, didh, LIST_STRING,
8942 0, sgp->sg_gui_fg_name, "guifg");
8943 didh = highlight_list_arg(id, didh, LIST_STRING,
8944 0, sgp->sg_gui_bg_name, "guibg");
8945 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008946 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008947#endif
8948#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008949 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008950 0, sgp->sg_font_name, "font");
8951#endif
8952
Bram Moolenaar661b1822005-07-28 22:36:45 +00008953 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008954 {
8955 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008956 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008957 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8958 msg_putchar(' ');
8959 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8960 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008961
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008962 if (!didh)
8963 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008964#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008965 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008966 last_set_msg(sgp->sg_scriptID);
8967#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008968}
8969
8970 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008971highlight_list_arg(
8972 int id,
8973 int didh,
8974 int type,
8975 int iarg,
8976 char_u *sarg,
8977 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008978{
8979 char_u buf[100];
8980 char_u *ts;
8981 int i;
8982
Bram Moolenaar661b1822005-07-28 22:36:45 +00008983 if (got_int)
8984 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008985 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8986 {
8987 ts = buf;
8988 if (type == LIST_INT)
8989 sprintf((char *)buf, "%d", iarg - 1);
8990 else if (type == LIST_STRING)
8991 ts = sarg;
8992 else /* type == LIST_ATTR */
8993 {
8994 buf[0] = NUL;
8995 for (i = 0; hl_attr_table[i] != 0; ++i)
8996 {
8997 if (iarg & hl_attr_table[i])
8998 {
8999 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02009000 vim_strcat(buf, (char_u *)",", 100);
9001 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009002 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9003 }
9004 }
9005 }
9006
9007 (void)syn_list_header(didh,
9008 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9009 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009010 if (!got_int)
9011 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009012 if (*name != NUL)
9013 {
9014 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
9015 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
9016 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009017 msg_outtrans(ts);
9018 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009019 }
9020 return didh;
9021}
9022
9023#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9024/*
9025 * Return "1" if highlight group "id" has attribute "flag".
9026 * Return NULL otherwise.
9027 */
9028 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009029highlight_has_attr(
9030 int id,
9031 int flag,
9032 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009033{
9034 int attr;
9035
9036 if (id <= 0 || id > highlight_ga.ga_len)
9037 return NULL;
9038
Bram Moolenaar61623362010-07-14 22:04:22 +02009039#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009040 if (modec == 'g')
9041 attr = HL_TABLE()[id - 1].sg_gui;
9042 else
9043#endif
9044 if (modec == 'c')
9045 attr = HL_TABLE()[id - 1].sg_cterm;
9046 else
9047 attr = HL_TABLE()[id - 1].sg_term;
9048
9049 if (attr & flag)
9050 return (char_u *)"1";
9051 return NULL;
9052}
9053#endif
9054
9055#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9056/*
9057 * Return color name of highlight group "id".
9058 */
9059 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009060highlight_color(
9061 int id,
9062 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
9063 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009064{
9065 static char_u name[20];
9066 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009067 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009068 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009069 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009070
9071 if (id <= 0 || id > highlight_ga.ga_len)
9072 return NULL;
9073
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009074 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009075 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009076 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009077 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009078 font = TRUE;
9079 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009080 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009081 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9082 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009083 if (modec == 'g')
9084 {
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009085# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009086# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009087 /* return font name */
9088 if (font)
9089 return HL_TABLE()[id - 1].sg_font_name;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009090# endif
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009091
Bram Moolenaar071d4272004-06-13 20:20:40 +00009092 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009093 if ((USE_24BIT) && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009094 {
9095 guicolor_T color;
9096 long_u rgb;
9097 static char_u buf[10];
9098
9099 if (fg)
9100 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009101 else if (sp)
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009102# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009103 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009104# else
9105 color = INVALCOLOR;
9106# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009107 else
9108 color = HL_TABLE()[id - 1].sg_gui_bg;
9109 if (color == INVALCOLOR)
9110 return NULL;
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02009111 rgb = (long_u)GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009112 sprintf((char *)buf, "#%02x%02x%02x",
9113 (unsigned)(rgb >> 16),
9114 (unsigned)(rgb >> 8) & 255,
9115 (unsigned)rgb & 255);
9116 return buf;
9117 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009118# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009119 if (fg)
9120 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009121 if (sp)
9122 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009123 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9124 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009125 if (font || sp)
9126 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009127 if (modec == 'c')
9128 {
9129 if (fg)
9130 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9131 else
9132 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009133 if (n < 0)
9134 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009135 sprintf((char *)name, "%d", n);
9136 return name;
9137 }
9138 /* term doesn't have color */
9139 return NULL;
9140}
9141#endif
9142
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009143#if (defined(FEAT_SYN_HL) \
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009144 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009145 && defined(FEAT_PRINTER)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009146/*
9147 * Return color name of highlight group "id" as RGB value.
9148 */
9149 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009150highlight_gui_color_rgb(
9151 int id,
9152 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009153{
9154 guicolor_T color;
9155
9156 if (id <= 0 || id > highlight_ga.ga_len)
9157 return 0L;
9158
9159 if (fg)
9160 color = HL_TABLE()[id - 1].sg_gui_fg;
9161 else
9162 color = HL_TABLE()[id - 1].sg_gui_bg;
9163
9164 if (color == INVALCOLOR)
9165 return 0L;
9166
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009167 return GUI_MCH_GET_RGB(color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009168}
9169#endif
9170
9171/*
9172 * Output the syntax list header.
9173 * Return TRUE when started a new line.
9174 */
9175 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009176syn_list_header(
9177 int did_header, /* did header already */
9178 int outlen, /* length of string that comes */
9179 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009180{
9181 int endcol = 19;
9182 int newline = TRUE;
9183
9184 if (!did_header)
9185 {
9186 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009187 if (got_int)
9188 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009189 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9190 endcol = 15;
9191 }
9192 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009193 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009194 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009195 if (got_int)
9196 return TRUE;
9197 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009198 else
9199 {
9200 if (msg_col >= endcol) /* wrap around is like starting a new line */
9201 newline = FALSE;
9202 }
9203
9204 if (msg_col >= endcol) /* output at least one space */
9205 endcol = msg_col + 1;
9206 if (Columns <= endcol) /* avoid hang for tiny window */
9207 endcol = Columns - 1;
9208
9209 msg_advance(endcol);
9210
9211 /* Show "xxx" with the attributes. */
9212 if (!did_header)
9213 {
9214 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9215 msg_putchar(' ');
9216 }
9217
9218 return newline;
9219}
9220
9221/*
9222 * Set the attribute numbers for a highlight group.
9223 * Called after one of the attributes has changed.
9224 */
9225 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009226set_hl_attr(
9227 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009228{
9229 attrentry_T at_en;
9230 struct hl_group *sgp = HL_TABLE() + idx;
9231
9232 /* The "Normal" group doesn't need an attribute number */
9233 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9234 return;
9235
9236#ifdef FEAT_GUI
9237 /*
9238 * For the GUI mode: If there are other than "normal" highlighting
9239 * attributes, need to allocate an attr number.
9240 */
9241 if (sgp->sg_gui_fg == INVALCOLOR
9242 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009243 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009244 && sgp->sg_font == NOFONT
9245# ifdef FEAT_XFONTSET
9246 && sgp->sg_fontset == NOFONTSET
9247# endif
9248 )
9249 {
9250 sgp->sg_gui_attr = sgp->sg_gui;
9251 }
9252 else
9253 {
9254 at_en.ae_attr = sgp->sg_gui;
9255 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9256 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009257 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009258 at_en.ae_u.gui.font = sgp->sg_font;
9259# ifdef FEAT_XFONTSET
9260 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9261# endif
9262 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9263 }
9264#endif
9265 /*
9266 * For the term mode: If there are other than "normal" highlighting
9267 * attributes, need to allocate an attr number.
9268 */
9269 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9270 sgp->sg_term_attr = sgp->sg_term;
9271 else
9272 {
9273 at_en.ae_attr = sgp->sg_term;
9274 at_en.ae_u.term.start = sgp->sg_start;
9275 at_en.ae_u.term.stop = sgp->sg_stop;
9276 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9277 }
9278
9279 /*
9280 * For the color term mode: If there are other than "normal"
9281 * highlighting attributes, need to allocate an attr number.
9282 */
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009283 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009284# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009285 && sgp->sg_gui_fg == INVALCOLOR
9286 && sgp->sg_gui_bg == INVALCOLOR
9287# endif
9288 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00009289 sgp->sg_cterm_attr = sgp->sg_cterm;
9290 else
9291 {
9292 at_en.ae_attr = sgp->sg_cterm;
9293 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9294 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009295# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar187147a2016-05-01 13:09:57 +02009296 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
9297 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009298# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009299 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9300 }
9301}
9302
9303/*
9304 * Lookup a highlight group name and return it's ID.
9305 * If it is not found, 0 is returned.
9306 */
9307 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009308syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009309{
9310 int i;
9311 char_u name_u[200];
9312
9313 /* Avoid using stricmp() too much, it's slow on some systems */
9314 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9315 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009316 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 vim_strup(name_u);
9318 for (i = highlight_ga.ga_len; --i >= 0; )
9319 if (HL_TABLE()[i].sg_name_u != NULL
9320 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9321 break;
9322 return i + 1;
9323}
9324
9325#if defined(FEAT_EVAL) || defined(PROTO)
9326/*
9327 * Return TRUE if highlight group "name" exists.
9328 */
9329 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009330highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009331{
9332 return (syn_name2id(name) > 0);
9333}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009334
9335# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9336/*
9337 * Return the name of highlight group "id".
9338 * When not a valid ID return an empty string.
9339 */
9340 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009341syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009342{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009343 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009344 return (char_u *)"";
9345 return HL_TABLE()[id - 1].sg_name;
9346}
9347# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009348#endif
9349
9350/*
9351 * Like syn_name2id(), but take a pointer + length argument.
9352 */
9353 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009354syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355{
9356 char_u *name;
9357 int id = 0;
9358
9359 name = vim_strnsave(linep, len);
9360 if (name != NULL)
9361 {
9362 id = syn_name2id(name);
9363 vim_free(name);
9364 }
9365 return id;
9366}
9367
9368/*
9369 * Find highlight group name in the table and return it's ID.
9370 * The argument is a pointer to the name and the length of the name.
9371 * If it doesn't exist yet, a new entry is created.
9372 * Return 0 for failure.
9373 */
9374 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009375syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009376{
9377 int id;
9378 char_u *name;
9379
9380 name = vim_strnsave(pp, len);
9381 if (name == NULL)
9382 return 0;
9383
9384 id = syn_name2id(name);
9385 if (id == 0) /* doesn't exist yet */
9386 id = syn_add_group(name);
9387 else
9388 vim_free(name);
9389 return id;
9390}
9391
9392/*
9393 * Add new highlight group and return it's ID.
9394 * "name" must be an allocated string, it will be consumed.
9395 * Return 0 for failure.
9396 */
9397 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009398syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009399{
9400 char_u *p;
9401
9402 /* Check that the name is ASCII letters, digits and underscore. */
9403 for (p = name; *p != NUL; ++p)
9404 {
9405 if (!vim_isprintc(*p))
9406 {
9407 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009408 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009409 return 0;
9410 }
9411 else if (!ASCII_ISALNUM(*p) && *p != '_')
9412 {
9413 /* This is an error, but since there previously was no check only
9414 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009415 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009416 MSG(_("W18: Invalid character in group name"));
9417 break;
9418 }
9419 }
9420
9421 /*
9422 * First call for this growarray: init growing array.
9423 */
9424 if (highlight_ga.ga_data == NULL)
9425 {
9426 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9427 highlight_ga.ga_growsize = 10;
9428 }
9429
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009430 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009431 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009432 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009433 vim_free(name);
9434 return 0;
9435 }
9436
Bram Moolenaar071d4272004-06-13 20:20:40 +00009437 /*
9438 * Make room for at least one other syntax_highlight entry.
9439 */
9440 if (ga_grow(&highlight_ga, 1) == FAIL)
9441 {
9442 vim_free(name);
9443 return 0;
9444 }
9445
9446 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9447 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9448 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009449#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009450 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9451 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009452# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009453 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009454# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009455#endif
9456 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009457
9458 return highlight_ga.ga_len; /* ID is index plus one */
9459}
9460
9461/*
9462 * When, just after calling syn_add_group(), an error is discovered, this
9463 * function deletes the new name.
9464 */
9465 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009466syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009467{
9468 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009469 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9470 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9471}
9472
9473/*
9474 * Translate a group ID to highlight attributes.
9475 */
9476 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009477syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009478{
9479 int attr;
9480 struct hl_group *sgp;
9481
9482 hl_id = syn_get_final_id(hl_id);
9483 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9484
9485#ifdef FEAT_GUI
9486 /*
9487 * Only use GUI attr when the GUI is being used.
9488 */
9489 if (gui.in_use)
9490 attr = sgp->sg_gui_attr;
9491 else
9492#endif
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009493 if (IS_CTERM)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009494 attr = sgp->sg_cterm_attr;
9495 else
9496 attr = sgp->sg_term_attr;
9497
9498 return attr;
9499}
9500
9501#ifdef FEAT_GUI
9502/*
9503 * Get the GUI colors and attributes for a group ID.
9504 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9505 */
9506 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009507syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009508{
9509 struct hl_group *sgp;
9510
9511 hl_id = syn_get_final_id(hl_id);
9512 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9513
9514 *fgp = sgp->sg_gui_fg;
9515 *bgp = sgp->sg_gui_bg;
9516 return sgp->sg_gui;
9517}
9518#endif
9519
9520/*
9521 * Translate a group ID to the final group ID (following links).
9522 */
9523 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009524syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009525{
9526 int count;
9527 struct hl_group *sgp;
9528
9529 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9530 return 0; /* Can be called from eval!! */
9531
9532 /*
9533 * Follow links until there is no more.
9534 * Look out for loops! Break after 100 links.
9535 */
9536 for (count = 100; --count >= 0; )
9537 {
9538 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9539 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9540 break;
9541 hl_id = sgp->sg_link;
9542 }
9543
9544 return hl_id;
9545}
9546
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009547#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009548/*
9549 * Call this function just after the GUI has started.
9550 * It finds the font and color handles for the highlighting groups.
9551 */
9552 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009553highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009554{
9555 int idx;
9556
9557 /* First get the colors from the "Normal" and "Menu" group, if set */
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009558# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
9559# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009560 if (USE_24BIT)
9561# endif
9562 set_normal_colors();
9563# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009564
9565 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9566 gui_do_one_color(idx, FALSE, FALSE);
9567
9568 highlight_changed();
9569}
9570
9571 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009572gui_do_one_color(
9573 int idx,
Bram Moolenaar380130f2016-04-22 11:24:43 +02009574 int do_menu UNUSED, /* TRUE: might set the menu font */
9575 int do_tooltip UNUSED) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009576{
9577 int didit = FALSE;
9578
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009579# ifdef FEAT_GUI
Bram Moolenaar61be73b2016-04-29 22:59:22 +02009580# ifdef FEAT_TERMGUICOLORS
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009581 if (gui.in_use)
9582# endif
9583 if (HL_TABLE()[idx].sg_font_name != NULL)
9584 {
9585 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009586 do_tooltip, TRUE);
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009587 didit = TRUE;
9588 }
9589# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009590 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9591 {
9592 HL_TABLE()[idx].sg_gui_fg =
9593 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9594 didit = TRUE;
9595 }
9596 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9597 {
9598 HL_TABLE()[idx].sg_gui_bg =
9599 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9600 didit = TRUE;
9601 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009602# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009603 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9604 {
9605 HL_TABLE()[idx].sg_gui_sp =
9606 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9607 didit = TRUE;
9608 }
Bram Moolenaar8a633e32016-04-21 21:10:14 +02009609# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009610 if (didit) /* need to get a new attr number */
9611 set_hl_attr(idx);
9612}
Bram Moolenaar071d4272004-06-13 20:20:40 +00009613#endif
9614
9615/*
9616 * Translate the 'highlight' option into attributes in highlight_attr[] and
9617 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9618 * corresponding highlights to use on top of HLF_SNC is computed.
9619 * Called only when the 'highlight' option has been changed and upon first
9620 * screen redraw after any :highlight command.
9621 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9622 */
9623 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009624highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009625{
9626 int hlf;
9627 int i;
9628 char_u *p;
9629 int attr;
9630 char_u *end;
9631 int id;
9632#ifdef USER_HIGHLIGHT
9633 char_u userhl[10];
9634# ifdef FEAT_STL_OPT
9635 int id_SNC = -1;
9636 int id_S = -1;
9637 int hlcnt;
9638# endif
9639#endif
9640 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9641
9642 need_highlight_changed = FALSE;
9643
9644 /*
9645 * Clear all attributes.
9646 */
9647 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9648 highlight_attr[hlf] = 0;
9649
9650 /*
9651 * First set all attributes to their default value.
9652 * Then use the attributes from the 'highlight' option.
9653 */
9654 for (i = 0; i < 2; ++i)
9655 {
9656 if (i)
9657 p = p_hl;
9658 else
9659 p = get_highlight_default();
9660 if (p == NULL) /* just in case */
9661 continue;
9662
9663 while (*p)
9664 {
9665 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9666 if (hl_flags[hlf] == *p)
9667 break;
9668 ++p;
9669 if (hlf == (int)HLF_COUNT || *p == NUL)
9670 return FAIL;
9671
9672 /*
9673 * Allow several hl_flags to be combined, like "bu" for
9674 * bold-underlined.
9675 */
9676 attr = 0;
9677 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9678 {
9679 if (vim_iswhite(*p)) /* ignore white space */
9680 continue;
9681
9682 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9683 return FAIL;
9684
9685 switch (*p)
9686 {
9687 case 'b': attr |= HL_BOLD;
9688 break;
9689 case 'i': attr |= HL_ITALIC;
9690 break;
9691 case '-':
9692 case 'n': /* no highlighting */
9693 break;
9694 case 'r': attr |= HL_INVERSE;
9695 break;
9696 case 's': attr |= HL_STANDOUT;
9697 break;
9698 case 'u': attr |= HL_UNDERLINE;
9699 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009700 case 'c': attr |= HL_UNDERCURL;
9701 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009702 case ':': ++p; /* highlight group name */
9703 if (attr || *p == NUL) /* no combinations */
9704 return FAIL;
9705 end = vim_strchr(p, ',');
9706 if (end == NULL)
9707 end = p + STRLEN(p);
9708 id = syn_check_group(p, (int)(end - p));
9709 if (id == 0)
9710 return FAIL;
9711 attr = syn_id2attr(id);
9712 p = end - 1;
9713#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9714 if (hlf == (int)HLF_SNC)
9715 id_SNC = syn_get_final_id(id);
9716 else if (hlf == (int)HLF_S)
9717 id_S = syn_get_final_id(id);
9718#endif
9719 break;
9720 default: return FAIL;
9721 }
9722 }
9723 highlight_attr[hlf] = attr;
9724
9725 p = skip_to_option_part(p); /* skip comma and spaces */
9726 }
9727 }
9728
9729#ifdef USER_HIGHLIGHT
9730 /* Setup the user highlights
9731 *
9732 * Temporarily utilize 10 more hl entries. Have to be in there
9733 * simultaneously in case of table overflows in get_attr_entry()
9734 */
9735# ifdef FEAT_STL_OPT
9736 if (ga_grow(&highlight_ga, 10) == FAIL)
9737 return FAIL;
9738 hlcnt = highlight_ga.ga_len;
9739 if (id_S == 0)
9740 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009741 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009742 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9743 id_S = hlcnt + 10;
9744 }
9745# endif
9746 for (i = 0; i < 9; i++)
9747 {
9748 sprintf((char *)userhl, "User%d", i + 1);
9749 id = syn_name2id(userhl);
9750 if (id == 0)
9751 {
9752 highlight_user[i] = 0;
9753# ifdef FEAT_STL_OPT
9754 highlight_stlnc[i] = 0;
9755# endif
9756 }
9757 else
9758 {
9759# ifdef FEAT_STL_OPT
9760 struct hl_group *hlt = HL_TABLE();
9761# endif
9762
9763 highlight_user[i] = syn_id2attr(id);
9764# ifdef FEAT_STL_OPT
9765 if (id_SNC == 0)
9766 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009767 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009768 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9769 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009770# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009771 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9772# endif
9773 }
9774 else
9775 mch_memmove(&hlt[hlcnt + i],
9776 &hlt[id_SNC - 1],
9777 sizeof(struct hl_group));
9778 hlt[hlcnt + i].sg_link = 0;
9779
9780 /* Apply difference between UserX and HLF_S to HLF_SNC */
9781 hlt[hlcnt + i].sg_term ^=
9782 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9783 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9784 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9785 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9786 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9787 hlt[hlcnt + i].sg_cterm ^=
9788 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9789 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9790 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9791 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9792 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009793# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009794 hlt[hlcnt + i].sg_gui ^=
9795 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009796# endif
9797# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009798 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9799 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9800 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9801 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009802 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9803 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009804 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9805 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9806# ifdef FEAT_XFONTSET
9807 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9808 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9809# endif
9810# endif
9811 highlight_ga.ga_len = hlcnt + i + 1;
9812 set_hl_attr(hlcnt + i); /* At long last we can apply */
9813 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9814# endif
9815 }
9816 }
9817# ifdef FEAT_STL_OPT
9818 highlight_ga.ga_len = hlcnt;
9819# endif
9820
9821#endif /* USER_HIGHLIGHT */
9822
9823 return OK;
9824}
9825
Bram Moolenaar4f688582007-07-24 12:34:30 +00009826#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009827
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009828static void highlight_list(void);
9829static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009830
9831/*
9832 * Handle command line completion for :highlight command.
9833 */
9834 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009835set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009836{
9837 char_u *p;
9838
9839 /* Default: expand group names */
9840 xp->xp_context = EXPAND_HIGHLIGHT;
9841 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009842 include_link = 2;
9843 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009844
9845 /* (part of) subcommand already typed */
9846 if (*arg != NUL)
9847 {
9848 p = skiptowhite(arg);
9849 if (*p != NUL) /* past "default" or group name */
9850 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009851 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009852 if (STRNCMP("default", arg, p - arg) == 0)
9853 {
9854 arg = skipwhite(p);
9855 xp->xp_pattern = arg;
9856 p = skiptowhite(arg);
9857 }
9858 if (*p != NUL) /* past group name */
9859 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009860 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009861 if (arg[1] == 'i' && arg[0] == 'N')
9862 highlight_list();
9863 if (STRNCMP("link", arg, p - arg) == 0
9864 || STRNCMP("clear", arg, p - arg) == 0)
9865 {
9866 xp->xp_pattern = skipwhite(p);
9867 p = skiptowhite(xp->xp_pattern);
9868 if (*p != NUL) /* past first group name */
9869 {
9870 xp->xp_pattern = skipwhite(p);
9871 p = skiptowhite(xp->xp_pattern);
9872 }
9873 }
9874 if (*p != NUL) /* past group name(s) */
9875 xp->xp_context = EXPAND_NOTHING;
9876 }
9877 }
9878 }
9879}
9880
9881/*
9882 * List highlighting matches in a nice way.
9883 */
9884 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009885highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009886{
9887 int i;
9888
9889 for (i = 10; --i >= 0; )
9890 highlight_list_two(i, hl_attr(HLF_D));
9891 for (i = 40; --i >= 0; )
9892 highlight_list_two(99, 0);
9893}
9894
9895 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009896highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009897{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009898 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009899 msg_clr_eos();
9900 out_flush();
9901 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9902}
9903
9904#endif /* FEAT_CMDL_COMPL */
9905
9906#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9907 || defined(FEAT_SIGNS) || defined(PROTO)
9908/*
9909 * Function given to ExpandGeneric() to obtain the list of group names.
9910 * Also used for synIDattr() function.
9911 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009912 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009913get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009914{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009915#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009916 if (idx == highlight_ga.ga_len && include_none != 0)
9917 return (char_u *)"none";
9918 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009919 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009920 if (idx == highlight_ga.ga_len + include_none + include_default
9921 && include_link != 0)
9922 return (char_u *)"link";
9923 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9924 && include_link != 0)
9925 return (char_u *)"clear";
9926#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009927 if (idx < 0 || idx >= highlight_ga.ga_len)
9928 return NULL;
9929 return HL_TABLE()[idx].sg_name;
9930}
9931#endif
9932
Bram Moolenaar4f688582007-07-24 12:34:30 +00009933#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009934/*
9935 * Free all the highlight group fonts.
9936 * Used when quitting for systems which need it.
9937 */
9938 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009939free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009940{
9941 int idx;
9942
9943 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9944 {
9945 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9946 HL_TABLE()[idx].sg_font = NOFONT;
9947# ifdef FEAT_XFONTSET
9948 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9949 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9950# endif
9951 }
9952
9953 gui_mch_free_font(gui.norm_font);
9954# ifdef FEAT_XFONTSET
9955 gui_mch_free_fontset(gui.fontset);
9956# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009957# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009958 gui_mch_free_font(gui.bold_font);
9959 gui_mch_free_font(gui.ital_font);
9960 gui_mch_free_font(gui.boldital_font);
9961# endif
9962}
9963#endif
9964
9965/**************************************
9966 * End of Highlighting stuff *
9967 **************************************/