blob: 29b68886b9634edec4ef2222b295454ca5b65f8c [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
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 */
36#ifdef FEAT_GUI
37/* for when using the GUI */
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 Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020071#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
72
Bram Moolenaar071d4272004-06-13 20:20:40 +000073#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000074/* Flags to indicate an additional string for highlight name completion. */
75static int include_none = 0; /* when 1 include "None" */
76static int include_default = 0; /* when 1 include "default" */
77static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000078#endif
79
80/*
81 * The "term", "cterm" and "gui" arguments can be any combination of the
82 * following names, separated by commas (but no spaces!).
83 */
84static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000085 {"bold", "standout", "underline", "undercurl",
86 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010090static int get_attr_entry(garray_T *table, attrentry_T *aep);
91static void syn_unadd_group(void);
92static void set_hl_attr(int idx);
93static void highlight_list_one(int id);
94static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
95static int syn_add_group(char_u *name);
96static int syn_list_header(int did_header, int outlen, int id);
97static int hl_has_settings(int idx, int check_link);
98static void highlight_clear(int idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +000099
100#ifdef FEAT_GUI
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100101static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
102static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
103static guicolor_T color_name2handle(char_u *name);
104static GuiFont font_name2handle(char_u *name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105# ifdef FEAT_XFONTSET
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100106static GuiFontset fontset_name2handle(char_u *name, int fixed_width);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000107# endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100108static 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 +0000109#endif
110
111/*
112 * An attribute number is the index in attr_table plus ATTR_OFF.
113 */
114#define ATTR_OFF (HL_ALL + 1)
115
116#if defined(FEAT_SYN_HL) || defined(PROTO)
117
118#define SYN_NAMELEN 50 /* maximum length of a syntax name */
119
120/* different types of offsets that are possible */
121#define SPO_MS_OFF 0 /* match start offset */
122#define SPO_ME_OFF 1 /* match end offset */
123#define SPO_HS_OFF 2 /* highl. start offset */
124#define SPO_HE_OFF 3 /* highl. end offset */
125#define SPO_RS_OFF 4 /* region start offset */
126#define SPO_RE_OFF 5 /* region end offset */
127#define SPO_LC_OFF 6 /* leading context offset */
128#define SPO_COUNT 7
129
130static char *(spo_name_tab[SPO_COUNT]) =
131 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
132
133/*
134 * The patterns that are being searched for are stored in a syn_pattern.
135 * A match item consists of one pattern.
136 * A start/end item consists of n start patterns and m end patterns.
137 * A start/skip/end item consists of n start patterns, one skip pattern and m
138 * end patterns.
139 * For the latter two, the patterns are always consecutive: start-skip-end.
140 *
141 * A character offset can be given for the matched text (_m_start and _m_end)
142 * and for the actually highlighted text (_h_start and _h_end).
143 */
144typedef struct syn_pattern
145{
146 char sp_type; /* see SPTYPE_ defines below */
147 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200148 int sp_flags; /* see HL_ defines below */
149#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200150 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 struct sp_syn sp_syn; /* struct passed to in_id_list() */
153 short sp_syn_match_id; /* highlight group ID of pattern */
154 char_u *sp_pattern; /* regexp to match, pattern */
155 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200156#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200157 syn_time_T sp_time;
158#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 int sp_ic; /* ignore-case flag for sp_prog */
160 short sp_off_flags; /* see below */
161 int sp_offsets[SPO_COUNT]; /* offsets */
162 short *sp_cont_list; /* cont. group IDs, if non-zero */
163 short *sp_next_list; /* next group IDs, if non-zero */
164 int sp_sync_idx; /* sync item index (syncing only) */
165 int sp_line_id; /* ID of last line where tried */
166 int sp_startcol; /* next match in sp_line_id line */
167} synpat_T;
168
169/* The sp_off_flags are computed like this:
170 * offset from the start of the matched text: (1 << SPO_XX_OFF)
171 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
172 * When both are present, only one is used.
173 */
174
175#define SPTYPE_MATCH 1 /* match keyword with this group ID */
176#define SPTYPE_START 2 /* match a regexp, start of item */
177#define SPTYPE_END 3 /* match a regexp, end of item */
178#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
179
Bram Moolenaar071d4272004-06-13 20:20:40 +0000180
181#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
182
183#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
184
185/*
186 * Flags for b_syn_sync_flags:
187 */
188#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
189#define SF_MATCH 0x02 /* sync by matching a pattern */
190
191#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
192
Bram Moolenaar071d4272004-06-13 20:20:40 +0000193#define MAXKEYWLEN 80 /* maximum length of a keyword */
194
195/*
196 * The attributes of the syntax item that has been recognized.
197 */
198static int current_attr = 0; /* attr of current syntax word */
199#ifdef FEAT_EVAL
200static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000201static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200203#ifdef FEAT_CONCEAL
204static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200205static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200206static int current_sub_char = 0;
207#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208
Bram Moolenaar217ad922005-03-20 22:37:15 +0000209typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210{
211 char_u *scl_name; /* syntax cluster name */
212 char_u *scl_name_u; /* uppercase of scl_name */
213 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215
216/*
217 * Methods of combining two clusters
218 */
219#define CLUSTER_REPLACE 1 /* replace first list with second */
220#define CLUSTER_ADD 2 /* add second list to first */
221#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
222
Bram Moolenaar217ad922005-03-20 22:37:15 +0000223#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224
225/*
226 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200227 * 0 - 19999 normal syntax groups
228 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
229 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
230 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
231 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200233#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200234#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
235#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
236#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
237
Bram Moolenaar42431a72011-04-01 14:44:59 +0200238#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
239#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000240
241/*
242 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
243 * expand_filename(). Most of the other syntax commands don't need it, so
244 * instead of passing it to them, we stow it here.
245 */
246static char_u **syn_cmdlinep;
247
248/*
249 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200250 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000251 * rules in each ":syn include"'d file.
252 */
253static int current_syn_inc_tag = 0;
254static int running_syn_inc_tag = 0;
255
256/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000257 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
258 * This avoids adding a pointer to the hashtable item.
259 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
260 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
261 * HI2KE() converts a hashitem pointer to a var pointer.
262 */
263static keyentry_T dumkey;
264#define KE2HIKEY(kp) ((kp)->keyword)
265#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
266#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
267
268/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000269 * To reduce the time spent in keepend(), remember at which level in the state
270 * stack the first item with "keepend" is present. When "-1", there is no
271 * "keepend" on the stack.
272 */
273static int keepend_level = -1;
274
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200275static char msg_no_items[] = N_("No Syntax items defined for this buffer");
276
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277/*
278 * For the current state we need to remember more than just the idx.
279 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
280 * (The end positions have the column number of the next char)
281 */
282typedef struct state_item
283{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000284 int si_idx; /* index of syntax pattern or
285 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000286 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000287 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288 int si_m_lnum; /* lnum of the match */
289 int si_m_startcol; /* starting column of the match */
290 lpos_T si_m_endpos; /* just after end posn of the match */
291 lpos_T si_h_startpos; /* start position of the highlighting */
292 lpos_T si_h_endpos; /* end position of the highlighting */
293 lpos_T si_eoe_pos; /* end position of end pattern */
294 int si_end_idx; /* group ID for end pattern or zero */
295 int si_ends; /* if match ends before si_m_endpos */
296 int si_attr; /* attributes in this state */
297 long si_flags; /* HL_HAS_EOL flag in this state, and
298 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200299#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200300 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200301 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200302#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303 short *si_cont_list; /* list of contained groups */
304 short *si_next_list; /* nextgroup IDs after this item ends */
305 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
306 * pattern */
307} stateitem_T;
308
309#define KEYWORD_IDX -1 /* value of si_idx for keywords */
310#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
311 but contained groups */
312
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200313#ifdef FEAT_CONCEAL
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100314static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200315#endif
316
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000318 * Struct to reduce the number of arguments to get_syn_options(), it's used
319 * very often.
320 */
321typedef struct
322{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000323 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000324 int keyword; /* TRUE for ":syn keyword" */
325 int *sync_idx; /* syntax item for "grouphere" argument, NULL
326 if not allowed */
327 char has_cont_list; /* TRUE if "cont_list" can be used */
328 short *cont_list; /* group IDs for "contains" argument */
329 short *cont_in_list; /* group IDs for "containedin" argument */
330 short *next_list; /* group IDs for "nextgroup" argument */
331} syn_opt_arg_T;
332
333/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 * The next possible match in the current line for any pattern is remembered,
335 * to avoid having to try for a match in each column.
336 * If next_match_idx == -1, not tried (in this line) yet.
337 * If next_match_col == MAXCOL, no match found in this line.
338 * (All end positions have the column of the char after the end)
339 */
340static int next_match_col; /* column for start of next match */
341static lpos_T next_match_m_endpos; /* position for end of next match */
342static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
343static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
344static int next_match_idx; /* index of matched item */
345static long next_match_flags; /* flags for next match */
346static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
347static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
348static int next_match_end_idx; /* ID of group for end pattn or zero */
349static reg_extmatch_T *next_match_extmatch = NULL;
350
351/*
352 * A state stack is an array of integers or stateitem_T, stored in a
353 * garray_T. A state stack is invalid if it's itemsize entry is zero.
354 */
355#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
356#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
357
358/*
359 * The current state (within the line) of the recognition engine.
360 * When current_state.ga_itemsize is 0 the current state is invalid.
361 */
362static win_T *syn_win; /* current window for highlighting */
363static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200364static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365static linenr_T current_lnum = 0; /* lnum of current state */
366static colnr_T current_col = 0; /* column of current state */
367static int current_state_stored = 0; /* TRUE if stored current state
368 * after setting current_finished */
369static int current_finished = 0; /* current line has been finished */
370static garray_T current_state /* current stack of state_items */
371 = {0, 0, 0, 0, NULL};
372static short *current_next_list = NULL; /* when non-zero, nextgroup list */
373static int current_next_flags = 0; /* flags for current_next_list */
374static int current_line_id = 0; /* unique number for current line */
375
376#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
377
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100378static void syn_sync(win_T *wp, linenr_T lnum, synstate_T *last_valid);
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100379static void save_chartab(char_u *chartab);
380static void restore_chartab(char_u *chartab);
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100381static int syn_match_linecont(linenr_T lnum);
382static void syn_start_line(void);
383static void syn_update_ends(int startofline);
384static void syn_stack_alloc(void);
385static int syn_stack_cleanup(void);
386static void syn_stack_free_entry(synblock_T *block, synstate_T *p);
387static synstate_T *syn_stack_find_entry(linenr_T lnum);
388static synstate_T *store_current_state(void);
389static void load_current_state(synstate_T *from);
390static void invalidate_current_state(void);
391static int syn_stack_equal(synstate_T *sp);
392static void validate_current_state(void);
393static int syn_finish_line(int syncing);
394static int syn_current_attr(int syncing, int displaying, int *can_spell, int keep_state);
395static int did_match_already(int idx, garray_T *gap);
396static stateitem_T *push_next_match(stateitem_T *cur_si);
397static void check_state_ends(void);
398static void update_si_attr(int idx);
399static void check_keepend(void);
400static void update_si_end(stateitem_T *sip, int startcol, int force);
401static short *copy_id_list(short *list);
402static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained);
403static int push_current_state(int idx);
404static void pop_current_state(void);
Bram Moolenaarf7512552013-06-06 14:55:19 +0200405#ifdef FEAT_PROFILE
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100406static void syn_clear_time(syn_time_T *tt);
407static void syntime_clear(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200408#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100409static int _RTLENTRYF syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200410#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100411static int syn_compare_syntime(const void *v1, const void *v2);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200412#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100413static void syntime_report(void);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200414static int syn_time_on = FALSE;
415# define IF_SYN_TIME(p) (p)
416#else
417# define IF_SYN_TIME(p) NULL
418typedef int syn_time_T;
419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100421static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf);
422static 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);
423static void clear_syn_state(synstate_T *p);
424static void clear_current_state(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100426static void limit_pos(lpos_T *pos, lpos_T *limit);
427static void limit_pos_zero(lpos_T *pos, lpos_T *limit);
428static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
429static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra);
430static char_u *syn_getcurline(void);
431static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st);
432static int check_keyword_id(char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp);
433static void syn_cmd_case(exarg_T *eap, int syncing);
434static void syn_cmd_spell(exarg_T *eap, int syncing);
435static void syntax_sync_clear(void);
436static void syn_remove_pattern(synblock_T *block, int idx);
437static void syn_clear_pattern(synblock_T *block, int i);
438static void syn_clear_cluster(synblock_T *block, int i);
439static void syn_cmd_clear(exarg_T *eap, int syncing);
440static void syn_cmd_conceal(exarg_T *eap, int syncing);
441static void syn_clear_one(int id, int syncing);
442static void syn_cmd_on(exarg_T *eap, int syncing);
443static void syn_cmd_enable(exarg_T *eap, int syncing);
444static void syn_cmd_reset(exarg_T *eap, int syncing);
445static void syn_cmd_manual(exarg_T *eap, int syncing);
446static void syn_cmd_off(exarg_T *eap, int syncing);
447static void syn_cmd_onoff(exarg_T *eap, char *name);
448static void syn_cmd_list(exarg_T *eap, int syncing);
449static void syn_lines_msg(void);
450static void syn_match_msg(void);
451static void syn_stack_free_block(synblock_T *block);
452static void syn_list_one(int id, int syncing, int link_only);
453static void syn_list_cluster(int id);
454static void put_id_list(char_u *name, short *list, int attr);
455static void put_pattern(char *s, int c, synpat_T *spp, int attr);
456static int syn_list_keywords(int id, hashtab_T *ht, int did_header, int attr);
457static void syn_clear_keyword(int id, hashtab_T *ht);
458static void clear_keywtab(hashtab_T *ht);
459static void add_keyword(char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char);
460static char_u *get_group_name(char_u *arg, char_u **name_end);
461static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char);
462static void syn_cmd_include(exarg_T *eap, int syncing);
463static void syn_cmd_iskeyword(exarg_T *eap, int syncing);
464static void syn_cmd_keyword(exarg_T *eap, int syncing);
465static void syn_cmd_match(exarg_T *eap, int syncing);
466static void syn_cmd_region(exarg_T *eap, int syncing);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000467#ifdef __BORLANDC__
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100468static int _RTLENTRYF syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469#else
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100470static int syn_compare_stub(const void *v1, const void *v2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471#endif
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100472static void syn_cmd_cluster(exarg_T *eap, int syncing);
473static int syn_scl_name2id(char_u *name);
474static int syn_scl_namen2id(char_u *linep, int len);
475static int syn_check_cluster(char_u *pp, int len);
476static int syn_add_cluster(char_u *name);
477static void init_syn_patterns(void);
478static char_u *get_syn_pattern(char_u *arg, synpat_T *ci);
479static void syn_cmd_sync(exarg_T *eap, int syncing);
480static int get_id_list(char_u **arg, int keylen, short **list);
481static void syn_combine_list(short **clstr1, short **clstr2, int list_op);
482static void syn_incl_toplevel(int id, int *flagsp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483
484/*
485 * Start the syntax recognition for a line. This function is normally called
486 * from the screen updating, once for each displayed line.
487 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
488 * it. Careful: curbuf and curwin are likely to point to another buffer and
489 * window.
490 */
491 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100492syntax_start(win_T *wp, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493{
494 synstate_T *p;
495 synstate_T *last_valid = NULL;
496 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000497 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 linenr_T parsed_lnum;
499 linenr_T first_stored;
500 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000501 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200503#ifdef FEAT_CONCEAL
504 current_sub_char = NUL;
505#endif
506
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507 /*
508 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000509 * Also do this when a change was made, the current state may be invalid
510 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200512 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 {
514 invalidate_current_state();
515 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200516 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000518 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 syn_win = wp;
520
521 /*
522 * Allocate syntax stack when needed.
523 */
524 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200525 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000526 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200527 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000528
529 /*
530 * If the state of the end of the previous line is useful, store it.
531 */
532 if (VALID_STATE(&current_state)
533 && current_lnum < lnum
534 && current_lnum < syn_buf->b_ml.ml_line_count)
535 {
536 (void)syn_finish_line(FALSE);
537 if (!current_state_stored)
538 {
539 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000540 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 }
542
543 /*
544 * If the current_lnum is now the same as "lnum", keep the current
545 * state (this happens very often!). Otherwise invalidate
546 * current_state and figure it out below.
547 */
548 if (current_lnum != lnum)
549 invalidate_current_state();
550 }
551 else
552 invalidate_current_state();
553
554 /*
555 * Try to synchronize from a saved state in b_sst_array[].
556 * Only do this if lnum is not before and not to far beyond a saved state.
557 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200558 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000559 {
560 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200561 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 {
563 if (p->sst_lnum > lnum)
564 break;
565 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
566 {
567 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200568 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 last_min_valid = p;
570 }
571 }
572 if (last_min_valid != NULL)
573 load_current_state(last_min_valid);
574 }
575
576 /*
577 * If "lnum" is before or far beyond a line with a saved state, need to
578 * re-synchronize.
579 */
580 if (INVALID_STATE(&current_state))
581 {
582 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200583 if (current_lnum == 1)
584 /* First line is always valid, no matter "minlines". */
585 first_stored = 1;
586 else
587 /* Need to parse "minlines" lines before state can be considered
588 * valid to store. */
589 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000590 }
591 else
592 first_stored = current_lnum;
593
594 /*
595 * Advance from the sync point or saved state until the current line.
596 * Save some entries for syncing with later on.
597 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200598 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000599 dist = 999999;
600 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200601 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602 while (current_lnum < lnum)
603 {
604 syn_start_line();
605 (void)syn_finish_line(FALSE);
606 ++current_lnum;
607
608 /* If we parsed at least "minlines" lines or started at a valid
609 * state, the current state is considered valid. */
610 if (current_lnum >= first_stored)
611 {
612 /* Check if the saved state entry is for the current line and is
613 * equal to the current state. If so, then validate all saved
614 * states that depended on a change before the parsed line. */
615 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000616 prev = syn_stack_find_entry(current_lnum - 1);
617 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200618 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000619 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000620 sp = prev;
621 while (sp != NULL && sp->sst_lnum < current_lnum)
622 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623 if (sp != NULL
624 && sp->sst_lnum == current_lnum
625 && syn_stack_equal(sp))
626 {
627 parsed_lnum = current_lnum;
628 prev = sp;
629 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
630 {
631 if (sp->sst_lnum <= lnum)
632 /* valid state before desired line, use this one */
633 prev = sp;
634 else if (sp->sst_change_lnum == 0)
635 /* past saved states depending on change, break here. */
636 break;
637 sp->sst_change_lnum = 0;
638 sp = sp->sst_next;
639 }
640 load_current_state(prev);
641 }
642 /* Store the state at this line when it's the first one, the line
643 * where we start parsing, or some distance from the previously
644 * saved state. But only when parsed at least 'minlines'. */
645 else if (prev == NULL
646 || current_lnum == lnum
647 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000648 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000649 }
650
651 /* This can take a long time: break when CTRL-C pressed. The current
652 * state will be wrong then. */
653 line_breakcheck();
654 if (got_int)
655 {
656 current_lnum = lnum;
657 break;
658 }
659 }
660
661 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662}
663
664/*
665 * We cannot simply discard growarrays full of state_items or buf_states; we
666 * have to manually release their extmatch pointers first.
667 */
668 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100669clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670{
671 int i;
672 garray_T *gap;
673
674 if (p->sst_stacksize > SST_FIX_STATES)
675 {
676 gap = &(p->sst_union.sst_ga);
677 for (i = 0; i < gap->ga_len; i++)
678 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
679 ga_clear(gap);
680 }
681 else
682 {
683 for (i = 0; i < p->sst_stacksize; i++)
684 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
685 }
686}
687
688/*
689 * Cleanup the current_state stack.
690 */
691 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100692clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000693{
694 int i;
695 stateitem_T *sip;
696
697 sip = (stateitem_T *)(current_state.ga_data);
698 for (i = 0; i < current_state.ga_len; i++)
699 unref_extmatch(sip[i].si_extmatch);
700 ga_clear(&current_state);
701}
702
703/*
704 * Try to find a synchronisation point for line "lnum".
705 *
706 * This sets current_lnum and the current state. One of three methods is
707 * used:
708 * 1. Search backwards for the end of a C-comment.
709 * 2. Search backwards for given sync patterns.
710 * 3. Simply start on a given number of lines above "lnum".
711 */
712 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100713syn_sync(
714 win_T *wp,
715 linenr_T start_lnum,
716 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717{
718 buf_T *curbuf_save;
719 win_T *curwin_save;
720 pos_T cursor_save;
721 int idx;
722 linenr_T lnum;
723 linenr_T end_lnum;
724 linenr_T break_lnum;
725 int had_sync_point;
726 stateitem_T *cur_si;
727 synpat_T *spp;
728 char_u *line;
729 int found_flags = 0;
730 int found_match_idx = 0;
731 linenr_T found_current_lnum = 0;
732 int found_current_col= 0;
733 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000734 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735
736 /*
737 * Clear any current state that might be hanging around.
738 */
739 invalidate_current_state();
740
741 /*
742 * Start at least "minlines" back. Default starting point for parsing is
743 * there.
744 * Start further back, to avoid that scrolling backwards will result in
745 * resyncing for every line. Now it resyncs only one out of N lines,
746 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
747 * Watch out for overflow when minlines is MAXLNUM.
748 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200749 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 start_lnum = 1;
751 else
752 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200753 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000754 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200755 else if (syn_block->b_syn_sync_minlines < 10)
756 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200758 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
759 if (syn_block->b_syn_sync_maxlines != 0
760 && lnum > syn_block->b_syn_sync_maxlines)
761 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 if (lnum >= start_lnum)
763 start_lnum = 1;
764 else
765 start_lnum -= lnum;
766 }
767 current_lnum = start_lnum;
768
769 /*
770 * 1. Search backwards for the end of a C-style comment.
771 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200772 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000773 {
774 /* Need to make syn_buf the current buffer for a moment, to be able to
775 * use find_start_comment(). */
776 curwin_save = curwin;
777 curwin = wp;
778 curbuf_save = curbuf;
779 curbuf = syn_buf;
780
781 /*
782 * Skip lines that end in a backslash.
783 */
784 for ( ; start_lnum > 1; --start_lnum)
785 {
786 line = ml_get(start_lnum - 1);
787 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
788 break;
789 }
790 current_lnum = start_lnum;
791
792 /* set cursor to start of search */
793 cursor_save = wp->w_cursor;
794 wp->w_cursor.lnum = start_lnum;
795 wp->w_cursor.col = 0;
796
797 /*
798 * If the line is inside a comment, need to find the syntax item that
799 * defines the comment.
800 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
801 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200802 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200804 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
805 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
806 == syn_block->b_syn_sync_id
807 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808 {
809 validate_current_state();
810 if (push_current_state(idx) == OK)
811 update_si_attr(current_state.ga_len - 1);
812 break;
813 }
814 }
815
816 /* restore cursor and buffer */
817 wp->w_cursor = cursor_save;
818 curwin = curwin_save;
819 curbuf = curbuf_save;
820 }
821
822 /*
823 * 2. Search backwards for given sync patterns.
824 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200825 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200827 if (syn_block->b_syn_sync_maxlines != 0
828 && start_lnum > syn_block->b_syn_sync_maxlines)
829 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830 else
831 break_lnum = 0;
832
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000833 found_m_endpos.lnum = 0;
834 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000835 end_lnum = start_lnum;
836 lnum = start_lnum;
837 while (--lnum > break_lnum)
838 {
839 /* This can take a long time: break when CTRL-C pressed. */
840 line_breakcheck();
841 if (got_int)
842 {
843 invalidate_current_state();
844 current_lnum = start_lnum;
845 break;
846 }
847
848 /* Check if we have run into a valid saved state stack now. */
849 if (last_valid != NULL && lnum == last_valid->sst_lnum)
850 {
851 load_current_state(last_valid);
852 break;
853 }
854
855 /*
856 * Check if the previous line has the line-continuation pattern.
857 */
858 if (lnum > 1 && syn_match_linecont(lnum - 1))
859 continue;
860
861 /*
862 * Start with nothing on the state stack
863 */
864 validate_current_state();
865
866 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
867 {
868 syn_start_line();
869 for (;;)
870 {
871 had_sync_point = syn_finish_line(TRUE);
872 /*
873 * When a sync point has been found, remember where, and
874 * continue to look for another one, further on in the line.
875 */
876 if (had_sync_point && current_state.ga_len)
877 {
878 cur_si = &CUR_STATE(current_state.ga_len - 1);
879 if (cur_si->si_m_endpos.lnum > start_lnum)
880 {
881 /* ignore match that goes to after where started */
882 current_lnum = end_lnum;
883 break;
884 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000885 if (cur_si->si_idx < 0)
886 {
887 /* Cannot happen? */
888 found_flags = 0;
889 found_match_idx = KEYWORD_IDX;
890 }
891 else
892 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200893 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000894 found_flags = spp->sp_flags;
895 found_match_idx = spp->sp_sync_idx;
896 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 found_current_lnum = current_lnum;
898 found_current_col = current_col;
899 found_m_endpos = cur_si->si_m_endpos;
900 /*
901 * Continue after the match (be aware of a zero-length
902 * match).
903 */
904 if (found_m_endpos.lnum > current_lnum)
905 {
906 current_lnum = found_m_endpos.lnum;
907 current_col = found_m_endpos.col;
908 if (current_lnum >= end_lnum)
909 break;
910 }
911 else if (found_m_endpos.col > current_col)
912 current_col = found_m_endpos.col;
913 else
914 ++current_col;
915
916 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000917 * an item that ends here, need to do that now. Be
918 * careful not to go past the NUL. */
919 prev_current_col = current_col;
920 if (syn_getcurline()[current_col] != NUL)
921 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000923 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000924 }
925 else
926 break;
927 }
928 }
929
930 /*
931 * If a sync point was encountered, break here.
932 */
933 if (found_flags)
934 {
935 /*
936 * Put the item that was specified by the sync point on the
937 * state stack. If there was no item specified, make the
938 * state stack empty.
939 */
940 clear_current_state();
941 if (found_match_idx >= 0
942 && push_current_state(found_match_idx) == OK)
943 update_si_attr(current_state.ga_len - 1);
944
945 /*
946 * When using "grouphere", continue from the sync point
947 * match, until the end of the line. Parsing starts at
948 * the next line.
949 * For "groupthere" the parsing starts at start_lnum.
950 */
951 if (found_flags & HL_SYNC_HERE)
952 {
953 if (current_state.ga_len)
954 {
955 cur_si = &CUR_STATE(current_state.ga_len - 1);
956 cur_si->si_h_startpos.lnum = found_current_lnum;
957 cur_si->si_h_startpos.col = found_current_col;
958 update_si_end(cur_si, (int)current_col, TRUE);
959 check_keepend();
960 }
961 current_col = found_m_endpos.col;
962 current_lnum = found_m_endpos.lnum;
963 (void)syn_finish_line(FALSE);
964 ++current_lnum;
965 }
966 else
967 current_lnum = start_lnum;
968
969 break;
970 }
971
972 end_lnum = lnum;
973 invalidate_current_state();
974 }
975
976 /* Ran into start of the file or exceeded maximum number of lines */
977 if (lnum <= break_lnum)
978 {
979 invalidate_current_state();
980 current_lnum = break_lnum + 1;
981 }
982 }
983
984 validate_current_state();
985}
986
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100987 static void
988save_chartab(char_u *chartab)
989{
990 if (syn_block->b_syn_isk != empty_option)
991 {
992 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
993 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
994 (size_t)32);
995 }
996}
997
998 static void
999restore_chartab(char_u *chartab)
1000{
1001 if (syn_win->w_s->b_syn_isk != empty_option)
1002 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1003}
1004
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005/*
1006 * Return TRUE if the line-continuation pattern matches in line "lnum".
1007 */
1008 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001009syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001010{
1011 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001012 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001013 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001014
Bram Moolenaar860cae12010-06-05 23:22:07 +02001015 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001017 /* use syntax iskeyword option */
1018 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001019 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1020 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001021 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001022 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001023 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001024 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001025 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001026 }
1027 return FALSE;
1028}
1029
1030/*
1031 * Prepare the current state for the start of a line.
1032 */
1033 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001034syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035{
1036 current_finished = FALSE;
1037 current_col = 0;
1038
1039 /*
1040 * Need to update the end of a start/skip/end that continues from the
1041 * previous line and regions that have "keepend".
1042 */
1043 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001044 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001046 check_state_ends();
1047 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048
1049 next_match_idx = -1;
1050 ++current_line_id;
1051}
1052
1053/*
1054 * Check for items in the stack that need their end updated.
1055 * When "startofline" is TRUE the last item is always updated.
1056 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1057 */
1058 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001059syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060{
1061 stateitem_T *cur_si;
1062 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001063 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064
1065 if (startofline)
1066 {
1067 /* Check for a match carried over from a previous line with a
1068 * contained region. The match ends as soon as the region ends. */
1069 for (i = 0; i < current_state.ga_len; ++i)
1070 {
1071 cur_si = &CUR_STATE(i);
1072 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001073 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 == SPTYPE_MATCH
1075 && cur_si->si_m_endpos.lnum < current_lnum)
1076 {
1077 cur_si->si_flags |= HL_MATCHCONT;
1078 cur_si->si_m_endpos.lnum = 0;
1079 cur_si->si_m_endpos.col = 0;
1080 cur_si->si_h_endpos = cur_si->si_m_endpos;
1081 cur_si->si_ends = TRUE;
1082 }
1083 }
1084 }
1085
1086 /*
1087 * Need to update the end of a start/skip/end that continues from the
1088 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001089 * influence contained items. If we've just removed "extend"
1090 * (startofline == 0) then we should update ends of normal regions
1091 * contained inside "keepend" because "extend" could have extended
1092 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093 * Then check for items ending in column 0.
1094 */
1095 i = current_state.ga_len - 1;
1096 if (keepend_level >= 0)
1097 for ( ; i > keepend_level; --i)
1098 if (CUR_STATE(i).si_flags & HL_EXTEND)
1099 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001100
1101 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001102 for ( ; i < current_state.ga_len; ++i)
1103 {
1104 cur_si = &CUR_STATE(i);
1105 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001106 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001107 || (i == current_state.ga_len - 1 && startofline))
1108 {
1109 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1110 cur_si->si_h_startpos.lnum = current_lnum;
1111
1112 if (!(cur_si->si_flags & HL_MATCHCONT))
1113 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001114
1115 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1116 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001117 }
1118 }
1119 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120}
1121
1122/****************************************
1123 * Handling of the state stack cache.
1124 */
1125
1126/*
1127 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1128 *
1129 * To speed up syntax highlighting, the state stack for the start of some
1130 * lines is cached. These entries can be used to start parsing at that point.
1131 *
1132 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1133 * valid entries. b_sst_first points to the first one, then follow sst_next.
1134 * The entries are sorted on line number. The first entry is often for line 2
1135 * (line 1 always starts with an empty stack).
1136 * There is also a list for free entries. This construction is used to avoid
1137 * having to allocate and free memory blocks too often.
1138 *
1139 * When making changes to the buffer, this is logged in b_mod_*. When calling
1140 * update_screen() to update the display, it will call
1141 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1142 * entries. The entries which are inside the changed area are removed,
1143 * because they must be recomputed. Entries below the changed have their line
1144 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1145 * set to indicate that a check must be made if the changed lines would change
1146 * the cached entry.
1147 *
1148 * When later displaying lines, an entry is stored for each line. Displayed
1149 * lines are likely to be displayed again, in which case the state at the
1150 * start of the line is needed.
1151 * For not displayed lines, an entry is stored for every so many lines. These
1152 * entries will be used e.g., when scrolling backwards. The distance between
1153 * entries depends on the number of lines in the buffer. For small buffers
1154 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1155 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1156 */
1157
Bram Moolenaar860cae12010-06-05 23:22:07 +02001158 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001159syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001160{
1161 synstate_T *p;
1162
1163 if (block->b_sst_array != NULL)
1164 {
1165 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1166 clear_syn_state(p);
1167 vim_free(block->b_sst_array);
1168 block->b_sst_array = NULL;
1169 block->b_sst_len = 0;
1170 }
1171}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172/*
1173 * Free b_sst_array[] for buffer "buf".
1174 * Used when syntax items changed to force resyncing everywhere.
1175 */
1176 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001177syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179 win_T *wp;
1180
Bram Moolenaar860cae12010-06-05 23:22:07 +02001181 syn_stack_free_block(block);
1182
1183
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184#ifdef FEAT_FOLDING
1185 /* When using "syntax" fold method, must update all folds. */
1186 FOR_ALL_WINDOWS(wp)
1187 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001188 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 foldUpdateAll(wp);
1190 }
1191#endif
1192}
1193
1194/*
1195 * Allocate the syntax state stack for syn_buf when needed.
1196 * If the number of entries in b_sst_array[] is much too big or a bit too
1197 * small, reallocate it.
1198 * Also used to allocate b_sst_array[] for the first time.
1199 */
1200 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001201syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202{
1203 long len;
1204 synstate_T *to, *from;
1205 synstate_T *sstp;
1206
1207 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1208 if (len < SST_MIN_ENTRIES)
1209 len = SST_MIN_ENTRIES;
1210 else if (len > SST_MAX_ENTRIES)
1211 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001212 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213 {
1214 /* Allocate 50% too much, to avoid reallocating too often. */
1215 len = syn_buf->b_ml.ml_line_count;
1216 len = (len + len / 2) / SST_DIST + Rows * 2;
1217 if (len < SST_MIN_ENTRIES)
1218 len = SST_MIN_ENTRIES;
1219 else if (len > SST_MAX_ENTRIES)
1220 len = SST_MAX_ENTRIES;
1221
Bram Moolenaar860cae12010-06-05 23:22:07 +02001222 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001223 {
1224 /* When shrinking the array, cleanup the existing stack.
1225 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001226 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227 && syn_stack_cleanup())
1228 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1230 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 }
1232
1233 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1234 if (sstp == NULL) /* out of memory! */
1235 return;
1236
1237 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001238 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 {
1240 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 from = from->sst_next)
1243 {
1244 ++to;
1245 *to = *from;
1246 to->sst_next = to + 1;
1247 }
1248 }
1249 if (to != sstp - 1)
1250 {
1251 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 syn_block->b_sst_first = sstp;
1253 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 }
1255 else
1256 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001257 syn_block->b_sst_first = NULL;
1258 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 }
1260
1261 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001262 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001263 while (++to < sstp + len)
1264 to->sst_next = to + 1;
1265 (sstp + len - 1)->sst_next = NULL;
1266
Bram Moolenaar860cae12010-06-05 23:22:07 +02001267 vim_free(syn_block->b_sst_array);
1268 syn_block->b_sst_array = sstp;
1269 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 }
1271}
1272
1273/*
1274 * Check for changes in a buffer to affect stored syntax states. Uses the
1275 * b_mod_* fields.
1276 * Called from update_screen(), before screen is being updated, once for each
1277 * displayed buffer.
1278 */
1279 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001280syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001282 win_T *wp;
1283
1284 syn_stack_apply_changes_block(&buf->b_s, buf);
1285
1286 FOR_ALL_WINDOWS(wp)
1287 {
1288 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1289 syn_stack_apply_changes_block(wp->w_s, buf);
1290 }
1291}
1292
1293 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001294syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001295{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296 synstate_T *p, *prev, *np;
1297 linenr_T n;
1298
Bram Moolenaar860cae12010-06-05 23:22:07 +02001299 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 return;
1301
1302 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001303 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001305 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 {
1307 n = p->sst_lnum + buf->b_mod_xlines;
1308 if (n <= buf->b_mod_bot)
1309 {
1310 /* this state is inside the changed area, remove it */
1311 np = p->sst_next;
1312 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 else
1315 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 p = np;
1318 continue;
1319 }
1320 /* This state is below the changed area. Remember the line
1321 * that needs to be parsed before this entry can be made valid
1322 * again. */
1323 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1324 {
1325 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1326 p->sst_change_lnum += buf->b_mod_xlines;
1327 else
1328 p->sst_change_lnum = buf->b_mod_top;
1329 }
1330 if (p->sst_change_lnum == 0
1331 || p->sst_change_lnum < buf->b_mod_bot)
1332 p->sst_change_lnum = buf->b_mod_bot;
1333
1334 p->sst_lnum = n;
1335 }
1336 prev = p;
1337 p = p->sst_next;
1338 }
1339}
1340
1341/*
1342 * Reduce the number of entries in the state stack for syn_buf.
1343 * Returns TRUE if at least one entry was freed.
1344 */
1345 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001346syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347{
1348 synstate_T *p, *prev;
1349 disptick_T tick;
1350 int above;
1351 int dist;
1352 int retval = FALSE;
1353
Bram Moolenaar860cae12010-06-05 23:22:07 +02001354 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355 return retval;
1356
1357 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001359 dist = 999999;
1360 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001361 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362
1363 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001364 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365 * be removed. Set "above" when the "tick" for the oldest entry is above
1366 * "b_sst_lasttick" (the display tick wraps around).
1367 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001368 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001370 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1372 {
1373 if (prev->sst_lnum + dist > p->sst_lnum)
1374 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 {
1377 if (!above || p->sst_tick < tick)
1378 tick = p->sst_tick;
1379 above = TRUE;
1380 }
1381 else if (!above && p->sst_tick < tick)
1382 tick = p->sst_tick;
1383 }
1384 }
1385
1386 /*
1387 * Go through the list to make the entries for the oldest tick at an
1388 * interval of several lines.
1389 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001390 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1392 {
1393 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1394 {
1395 /* Move this entry from used list to free list */
1396 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001397 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001398 p = prev;
1399 retval = TRUE;
1400 }
1401 }
1402 return retval;
1403}
1404
1405/*
1406 * Free the allocated memory for a syn_state item.
1407 * Move the entry into the free list.
1408 */
1409 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001410syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411{
1412 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001413 p->sst_next = block->b_sst_firstfree;
1414 block->b_sst_firstfree = p;
1415 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001416}
1417
1418/*
1419 * Find an entry in the list of state stacks at or before "lnum".
1420 * Returns NULL when there is no entry or the first entry is after "lnum".
1421 */
1422 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001423syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424{
1425 synstate_T *p, *prev;
1426
1427 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001428 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429 {
1430 if (p->sst_lnum == lnum)
1431 return p;
1432 if (p->sst_lnum > lnum)
1433 break;
1434 }
1435 return prev;
1436}
1437
1438/*
1439 * Try saving the current state in b_sst_array[].
1440 * The current state must be valid for the start of the current_lnum line!
1441 */
1442 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001443store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444{
1445 int i;
1446 synstate_T *p;
1447 bufstate_T *bp;
1448 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001449 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001450
1451 /*
1452 * If the current state contains a start or end pattern that continues
1453 * from the previous line, we can't use it. Don't store it then.
1454 */
1455 for (i = current_state.ga_len - 1; i >= 0; --i)
1456 {
1457 cur_si = &CUR_STATE(i);
1458 if (cur_si->si_h_startpos.lnum >= current_lnum
1459 || cur_si->si_m_endpos.lnum >= current_lnum
1460 || cur_si->si_h_endpos.lnum >= current_lnum
1461 || (cur_si->si_end_idx
1462 && cur_si->si_eoe_pos.lnum >= current_lnum))
1463 break;
1464 }
1465 if (i >= 0)
1466 {
1467 if (sp != NULL)
1468 {
1469 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001470 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001472 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001473 else
1474 {
1475 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001476 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 if (p->sst_next == sp)
1478 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001479 if (p != NULL) /* just in case */
1480 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001482 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 sp = NULL;
1484 }
1485 }
1486 else if (sp == NULL || sp->sst_lnum != current_lnum)
1487 {
1488 /*
1489 * Add a new entry
1490 */
1491 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001492 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 {
1494 (void)syn_stack_cleanup();
1495 /* "sp" may have been moved to the freelist now */
1496 sp = syn_stack_find_entry(current_lnum);
1497 }
1498 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 sp = NULL;
1501 else
1502 {
1503 /* Take the first item from the free list and put it in the used
1504 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001505 p = syn_block->b_sst_firstfree;
1506 syn_block->b_sst_firstfree = p->sst_next;
1507 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 if (sp == NULL)
1509 {
1510 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001511 p->sst_next = syn_block->b_sst_first;
1512 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001513 }
1514 else
1515 {
1516 /* insert in list after *sp */
1517 p->sst_next = sp->sst_next;
1518 sp->sst_next = p;
1519 }
1520 sp = p;
1521 sp->sst_stacksize = 0;
1522 sp->sst_lnum = current_lnum;
1523 }
1524 }
1525 if (sp != NULL)
1526 {
1527 /* When overwriting an existing state stack, clear it first */
1528 clear_syn_state(sp);
1529 sp->sst_stacksize = current_state.ga_len;
1530 if (current_state.ga_len > SST_FIX_STATES)
1531 {
1532 /* Need to clear it, might be something remaining from when the
1533 * length was less than SST_FIX_STATES. */
1534 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1535 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1536 sp->sst_stacksize = 0;
1537 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001538 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1540 }
1541 else
1542 bp = sp->sst_union.sst_stack;
1543 for (i = 0; i < sp->sst_stacksize; ++i)
1544 {
1545 bp[i].bs_idx = CUR_STATE(i).si_idx;
1546 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001547#ifdef FEAT_CONCEAL
1548 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1549 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1550#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1552 }
1553 sp->sst_next_flags = current_next_flags;
1554 sp->sst_next_list = current_next_list;
1555 sp->sst_tick = display_tick;
1556 sp->sst_change_lnum = 0;
1557 }
1558 current_state_stored = TRUE;
1559 return sp;
1560}
1561
1562/*
1563 * Copy a state stack from "from" in b_sst_array[] to current_state;
1564 */
1565 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001566load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567{
1568 int i;
1569 bufstate_T *bp;
1570
1571 clear_current_state();
1572 validate_current_state();
1573 keepend_level = -1;
1574 if (from->sst_stacksize
1575 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1576 {
1577 if (from->sst_stacksize > SST_FIX_STATES)
1578 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1579 else
1580 bp = from->sst_union.sst_stack;
1581 for (i = 0; i < from->sst_stacksize; ++i)
1582 {
1583 CUR_STATE(i).si_idx = bp[i].bs_idx;
1584 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001585#ifdef FEAT_CONCEAL
1586 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1587 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1588#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001589 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1590 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1591 keepend_level = i;
1592 CUR_STATE(i).si_ends = FALSE;
1593 CUR_STATE(i).si_m_lnum = 0;
1594 if (CUR_STATE(i).si_idx >= 0)
1595 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001596 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597 else
1598 CUR_STATE(i).si_next_list = NULL;
1599 update_si_attr(i);
1600 }
1601 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001602 }
1603 current_next_list = from->sst_next_list;
1604 current_next_flags = from->sst_next_flags;
1605 current_lnum = from->sst_lnum;
1606}
1607
1608/*
1609 * Compare saved state stack "*sp" with the current state.
1610 * Return TRUE when they are equal.
1611 */
1612 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001613syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614{
1615 int i, j;
1616 bufstate_T *bp;
1617 reg_extmatch_T *six, *bsx;
1618
1619 /* First a quick check if the stacks have the same size end nextlist. */
1620 if (sp->sst_stacksize == current_state.ga_len
1621 && sp->sst_next_list == current_next_list)
1622 {
1623 /* Need to compare all states on both stacks. */
1624 if (sp->sst_stacksize > SST_FIX_STATES)
1625 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1626 else
1627 bp = sp->sst_union.sst_stack;
1628
1629 for (i = current_state.ga_len; --i >= 0; )
1630 {
1631 /* If the item has another index the state is different. */
1632 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1633 break;
1634 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1635 {
1636 /* When the extmatch pointers are different, the strings in
1637 * them can still be the same. Check if the extmatch
1638 * references are equal. */
1639 bsx = bp[i].bs_extmatch;
1640 six = CUR_STATE(i).si_extmatch;
1641 /* If one of the extmatch pointers is NULL the states are
1642 * different. */
1643 if (bsx == NULL || six == NULL)
1644 break;
1645 for (j = 0; j < NSUBEXP; ++j)
1646 {
1647 /* Check each referenced match string. They must all be
1648 * equal. */
1649 if (bsx->matches[j] != six->matches[j])
1650 {
1651 /* If the pointer is different it can still be the
1652 * same text. Compare the strings, ignore case when
1653 * the start item has the sp_ic flag set. */
1654 if (bsx->matches[j] == NULL
1655 || six->matches[j] == NULL)
1656 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001657 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001658 ? MB_STRICMP(bsx->matches[j],
1659 six->matches[j]) != 0
1660 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1661 break;
1662 }
1663 }
1664 if (j != NSUBEXP)
1665 break;
1666 }
1667 }
1668 if (i < 0)
1669 return TRUE;
1670 }
1671 return FALSE;
1672}
1673
1674/*
1675 * We stop parsing syntax above line "lnum". If the stored state at or below
1676 * this line depended on a change before it, it now depends on the line below
1677 * the last parsed line.
1678 * The window looks like this:
1679 * line which changed
1680 * displayed line
1681 * displayed line
1682 * lnum -> line below window
1683 */
1684 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001685syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001686{
1687 synstate_T *sp;
1688
1689 sp = syn_stack_find_entry(lnum);
1690 if (sp != NULL && sp->sst_lnum < lnum)
1691 sp = sp->sst_next;
1692
1693 if (sp != NULL && sp->sst_change_lnum != 0)
1694 sp->sst_change_lnum = lnum;
1695}
1696
1697/*
1698 * End of handling of the state stack.
1699 ****************************************/
1700
1701 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001702invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703{
1704 clear_current_state();
1705 current_state.ga_itemsize = 0; /* mark current_state invalid */
1706 current_next_list = NULL;
1707 keepend_level = -1;
1708}
1709
1710 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001711validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712{
1713 current_state.ga_itemsize = sizeof(stateitem_T);
1714 current_state.ga_growsize = 3;
1715}
1716
1717/*
1718 * Return TRUE if the syntax at start of lnum changed since last time.
1719 * This will only be called just after get_syntax_attr() for the previous
1720 * line, to check if the next line needs to be redrawn too.
1721 */
1722 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001723syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001724{
1725 int retval = TRUE;
1726 synstate_T *sp;
1727
Bram Moolenaar071d4272004-06-13 20:20:40 +00001728 /*
1729 * Check the state stack when:
1730 * - lnum is just below the previously syntaxed line.
1731 * - lnum is not before the lines with saved states.
1732 * - lnum is not past the lines with saved states.
1733 * - lnum is at or before the last changed line.
1734 */
1735 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1736 {
1737 sp = syn_stack_find_entry(lnum);
1738 if (sp != NULL && sp->sst_lnum == lnum)
1739 {
1740 /*
1741 * finish the previous line (needed when not all of the line was
1742 * drawn)
1743 */
1744 (void)syn_finish_line(FALSE);
1745
1746 /*
1747 * Compare the current state with the previously saved state of
1748 * the line.
1749 */
1750 if (syn_stack_equal(sp))
1751 retval = FALSE;
1752
1753 /*
1754 * Store the current state in b_sst_array[] for later use.
1755 */
1756 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001757 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001758 }
1759 }
1760
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761 return retval;
1762}
1763
1764/*
1765 * Finish the current line.
1766 * This doesn't return any attributes, it only gets the state at the end of
1767 * the line. It can start anywhere in the line, as long as the current state
1768 * is valid.
1769 */
1770 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001771syn_finish_line(
1772 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773{
1774 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001775 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001776
1777 if (!current_finished)
1778 {
1779 while (!current_finished)
1780 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001781 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 /*
1783 * When syncing, and found some item, need to check the item.
1784 */
1785 if (syncing && current_state.ga_len)
1786 {
1787 /*
1788 * Check for match with sync item.
1789 */
1790 cur_si = &CUR_STATE(current_state.ga_len - 1);
1791 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001792 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1794 return TRUE;
1795
1796 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001797 * that ends here, need to do that now. Be careful not to go
1798 * past the NUL. */
1799 prev_current_col = current_col;
1800 if (syn_getcurline()[current_col] != NUL)
1801 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001803 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 }
1805 ++current_col;
1806 }
1807 }
1808 return FALSE;
1809}
1810
1811/*
1812 * Return highlight attributes for next character.
1813 * Must first call syntax_start() once for the line.
1814 * "col" is normally 0 for the first use in a line, and increments by one each
1815 * time. It's allowed to skip characters and to stop before the end of the
1816 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001817 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1818 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 */
1820 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001821get_syntax_attr(
1822 colnr_T col,
1823 int *can_spell,
1824 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001825{
1826 int attr = 0;
1827
Bram Moolenaar349955a2007-08-14 21:07:36 +00001828 if (can_spell != NULL)
1829 /* Default: Only do spelling when there is no @Spell cluster or when
1830 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001831 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1832 ? (syn_block->b_spell_cluster_id == 0)
1833 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001834
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001836 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 return 0;
1838
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001839 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001840 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001841 {
1842 clear_current_state();
1843#ifdef FEAT_EVAL
1844 current_id = 0;
1845 current_trans_id = 0;
1846#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001847#ifdef FEAT_CONCEAL
1848 current_flags = 0;
1849#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001850 return 0;
1851 }
1852
Bram Moolenaar071d4272004-06-13 20:20:40 +00001853 /* Make sure current_state is valid */
1854 if (INVALID_STATE(&current_state))
1855 validate_current_state();
1856
1857 /*
1858 * Skip from the current column to "col", get the attributes for "col".
1859 */
1860 while (current_col <= col)
1861 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001862 attr = syn_current_attr(FALSE, TRUE, can_spell,
1863 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864 ++current_col;
1865 }
1866
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 return attr;
1868}
1869
1870/*
1871 * Get syntax attributes for current_lnum, current_col.
1872 */
1873 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001874syn_current_attr(
1875 int syncing, /* When 1: called for syncing */
1876 int displaying, /* result will be displayed */
1877 int *can_spell, /* return: do spell checking */
1878 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879{
1880 int syn_id;
1881 lpos_T endpos; /* was: char_u *endp; */
1882 lpos_T hl_startpos; /* was: int hl_startcol; */
1883 lpos_T hl_endpos;
1884 lpos_T eos_pos; /* end-of-start match (start region) */
1885 lpos_T eoe_pos; /* end-of-end pattern */
1886 int end_idx; /* group ID for end pattern */
1887 int idx;
1888 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001889 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890 int startcol;
1891 int endcol;
1892 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001893 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 short *next_list;
1895 int found_match; /* found usable match */
1896 static int try_next_column = FALSE; /* must try in next col */
1897 int do_keywords;
1898 regmmatch_T regmatch;
1899 lpos_T pos;
1900 int lc_col;
1901 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001902 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001903 char_u *line; /* current line. NOTE: becomes invalid after
1904 looking for a pattern match! */
1905
1906 /* variables for zero-width matches that have a "nextgroup" argument */
1907 int keep_next_list;
1908 int zero_width_next_list = FALSE;
1909 garray_T zero_width_next_ga;
1910
1911 /*
1912 * No character, no attributes! Past end of line?
1913 * Do try matching with an empty line (could be the start of a region).
1914 */
1915 line = syn_getcurline();
1916 if (line[current_col] == NUL && current_col != 0)
1917 {
1918 /*
1919 * If we found a match after the last column, use it.
1920 */
1921 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1922 && next_match_col != MAXCOL)
1923 (void)push_next_match(NULL);
1924
1925 current_finished = TRUE;
1926 current_state_stored = FALSE;
1927 return 0;
1928 }
1929
1930 /* if the current or next character is NUL, we will finish the line now */
1931 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1932 {
1933 current_finished = TRUE;
1934 current_state_stored = FALSE;
1935 }
1936
1937 /*
1938 * When in the previous column there was a match but it could not be used
1939 * (empty match or already matched in this column) need to try again in
1940 * the next column.
1941 */
1942 if (try_next_column)
1943 {
1944 next_match_idx = -1;
1945 try_next_column = FALSE;
1946 }
1947
1948 /* Only check for keywords when not syncing and there are some. */
1949 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001950 && (syn_block->b_keywtab.ht_used > 0
1951 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952
1953 /* Init the list of zero-width matches with a nextlist. This is used to
1954 * avoid matching the same item in the same position twice. */
1955 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1956
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001957 /* use syntax iskeyword option */
1958 save_chartab(buf_chartab);
1959
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 /*
1961 * Repeat matching keywords and patterns, to find contained items at the
1962 * same column. This stops when there are no extra matches at the current
1963 * column.
1964 */
1965 do
1966 {
1967 found_match = FALSE;
1968 keep_next_list = FALSE;
1969 syn_id = 0;
1970
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001971
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 /*
1973 * 1. Check for a current state.
1974 * Only when there is no current state, or if the current state may
1975 * contain other things, we need to check for keywords and patterns.
1976 * Always need to check for contained items if some item has the
1977 * "containedin" argument (takes extra time!).
1978 */
1979 if (current_state.ga_len)
1980 cur_si = &CUR_STATE(current_state.ga_len - 1);
1981 else
1982 cur_si = NULL;
1983
Bram Moolenaar860cae12010-06-05 23:22:07 +02001984 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 || cur_si->si_cont_list != NULL)
1986 {
1987 /*
1988 * 2. Check for keywords, if on a keyword char after a non-keyword
1989 * char. Don't do this when syncing.
1990 */
1991 if (do_keywords)
1992 {
1993 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001994 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001996 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997#ifdef FEAT_MBYTE
1998 - (has_mbyte
1999 ? (*mb_head_off)(line, line + current_col - 1)
2000 : 0)
2001#endif
2002 , syn_buf)))
2003 {
2004 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002005 &endcol, &flags, &next_list, cur_si,
2006 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002007 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008 {
2009 if (push_current_state(KEYWORD_IDX) == OK)
2010 {
2011 cur_si = &CUR_STATE(current_state.ga_len - 1);
2012 cur_si->si_m_startcol = current_col;
2013 cur_si->si_h_startpos.lnum = current_lnum;
2014 cur_si->si_h_startpos.col = 0; /* starts right away */
2015 cur_si->si_m_endpos.lnum = current_lnum;
2016 cur_si->si_m_endpos.col = endcol;
2017 cur_si->si_h_endpos.lnum = current_lnum;
2018 cur_si->si_h_endpos.col = endcol;
2019 cur_si->si_ends = TRUE;
2020 cur_si->si_end_idx = 0;
2021 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002022#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002023 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002024 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002025 if (current_state.ga_len > 1)
2026 cur_si->si_flags |=
2027 CUR_STATE(current_state.ga_len - 2).si_flags
2028 & HL_CONCEAL;
2029#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 cur_si->si_id = syn_id;
2031 cur_si->si_trans_id = syn_id;
2032 if (flags & HL_TRANSP)
2033 {
2034 if (current_state.ga_len < 2)
2035 {
2036 cur_si->si_attr = 0;
2037 cur_si->si_trans_id = 0;
2038 }
2039 else
2040 {
2041 cur_si->si_attr = CUR_STATE(
2042 current_state.ga_len - 2).si_attr;
2043 cur_si->si_trans_id = CUR_STATE(
2044 current_state.ga_len - 2).si_trans_id;
2045 }
2046 }
2047 else
2048 cur_si->si_attr = syn_id2attr(syn_id);
2049 cur_si->si_cont_list = NULL;
2050 cur_si->si_next_list = next_list;
2051 check_keepend();
2052 }
2053 else
2054 vim_free(next_list);
2055 }
2056 }
2057 }
2058
2059 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002060 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002062 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 {
2064 /*
2065 * If we didn't check for a match yet, or we are past it, check
2066 * for any match with a pattern.
2067 */
2068 if (next_match_idx < 0 || next_match_col < (int)current_col)
2069 {
2070 /*
2071 * Check all relevant patterns for a match at this
2072 * position. This is complicated, because matching with a
2073 * pattern takes quite a bit of time, thus we want to
2074 * avoid doing it when it's not needed.
2075 */
2076 next_match_idx = 0; /* no match in this line yet */
2077 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002078 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002079 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002080 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002081 if ( spp->sp_syncing == syncing
2082 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2083 && (spp->sp_type == SPTYPE_MATCH
2084 || spp->sp_type == SPTYPE_START)
2085 && (current_next_list != NULL
2086 ? in_id_list(NULL, current_next_list,
2087 &spp->sp_syn, 0)
2088 : (cur_si == NULL
2089 ? !(spp->sp_flags & HL_CONTAINED)
2090 : in_id_list(cur_si,
2091 cur_si->si_cont_list, &spp->sp_syn,
2092 spp->sp_flags & HL_CONTAINED))))
2093 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002094 int r;
2095
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 /* If we already tried matching in this line, and
2097 * there isn't a match before next_match_col, skip
2098 * this item. */
2099 if (spp->sp_line_id == current_line_id
2100 && spp->sp_startcol >= next_match_col)
2101 continue;
2102 spp->sp_line_id = current_line_id;
2103
2104 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2105 if (lc_col < 0)
2106 lc_col = 0;
2107
2108 regmatch.rmm_ic = spp->sp_ic;
2109 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002110 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002111 current_lnum,
2112 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002113 IF_SYN_TIME(&spp->sp_time));
2114 spp->sp_prog = regmatch.regprog;
2115 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116 {
2117 /* no match in this line, try another one */
2118 spp->sp_startcol = MAXCOL;
2119 continue;
2120 }
2121
2122 /*
2123 * Compute the first column of the match.
2124 */
2125 syn_add_start_off(&pos, &regmatch,
2126 spp, SPO_MS_OFF, -1);
2127 if (pos.lnum > current_lnum)
2128 {
2129 /* must have used end of match in a next line,
2130 * we can't handle that */
2131 spp->sp_startcol = MAXCOL;
2132 continue;
2133 }
2134 startcol = pos.col;
2135
2136 /* remember the next column where this pattern
2137 * matches in the current line */
2138 spp->sp_startcol = startcol;
2139
2140 /*
2141 * If a previously found match starts at a lower
2142 * column number, don't use this one.
2143 */
2144 if (startcol >= next_match_col)
2145 continue;
2146
2147 /*
2148 * If we matched this pattern at this position
2149 * before, skip it. Must retry in the next
2150 * column, because it may match from there.
2151 */
2152 if (did_match_already(idx, &zero_width_next_ga))
2153 {
2154 try_next_column = TRUE;
2155 continue;
2156 }
2157
2158 endpos.lnum = regmatch.endpos[0].lnum;
2159 endpos.col = regmatch.endpos[0].col;
2160
2161 /* Compute the highlight start. */
2162 syn_add_start_off(&hl_startpos, &regmatch,
2163 spp, SPO_HS_OFF, -1);
2164
2165 /* Compute the region start. */
2166 /* Default is to use the end of the match. */
2167 syn_add_end_off(&eos_pos, &regmatch,
2168 spp, SPO_RS_OFF, 0);
2169
2170 /*
2171 * Grab the external submatches before they get
2172 * overwritten. Reference count doesn't change.
2173 */
2174 unref_extmatch(cur_extmatch);
2175 cur_extmatch = re_extmatch_out;
2176 re_extmatch_out = NULL;
2177
2178 flags = 0;
2179 eoe_pos.lnum = 0; /* avoid warning */
2180 eoe_pos.col = 0;
2181 end_idx = 0;
2182 hl_endpos.lnum = 0;
2183
2184 /*
2185 * For a "oneline" the end must be found in the
2186 * same line too. Search for it after the end of
2187 * the match with the start pattern. Set the
2188 * resulting end positions at the same time.
2189 */
2190 if (spp->sp_type == SPTYPE_START
2191 && (spp->sp_flags & HL_ONELINE))
2192 {
2193 lpos_T startpos;
2194
2195 startpos = endpos;
2196 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2197 &flags, &eoe_pos, &end_idx, cur_extmatch);
2198 if (endpos.lnum == 0)
2199 continue; /* not found */
2200 }
2201
2202 /*
2203 * For a "match" the size must be > 0 after the
2204 * end offset needs has been added. Except when
2205 * syncing.
2206 */
2207 else if (spp->sp_type == SPTYPE_MATCH)
2208 {
2209 syn_add_end_off(&hl_endpos, &regmatch, spp,
2210 SPO_HE_OFF, 0);
2211 syn_add_end_off(&endpos, &regmatch, spp,
2212 SPO_ME_OFF, 0);
2213 if (endpos.lnum == current_lnum
2214 && (int)endpos.col + syncing < startcol)
2215 {
2216 /*
2217 * If an empty string is matched, may need
2218 * to try matching again at next column.
2219 */
2220 if (regmatch.startpos[0].col
2221 == regmatch.endpos[0].col)
2222 try_next_column = TRUE;
2223 continue;
2224 }
2225 }
2226
2227 /*
2228 * keep the best match so far in next_match_*
2229 */
2230 /* Highlighting must start after startpos and end
2231 * before endpos. */
2232 if (hl_startpos.lnum == current_lnum
2233 && (int)hl_startpos.col < startcol)
2234 hl_startpos.col = startcol;
2235 limit_pos_zero(&hl_endpos, &endpos);
2236
2237 next_match_idx = idx;
2238 next_match_col = startcol;
2239 next_match_m_endpos = endpos;
2240 next_match_h_endpos = hl_endpos;
2241 next_match_h_startpos = hl_startpos;
2242 next_match_flags = flags;
2243 next_match_eos_pos = eos_pos;
2244 next_match_eoe_pos = eoe_pos;
2245 next_match_end_idx = end_idx;
2246 unref_extmatch(next_match_extmatch);
2247 next_match_extmatch = cur_extmatch;
2248 cur_extmatch = NULL;
2249 }
2250 }
2251 }
2252
2253 /*
2254 * If we found a match at the current column, use it.
2255 */
2256 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2257 {
2258 synpat_T *lspp;
2259
2260 /* When a zero-width item matched which has a nextgroup,
2261 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002262 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002263 if (next_match_m_endpos.lnum == current_lnum
2264 && next_match_m_endpos.col == current_col
2265 && lspp->sp_next_list != NULL)
2266 {
2267 current_next_list = lspp->sp_next_list;
2268 current_next_flags = lspp->sp_flags;
2269 keep_next_list = TRUE;
2270 zero_width_next_list = TRUE;
2271
2272 /* Add the index to a list, so that we can check
2273 * later that we don't match it again (and cause an
2274 * endless loop). */
2275 if (ga_grow(&zero_width_next_ga, 1) == OK)
2276 {
2277 ((int *)(zero_width_next_ga.ga_data))
2278 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279 }
2280 next_match_idx = -1;
2281 }
2282 else
2283 cur_si = push_next_match(cur_si);
2284 found_match = TRUE;
2285 }
2286 }
2287 }
2288
2289 /*
2290 * Handle searching for nextgroup match.
2291 */
2292 if (current_next_list != NULL && !keep_next_list)
2293 {
2294 /*
2295 * If a nextgroup was not found, continue looking for one if:
2296 * - this is an empty line and the "skipempty" option was given
2297 * - we are on white space and the "skipwhite" option was given
2298 */
2299 if (!found_match)
2300 {
2301 line = syn_getcurline();
2302 if (((current_next_flags & HL_SKIPWHITE)
2303 && vim_iswhite(line[current_col]))
2304 || ((current_next_flags & HL_SKIPEMPTY)
2305 && *line == NUL))
2306 break;
2307 }
2308
2309 /*
2310 * If a nextgroup was found: Use it, and continue looking for
2311 * contained matches.
2312 * If a nextgroup was not found: Continue looking for a normal
2313 * match.
2314 * When did set current_next_list for a zero-width item and no
2315 * match was found don't loop (would get stuck).
2316 */
2317 current_next_list = NULL;
2318 next_match_idx = -1;
2319 if (!zero_width_next_list)
2320 found_match = TRUE;
2321 }
2322
2323 } while (found_match);
2324
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002325 restore_chartab(buf_chartab);
2326
Bram Moolenaar071d4272004-06-13 20:20:40 +00002327 /*
2328 * Use attributes from the current state, if within its highlighting.
2329 * If not, use attributes from the current-but-one state, etc.
2330 */
2331 current_attr = 0;
2332#ifdef FEAT_EVAL
2333 current_id = 0;
2334 current_trans_id = 0;
2335#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002336#ifdef FEAT_CONCEAL
2337 current_flags = 0;
2338#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 if (cur_si != NULL)
2340 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002341#ifndef FEAT_EVAL
2342 int current_trans_id = 0;
2343#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2345 {
2346 sip = &CUR_STATE(idx);
2347 if ((current_lnum > sip->si_h_startpos.lnum
2348 || (current_lnum == sip->si_h_startpos.lnum
2349 && current_col >= sip->si_h_startpos.col))
2350 && (sip->si_h_endpos.lnum == 0
2351 || current_lnum < sip->si_h_endpos.lnum
2352 || (current_lnum == sip->si_h_endpos.lnum
2353 && current_col < sip->si_h_endpos.col)))
2354 {
2355 current_attr = sip->si_attr;
2356#ifdef FEAT_EVAL
2357 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002359 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002360#ifdef FEAT_CONCEAL
2361 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002362 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002363 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002364#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 break;
2366 }
2367 }
2368
Bram Moolenaar217ad922005-03-20 22:37:15 +00002369 if (can_spell != NULL)
2370 {
2371 struct sp_syn sps;
2372
2373 /*
2374 * set "can_spell" to TRUE if spell checking is supposed to be
2375 * done in the current item.
2376 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002377 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002378 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002379 /* There is no @Spell cluster: Do spelling for items without
2380 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002381 if (syn_block->b_nospell_cluster_id == 0
2382 || current_trans_id == 0)
2383 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002384 else
2385 {
2386 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002387 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002388 sps.cont_in_list = NULL;
2389 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2390 }
2391 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002392 else
2393 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002394 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002395 * the @Spell cluster. But not when @NoSpell is also there.
2396 * At the toplevel only spell check when ":syn spell toplevel"
2397 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002398 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002399 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002400 else
2401 {
2402 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002403 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002404 sps.cont_in_list = NULL;
2405 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2406
Bram Moolenaar860cae12010-06-05 23:22:07 +02002407 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002408 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002409 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002410 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2411 *can_spell = FALSE;
2412 }
2413 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002414 }
2415 }
2416
2417
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418 /*
2419 * Check for end of current state (and the states before it) at the
2420 * next column. Don't do this for syncing, because we would miss a
2421 * single character match.
2422 * First check if the current state ends at the current column. It
2423 * may be for an empty match and a containing item might end in the
2424 * current column.
2425 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002426 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002427 {
2428 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002429 if (current_state.ga_len > 0
2430 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 {
2432 ++current_col;
2433 check_state_ends();
2434 --current_col;
2435 }
2436 }
2437 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002438 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002439 /* Default: Only do spelling when there is no @Spell cluster or when
2440 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002441 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2442 ? (syn_block->b_spell_cluster_id == 0)
2443 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002445 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446 if (current_next_list != NULL
2447 && syn_getcurline()[current_col + 1] == NUL
2448 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2449 current_next_list = NULL;
2450
2451 if (zero_width_next_ga.ga_len > 0)
2452 ga_clear(&zero_width_next_ga);
2453
2454 /* No longer need external matches. But keep next_match_extmatch. */
2455 unref_extmatch(re_extmatch_out);
2456 re_extmatch_out = NULL;
2457 unref_extmatch(cur_extmatch);
2458
2459 return current_attr;
2460}
2461
2462
2463/*
2464 * Check if we already matched pattern "idx" at the current column.
2465 */
2466 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002467did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468{
2469 int i;
2470
2471 for (i = current_state.ga_len; --i >= 0; )
2472 if (CUR_STATE(i).si_m_startcol == (int)current_col
2473 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2474 && CUR_STATE(i).si_idx == idx)
2475 return TRUE;
2476
2477 /* Zero-width matches with a nextgroup argument are not put on the syntax
2478 * stack, and can only be matched once anyway. */
2479 for (i = gap->ga_len; --i >= 0; )
2480 if (((int *)(gap->ga_data))[i] == idx)
2481 return TRUE;
2482
2483 return FALSE;
2484}
2485
2486/*
2487 * Push the next match onto the stack.
2488 */
2489 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002490push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491{
2492 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002493#ifdef FEAT_CONCEAL
2494 int save_flags;
2495#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002496
Bram Moolenaar860cae12010-06-05 23:22:07 +02002497 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498
2499 /*
2500 * Push the item in current_state stack;
2501 */
2502 if (push_current_state(next_match_idx) == OK)
2503 {
2504 /*
2505 * If it's a start-skip-end type that crosses lines, figure out how
2506 * much it continues in this line. Otherwise just fill in the length.
2507 */
2508 cur_si = &CUR_STATE(current_state.ga_len - 1);
2509 cur_si->si_h_startpos = next_match_h_startpos;
2510 cur_si->si_m_startcol = current_col;
2511 cur_si->si_m_lnum = current_lnum;
2512 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002513#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002514 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002515 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002516 if (current_state.ga_len > 1)
2517 cur_si->si_flags |=
2518 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2519#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002520 cur_si->si_next_list = spp->sp_next_list;
2521 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2522 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2523 {
2524 /* Try to find the end pattern in the current line */
2525 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2526 check_keepend();
2527 }
2528 else
2529 {
2530 cur_si->si_m_endpos = next_match_m_endpos;
2531 cur_si->si_h_endpos = next_match_h_endpos;
2532 cur_si->si_ends = TRUE;
2533 cur_si->si_flags |= next_match_flags;
2534 cur_si->si_eoe_pos = next_match_eoe_pos;
2535 cur_si->si_end_idx = next_match_end_idx;
2536 }
2537 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2538 keepend_level = current_state.ga_len - 1;
2539 check_keepend();
2540 update_si_attr(current_state.ga_len - 1);
2541
Bram Moolenaar860cae12010-06-05 23:22:07 +02002542#ifdef FEAT_CONCEAL
2543 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2544#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545 /*
2546 * If the start pattern has another highlight group, push another item
2547 * on the stack for the start pattern.
2548 */
2549 if ( spp->sp_type == SPTYPE_START
2550 && spp->sp_syn_match_id != 0
2551 && push_current_state(next_match_idx) == OK)
2552 {
2553 cur_si = &CUR_STATE(current_state.ga_len - 1);
2554 cur_si->si_h_startpos = next_match_h_startpos;
2555 cur_si->si_m_startcol = current_col;
2556 cur_si->si_m_lnum = current_lnum;
2557 cur_si->si_m_endpos = next_match_eos_pos;
2558 cur_si->si_h_endpos = next_match_eos_pos;
2559 cur_si->si_ends = TRUE;
2560 cur_si->si_end_idx = 0;
2561 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002562#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002563 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002564 cur_si->si_flags |= save_flags;
2565 if (cur_si->si_flags & HL_CONCEALENDS)
2566 cur_si->si_flags |= HL_CONCEAL;
2567#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568 cur_si->si_next_list = NULL;
2569 check_keepend();
2570 update_si_attr(current_state.ga_len - 1);
2571 }
2572 }
2573
2574 next_match_idx = -1; /* try other match next time */
2575
2576 return cur_si;
2577}
2578
2579/*
2580 * Check for end of current state (and the states before it).
2581 */
2582 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002583check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584{
2585 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002586 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587
2588 cur_si = &CUR_STATE(current_state.ga_len - 1);
2589 for (;;)
2590 {
2591 if (cur_si->si_ends
2592 && (cur_si->si_m_endpos.lnum < current_lnum
2593 || (cur_si->si_m_endpos.lnum == current_lnum
2594 && cur_si->si_m_endpos.col <= current_col)))
2595 {
2596 /*
2597 * If there is an end pattern group ID, highlight the end pattern
2598 * now. No need to pop the current item from the stack.
2599 * Only do this if the end pattern continues beyond the current
2600 * position.
2601 */
2602 if (cur_si->si_end_idx
2603 && (cur_si->si_eoe_pos.lnum > current_lnum
2604 || (cur_si->si_eoe_pos.lnum == current_lnum
2605 && cur_si->si_eoe_pos.col > current_col)))
2606 {
2607 cur_si->si_idx = cur_si->si_end_idx;
2608 cur_si->si_end_idx = 0;
2609 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2610 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2611 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002612#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002613 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002614 if (cur_si->si_flags & HL_CONCEALENDS)
2615 cur_si->si_flags |= HL_CONCEAL;
2616#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002617 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002618
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002619 /* nextgroup= should not match in the end pattern */
2620 current_next_list = NULL;
2621
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002622 /* what matches next may be different now, clear it */
2623 next_match_idx = 0;
2624 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002625 break;
2626 }
2627 else
2628 {
2629 /* handle next_list, unless at end of line and no "skipnl" or
2630 * "skipempty" */
2631 current_next_list = cur_si->si_next_list;
2632 current_next_flags = cur_si->si_flags;
2633 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2634 && syn_getcurline()[current_col] == NUL)
2635 current_next_list = NULL;
2636
2637 /* When the ended item has "extend", another item with
2638 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002639 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002640
2641 pop_current_state();
2642
2643 if (current_state.ga_len == 0)
2644 break;
2645
Bram Moolenaar81993f42008-01-11 20:27:45 +00002646 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 {
2648 syn_update_ends(FALSE);
2649 if (current_state.ga_len == 0)
2650 break;
2651 }
2652
2653 cur_si = &CUR_STATE(current_state.ga_len - 1);
2654
2655 /*
2656 * Only for a region the search for the end continues after
2657 * the end of the contained item. If the contained match
2658 * included the end-of-line, break here, the region continues.
2659 * Don't do this when:
2660 * - "keepend" is used for the contained item
2661 * - not at the end of the line (could be end="x$"me=e-1).
2662 * - "excludenl" is used (HL_HAS_EOL won't be set)
2663 */
2664 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002665 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 == SPTYPE_START
2667 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2668 {
2669 update_si_end(cur_si, (int)current_col, TRUE);
2670 check_keepend();
2671 if ((current_next_flags & HL_HAS_EOL)
2672 && keepend_level < 0
2673 && syn_getcurline()[current_col] == NUL)
2674 break;
2675 }
2676 }
2677 }
2678 else
2679 break;
2680 }
2681}
2682
2683/*
2684 * Update an entry in the current_state stack for a match or region. This
2685 * fills in si_attr, si_next_list and si_cont_list.
2686 */
2687 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002688update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002689{
2690 stateitem_T *sip = &CUR_STATE(idx);
2691 synpat_T *spp;
2692
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002693 /* This should not happen... */
2694 if (sip->si_idx < 0)
2695 return;
2696
Bram Moolenaar860cae12010-06-05 23:22:07 +02002697 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002698 if (sip->si_flags & HL_MATCH)
2699 sip->si_id = spp->sp_syn_match_id;
2700 else
2701 sip->si_id = spp->sp_syn.id;
2702 sip->si_attr = syn_id2attr(sip->si_id);
2703 sip->si_trans_id = sip->si_id;
2704 if (sip->si_flags & HL_MATCH)
2705 sip->si_cont_list = NULL;
2706 else
2707 sip->si_cont_list = spp->sp_cont_list;
2708
2709 /*
2710 * For transparent items, take attr from outer item.
2711 * Also take cont_list, if there is none.
2712 * Don't do this for the matchgroup of a start or end pattern.
2713 */
2714 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2715 {
2716 if (idx == 0)
2717 {
2718 sip->si_attr = 0;
2719 sip->si_trans_id = 0;
2720 if (sip->si_cont_list == NULL)
2721 sip->si_cont_list = ID_LIST_ALL;
2722 }
2723 else
2724 {
2725 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2726 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002727 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2728 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 if (sip->si_cont_list == NULL)
2730 {
2731 sip->si_flags |= HL_TRANS_CONT;
2732 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2733 }
2734 }
2735 }
2736}
2737
2738/*
2739 * Check the current stack for patterns with "keepend" flag.
2740 * Propagate the match-end to contained items, until a "skipend" item is found.
2741 */
2742 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002743check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002744{
2745 int i;
2746 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002747 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748 stateitem_T *sip;
2749
2750 /*
2751 * This check can consume a lot of time; only do it from the level where
2752 * there really is a keepend.
2753 */
2754 if (keepend_level < 0)
2755 return;
2756
2757 /*
2758 * Find the last index of an "extend" item. "keepend" items before that
2759 * won't do anything. If there is no "extend" item "i" will be
2760 * "keepend_level" and all "keepend" items will work normally.
2761 */
2762 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2763 if (CUR_STATE(i).si_flags & HL_EXTEND)
2764 break;
2765
2766 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002767 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002768 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002769 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 for ( ; i < current_state.ga_len; ++i)
2771 {
2772 sip = &CUR_STATE(i);
2773 if (maxpos.lnum != 0)
2774 {
2775 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002776 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2778 sip->si_ends = TRUE;
2779 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002780 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2781 {
2782 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002783 || maxpos.lnum > sip->si_m_endpos.lnum
2784 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002785 && maxpos.col > sip->si_m_endpos.col))
2786 maxpos = sip->si_m_endpos;
2787 if (maxpos_h.lnum == 0
2788 || maxpos_h.lnum > sip->si_h_endpos.lnum
2789 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2790 && maxpos_h.col > sip->si_h_endpos.col))
2791 maxpos_h = sip->si_h_endpos;
2792 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002793 }
2794}
2795
2796/*
2797 * Update an entry in the current_state stack for a start-skip-end pattern.
2798 * This finds the end of the current item, if it's in the current line.
2799 *
2800 * Return the flags for the matched END.
2801 */
2802 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002803update_si_end(
2804 stateitem_T *sip,
2805 int startcol, /* where to start searching for the end */
2806 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807{
2808 lpos_T startpos;
2809 lpos_T endpos;
2810 lpos_T hl_endpos;
2811 lpos_T end_endpos;
2812 int end_idx;
2813
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002814 /* return quickly for a keyword */
2815 if (sip->si_idx < 0)
2816 return;
2817
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818 /* Don't update when it's already done. Can be a match of an end pattern
2819 * that started in a previous line. Watch out: can also be a "keepend"
2820 * from a containing item. */
2821 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2822 return;
2823
2824 /*
2825 * We need to find the end of the region. It may continue in the next
2826 * line.
2827 */
2828 end_idx = 0;
2829 startpos.lnum = current_lnum;
2830 startpos.col = startcol;
2831 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2832 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2833
2834 if (endpos.lnum == 0)
2835 {
2836 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002837 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838 {
2839 /* a "oneline" never continues in the next line */
2840 sip->si_ends = TRUE;
2841 sip->si_m_endpos.lnum = current_lnum;
2842 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2843 }
2844 else
2845 {
2846 /* continues in the next line */
2847 sip->si_ends = FALSE;
2848 sip->si_m_endpos.lnum = 0;
2849 }
2850 sip->si_h_endpos = sip->si_m_endpos;
2851 }
2852 else
2853 {
2854 /* match within this line */
2855 sip->si_m_endpos = endpos;
2856 sip->si_h_endpos = hl_endpos;
2857 sip->si_eoe_pos = end_endpos;
2858 sip->si_ends = TRUE;
2859 sip->si_end_idx = end_idx;
2860 }
2861}
2862
2863/*
2864 * Add a new state to the current state stack.
2865 * It is cleared and the index set to "idx".
2866 * Return FAIL if it's not possible (out of memory).
2867 */
2868 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002869push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870{
2871 if (ga_grow(&current_state, 1) == FAIL)
2872 return FAIL;
2873 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2874 CUR_STATE(current_state.ga_len).si_idx = idx;
2875 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002876 return OK;
2877}
2878
2879/*
2880 * Remove a state from the current_state stack.
2881 */
2882 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002883pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884{
2885 if (current_state.ga_len)
2886 {
2887 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2888 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889 }
2890 /* after the end of a pattern, try matching a keyword or pattern */
2891 next_match_idx = -1;
2892
2893 /* if first state with "keepend" is popped, reset keepend_level */
2894 if (keepend_level >= current_state.ga_len)
2895 keepend_level = -1;
2896}
2897
2898/*
2899 * Find the end of a start/skip/end syntax region after "startpos".
2900 * Only checks one line.
2901 * Also handles a match item that continued from a previous line.
2902 * If not found, the syntax item continues in the next line. m_endpos->lnum
2903 * will be 0.
2904 * If found, the end of the region and the end of the highlighting is
2905 * computed.
2906 */
2907 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002908find_endpos(
2909 int idx, /* index of the pattern */
2910 lpos_T *startpos, /* where to start looking for an END match */
2911 lpos_T *m_endpos, /* return: end of match */
2912 lpos_T *hl_endpos, /* return: end of highlighting */
2913 long *flagsp, /* return: flags of matching END */
2914 lpos_T *end_endpos, /* return: end of end pattern match */
2915 int *end_idx, /* return: group ID for end pat. match, or 0 */
2916 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002917{
2918 colnr_T matchcol;
2919 synpat_T *spp, *spp_skip;
2920 int start_idx;
2921 int best_idx;
2922 regmmatch_T regmatch;
2923 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2924 lpos_T pos;
2925 char_u *line;
2926 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002927 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002928
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002929 /* just in case we are invoked for a keyword */
2930 if (idx < 0)
2931 return;
2932
Bram Moolenaar071d4272004-06-13 20:20:40 +00002933 /*
2934 * Check for being called with a START pattern.
2935 * Can happen with a match that continues to the next line, because it
2936 * contained a region.
2937 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002938 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 if (spp->sp_type != SPTYPE_START)
2940 {
2941 *hl_endpos = *startpos;
2942 return;
2943 }
2944
2945 /*
2946 * Find the SKIP or first END pattern after the last START pattern.
2947 */
2948 for (;;)
2949 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002950 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002951 if (spp->sp_type != SPTYPE_START)
2952 break;
2953 ++idx;
2954 }
2955
2956 /*
2957 * Lookup the SKIP pattern (if present)
2958 */
2959 if (spp->sp_type == SPTYPE_SKIP)
2960 {
2961 spp_skip = spp;
2962 ++idx;
2963 }
2964 else
2965 spp_skip = NULL;
2966
2967 /* Setup external matches for syn_regexec(). */
2968 unref_extmatch(re_extmatch_in);
2969 re_extmatch_in = ref_extmatch(start_ext);
2970
2971 matchcol = startpos->col; /* start looking for a match at sstart */
2972 start_idx = idx; /* remember the first END pattern. */
2973 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002974
2975 /* use syntax iskeyword option */
2976 save_chartab(buf_chartab);
2977
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 for (;;)
2979 {
2980 /*
2981 * Find end pattern that matches first after "matchcol".
2982 */
2983 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002984 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985 {
2986 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002987 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002988
Bram Moolenaar860cae12010-06-05 23:22:07 +02002989 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2991 break;
2992 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2993 if (lc_col < 0)
2994 lc_col = 0;
2995
2996 regmatch.rmm_ic = spp->sp_ic;
2997 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002998 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2999 IF_SYN_TIME(&spp->sp_time));
3000 spp->sp_prog = regmatch.regprog;
3001 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002 {
3003 if (best_idx == -1 || regmatch.startpos[0].col
3004 < best_regmatch.startpos[0].col)
3005 {
3006 best_idx = idx;
3007 best_regmatch.startpos[0] = regmatch.startpos[0];
3008 best_regmatch.endpos[0] = regmatch.endpos[0];
3009 }
3010 }
3011 }
3012
3013 /*
3014 * If all end patterns have been tried, and there is no match, the
3015 * item continues until end-of-line.
3016 */
3017 if (best_idx == -1)
3018 break;
3019
3020 /*
3021 * If the skip pattern matches before the end pattern,
3022 * continue searching after the skip pattern.
3023 */
3024 if (spp_skip != NULL)
3025 {
3026 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003027 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028
3029 if (lc_col < 0)
3030 lc_col = 0;
3031 regmatch.rmm_ic = spp_skip->sp_ic;
3032 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003033 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3034 IF_SYN_TIME(&spp_skip->sp_time));
3035 spp_skip->sp_prog = regmatch.regprog;
3036 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037 <= best_regmatch.startpos[0].col)
3038 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003039 int line_len;
3040
Bram Moolenaar071d4272004-06-13 20:20:40 +00003041 /* Add offset to skip pattern match */
3042 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3043
3044 /* If the skip pattern goes on to the next line, there is no
3045 * match with an end pattern in this line. */
3046 if (pos.lnum > startpos->lnum)
3047 break;
3048
3049 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003050 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051
3052 /* take care of an empty match or negative offset */
3053 if (pos.col <= matchcol)
3054 ++matchcol;
3055 else if (pos.col <= regmatch.endpos[0].col)
3056 matchcol = pos.col;
3057 else
3058 /* Be careful not to jump over the NUL at the end-of-line */
3059 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003060 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061 ++matchcol)
3062 ;
3063
3064 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003065 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066 break;
3067
3068 continue; /* start with first end pattern again */
3069 }
3070 }
3071
3072 /*
3073 * Match from start pattern to end pattern.
3074 * Correct for match and highlight offset of end pattern.
3075 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003076 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3078 /* can't end before the start */
3079 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3080 m_endpos->col = startpos->col;
3081
3082 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3083 /* can't end before the start */
3084 if (end_endpos->lnum == startpos->lnum
3085 && end_endpos->col < startpos->col)
3086 end_endpos->col = startpos->col;
3087 /* can't end after the match */
3088 limit_pos(end_endpos, m_endpos);
3089
3090 /*
3091 * If the end group is highlighted differently, adjust the pointers.
3092 */
3093 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3094 {
3095 *end_idx = best_idx;
3096 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3097 {
3098 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3099 hl_endpos->col = best_regmatch.endpos[0].col;
3100 }
3101 else
3102 {
3103 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3104 hl_endpos->col = best_regmatch.startpos[0].col;
3105 }
3106 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3107
3108 /* can't end before the start */
3109 if (hl_endpos->lnum == startpos->lnum
3110 && hl_endpos->col < startpos->col)
3111 hl_endpos->col = startpos->col;
3112 limit_pos(hl_endpos, m_endpos);
3113
3114 /* now the match ends where the highlighting ends, it is turned
3115 * into the matchgroup for the end */
3116 *m_endpos = *hl_endpos;
3117 }
3118 else
3119 {
3120 *end_idx = 0;
3121 *hl_endpos = *end_endpos;
3122 }
3123
3124 *flagsp = spp->sp_flags;
3125
3126 had_match = TRUE;
3127 break;
3128 }
3129
3130 /* no match for an END pattern in this line */
3131 if (!had_match)
3132 m_endpos->lnum = 0;
3133
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003134 restore_chartab(buf_chartab);
3135
Bram Moolenaar071d4272004-06-13 20:20:40 +00003136 /* Remove external matches. */
3137 unref_extmatch(re_extmatch_in);
3138 re_extmatch_in = NULL;
3139}
3140
3141/*
3142 * Limit "pos" not to be after "limit".
3143 */
3144 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003145limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146{
3147 if (pos->lnum > limit->lnum)
3148 *pos = *limit;
3149 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3150 pos->col = limit->col;
3151}
3152
3153/*
3154 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3155 */
3156 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003157limit_pos_zero(
3158 lpos_T *pos,
3159 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160{
3161 if (pos->lnum == 0)
3162 *pos = *limit;
3163 else
3164 limit_pos(pos, limit);
3165}
3166
3167/*
3168 * Add offset to matched text for end of match or highlight.
3169 */
3170 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003171syn_add_end_off(
3172 lpos_T *result, /* returned position */
3173 regmmatch_T *regmatch, /* start/end of match */
3174 synpat_T *spp, /* matched pattern */
3175 int idx, /* index of offset */
3176 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177{
3178 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003179 int off;
3180 char_u *base;
3181 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003182
3183 if (spp->sp_off_flags & (1 << idx))
3184 {
3185 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003186 col = regmatch->startpos[0].col;
3187 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188 }
3189 else
3190 {
3191 result->lnum = regmatch->endpos[0].lnum;
3192 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003193 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003195 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3196 * is a matchgroup. Watch out for match with last NL in the buffer. */
3197 if (result->lnum > syn_buf->b_ml.ml_line_count)
3198 col = 0;
3199 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003200 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003201 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3202 p = base + col;
3203 if (off > 0)
3204 {
3205 while (off-- > 0 && *p != NUL)
3206 mb_ptr_adv(p);
3207 }
3208 else if (off < 0)
3209 {
3210 while (off++ < 0 && base < p)
3211 mb_ptr_back(base, p);
3212 }
3213 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003214 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003215 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003216}
3217
3218/*
3219 * Add offset to matched text for start of match or highlight.
3220 * Avoid resulting column to become negative.
3221 */
3222 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003223syn_add_start_off(
3224 lpos_T *result, /* returned position */
3225 regmmatch_T *regmatch, /* start/end of match */
3226 synpat_T *spp,
3227 int idx,
3228 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229{
3230 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003231 int off;
3232 char_u *base;
3233 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003234
3235 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3236 {
3237 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003238 col = regmatch->endpos[0].col;
3239 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 }
3241 else
3242 {
3243 result->lnum = regmatch->startpos[0].lnum;
3244 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003245 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003247 if (result->lnum > syn_buf->b_ml.ml_line_count)
3248 {
3249 /* a "\n" at the end of the pattern may take us below the last line */
3250 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003251 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003252 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003253 if (off != 0)
3254 {
3255 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3256 p = base + col;
3257 if (off > 0)
3258 {
3259 while (off-- && *p != NUL)
3260 mb_ptr_adv(p);
3261 }
3262 else if (off < 0)
3263 {
3264 while (off++ && base < p)
3265 mb_ptr_back(base, p);
3266 }
3267 col = (int)(p - base);
3268 }
3269 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270}
3271
3272/*
3273 * Get current line in syntax buffer.
3274 */
3275 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003276syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277{
3278 return ml_get_buf(syn_buf, current_lnum, FALSE);
3279}
3280
3281/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003282 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283 * Returns TRUE when there is a match.
3284 */
3285 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003286syn_regexec(
3287 regmmatch_T *rmp,
3288 linenr_T lnum,
3289 colnr_T col,
3290 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003292 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003293#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003294 proftime_T pt;
3295
3296 if (syn_time_on)
3297 profile_start(&pt);
3298#endif
3299
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003300 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003301 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3302
Bram Moolenaarf7512552013-06-06 14:55:19 +02003303#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003304 if (syn_time_on)
3305 {
3306 profile_end(&pt);
3307 profile_add(&st->total, &pt);
3308 if (profile_cmp(&pt, &st->slowest) < 0)
3309 st->slowest = pt;
3310 ++st->count;
3311 if (r > 0)
3312 ++st->match;
3313 }
3314#endif
3315
3316 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317 {
3318 rmp->startpos[0].lnum += lnum;
3319 rmp->endpos[0].lnum += lnum;
3320 return TRUE;
3321 }
3322 return FALSE;
3323}
3324
3325/*
3326 * Check one position in a line for a matching keyword.
3327 * The caller must check if a keyword can start at startcol.
3328 * Return it's ID if found, 0 otherwise.
3329 */
3330 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003331check_keyword_id(
3332 char_u *line,
3333 int startcol, /* position in line to check for keyword */
3334 int *endcolp, /* return: character after found keyword */
3335 long *flagsp, /* return: flags of matching keyword */
3336 short **next_listp, /* return: next_list of matching keyword */
3337 stateitem_T *cur_si, /* item at the top of the stack */
3338 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003340 keyentry_T *kp;
3341 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003342 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003343 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003345 hashtab_T *ht;
3346 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347
3348 /* Find first character after the keyword. First character was already
3349 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003350 kwp = line + startcol;
3351 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 do
3353 {
3354#ifdef FEAT_MBYTE
3355 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003356 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 else
3358#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003359 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003361 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003362
Bram Moolenaardad6b692005-01-25 22:14:34 +00003363 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364 return 0;
3365
3366 /*
3367 * Must make a copy of the keyword, so we can add a NUL and make it
3368 * lowercase.
3369 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003370 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371
3372 /*
3373 * Try twice:
3374 * 1. matching case
3375 * 2. ignoring case
3376 */
3377 for (round = 1; round <= 2; ++round)
3378 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003379 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003380 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003382 if (round == 2) /* ignore case */
3383 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384
3385 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003386 * Find keywords that match. There can be several with different
3387 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 * When current_next_list is non-zero accept only that group, otherwise:
3389 * Accept a not-contained keyword at toplevel.
3390 * Accept a keyword at other levels only if it is in the contains list.
3391 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 hi = hash_find(ht, keyword);
3393 if (!HASHITEM_EMPTY(hi))
3394 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003396 if (current_next_list != 0
3397 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3398 : (cur_si == NULL
3399 ? !(kp->flags & HL_CONTAINED)
3400 : in_id_list(cur_si, cur_si->si_cont_list,
3401 &kp->k_syn, kp->flags & HL_CONTAINED)))
3402 {
3403 *endcolp = startcol + kwlen;
3404 *flagsp = kp->flags;
3405 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003406#ifdef FEAT_CONCEAL
3407 *ccharp = kp->k_char;
3408#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003409 return kp->k_syn.id;
3410 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411 }
3412 }
3413 return 0;
3414}
3415
3416/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003417 * Handle ":syntax conceal" command.
3418 */
3419 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003420syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003421{
3422#ifdef FEAT_CONCEAL
3423 char_u *arg = eap->arg;
3424 char_u *next;
3425
3426 eap->nextcmd = find_nextcmd(arg);
3427 if (eap->skip)
3428 return;
3429
3430 next = skiptowhite(arg);
3431 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3432 curwin->w_s->b_syn_conceal = TRUE;
3433 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3434 curwin->w_s->b_syn_conceal = FALSE;
3435 else
3436 EMSG2(_("E390: Illegal argument: %s"), arg);
3437#endif
3438}
3439
3440/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441 * Handle ":syntax case" command.
3442 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003444syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445{
3446 char_u *arg = eap->arg;
3447 char_u *next;
3448
3449 eap->nextcmd = find_nextcmd(arg);
3450 if (eap->skip)
3451 return;
3452
3453 next = skiptowhite(arg);
3454 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003455 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003457 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458 else
3459 EMSG2(_("E390: Illegal argument: %s"), arg);
3460}
3461
3462/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003463 * Handle ":syntax spell" command.
3464 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003465 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003466syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003467{
3468 char_u *arg = eap->arg;
3469 char_u *next;
3470
3471 eap->nextcmd = find_nextcmd(arg);
3472 if (eap->skip)
3473 return;
3474
3475 next = skiptowhite(arg);
3476 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003478 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003480 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003481 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003482 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003483 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003484 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003485 return;
3486 }
3487
3488 /* assume spell checking changed, force a redraw */
3489 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003490}
3491
3492/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003493 * Handle ":syntax iskeyword" command.
3494 */
3495 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003496syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003497{
3498 char_u *arg = eap->arg;
3499 char_u save_chartab[32];
3500 char_u *save_isk;
3501
3502 if (eap->skip)
3503 return;
3504
3505 arg = skipwhite(arg);
3506 if (*arg == NUL)
3507 {
3508 MSG_PUTS("\n");
3509 MSG_PUTS(_("syntax iskeyword "));
3510 if (curwin->w_s->b_syn_isk != empty_option)
3511 msg_outtrans(curwin->w_s->b_syn_isk);
3512 else
3513 msg_outtrans((char_u *)"not set");
3514 }
3515 else
3516 {
3517 if (STRNICMP(arg, "clear", 5) == 0)
3518 {
3519 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3520 (size_t)32);
3521 clear_string_option(&curwin->w_s->b_syn_isk);
3522 }
3523 else
3524 {
3525 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3526 save_isk = curbuf->b_p_isk;
3527 curbuf->b_p_isk = vim_strsave(arg);
3528
3529 buf_init_chartab(curbuf, FALSE);
3530 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3531 (size_t)32);
3532 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3533 clear_string_option(&curwin->w_s->b_syn_isk);
3534 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3535 curbuf->b_p_isk = save_isk;
3536 }
3537 }
3538 redraw_win_later(curwin, NOT_VALID);
3539}
3540
3541/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003542 * Clear all syntax info for one buffer.
3543 */
3544 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003545syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003546{
3547 int i;
3548
Bram Moolenaar860cae12010-06-05 23:22:07 +02003549 block->b_syn_error = FALSE; /* clear previous error */
3550 block->b_syn_ic = FALSE; /* Use case, by default */
3551 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3552 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003553
3554 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003555 clear_keywtab(&block->b_keywtab);
3556 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003557
3558 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3560 syn_clear_pattern(block, i);
3561 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562
3563 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003564 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3565 syn_clear_cluster(block, i);
3566 ga_clear(&block->b_syn_clusters);
3567 block->b_spell_cluster_id = 0;
3568 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569
Bram Moolenaar860cae12010-06-05 23:22:07 +02003570 block->b_syn_sync_flags = 0;
3571 block->b_syn_sync_minlines = 0;
3572 block->b_syn_sync_maxlines = 0;
3573 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574
Bram Moolenaar473de612013-06-08 18:19:48 +02003575 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003576 block->b_syn_linecont_prog = NULL;
3577 vim_free(block->b_syn_linecont_pat);
3578 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003580 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003581#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003582 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583
3584 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003586 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003587
3588 /* Reset the counter for ":syn include" */
3589 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590}
3591
3592/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003593 * Get rid of ownsyntax for window "wp".
3594 */
3595 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003596reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003597{
3598 if (wp->w_s != &wp->w_buffer->b_s)
3599 {
3600 syntax_clear(wp->w_s);
3601 vim_free(wp->w_s);
3602 wp->w_s = &wp->w_buffer->b_s;
3603 }
3604}
3605
3606/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607 * Clear syncing info for one buffer.
3608 */
3609 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003610syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611{
3612 int i;
3613
3614 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003615 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3616 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3617 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618
Bram Moolenaar860cae12010-06-05 23:22:07 +02003619 curwin->w_s->b_syn_sync_flags = 0;
3620 curwin->w_s->b_syn_sync_minlines = 0;
3621 curwin->w_s->b_syn_sync_maxlines = 0;
3622 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623
Bram Moolenaar473de612013-06-08 18:19:48 +02003624 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003625 curwin->w_s->b_syn_linecont_prog = NULL;
3626 vim_free(curwin->w_s->b_syn_linecont_pat);
3627 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003628 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003630 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631}
3632
3633/*
3634 * Remove one pattern from the buffer's pattern list.
3635 */
3636 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003637syn_remove_pattern(
3638 synblock_T *block,
3639 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640{
3641 synpat_T *spp;
3642
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644#ifdef FEAT_FOLDING
3645 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003646 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3651 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652}
3653
3654/*
3655 * Clear and free one syntax pattern. When clearing all, must be called from
3656 * last to first!
3657 */
3658 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003659syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003661 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003662 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003664 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003666 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3667 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3668 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669 }
3670}
3671
3672/*
3673 * Clear and free one syntax cluster.
3674 */
3675 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003676syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003678 vim_free(SYN_CLSTR(block)[i].scl_name);
3679 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3680 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681}
3682
3683/*
3684 * Handle ":syntax clear" command.
3685 */
3686 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003687syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688{
3689 char_u *arg = eap->arg;
3690 char_u *arg_end;
3691 int id;
3692
3693 eap->nextcmd = find_nextcmd(arg);
3694 if (eap->skip)
3695 return;
3696
3697 /*
3698 * We have to disable this within ":syn include @group filename",
3699 * because otherwise @group would get deleted.
3700 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3701 * clear".
3702 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003703 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003704 return;
3705
3706 if (ends_excmd(*arg))
3707 {
3708 /*
3709 * No argument: Clear all syntax items.
3710 */
3711 if (syncing)
3712 syntax_sync_clear();
3713 else
3714 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003715 syntax_clear(curwin->w_s);
3716 if (curwin->w_s == &curwin->w_buffer->b_s)
3717 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003718 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 }
3720 }
3721 else
3722 {
3723 /*
3724 * Clear the group IDs that are in the argument.
3725 */
3726 while (!ends_excmd(*arg))
3727 {
3728 arg_end = skiptowhite(arg);
3729 if (*arg == '@')
3730 {
3731 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3732 if (id == 0)
3733 {
3734 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3735 break;
3736 }
3737 else
3738 {
3739 /*
3740 * We can't physically delete a cluster without changing
3741 * the IDs of other clusters, so we do the next best thing
3742 * and make it empty.
3743 */
3744 short scl_id = id - SYNID_CLUSTER;
3745
Bram Moolenaar860cae12010-06-05 23:22:07 +02003746 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3747 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748 }
3749 }
3750 else
3751 {
3752 id = syn_namen2id(arg, (int)(arg_end - arg));
3753 if (id == 0)
3754 {
3755 EMSG2(_(e_nogroup), arg);
3756 break;
3757 }
3758 else
3759 syn_clear_one(id, syncing);
3760 }
3761 arg = skipwhite(arg_end);
3762 }
3763 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003764 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003765 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003766}
3767
3768/*
3769 * Clear one syntax group for the current buffer.
3770 */
3771 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003772syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003773{
3774 synpat_T *spp;
3775 int idx;
3776
3777 /* Clear keywords only when not ":syn sync clear group-name" */
3778 if (!syncing)
3779 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003780 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3781 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 }
3783
3784 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003785 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003787 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3789 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003790 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003791 }
3792}
3793
3794/*
3795 * Handle ":syntax on" command.
3796 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003798syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003799{
3800 syn_cmd_onoff(eap, "syntax");
3801}
3802
3803/*
3804 * Handle ":syntax enable" command.
3805 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003806 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003807syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003808{
3809 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3810 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003811 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003812}
3813
3814/*
3815 * Handle ":syntax reset" command.
3816 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003818syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819{
3820 eap->nextcmd = check_nextcmd(eap->arg);
3821 if (!eap->skip)
3822 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003823 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3825 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003826 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003827 }
3828}
3829
3830/*
3831 * Handle ":syntax manual" command.
3832 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003834syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835{
3836 syn_cmd_onoff(eap, "manual");
3837}
3838
3839/*
3840 * Handle ":syntax off" command.
3841 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003843syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844{
3845 syn_cmd_onoff(eap, "nosyntax");
3846}
3847
3848 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003849syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850{
3851 char_u buf[100];
3852
3853 eap->nextcmd = check_nextcmd(eap->arg);
3854 if (!eap->skip)
3855 {
3856 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003857 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 do_cmdline_cmd(buf);
3859 }
3860}
3861
3862/*
3863 * Handle ":syntax [list]" command: list current syntax words.
3864 */
3865 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003866syn_cmd_list(
3867 exarg_T *eap,
3868 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869{
3870 char_u *arg = eap->arg;
3871 int id;
3872 char_u *arg_end;
3873
3874 eap->nextcmd = find_nextcmd(arg);
3875 if (eap->skip)
3876 return;
3877
Bram Moolenaar860cae12010-06-05 23:22:07 +02003878 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003879 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003880 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 return;
3882 }
3883
3884 if (syncing)
3885 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003886 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003887 {
3888 MSG_PUTS(_("syncing on C-style comments"));
3889 syn_lines_msg();
3890 syn_match_msg();
3891 return;
3892 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003893 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003895 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 MSG_PUTS(_("no syncing"));
3897 else
3898 {
3899 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003900 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901 MSG_PUTS(_(" lines before top line"));
3902 syn_match_msg();
3903 }
3904 return;
3905 }
3906 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003907 if (curwin->w_s->b_syn_sync_minlines > 0
3908 || curwin->w_s->b_syn_sync_maxlines > 0
3909 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 {
3911 MSG_PUTS(_("\nsyncing on items"));
3912 syn_lines_msg();
3913 syn_match_msg();
3914 }
3915 }
3916 else
3917 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3918 if (ends_excmd(*arg))
3919 {
3920 /*
3921 * No argument: List all group IDs and all syntax clusters.
3922 */
3923 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3924 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003925 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 syn_list_cluster(id);
3927 }
3928 else
3929 {
3930 /*
3931 * List the group IDs and syntax clusters that are in the argument.
3932 */
3933 while (!ends_excmd(*arg) && !got_int)
3934 {
3935 arg_end = skiptowhite(arg);
3936 if (*arg == '@')
3937 {
3938 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3939 if (id == 0)
3940 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3941 else
3942 syn_list_cluster(id - SYNID_CLUSTER);
3943 }
3944 else
3945 {
3946 id = syn_namen2id(arg, (int)(arg_end - arg));
3947 if (id == 0)
3948 EMSG2(_(e_nogroup), arg);
3949 else
3950 syn_list_one(id, syncing, TRUE);
3951 }
3952 arg = skipwhite(arg_end);
3953 }
3954 }
3955 eap->nextcmd = check_nextcmd(arg);
3956}
3957
3958 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003959syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003961 if (curwin->w_s->b_syn_sync_maxlines > 0
3962 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 {
3964 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003965 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003966 {
3967 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003968 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3969 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 MSG_PUTS(", ");
3971 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003972 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 {
3974 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003975 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 }
3977 MSG_PUTS(_(" lines before top line"));
3978 }
3979}
3980
3981 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003982syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003983{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003984 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985 {
3986 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003987 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988 MSG_PUTS(_(" line breaks"));
3989 }
3990}
3991
3992static int last_matchgroup;
3993
3994struct name_list
3995{
3996 int flag;
3997 char *name;
3998};
3999
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004000static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001
4002/*
4003 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4004 */
4005 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004006syn_list_one(
4007 int id,
4008 int syncing, /* when TRUE: list syncing items */
4009 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010{
4011 int attr;
4012 int idx;
4013 int did_header = FALSE;
4014 synpat_T *spp;
4015 static struct name_list namelist1[] =
4016 {
4017 {HL_DISPLAY, "display"},
4018 {HL_CONTAINED, "contained"},
4019 {HL_ONELINE, "oneline"},
4020 {HL_KEEPEND, "keepend"},
4021 {HL_EXTEND, "extend"},
4022 {HL_EXCLUDENL, "excludenl"},
4023 {HL_TRANSP, "transparent"},
4024 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004025#ifdef FEAT_CONCEAL
4026 {HL_CONCEAL, "conceal"},
4027 {HL_CONCEALENDS, "concealends"},
4028#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 {0, NULL}
4030 };
4031 static struct name_list namelist2[] =
4032 {
4033 {HL_SKIPWHITE, "skipwhite"},
4034 {HL_SKIPNL, "skipnl"},
4035 {HL_SKIPEMPTY, "skipempty"},
4036 {0, NULL}
4037 };
4038
4039 attr = hl_attr(HLF_D); /* highlight like directories */
4040
4041 /* list the keywords for "id" */
4042 if (!syncing)
4043 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004044 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4045 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 did_header, attr);
4047 }
4048
4049 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004050 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004052 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4054 continue;
4055
4056 (void)syn_list_header(did_header, 999, id);
4057 did_header = TRUE;
4058 last_matchgroup = 0;
4059 if (spp->sp_type == SPTYPE_MATCH)
4060 {
4061 put_pattern("match", ' ', spp, attr);
4062 msg_putchar(' ');
4063 }
4064 else if (spp->sp_type == SPTYPE_START)
4065 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004066 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4067 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4068 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4069 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4070 while (idx < curwin->w_s->b_syn_patterns.ga_len
4071 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4072 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 --idx;
4074 msg_putchar(' ');
4075 }
4076 syn_list_flags(namelist1, spp->sp_flags, attr);
4077
4078 if (spp->sp_cont_list != NULL)
4079 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4080
4081 if (spp->sp_syn.cont_in_list != NULL)
4082 put_id_list((char_u *)"containedin",
4083 spp->sp_syn.cont_in_list, attr);
4084
4085 if (spp->sp_next_list != NULL)
4086 {
4087 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4088 syn_list_flags(namelist2, spp->sp_flags, attr);
4089 }
4090 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4091 {
4092 if (spp->sp_flags & HL_SYNC_HERE)
4093 msg_puts_attr((char_u *)"grouphere", attr);
4094 else
4095 msg_puts_attr((char_u *)"groupthere", attr);
4096 msg_putchar(' ');
4097 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004098 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4100 else
4101 MSG_PUTS("NONE");
4102 msg_putchar(' ');
4103 }
4104 }
4105
4106 /* list the link, if there is one */
4107 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4108 {
4109 (void)syn_list_header(did_header, 999, id);
4110 msg_puts_attr((char_u *)"links to", attr);
4111 msg_putchar(' ');
4112 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4113 }
4114}
4115
4116 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004117syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004118{
4119 int i;
4120
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004121 for (i = 0; nlist[i].flag != 0; ++i)
4122 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004124 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004125 msg_putchar(' ');
4126 }
4127}
4128
4129/*
4130 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4131 */
4132 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004133syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004134{
4135 int endcol = 15;
4136
4137 /* slight hack: roughly duplicate the guts of syn_list_header() */
4138 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004139 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140
4141 if (msg_col >= endcol) /* output at least one space */
4142 endcol = msg_col + 1;
4143 if (Columns <= endcol) /* avoid hang for tiny window */
4144 endcol = Columns - 1;
4145
4146 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004147 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004149 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 hl_attr(HLF_D));
4151 }
4152 else
4153 {
4154 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4155 msg_puts((char_u *)"=NONE");
4156 }
4157}
4158
4159 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004160put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161{
4162 short *p;
4163
4164 msg_puts_attr(name, attr);
4165 msg_putchar('=');
4166 for (p = list; *p; ++p)
4167 {
4168 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4169 {
4170 if (p[1])
4171 MSG_PUTS("ALLBUT");
4172 else
4173 MSG_PUTS("ALL");
4174 }
4175 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4176 {
4177 MSG_PUTS("TOP");
4178 }
4179 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4180 {
4181 MSG_PUTS("CONTAINED");
4182 }
4183 else if (*p >= SYNID_CLUSTER)
4184 {
4185 short scl_id = *p - SYNID_CLUSTER;
4186
4187 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004188 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 }
4190 else
4191 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4192 if (p[1])
4193 msg_putchar(',');
4194 }
4195 msg_putchar(' ');
4196}
4197
4198 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004199put_pattern(
4200 char *s,
4201 int c,
4202 synpat_T *spp,
4203 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204{
4205 long n;
4206 int mask;
4207 int first;
4208 static char *sepchars = "/+=-#@\"|'^&";
4209 int i;
4210
4211 /* May have to write "matchgroup=group" */
4212 if (last_matchgroup != spp->sp_syn_match_id)
4213 {
4214 last_matchgroup = spp->sp_syn_match_id;
4215 msg_puts_attr((char_u *)"matchgroup", attr);
4216 msg_putchar('=');
4217 if (last_matchgroup == 0)
4218 msg_outtrans((char_u *)"NONE");
4219 else
4220 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4221 msg_putchar(' ');
4222 }
4223
4224 /* output the name of the pattern and an '=' or ' ' */
4225 msg_puts_attr((char_u *)s, attr);
4226 msg_putchar(c);
4227
4228 /* output the pattern, in between a char that is not in the pattern */
4229 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4230 if (sepchars[++i] == NUL)
4231 {
4232 i = 0; /* no good char found, just use the first one */
4233 break;
4234 }
4235 msg_putchar(sepchars[i]);
4236 msg_outtrans(spp->sp_pattern);
4237 msg_putchar(sepchars[i]);
4238
4239 /* output any pattern options */
4240 first = TRUE;
4241 for (i = 0; i < SPO_COUNT; ++i)
4242 {
4243 mask = (1 << i);
4244 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4245 {
4246 if (!first)
4247 msg_putchar(','); /* separate with commas */
4248 msg_puts((char_u *)spo_name_tab[i]);
4249 n = spp->sp_offsets[i];
4250 if (i != SPO_LC_OFF)
4251 {
4252 if (spp->sp_off_flags & mask)
4253 msg_putchar('s');
4254 else
4255 msg_putchar('e');
4256 if (n > 0)
4257 msg_putchar('+');
4258 }
4259 if (n || i == SPO_LC_OFF)
4260 msg_outnum(n);
4261 first = FALSE;
4262 }
4263 }
4264 msg_putchar(' ');
4265}
4266
4267/*
4268 * List or clear the keywords for one syntax group.
4269 * Return TRUE if the header has been printed.
4270 */
4271 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004272syn_list_keywords(
4273 int id,
4274 hashtab_T *ht,
4275 int did_header, /* header has already been printed */
4276 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 hashitem_T *hi;
4280 keyentry_T *kp;
4281 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282 int prev_contained = 0;
4283 short *prev_next_list = NULL;
4284 short *prev_cont_in_list = NULL;
4285 int prev_skipnl = 0;
4286 int prev_skipwhite = 0;
4287 int prev_skipempty = 0;
4288
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289 /*
4290 * Unfortunately, this list of keywords is not sorted on alphabet but on
4291 * hash value...
4292 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004293 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004294 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 --todo;
4299 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004301 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004303 if (prev_contained != (kp->flags & HL_CONTAINED)
4304 || prev_skipnl != (kp->flags & HL_SKIPNL)
4305 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4306 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4307 || prev_cont_in_list != kp->k_syn.cont_in_list
4308 || prev_next_list != kp->next_list)
4309 outlen = 9999;
4310 else
4311 outlen = (int)STRLEN(kp->keyword);
4312 /* output "contained" and "nextgroup" on each line */
4313 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004314 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004315 prev_contained = 0;
4316 prev_next_list = NULL;
4317 prev_cont_in_list = NULL;
4318 prev_skipnl = 0;
4319 prev_skipwhite = 0;
4320 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004322 did_header = TRUE;
4323 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004324 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 put_id_list((char_u *)"containedin",
4332 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 if (kp->next_list != prev_next_list)
4337 {
4338 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4339 msg_putchar(' ');
4340 prev_next_list = kp->next_list;
4341 if (kp->flags & HL_SKIPNL)
4342 {
4343 msg_puts_attr((char_u *)"skipnl", attr);
4344 msg_putchar(' ');
4345 prev_skipnl = (kp->flags & HL_SKIPNL);
4346 }
4347 if (kp->flags & HL_SKIPWHITE)
4348 {
4349 msg_puts_attr((char_u *)"skipwhite", attr);
4350 msg_putchar(' ');
4351 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4352 }
4353 if (kp->flags & HL_SKIPEMPTY)
4354 {
4355 msg_puts_attr((char_u *)"skipempty", attr);
4356 msg_putchar(' ');
4357 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4358 }
4359 }
4360 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 }
4363 }
4364 }
4365
4366 return did_header;
4367}
4368
4369 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004370syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004372 hashitem_T *hi;
4373 keyentry_T *kp;
4374 keyentry_T *kp_prev;
4375 keyentry_T *kp_next;
4376 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377
Bram Moolenaardad6b692005-01-25 22:14:34 +00004378 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004379 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004382 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 --todo;
4385 kp_prev = NULL;
4386 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 if (kp->k_syn.id == id)
4389 {
4390 kp_next = kp->ke_next;
4391 if (kp_prev == NULL)
4392 {
4393 if (kp_next == NULL)
4394 hash_remove(ht, hi);
4395 else
4396 hi->hi_key = KE2HIKEY(kp_next);
4397 }
4398 else
4399 kp_prev->ke_next = kp_next;
4400 vim_free(kp->next_list);
4401 vim_free(kp->k_syn.cont_in_list);
4402 vim_free(kp);
4403 kp = kp_next;
4404 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004406 {
4407 kp_prev = kp;
4408 kp = kp->ke_next;
4409 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410 }
4411 }
4412 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004413 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004414}
4415
4416/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004417 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004418 */
4419 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004420clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004421{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004422 hashitem_T *hi;
4423 int todo;
4424 keyentry_T *kp;
4425 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004426
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004427 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004428 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004430 if (!HASHITEM_EMPTY(hi))
4431 {
4432 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004433 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004435 kp_next = kp->ke_next;
4436 vim_free(kp->next_list);
4437 vim_free(kp->k_syn.cont_in_list);
4438 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004440 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 hash_clear(ht);
4443 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444}
4445
4446/*
4447 * Add a keyword to the list of keywords.
4448 */
4449 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004450add_keyword(
4451 char_u *name, /* name of keyword */
4452 int id, /* group ID for this keyword */
4453 int flags, /* flags for this keyword */
4454 short *cont_in_list, /* containedin for this keyword */
4455 short *next_list, /* nextgroup for this keyword */
4456 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004457{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004458 keyentry_T *kp;
4459 hashtab_T *ht;
4460 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004461 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004462 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004463 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464
Bram Moolenaar860cae12010-06-05 23:22:07 +02004465 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004466 name_ic = str_foldcase(name, (int)STRLEN(name),
4467 name_folded, MAXKEYWLEN + 1);
4468 else
4469 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004470 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4471 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004472 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004473 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004474 kp->k_syn.id = id;
4475 kp->k_syn.inc_tag = current_syn_inc_tag;
4476 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004477 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004478 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004480 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004481 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482
Bram Moolenaar860cae12010-06-05 23:22:07 +02004483 if (curwin->w_s->b_syn_ic)
4484 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004486 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487
Bram Moolenaardad6b692005-01-25 22:14:34 +00004488 hash = hash_hash(kp->keyword);
4489 hi = hash_lookup(ht, kp->keyword, hash);
4490 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004492 /* new keyword, add to hashtable */
4493 kp->ke_next = NULL;
4494 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004496 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 /* keyword already exists, prepend to list */
4499 kp->ke_next = HI2KE(hi);
4500 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502}
4503
4504/*
4505 * Get the start and end of the group name argument.
4506 * Return a pointer to the first argument.
4507 * Return NULL if the end of the command was found instead of further args.
4508 */
4509 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004510get_group_name(
4511 char_u *arg, /* start of the argument */
4512 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513{
4514 char_u *rest;
4515
4516 *name_end = skiptowhite(arg);
4517 rest = skipwhite(*name_end);
4518
4519 /*
4520 * Check if there are enough arguments. The first argument may be a
4521 * pattern, where '|' is allowed, so only check for NUL.
4522 */
4523 if (ends_excmd(*arg) || *rest == NUL)
4524 return NULL;
4525 return rest;
4526}
4527
4528/*
4529 * Check for syntax command option arguments.
4530 * This can be called at any place in the list of arguments, and just picks
4531 * out the arguments that are known. Can be called several times in a row to
4532 * collect all options in between other arguments.
4533 * Return a pointer to the next argument (which isn't an option).
4534 * Return NULL for any error;
4535 */
4536 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004537get_syn_options(
4538 char_u *arg, /* next argument to be checked */
4539 syn_opt_arg_T *opt, /* various things */
4540 int *conceal_char UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542 char_u *gname_start, *gname;
4543 int syn_id;
4544 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004545 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546 int i;
4547 int fidx;
4548 static struct flag
4549 {
4550 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004551 int argtype;
4552 int flags;
4553 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4554 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4555 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4556 {"eExXtTeEnNdD", 0, HL_EXTEND},
4557 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4558 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4559 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4560 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4561 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4562 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4563 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4564 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4565 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004566 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4567 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4568 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004569 {"cCoOnNtTaAiInNsS", 1, 0},
4570 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4571 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004573 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574
4575 if (arg == NULL) /* already detected error */
4576 return NULL;
4577
Bram Moolenaar860cae12010-06-05 23:22:07 +02004578#ifdef FEAT_CONCEAL
4579 if (curwin->w_s->b_syn_conceal)
4580 opt->flags |= HL_CONCEAL;
4581#endif
4582
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583 for (;;)
4584 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004585 /*
4586 * This is used very often when a large number of keywords is defined.
4587 * Need to skip quickly when no option name is found.
4588 * Also avoid tolower(), it's slow.
4589 */
4590 if (strchr(first_letters, *arg) == NULL)
4591 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004592
4593 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4594 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004595 p = flagtab[fidx].name;
4596 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4597 if (arg[len] != p[i] && arg[len] != p[i + 1])
4598 break;
4599 if (p[i] == NUL && (vim_iswhite(arg[len])
4600 || (flagtab[fidx].argtype > 0
4601 ? arg[len] == '='
4602 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004604 if (opt->keyword
4605 && (flagtab[fidx].flags == HL_DISPLAY
4606 || flagtab[fidx].flags == HL_FOLD
4607 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608 /* treat "display", "fold" and "extend" as a keyword */
4609 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 break;
4611 }
4612 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004613 if (fidx < 0) /* no match found */
4614 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004616 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004618 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004619 {
4620 EMSG(_("E395: contains argument not accepted here"));
4621 return NULL;
4622 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004623 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004624 return NULL;
4625 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004626 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004627 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 return NULL;
4630 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004631 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004633 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634 return NULL;
4635 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004636 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4637 {
4638#ifdef FEAT_MBYTE
4639 /* cchar=? */
4640 if (has_mbyte)
4641 {
4642# ifdef FEAT_CONCEAL
4643 *conceal_char = mb_ptr2char(arg + 6);
4644# endif
4645 arg += mb_ptr2len(arg + 6) - 1;
4646 }
4647 else
4648#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004649 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004650#ifdef FEAT_CONCEAL
4651 *conceal_char = arg[6];
4652#else
4653 ;
4654#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004655 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004656#ifdef FEAT_CONCEAL
4657 if (!vim_isprintc_strict(*conceal_char))
4658 {
4659 EMSG(_("E844: invalid cchar value"));
4660 return NULL;
4661 }
4662#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004663 arg = skipwhite(arg + 7);
4664 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004666 {
4667 opt->flags |= flagtab[fidx].flags;
4668 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004669
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004670 if (flagtab[fidx].flags == HL_SYNC_HERE
4671 || flagtab[fidx].flags == HL_SYNC_THERE)
4672 {
4673 if (opt->sync_idx == NULL)
4674 {
4675 EMSG(_("E393: group[t]here not accepted here"));
4676 return NULL;
4677 }
4678 gname_start = arg;
4679 arg = skiptowhite(arg);
4680 if (gname_start == arg)
4681 return NULL;
4682 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4683 if (gname == NULL)
4684 return NULL;
4685 if (STRCMP(gname, "NONE") == 0)
4686 *opt->sync_idx = NONE_IDX;
4687 else
4688 {
4689 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004690 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4691 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4692 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004693 {
4694 *opt->sync_idx = i;
4695 break;
4696 }
4697 if (i < 0)
4698 {
4699 EMSG2(_("E394: Didn't find region item for %s"), gname);
4700 vim_free(gname);
4701 return NULL;
4702 }
4703 }
4704
4705 vim_free(gname);
4706 arg = skipwhite(arg);
4707 }
4708#ifdef FEAT_FOLDING
4709 else if (flagtab[fidx].flags == HL_FOLD
4710 && foldmethodIsSyntax(curwin))
4711 /* Need to update folds later. */
4712 foldUpdateAll(curwin);
4713#endif
4714 }
4715 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716
4717 return arg;
4718}
4719
4720/*
4721 * Adjustments to syntax item when declared in a ":syn include"'d file.
4722 * Set the contained flag, and if the item is not already contained, add it
4723 * to the specified top-level group, if any.
4724 */
4725 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004726syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004727{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004728 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729 return;
4730 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004731 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 {
4733 /* We have to alloc this, because syn_combine_list() will free it. */
4734 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004735 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736
4737 if (grp_list != NULL)
4738 {
4739 grp_list[0] = id;
4740 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004741 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004742 CLUSTER_ADD);
4743 }
4744 }
4745}
4746
4747/*
4748 * Handle ":syntax include [@{group-name}] filename" command.
4749 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004751syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004752{
4753 char_u *arg = eap->arg;
4754 int sgl_id = 1;
4755 char_u *group_name_end;
4756 char_u *rest;
4757 char_u *errormsg = NULL;
4758 int prev_toplvl_grp;
4759 int prev_syn_inc_tag;
4760 int source = FALSE;
4761
4762 eap->nextcmd = find_nextcmd(arg);
4763 if (eap->skip)
4764 return;
4765
4766 if (arg[0] == '@')
4767 {
4768 ++arg;
4769 rest = get_group_name(arg, &group_name_end);
4770 if (rest == NULL)
4771 {
4772 EMSG((char_u *)_("E397: Filename required"));
4773 return;
4774 }
4775 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004776 if (sgl_id == 0)
4777 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004778 /* separate_nextcmd() and expand_filename() depend on this */
4779 eap->arg = rest;
4780 }
4781
4782 /*
4783 * Everything that's left, up to the next command, should be the
4784 * filename to include.
4785 */
4786 eap->argt |= (XFILE | NOSPC);
4787 separate_nextcmd(eap);
4788 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4789 {
4790 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4791 * file. Need to expand the file name first. In other cases
4792 * ":runtime!" is used. */
4793 source = TRUE;
4794 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4795 {
4796 if (errormsg != NULL)
4797 EMSG(errormsg);
4798 return;
4799 }
4800 }
4801
4802 /*
4803 * Save and restore the existing top-level grouplist id and ":syn
4804 * include" tag around the actual inclusion.
4805 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004806 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4807 {
4808 EMSG((char_u *)_("E847: Too many syntax includes"));
4809 return;
4810 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811 prev_syn_inc_tag = current_syn_inc_tag;
4812 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004813 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4814 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004815 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004816 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004818 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004819 current_syn_inc_tag = prev_syn_inc_tag;
4820}
4821
4822/*
4823 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4824 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004826syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827{
4828 char_u *arg = eap->arg;
4829 char_u *group_name_end;
4830 int syn_id;
4831 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004832 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004834 char_u *kw;
4835 syn_opt_arg_T syn_opt_arg;
4836 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004837 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838
4839 rest = get_group_name(arg, &group_name_end);
4840
4841 if (rest != NULL)
4842 {
4843 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004844 if (syn_id != 0)
4845 /* allocate a buffer, for removing backslashes in the keyword */
4846 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004847 if (keyword_copy != NULL)
4848 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004849 syn_opt_arg.flags = 0;
4850 syn_opt_arg.keyword = TRUE;
4851 syn_opt_arg.sync_idx = NULL;
4852 syn_opt_arg.has_cont_list = FALSE;
4853 syn_opt_arg.cont_in_list = NULL;
4854 syn_opt_arg.next_list = NULL;
4855
Bram Moolenaar071d4272004-06-13 20:20:40 +00004856 /*
4857 * The options given apply to ALL keywords, so all options must be
4858 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004859 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004861 cnt = 0;
4862 p = keyword_copy;
4863 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004864 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004865 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004866 if (rest == NULL || ends_excmd(*rest))
4867 break;
4868 /* Copy the keyword, removing backslashes, and add a NUL. */
4869 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004871 if (*rest == '\\' && rest[1] != NUL)
4872 ++rest;
4873 *p++ = *rest++;
4874 }
4875 *p++ = NUL;
4876 ++cnt;
4877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004879 if (!eap->skip)
4880 {
4881 /* Adjust flags for use of ":syn include". */
4882 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4883
4884 /*
4885 * 2: Add an entry for each keyword.
4886 */
4887 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4888 {
4889 for (p = vim_strchr(kw, '['); ; )
4890 {
4891 if (p != NULL)
4892 *p = NUL;
4893 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004894 syn_opt_arg.cont_in_list,
4895 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004896 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004898 if (p[1] == NUL)
4899 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004900 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004901 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004902 }
4903 if (p[1] == ']')
4904 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004905 if (p[2] != NUL)
4906 {
4907 EMSG3(_("E890: trailing char after ']': %s]%s"),
4908 kw, &p[2]);
4909 goto error;
4910 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004911 kw = p + 1; /* skip over the "]" */
4912 break;
4913 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004914#ifdef FEAT_MBYTE
4915 if (has_mbyte)
4916 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004917 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004918
4919 mch_memmove(p, p + 1, l);
4920 p += l;
4921 }
4922 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004924 {
4925 p[0] = p[1];
4926 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 }
4928 }
4929 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004930 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004931error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004933 vim_free(syn_opt_arg.cont_in_list);
4934 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004935 }
4936 }
4937
4938 if (rest != NULL)
4939 eap->nextcmd = check_nextcmd(rest);
4940 else
4941 EMSG2(_(e_invarg2), arg);
4942
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004943 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004944 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004945}
4946
4947/*
4948 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4949 *
4950 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4951 */
4952 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004953syn_cmd_match(
4954 exarg_T *eap,
4955 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004956{
4957 char_u *arg = eap->arg;
4958 char_u *group_name_end;
4959 char_u *rest;
4960 synpat_T item; /* the item found in the line */
4961 int syn_id;
4962 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004963 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004965 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966
4967 /* Isolate the group name, check for validity */
4968 rest = get_group_name(arg, &group_name_end);
4969
4970 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004971 syn_opt_arg.flags = 0;
4972 syn_opt_arg.keyword = FALSE;
4973 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4974 syn_opt_arg.has_cont_list = TRUE;
4975 syn_opt_arg.cont_list = NULL;
4976 syn_opt_arg.cont_in_list = NULL;
4977 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004978 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979
4980 /* get the pattern. */
4981 init_syn_patterns();
4982 vim_memset(&item, 0, sizeof(item));
4983 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004984 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4985 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986
4987 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004988 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004989
4990 if (rest != NULL) /* all arguments are valid */
4991 {
4992 /*
4993 * Check for trailing command and illegal trailing arguments.
4994 */
4995 eap->nextcmd = check_nextcmd(rest);
4996 if (!ends_excmd(*rest) || eap->skip)
4997 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004998 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999 && (syn_id = syn_check_group(arg,
5000 (int)(group_name_end - arg))) != 0)
5001 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005002 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 /*
5004 * Store the pattern in the syn_items list
5005 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005006 idx = curwin->w_s->b_syn_patterns.ga_len;
5007 SYN_ITEMS(curwin->w_s)[idx] = item;
5008 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5009 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5010 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5011 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5012 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5013 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5014 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5015 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005016 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005017#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005018 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005019#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005020 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005021 curwin->w_s->b_syn_containedin = TRUE;
5022 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5023 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005024
5025 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005026 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005027 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005028#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005029 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005030 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031#endif
5032
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005033 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005034 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 return; /* don't free the progs and patterns now */
5036 }
5037 }
5038
5039 /*
5040 * Something failed, free the allocated memory.
5041 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005042 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005043 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005044 vim_free(syn_opt_arg.cont_list);
5045 vim_free(syn_opt_arg.cont_in_list);
5046 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005047
5048 if (rest == NULL)
5049 EMSG2(_(e_invarg2), arg);
5050}
5051
5052/*
5053 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5054 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5055 */
5056 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005057syn_cmd_region(
5058 exarg_T *eap,
5059 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060{
5061 char_u *arg = eap->arg;
5062 char_u *group_name_end;
5063 char_u *rest; /* next arg, NULL on error */
5064 char_u *key_end;
5065 char_u *key = NULL;
5066 char_u *p;
5067 int item;
5068#define ITEM_START 0
5069#define ITEM_SKIP 1
5070#define ITEM_END 2
5071#define ITEM_MATCHGROUP 3
5072 struct pat_ptr
5073 {
5074 synpat_T *pp_synp; /* pointer to syn_pattern */
5075 int pp_matchgroup_id; /* matchgroup ID */
5076 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5077 } *(pat_ptrs[3]);
5078 /* patterns found in the line */
5079 struct pat_ptr *ppp;
5080 struct pat_ptr *ppp_next;
5081 int pat_count = 0; /* nr of syn_patterns found */
5082 int syn_id;
5083 int matchgroup_id = 0;
5084 int not_enough = FALSE; /* not enough arguments */
5085 int illegal = FALSE; /* illegal arguments */
5086 int success = FALSE;
5087 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005088 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005089 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005090
5091 /* Isolate the group name, check for validity */
5092 rest = get_group_name(arg, &group_name_end);
5093
5094 pat_ptrs[0] = NULL;
5095 pat_ptrs[1] = NULL;
5096 pat_ptrs[2] = NULL;
5097
5098 init_syn_patterns();
5099
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005100 syn_opt_arg.flags = 0;
5101 syn_opt_arg.keyword = FALSE;
5102 syn_opt_arg.sync_idx = NULL;
5103 syn_opt_arg.has_cont_list = TRUE;
5104 syn_opt_arg.cont_list = NULL;
5105 syn_opt_arg.cont_in_list = NULL;
5106 syn_opt_arg.next_list = NULL;
5107
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108 /*
5109 * get the options, patterns and matchgroup.
5110 */
5111 while (rest != NULL && !ends_excmd(*rest))
5112 {
5113 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005114 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 if (rest == NULL || ends_excmd(*rest))
5116 break;
5117
5118 /* must be a pattern or matchgroup then */
5119 key_end = rest;
5120 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5121 ++key_end;
5122 vim_free(key);
5123 key = vim_strnsave_up(rest, (int)(key_end - rest));
5124 if (key == NULL) /* out of memory */
5125 {
5126 rest = NULL;
5127 break;
5128 }
5129 if (STRCMP(key, "MATCHGROUP") == 0)
5130 item = ITEM_MATCHGROUP;
5131 else if (STRCMP(key, "START") == 0)
5132 item = ITEM_START;
5133 else if (STRCMP(key, "END") == 0)
5134 item = ITEM_END;
5135 else if (STRCMP(key, "SKIP") == 0)
5136 {
5137 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5138 {
5139 illegal = TRUE;
5140 break;
5141 }
5142 item = ITEM_SKIP;
5143 }
5144 else
5145 break;
5146 rest = skipwhite(key_end);
5147 if (*rest != '=')
5148 {
5149 rest = NULL;
5150 EMSG2(_("E398: Missing '=': %s"), arg);
5151 break;
5152 }
5153 rest = skipwhite(rest + 1);
5154 if (*rest == NUL)
5155 {
5156 not_enough = TRUE;
5157 break;
5158 }
5159
5160 if (item == ITEM_MATCHGROUP)
5161 {
5162 p = skiptowhite(rest);
5163 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5164 matchgroup_id = 0;
5165 else
5166 {
5167 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5168 if (matchgroup_id == 0)
5169 {
5170 illegal = TRUE;
5171 break;
5172 }
5173 }
5174 rest = skipwhite(p);
5175 }
5176 else
5177 {
5178 /*
5179 * Allocate room for a syn_pattern, and link it in the list of
5180 * syn_patterns for this item, at the start (because the list is
5181 * used from end to start).
5182 */
5183 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5184 if (ppp == NULL)
5185 {
5186 rest = NULL;
5187 break;
5188 }
5189 ppp->pp_next = pat_ptrs[item];
5190 pat_ptrs[item] = ppp;
5191 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5192 if (ppp->pp_synp == NULL)
5193 {
5194 rest = NULL;
5195 break;
5196 }
5197
5198 /*
5199 * Get the syntax pattern and the following offset(s).
5200 */
5201 /* Enable the appropriate \z specials. */
5202 if (item == ITEM_START)
5203 reg_do_extmatch = REX_SET;
5204 else if (item == ITEM_SKIP || item == ITEM_END)
5205 reg_do_extmatch = REX_USE;
5206 rest = get_syn_pattern(rest, ppp->pp_synp);
5207 reg_do_extmatch = 0;
5208 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005209 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005210 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5211 ppp->pp_matchgroup_id = matchgroup_id;
5212 ++pat_count;
5213 }
5214 }
5215 vim_free(key);
5216 if (illegal || not_enough)
5217 rest = NULL;
5218
5219 /*
5220 * Must have a "start" and "end" pattern.
5221 */
5222 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5223 pat_ptrs[ITEM_END] == NULL))
5224 {
5225 not_enough = TRUE;
5226 rest = NULL;
5227 }
5228
5229 if (rest != NULL)
5230 {
5231 /*
5232 * Check for trailing garbage or command.
5233 * If OK, add the item.
5234 */
5235 eap->nextcmd = check_nextcmd(rest);
5236 if (!ends_excmd(*rest) || eap->skip)
5237 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005238 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005239 && (syn_id = syn_check_group(arg,
5240 (int)(group_name_end - arg))) != 0)
5241 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005242 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243 /*
5244 * Store the start/skip/end in the syn_items list
5245 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005246 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005247 for (item = ITEM_START; item <= ITEM_END; ++item)
5248 {
5249 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5250 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005251 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5252 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5253 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005254 (item == ITEM_START) ? SPTYPE_START :
5255 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005256 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5257 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005258 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5259 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005260 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005261 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005262#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005263 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005264#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265 if (item == ITEM_START)
5266 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005267 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005268 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005269 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005270 syn_opt_arg.cont_in_list;
5271 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005272 curwin->w_s->b_syn_containedin = TRUE;
5273 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005274 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005276 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005277 ++idx;
5278#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005279 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005280 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005281#endif
5282 }
5283 }
5284
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005285 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005286 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287 success = TRUE; /* don't free the progs and patterns now */
5288 }
5289 }
5290
5291 /*
5292 * Free the allocated memory.
5293 */
5294 for (item = ITEM_START; item <= ITEM_END; ++item)
5295 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5296 {
5297 if (!success)
5298 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005299 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005300 vim_free(ppp->pp_synp->sp_pattern);
5301 }
5302 vim_free(ppp->pp_synp);
5303 ppp_next = ppp->pp_next;
5304 vim_free(ppp);
5305 }
5306
5307 if (!success)
5308 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005309 vim_free(syn_opt_arg.cont_list);
5310 vim_free(syn_opt_arg.cont_in_list);
5311 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005312 if (not_enough)
5313 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5314 else if (illegal || rest == NULL)
5315 EMSG2(_(e_invarg2), arg);
5316 }
5317}
5318
5319/*
5320 * A simple syntax group ID comparison function suitable for use in qsort()
5321 */
5322 static int
5323#ifdef __BORLANDC__
5324_RTLENTRYF
5325#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005326syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005327{
5328 const short *s1 = v1;
5329 const short *s2 = v2;
5330
5331 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5332}
5333
5334/*
5335 * Combines lists of syntax clusters.
5336 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5337 */
5338 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005339syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005340{
5341 int count1 = 0;
5342 int count2 = 0;
5343 short *g1;
5344 short *g2;
5345 short *clstr = NULL;
5346 int count;
5347 int round;
5348
5349 /*
5350 * Handle degenerate cases.
5351 */
5352 if (*clstr2 == NULL)
5353 return;
5354 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5355 {
5356 if (list_op == CLUSTER_REPLACE)
5357 vim_free(*clstr1);
5358 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5359 *clstr1 = *clstr2;
5360 else
5361 vim_free(*clstr2);
5362 return;
5363 }
5364
5365 for (g1 = *clstr1; *g1; g1++)
5366 ++count1;
5367 for (g2 = *clstr2; *g2; g2++)
5368 ++count2;
5369
5370 /*
5371 * For speed purposes, sort both lists.
5372 */
5373 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5374 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5375
5376 /*
5377 * We proceed in two passes; in round 1, we count the elements to place
5378 * in the new list, and in round 2, we allocate and populate the new
5379 * list. For speed, we use a mergesort-like method, adding the smaller
5380 * of the current elements in each list to the new list.
5381 */
5382 for (round = 1; round <= 2; round++)
5383 {
5384 g1 = *clstr1;
5385 g2 = *clstr2;
5386 count = 0;
5387
5388 /*
5389 * First, loop through the lists until one of them is empty.
5390 */
5391 while (*g1 && *g2)
5392 {
5393 /*
5394 * We always want to add from the first list.
5395 */
5396 if (*g1 < *g2)
5397 {
5398 if (round == 2)
5399 clstr[count] = *g1;
5400 count++;
5401 g1++;
5402 continue;
5403 }
5404 /*
5405 * We only want to add from the second list if we're adding the
5406 * lists.
5407 */
5408 if (list_op == CLUSTER_ADD)
5409 {
5410 if (round == 2)
5411 clstr[count] = *g2;
5412 count++;
5413 }
5414 if (*g1 == *g2)
5415 g1++;
5416 g2++;
5417 }
5418
5419 /*
5420 * Now add the leftovers from whichever list didn't get finished
5421 * first. As before, we only want to add from the second list if
5422 * we're adding the lists.
5423 */
5424 for (; *g1; g1++, count++)
5425 if (round == 2)
5426 clstr[count] = *g1;
5427 if (list_op == CLUSTER_ADD)
5428 for (; *g2; g2++, count++)
5429 if (round == 2)
5430 clstr[count] = *g2;
5431
5432 if (round == 1)
5433 {
5434 /*
5435 * If the group ended up empty, we don't need to allocate any
5436 * space for it.
5437 */
5438 if (count == 0)
5439 {
5440 clstr = NULL;
5441 break;
5442 }
5443 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5444 if (clstr == NULL)
5445 break;
5446 clstr[count] = 0;
5447 }
5448 }
5449
5450 /*
5451 * Finally, put the new list in place.
5452 */
5453 vim_free(*clstr1);
5454 vim_free(*clstr2);
5455 *clstr1 = clstr;
5456}
5457
5458/*
5459 * Lookup a syntax cluster name and return it's ID.
5460 * If it is not found, 0 is returned.
5461 */
5462 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005463syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464{
5465 int i;
5466 char_u *name_u;
5467
5468 /* Avoid using stricmp() too much, it's slow on some systems */
5469 name_u = vim_strsave_up(name);
5470 if (name_u == NULL)
5471 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005472 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5473 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5474 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475 break;
5476 vim_free(name_u);
5477 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5478}
5479
5480/*
5481 * Like syn_scl_name2id(), but take a pointer + length argument.
5482 */
5483 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005484syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005485{
5486 char_u *name;
5487 int id = 0;
5488
5489 name = vim_strnsave(linep, len);
5490 if (name != NULL)
5491 {
5492 id = syn_scl_name2id(name);
5493 vim_free(name);
5494 }
5495 return id;
5496}
5497
5498/*
5499 * Find syntax cluster name in the table and return it's ID.
5500 * The argument is a pointer to the name and the length of the name.
5501 * If it doesn't exist yet, a new entry is created.
5502 * Return 0 for failure.
5503 */
5504 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005505syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005506{
5507 int id;
5508 char_u *name;
5509
5510 name = vim_strnsave(pp, len);
5511 if (name == NULL)
5512 return 0;
5513
5514 id = syn_scl_name2id(name);
5515 if (id == 0) /* doesn't exist yet */
5516 id = syn_add_cluster(name);
5517 else
5518 vim_free(name);
5519 return id;
5520}
5521
5522/*
5523 * Add new syntax cluster and return it's ID.
5524 * "name" must be an allocated string, it will be consumed.
5525 * Return 0 for failure.
5526 */
5527 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005528syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005529{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005530 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531
5532 /*
5533 * First call for this growarray: init growing array.
5534 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005535 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005537 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5538 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 }
5540
Bram Moolenaar42431a72011-04-01 14:44:59 +02005541 len = curwin->w_s->b_syn_clusters.ga_len;
5542 if (len >= MAX_CLUSTER_ID)
5543 {
5544 EMSG((char_u *)_("E848: Too many syntax clusters"));
5545 vim_free(name);
5546 return 0;
5547 }
5548
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549 /*
5550 * Make room for at least one other cluster entry.
5551 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005552 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553 {
5554 vim_free(name);
5555 return 0;
5556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005557
Bram Moolenaar860cae12010-06-05 23:22:07 +02005558 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5559 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5560 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5561 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5562 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563
Bram Moolenaar217ad922005-03-20 22:37:15 +00005564 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005565 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005566 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005567 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005568
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569 return len + SYNID_CLUSTER;
5570}
5571
5572/*
5573 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5574 * [add={groupname},..] [remove={groupname},..]".
5575 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005576 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005577syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578{
5579 char_u *arg = eap->arg;
5580 char_u *group_name_end;
5581 char_u *rest;
5582 int scl_id;
5583 short *clstr_list;
5584 int got_clstr = FALSE;
5585 int opt_len;
5586 int list_op;
5587
5588 eap->nextcmd = find_nextcmd(arg);
5589 if (eap->skip)
5590 return;
5591
5592 rest = get_group_name(arg, &group_name_end);
5593
5594 if (rest != NULL)
5595 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005596 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5597 if (scl_id == 0)
5598 return;
5599 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600
5601 for (;;)
5602 {
5603 if (STRNICMP(rest, "add", 3) == 0
5604 && (vim_iswhite(rest[3]) || rest[3] == '='))
5605 {
5606 opt_len = 3;
5607 list_op = CLUSTER_ADD;
5608 }
5609 else if (STRNICMP(rest, "remove", 6) == 0
5610 && (vim_iswhite(rest[6]) || rest[6] == '='))
5611 {
5612 opt_len = 6;
5613 list_op = CLUSTER_SUBTRACT;
5614 }
5615 else if (STRNICMP(rest, "contains", 8) == 0
5616 && (vim_iswhite(rest[8]) || rest[8] == '='))
5617 {
5618 opt_len = 8;
5619 list_op = CLUSTER_REPLACE;
5620 }
5621 else
5622 break;
5623
5624 clstr_list = NULL;
5625 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5626 {
5627 EMSG2(_(e_invarg2), rest);
5628 break;
5629 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005630 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005631 &clstr_list, list_op);
5632 got_clstr = TRUE;
5633 }
5634
5635 if (got_clstr)
5636 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005637 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005638 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639 }
5640 }
5641
5642 if (!got_clstr)
5643 EMSG(_("E400: No cluster specified"));
5644 if (rest == NULL || !ends_excmd(*rest))
5645 EMSG2(_(e_invarg2), arg);
5646}
5647
5648/*
5649 * On first call for current buffer: Init growing array.
5650 */
5651 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005652init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005653{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005654 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5655 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005656}
5657
5658/*
5659 * Get one pattern for a ":syntax match" or ":syntax region" command.
5660 * Stores the pattern and program in a synpat_T.
5661 * Returns a pointer to the next argument, or NULL in case of an error.
5662 */
5663 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005664get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665{
5666 char_u *end;
5667 int *p;
5668 int idx;
5669 char_u *cpo_save;
5670
5671 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005672 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005673 return NULL;
5674
5675 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5676 if (*end != *arg) /* end delimiter not found */
5677 {
5678 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5679 return NULL;
5680 }
5681 /* store the pattern and compiled regexp program */
5682 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5683 return NULL;
5684
5685 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5686 cpo_save = p_cpo;
5687 p_cpo = (char_u *)"";
5688 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5689 p_cpo = cpo_save;
5690
5691 if (ci->sp_prog == NULL)
5692 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005693 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005694#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005695 syn_clear_time(&ci->sp_time);
5696#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005697
5698 /*
5699 * Check for a match, highlight or region offset.
5700 */
5701 ++end;
5702 do
5703 {
5704 for (idx = SPO_COUNT; --idx >= 0; )
5705 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5706 break;
5707 if (idx >= 0)
5708 {
5709 p = &(ci->sp_offsets[idx]);
5710 if (idx != SPO_LC_OFF)
5711 switch (end[3])
5712 {
5713 case 's': break;
5714 case 'b': break;
5715 case 'e': idx += SPO_COUNT; break;
5716 default: idx = -1; break;
5717 }
5718 if (idx >= 0)
5719 {
5720 ci->sp_off_flags |= (1 << idx);
5721 if (idx == SPO_LC_OFF) /* lc=99 */
5722 {
5723 end += 3;
5724 *p = getdigits(&end);
5725
5726 /* "lc=" offset automatically sets "ms=" offset */
5727 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5728 {
5729 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5730 ci->sp_offsets[SPO_MS_OFF] = *p;
5731 }
5732 }
5733 else /* yy=x+99 */
5734 {
5735 end += 4;
5736 if (*end == '+')
5737 {
5738 ++end;
5739 *p = getdigits(&end); /* positive offset */
5740 }
5741 else if (*end == '-')
5742 {
5743 ++end;
5744 *p = -getdigits(&end); /* negative offset */
5745 }
5746 }
5747 if (*end != ',')
5748 break;
5749 ++end;
5750 }
5751 }
5752 } while (idx >= 0);
5753
5754 if (!ends_excmd(*end) && !vim_iswhite(*end))
5755 {
5756 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5757 return NULL;
5758 }
5759 return skipwhite(end);
5760}
5761
5762/*
5763 * Handle ":syntax sync .." command.
5764 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005766syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005767{
5768 char_u *arg_start = eap->arg;
5769 char_u *arg_end;
5770 char_u *key = NULL;
5771 char_u *next_arg;
5772 int illegal = FALSE;
5773 int finished = FALSE;
5774 long n;
5775 char_u *cpo_save;
5776
5777 if (ends_excmd(*arg_start))
5778 {
5779 syn_cmd_list(eap, TRUE);
5780 return;
5781 }
5782
5783 while (!ends_excmd(*arg_start))
5784 {
5785 arg_end = skiptowhite(arg_start);
5786 next_arg = skipwhite(arg_end);
5787 vim_free(key);
5788 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5789 if (STRCMP(key, "CCOMMENT") == 0)
5790 {
5791 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005792 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 if (!ends_excmd(*next_arg))
5794 {
5795 arg_end = skiptowhite(next_arg);
5796 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005797 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 (int)(arg_end - next_arg));
5799 next_arg = skipwhite(arg_end);
5800 }
5801 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005802 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803 }
5804 else if ( STRNCMP(key, "LINES", 5) == 0
5805 || STRNCMP(key, "MINLINES", 8) == 0
5806 || STRNCMP(key, "MAXLINES", 8) == 0
5807 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5808 {
5809 if (key[4] == 'S')
5810 arg_end = key + 6;
5811 else if (key[0] == 'L')
5812 arg_end = key + 11;
5813 else
5814 arg_end = key + 9;
5815 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5816 {
5817 illegal = TRUE;
5818 break;
5819 }
5820 n = getdigits(&arg_end);
5821 if (!eap->skip)
5822 {
5823 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005824 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005825 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005826 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005827 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005828 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 }
5830 }
5831 else if (STRCMP(key, "FROMSTART") == 0)
5832 {
5833 if (!eap->skip)
5834 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005835 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5836 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005837 }
5838 }
5839 else if (STRCMP(key, "LINECONT") == 0)
5840 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005841 if (*next_arg == NUL) /* missing pattern */
5842 {
5843 illegal = TRUE;
5844 break;
5845 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005846 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005847 {
5848 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5849 finished = TRUE;
5850 break;
5851 }
5852 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5853 if (*arg_end != *next_arg) /* end delimiter not found */
5854 {
5855 illegal = TRUE;
5856 break;
5857 }
5858
5859 if (!eap->skip)
5860 {
5861 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005862 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863 (int)(arg_end - next_arg - 1))) == NULL)
5864 {
5865 finished = TRUE;
5866 break;
5867 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005869
5870 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5871 cpo_save = p_cpo;
5872 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005873 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005874 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005875 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005876#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005877 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5878#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879
Bram Moolenaar860cae12010-06-05 23:22:07 +02005880 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005882 vim_free(curwin->w_s->b_syn_linecont_pat);
5883 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005884 finished = TRUE;
5885 break;
5886 }
5887 }
5888 next_arg = skipwhite(arg_end + 1);
5889 }
5890 else
5891 {
5892 eap->arg = next_arg;
5893 if (STRCMP(key, "MATCH") == 0)
5894 syn_cmd_match(eap, TRUE);
5895 else if (STRCMP(key, "REGION") == 0)
5896 syn_cmd_region(eap, TRUE);
5897 else if (STRCMP(key, "CLEAR") == 0)
5898 syn_cmd_clear(eap, TRUE);
5899 else
5900 illegal = TRUE;
5901 finished = TRUE;
5902 break;
5903 }
5904 arg_start = next_arg;
5905 }
5906 vim_free(key);
5907 if (illegal)
5908 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5909 else if (!finished)
5910 {
5911 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005912 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005913 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005914 }
5915}
5916
5917/*
5918 * Convert a line of highlight group names into a list of group ID numbers.
5919 * "arg" should point to the "contains" or "nextgroup" keyword.
5920 * "arg" is advanced to after the last group name.
5921 * Careful: the argument is modified (NULs added).
5922 * returns FAIL for some error, OK for success.
5923 */
5924 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005925get_id_list(
5926 char_u **arg,
5927 int keylen, /* length of keyword */
5928 short **list) /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005929 NULL, the list is silently skipped! */
5930{
5931 char_u *p = NULL;
5932 char_u *end;
5933 int round;
5934 int count;
5935 int total_count = 0;
5936 short *retval = NULL;
5937 char_u *name;
5938 regmatch_T regmatch;
5939 int id;
5940 int i;
5941 int failed = FALSE;
5942
5943 /*
5944 * We parse the list twice:
5945 * round == 1: count the number of items, allocate the array.
5946 * round == 2: fill the array with the items.
5947 * In round 1 new groups may be added, causing the number of items to
5948 * grow when a regexp is used. In that case round 1 is done once again.
5949 */
5950 for (round = 1; round <= 2; ++round)
5951 {
5952 /*
5953 * skip "contains"
5954 */
5955 p = skipwhite(*arg + keylen);
5956 if (*p != '=')
5957 {
5958 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5959 break;
5960 }
5961 p = skipwhite(p + 1);
5962 if (ends_excmd(*p))
5963 {
5964 EMSG2(_("E406: Empty argument: %s"), *arg);
5965 break;
5966 }
5967
5968 /*
5969 * parse the arguments after "contains"
5970 */
5971 count = 0;
5972 while (!ends_excmd(*p))
5973 {
5974 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5975 ;
5976 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5977 if (name == NULL)
5978 {
5979 failed = TRUE;
5980 break;
5981 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005982 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005983 if ( STRCMP(name + 1, "ALLBUT") == 0
5984 || STRCMP(name + 1, "ALL") == 0
5985 || STRCMP(name + 1, "TOP") == 0
5986 || STRCMP(name + 1, "CONTAINED") == 0)
5987 {
5988 if (TOUPPER_ASC(**arg) != 'C')
5989 {
5990 EMSG2(_("E407: %s not allowed here"), name + 1);
5991 failed = TRUE;
5992 vim_free(name);
5993 break;
5994 }
5995 if (count != 0)
5996 {
5997 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5998 failed = TRUE;
5999 vim_free(name);
6000 break;
6001 }
6002 if (name[1] == 'A')
6003 id = SYNID_ALLBUT;
6004 else if (name[1] == 'T')
6005 id = SYNID_TOP;
6006 else
6007 id = SYNID_CONTAINED;
6008 id += current_syn_inc_tag;
6009 }
6010 else if (name[1] == '@')
6011 {
6012 id = syn_check_cluster(name + 2, (int)(end - p - 1));
6013 }
6014 else
6015 {
6016 /*
6017 * Handle full group name.
6018 */
6019 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6020 id = syn_check_group(name + 1, (int)(end - p));
6021 else
6022 {
6023 /*
6024 * Handle match of regexp with group names.
6025 */
6026 *name = '^';
6027 STRCAT(name, "$");
6028 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6029 if (regmatch.regprog == NULL)
6030 {
6031 failed = TRUE;
6032 vim_free(name);
6033 break;
6034 }
6035
6036 regmatch.rm_ic = TRUE;
6037 id = 0;
6038 for (i = highlight_ga.ga_len; --i >= 0; )
6039 {
6040 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6041 (colnr_T)0))
6042 {
6043 if (round == 2)
6044 {
6045 /* Got more items than expected; can happen
6046 * when adding items that match:
6047 * "contains=a.*b,axb".
6048 * Go back to first round */
6049 if (count >= total_count)
6050 {
6051 vim_free(retval);
6052 round = 1;
6053 }
6054 else
6055 retval[count] = i + 1;
6056 }
6057 ++count;
6058 id = -1; /* remember that we found one */
6059 }
6060 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006061 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006062 }
6063 }
6064 vim_free(name);
6065 if (id == 0)
6066 {
6067 EMSG2(_("E409: Unknown group name: %s"), p);
6068 failed = TRUE;
6069 break;
6070 }
6071 if (id > 0)
6072 {
6073 if (round == 2)
6074 {
6075 /* Got more items than expected, go back to first round */
6076 if (count >= total_count)
6077 {
6078 vim_free(retval);
6079 round = 1;
6080 }
6081 else
6082 retval[count] = id;
6083 }
6084 ++count;
6085 }
6086 p = skipwhite(end);
6087 if (*p != ',')
6088 break;
6089 p = skipwhite(p + 1); /* skip comma in between arguments */
6090 }
6091 if (failed)
6092 break;
6093 if (round == 1)
6094 {
6095 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6096 if (retval == NULL)
6097 break;
6098 retval[count] = 0; /* zero means end of the list */
6099 total_count = count;
6100 }
6101 }
6102
6103 *arg = p;
6104 if (failed || retval == NULL)
6105 {
6106 vim_free(retval);
6107 return FAIL;
6108 }
6109
6110 if (*list == NULL)
6111 *list = retval;
6112 else
6113 vim_free(retval); /* list already found, don't overwrite it */
6114
6115 return OK;
6116}
6117
6118/*
6119 * Make a copy of an ID list.
6120 */
6121 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006122copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006123{
6124 int len;
6125 int count;
6126 short *retval;
6127
6128 if (list == NULL)
6129 return NULL;
6130
6131 for (count = 0; list[count]; ++count)
6132 ;
6133 len = (count + 1) * sizeof(short);
6134 retval = (short *)alloc((unsigned)len);
6135 if (retval != NULL)
6136 mch_memmove(retval, list, (size_t)len);
6137
6138 return retval;
6139}
6140
6141/*
6142 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6143 * "cur_si" can be NULL if not checking the "containedin" list.
6144 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6145 * the current item.
6146 * This function is called very often, keep it fast!!
6147 */
6148 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006149in_id_list(
6150 stateitem_T *cur_si, /* current item or NULL */
6151 short *list, /* id list */
6152 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6153 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006154{
6155 int retval;
6156 short *scl_list;
6157 short item;
6158 short id = ssp->id;
6159 static int depth = 0;
6160 int r;
6161
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006162 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006163 if (cur_si != NULL && ssp->cont_in_list != NULL
6164 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006165 {
6166 /* Ignore transparent items without a contains argument. Double check
6167 * that we don't go back past the first one. */
6168 while ((cur_si->si_flags & HL_TRANS_CONT)
6169 && cur_si > (stateitem_T *)(current_state.ga_data))
6170 --cur_si;
6171 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6172 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006173 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6174 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006175 return TRUE;
6176 }
6177
6178 if (list == NULL)
6179 return FALSE;
6180
6181 /*
6182 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6183 * inside anything. Only allow not-contained groups.
6184 */
6185 if (list == ID_LIST_ALL)
6186 return !contained;
6187
6188 /*
6189 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6190 * contains list. We also require that "id" is at the same ":syn include"
6191 * level as the list.
6192 */
6193 item = *list;
6194 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6195 {
6196 if (item < SYNID_TOP)
6197 {
6198 /* ALL or ALLBUT: accept all groups in the same file */
6199 if (item - SYNID_ALLBUT != ssp->inc_tag)
6200 return FALSE;
6201 }
6202 else if (item < SYNID_CONTAINED)
6203 {
6204 /* TOP: accept all not-contained groups in the same file */
6205 if (item - SYNID_TOP != ssp->inc_tag || contained)
6206 return FALSE;
6207 }
6208 else
6209 {
6210 /* CONTAINED: accept all contained groups in the same file */
6211 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6212 return FALSE;
6213 }
6214 item = *++list;
6215 retval = FALSE;
6216 }
6217 else
6218 retval = TRUE;
6219
6220 /*
6221 * Return "retval" if id is in the contains list.
6222 */
6223 while (item != 0)
6224 {
6225 if (item == id)
6226 return retval;
6227 if (item >= SYNID_CLUSTER)
6228 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006229 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006230 /* restrict recursiveness to 30 to avoid an endless loop for a
6231 * cluster that includes itself (indirectly) */
6232 if (scl_list != NULL && depth < 30)
6233 {
6234 ++depth;
6235 r = in_id_list(NULL, scl_list, ssp, contained);
6236 --depth;
6237 if (r)
6238 return retval;
6239 }
6240 }
6241 item = *++list;
6242 }
6243 return !retval;
6244}
6245
6246struct subcommand
6247{
Bram Moolenaard99df422016-01-29 23:20:40 +01006248 char *name; /* subcommand name */
6249 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006250};
6251
6252static struct subcommand subcommands[] =
6253{
6254 {"case", syn_cmd_case},
6255 {"clear", syn_cmd_clear},
6256 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006257 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006258 {"enable", syn_cmd_enable},
6259 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006260 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261 {"keyword", syn_cmd_keyword},
6262 {"list", syn_cmd_list},
6263 {"manual", syn_cmd_manual},
6264 {"match", syn_cmd_match},
6265 {"on", syn_cmd_on},
6266 {"off", syn_cmd_off},
6267 {"region", syn_cmd_region},
6268 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006269 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006270 {"sync", syn_cmd_sync},
6271 {"", syn_cmd_list},
6272 {NULL, NULL}
6273};
6274
6275/*
6276 * ":syntax".
6277 * This searches the subcommands[] table for the subcommand name, and calls a
6278 * syntax_subcommand() function to do the rest.
6279 */
6280 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006281ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006282{
6283 char_u *arg = eap->arg;
6284 char_u *subcmd_end;
6285 char_u *subcmd_name;
6286 int i;
6287
6288 syn_cmdlinep = eap->cmdlinep;
6289
6290 /* isolate subcommand name */
6291 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6292 ;
6293 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6294 if (subcmd_name != NULL)
6295 {
6296 if (eap->skip) /* skip error messages for all subcommands */
6297 ++emsg_skip;
6298 for (i = 0; ; ++i)
6299 {
6300 if (subcommands[i].name == NULL)
6301 {
6302 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6303 break;
6304 }
6305 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6306 {
6307 eap->arg = skipwhite(subcmd_end);
6308 (subcommands[i].func)(eap, FALSE);
6309 break;
6310 }
6311 }
6312 vim_free(subcmd_name);
6313 if (eap->skip)
6314 --emsg_skip;
6315 }
6316}
6317
Bram Moolenaar860cae12010-06-05 23:22:07 +02006318 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006319ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006321 char_u *old_value;
6322 char_u *new_value;
6323
Bram Moolenaar860cae12010-06-05 23:22:07 +02006324 if (curwin->w_s == &curwin->w_buffer->b_s)
6325 {
6326 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6327 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006328 hash_init(&curwin->w_s->b_keywtab);
6329 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006330#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006331 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332 curwin->w_p_spell = FALSE; /* No spell checking */
6333 clear_string_option(&curwin->w_s->b_p_spc);
6334 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006335 clear_string_option(&curwin->w_s->b_p_spl);
6336#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006337 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006338 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006339
6340 /* save value of b:current_syntax */
6341 old_value = get_var_value((char_u *)"b:current_syntax");
6342 if (old_value != NULL)
6343 old_value = vim_strsave(old_value);
6344
Bram Moolenaard1413d92016-03-02 21:51:56 +01006345#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006346 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6347 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006348 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006349#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006350
6351 /* move value of b:current_syntax to w:current_syntax */
6352 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006353 if (new_value != NULL)
6354 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006355
6356 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006357 if (old_value == NULL)
6358 do_unlet((char_u *)"b:current_syntax", TRUE);
6359 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006360 {
6361 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6362 vim_free(old_value);
6363 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006364}
6365
6366 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006367syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006368{
6369 return (win->w_s->b_syn_patterns.ga_len != 0
6370 || win->w_s->b_syn_clusters.ga_len != 0
6371 || win->w_s->b_keywtab.ht_used > 0
6372 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373}
6374
6375#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6376
6377static enum
6378{
6379 EXP_SUBCMD, /* expand ":syn" sub-commands */
6380 EXP_CASE /* expand ":syn case" arguments */
6381} expand_what;
6382
Bram Moolenaar4f688582007-07-24 12:34:30 +00006383/*
6384 * Reset include_link, include_default, include_none to 0.
6385 * Called when we are done expanding.
6386 */
6387 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006388reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006389{
6390 include_link = include_default = include_none = 0;
6391}
6392
6393/*
6394 * Handle command line completion for :match and :echohl command: Add "None"
6395 * as highlight group.
6396 */
6397 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006398set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006399{
6400 xp->xp_context = EXPAND_HIGHLIGHT;
6401 xp->xp_pattern = arg;
6402 include_none = 1;
6403}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006404
6405/*
6406 * Handle command line completion for :syntax command.
6407 */
6408 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006409set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006410{
6411 char_u *p;
6412
6413 /* Default: expand subcommands */
6414 xp->xp_context = EXPAND_SYNTAX;
6415 expand_what = EXP_SUBCMD;
6416 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006417 include_link = 0;
6418 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006419
6420 /* (part of) subcommand already typed */
6421 if (*arg != NUL)
6422 {
6423 p = skiptowhite(arg);
6424 if (*p != NUL) /* past first word */
6425 {
6426 xp->xp_pattern = skipwhite(p);
6427 if (*skiptowhite(xp->xp_pattern) != NUL)
6428 xp->xp_context = EXPAND_NOTHING;
6429 else if (STRNICMP(arg, "case", p - arg) == 0)
6430 expand_what = EXP_CASE;
6431 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6432 || STRNICMP(arg, "region", p - arg) == 0
6433 || STRNICMP(arg, "match", p - arg) == 0
6434 || STRNICMP(arg, "list", p - arg) == 0)
6435 xp->xp_context = EXPAND_HIGHLIGHT;
6436 else
6437 xp->xp_context = EXPAND_NOTHING;
6438 }
6439 }
6440}
6441
6442static char *(case_args[]) = {"match", "ignore", NULL};
6443
6444/*
6445 * Function given to ExpandGeneric() to obtain the list syntax names for
6446 * expansion.
6447 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006449get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450{
6451 if (expand_what == EXP_SUBCMD)
6452 return (char_u *)subcommands[idx].name;
6453 return (char_u *)case_args[idx];
6454}
6455
6456#endif /* FEAT_CMDL_COMPL */
6457
Bram Moolenaar071d4272004-06-13 20:20:40 +00006458/*
6459 * Function called for expression evaluation: get syntax ID at file position.
6460 */
6461 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006462syn_get_id(
6463 win_T *wp,
6464 long lnum,
6465 colnr_T col,
6466 int trans, /* remove transparency */
6467 int *spellp, /* return: can do spell checking */
6468 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006469{
6470 /* When the position is not after the current position and in the same
6471 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006472 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006473 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006474 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006475 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006476 else if (wp->w_buffer == syn_buf
6477 && lnum == current_lnum
6478 && col > current_col)
6479 /* next_match may not be correct when moving around, e.g. with the
6480 * "skip" expression in searchpair() */
6481 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006483 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484
6485 return (trans ? current_trans_id : current_id);
6486}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006487
Bram Moolenaar860cae12010-06-05 23:22:07 +02006488#if defined(FEAT_CONCEAL) || defined(PROTO)
6489/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006490 * Get extra information about the syntax item. Must be called right after
6491 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006492 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006493 * Returns the current flags.
6494 */
6495 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006496get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006497{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006498 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006499 return current_flags;
6500}
6501
6502/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006503 * Return conceal substitution character
6504 */
6505 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006506syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006507{
6508 return current_sub_char;
6509}
6510#endif
6511
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006512#if defined(FEAT_EVAL) || defined(PROTO)
6513/*
6514 * Return the syntax ID at position "i" in the current stack.
6515 * The caller must have called syn_get_id() before to fill the stack.
6516 * Returns -1 when "i" is out of range.
6517 */
6518 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006519syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006520{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006521 if (i >= current_state.ga_len)
6522 {
6523 /* Need to invalidate the state, because we didn't properly finish it
6524 * for the last character, "keep_state" was TRUE. */
6525 invalidate_current_state();
6526 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006527 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006528 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006529 return CUR_STATE(i).si_id;
6530}
6531#endif
6532
Bram Moolenaar071d4272004-06-13 20:20:40 +00006533#if defined(FEAT_FOLDING) || defined(PROTO)
6534/*
6535 * Function called to get folding level for line "lnum" in window "wp".
6536 */
6537 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006538syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539{
6540 int level = 0;
6541 int i;
6542
6543 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006544 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006545 {
6546 syntax_start(wp, lnum);
6547
6548 for (i = 0; i < current_state.ga_len; ++i)
6549 if (CUR_STATE(i).si_flags & HL_FOLD)
6550 ++level;
6551 }
6552 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006553 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006554 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006555 if (level < 0)
6556 level = 0;
6557 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006558 return level;
6559}
6560#endif
6561
Bram Moolenaar01615492015-02-03 13:00:38 +01006562#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006563/*
6564 * ":syntime".
6565 */
6566 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006567ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006568{
6569 if (STRCMP(eap->arg, "on") == 0)
6570 syn_time_on = TRUE;
6571 else if (STRCMP(eap->arg, "off") == 0)
6572 syn_time_on = FALSE;
6573 else if (STRCMP(eap->arg, "clear") == 0)
6574 syntime_clear();
6575 else if (STRCMP(eap->arg, "report") == 0)
6576 syntime_report();
6577 else
6578 EMSG2(_(e_invarg2), eap->arg);
6579}
6580
6581 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006582syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006583{
6584 profile_zero(&st->total);
6585 profile_zero(&st->slowest);
6586 st->count = 0;
6587 st->match = 0;
6588}
6589
6590/*
6591 * Clear the syntax timing for the current buffer.
6592 */
6593 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006594syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006595{
6596 int idx;
6597 synpat_T *spp;
6598
6599 if (!syntax_present(curwin))
6600 {
6601 MSG(_(msg_no_items));
6602 return;
6603 }
6604 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6605 {
6606 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6607 syn_clear_time(&spp->sp_time);
6608 }
6609}
6610
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006611#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6612/*
6613 * Function given to ExpandGeneric() to obtain the possible arguments of the
6614 * ":syntime {on,off,clear,report}" command.
6615 */
6616 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006617get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006618{
6619 switch (idx)
6620 {
6621 case 0: return (char_u *)"on";
6622 case 1: return (char_u *)"off";
6623 case 2: return (char_u *)"clear";
6624 case 3: return (char_u *)"report";
6625 }
6626 return NULL;
6627}
6628#endif
6629
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006630typedef struct
6631{
6632 proftime_T total;
6633 int count;
6634 int match;
6635 proftime_T slowest;
6636 proftime_T average;
6637 int id;
6638 char_u *pattern;
6639} time_entry_T;
6640
6641 static int
6642#ifdef __BORLANDC__
6643_RTLENTRYF
6644#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006645syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006646{
6647 const time_entry_T *s1 = v1;
6648 const time_entry_T *s2 = v2;
6649
6650 return profile_cmp(&s1->total, &s2->total);
6651}
6652
6653/*
6654 * Clear the syntax timing for the current buffer.
6655 */
6656 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006657syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006658{
6659 int idx;
6660 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006661# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006662 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006663# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006664 int len;
6665 proftime_T total_total;
6666 int total_count = 0;
6667 garray_T ga;
6668 time_entry_T *p;
6669
6670 if (!syntax_present(curwin))
6671 {
6672 MSG(_(msg_no_items));
6673 return;
6674 }
6675
6676 ga_init2(&ga, sizeof(time_entry_T), 50);
6677 profile_zero(&total_total);
6678 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6679 {
6680 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6681 if (spp->sp_time.count > 0)
6682 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006683 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006684 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6685 p->total = spp->sp_time.total;
6686 profile_add(&total_total, &spp->sp_time.total);
6687 p->count = spp->sp_time.count;
6688 p->match = spp->sp_time.match;
6689 total_count += spp->sp_time.count;
6690 p->slowest = spp->sp_time.slowest;
6691# ifdef FEAT_FLOAT
6692 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6693 p->average = tm;
6694# endif
6695 p->id = spp->sp_syn.id;
6696 p->pattern = spp->sp_pattern;
6697 ++ga.ga_len;
6698 }
6699 }
6700
6701 /* sort on total time */
Bram Moolenaar4e312962013-06-06 21:19:51 +02006702 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6703 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006704
6705 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6706 MSG_PUTS("\n");
6707 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6708 {
6709 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6710 p = ((time_entry_T *)ga.ga_data) + idx;
6711
6712 MSG_PUTS(profile_msg(&p->total));
6713 MSG_PUTS(" "); /* make sure there is always a separating space */
6714 msg_advance(13);
6715 msg_outnum(p->count);
6716 MSG_PUTS(" ");
6717 msg_advance(20);
6718 msg_outnum(p->match);
6719 MSG_PUTS(" ");
6720 msg_advance(26);
6721 MSG_PUTS(profile_msg(&p->slowest));
6722 MSG_PUTS(" ");
6723 msg_advance(38);
6724# ifdef FEAT_FLOAT
6725 MSG_PUTS(profile_msg(&p->average));
6726 MSG_PUTS(" ");
6727# endif
6728 msg_advance(50);
6729 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6730 MSG_PUTS(" ");
6731
6732 msg_advance(69);
6733 if (Columns < 80)
6734 len = 20; /* will wrap anyway */
6735 else
6736 len = Columns - 70;
6737 if (len > (int)STRLEN(p->pattern))
6738 len = (int)STRLEN(p->pattern);
6739 msg_outtrans_len(p->pattern, len);
6740 MSG_PUTS("\n");
6741 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006742 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006743 if (!got_int)
6744 {
6745 MSG_PUTS("\n");
6746 MSG_PUTS(profile_msg(&total_total));
6747 msg_advance(13);
6748 msg_outnum(total_count);
6749 MSG_PUTS("\n");
6750 }
6751}
6752#endif
6753
Bram Moolenaar071d4272004-06-13 20:20:40 +00006754#endif /* FEAT_SYN_HL */
6755
Bram Moolenaar071d4272004-06-13 20:20:40 +00006756/**************************************
6757 * Highlighting stuff *
6758 **************************************/
6759
6760/*
6761 * The default highlight groups. These are compiled-in for fast startup and
6762 * they still work when the runtime files can't be found.
6763 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006764 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6765 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006766 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006767#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006768# define CENT(a, b) b
6769#else
6770# define CENT(a, b) a
6771#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006772static char *(highlight_init_both[]) =
6773 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006774 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6775 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6776 CENT("IncSearch term=reverse cterm=reverse",
6777 "IncSearch term=reverse cterm=reverse gui=reverse"),
6778 CENT("ModeMsg term=bold cterm=bold",
6779 "ModeMsg term=bold cterm=bold gui=bold"),
6780 CENT("NonText term=bold ctermfg=Blue",
6781 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6782 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6783 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6784 CENT("StatusLineNC term=reverse cterm=reverse",
6785 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006786#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006787 CENT("VertSplit term=reverse cterm=reverse",
6788 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006789#endif
6790#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006791 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6792 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006793#endif
6794#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006795 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6796 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006797#endif
6798#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006799 CENT("PmenuSbar ctermbg=Grey",
6800 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006801#endif
6802#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006803 CENT("TabLineSel term=bold cterm=bold",
6804 "TabLineSel term=bold cterm=bold gui=bold"),
6805 CENT("TabLineFill term=reverse cterm=reverse",
6806 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006807#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006808#ifdef FEAT_GUI
6809 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006810 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006811#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006812 NULL
6813 };
6814
6815static char *(highlight_init_light[]) =
6816 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006817 CENT("Directory term=bold ctermfg=DarkBlue",
6818 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6819 CENT("LineNr term=underline ctermfg=Brown",
6820 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006821 CENT("CursorLineNr term=bold ctermfg=Brown",
6822 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006823 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6824 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6825 CENT("Question term=standout ctermfg=DarkGreen",
6826 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6827 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6828 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006829#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006830 CENT("SpellBad term=reverse ctermbg=LightRed",
6831 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6832 CENT("SpellCap term=reverse ctermbg=LightBlue",
6833 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6834 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6835 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6836 CENT("SpellLocal term=underline ctermbg=Cyan",
6837 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006838#endif
6839#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006840 CENT("PmenuThumb ctermbg=Black",
6841 "PmenuThumb ctermbg=Black guibg=Black"),
6842 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6843 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6844 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6845 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006846#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006847 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6848 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6849 CENT("Title term=bold ctermfg=DarkMagenta",
6850 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6851 CENT("WarningMsg term=standout ctermfg=DarkRed",
6852 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006853#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006854 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6855 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006856#endif
6857#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006858 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6859 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6860 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6861 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006862#endif
6863#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006864 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6865 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006866#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006867 CENT("Visual term=reverse",
6868 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006869#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006870 CENT("DiffAdd term=bold ctermbg=LightBlue",
6871 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6872 CENT("DiffChange term=bold ctermbg=LightMagenta",
6873 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6874 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6875 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006876#endif
6877#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006878 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6879 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006880#endif
6881#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006882 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006883 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006884 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006885 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006886 CENT("ColorColumn term=reverse ctermbg=LightRed",
6887 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006888#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006889#ifdef FEAT_CONCEAL
6890 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6891 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6892#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006893#ifdef FEAT_AUTOCMD
6894 CENT("MatchParen term=reverse ctermbg=Cyan",
6895 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6896#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006897#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006898 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006899#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006900 NULL
6901 };
6902
6903static char *(highlight_init_dark[]) =
6904 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006905 CENT("Directory term=bold ctermfg=LightCyan",
6906 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6907 CENT("LineNr term=underline ctermfg=Yellow",
6908 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006909 CENT("CursorLineNr term=bold ctermfg=Yellow",
6910 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006911 CENT("MoreMsg term=bold ctermfg=LightGreen",
6912 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6913 CENT("Question term=standout ctermfg=LightGreen",
6914 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6915 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6916 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6917 CENT("SpecialKey term=bold ctermfg=LightBlue",
6918 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006919#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006920 CENT("SpellBad term=reverse ctermbg=Red",
6921 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6922 CENT("SpellCap term=reverse ctermbg=Blue",
6923 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6924 CENT("SpellRare term=reverse ctermbg=Magenta",
6925 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6926 CENT("SpellLocal term=underline ctermbg=Cyan",
6927 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006928#endif
6929#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006930 CENT("PmenuThumb ctermbg=White",
6931 "PmenuThumb ctermbg=White guibg=White"),
6932 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6933 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006934 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6935 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006936#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006937 CENT("Title term=bold ctermfg=LightMagenta",
6938 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6939 CENT("WarningMsg term=standout ctermfg=LightRed",
6940 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006941#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006942 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6943 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006944#endif
6945#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006946 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6947 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6948 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6949 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006950#endif
6951#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006952 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6953 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006954#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006955 CENT("Visual term=reverse",
6956 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006957#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006958 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6959 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6960 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6961 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6962 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6963 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006964#endif
6965#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006966 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6967 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006968#endif
6969#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006970 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006971 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006972 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006973 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006974 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6975 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006976#endif
6977#ifdef FEAT_AUTOCMD
6978 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6979 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006980#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006981#ifdef FEAT_CONCEAL
6982 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6983 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6984#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006985#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006987#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006988 NULL
6989 };
6990
6991 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006992init_highlight(
6993 int both, /* include groups where 'bg' doesn't matter */
6994 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006995{
6996 int i;
6997 char **pp;
6998 static int had_both = FALSE;
6999#ifdef FEAT_EVAL
7000 char_u *p;
7001
7002 /*
7003 * Try finding the color scheme file. Used when a color file was loaded
7004 * and 'background' or 't_Co' is changed.
7005 */
7006 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007007 if (p != NULL)
7008 {
7009 /* The value of g:colors_name could be freed when sourcing the script,
7010 * making "p" invalid, so copy it. */
7011 char_u *copy_p = vim_strsave(p);
7012 int r;
7013
7014 if (copy_p != NULL)
7015 {
7016 r = load_colors(copy_p);
7017 vim_free(copy_p);
7018 if (r == OK)
7019 return;
7020 }
7021 }
7022
Bram Moolenaar071d4272004-06-13 20:20:40 +00007023#endif
7024
7025 /*
7026 * Didn't use a color file, use the compiled-in colors.
7027 */
7028 if (both)
7029 {
7030 had_both = TRUE;
7031 pp = highlight_init_both;
7032 for (i = 0; pp[i] != NULL; ++i)
7033 do_highlight((char_u *)pp[i], reset, TRUE);
7034 }
7035 else if (!had_both)
7036 /* Don't do anything before the call with both == TRUE from main().
7037 * Not everything has been setup then, and that call will overrule
7038 * everything anyway. */
7039 return;
7040
7041 if (*p_bg == 'l')
7042 pp = highlight_init_light;
7043 else
7044 pp = highlight_init_dark;
7045 for (i = 0; pp[i] != NULL; ++i)
7046 do_highlight((char_u *)pp[i], reset, TRUE);
7047
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007048 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007049 * depend on the number of colors available.
7050 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007051 * to avoid Statement highlighted text disappears.
7052 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007053 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007054 do_highlight((char_u *)(*p_bg == 'l'
7055 ? "Visual cterm=NONE ctermbg=LightGrey"
7056 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007057 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007058 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007059 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7060 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007061 if (*p_bg == 'l')
7062 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7063 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007064
Bram Moolenaar071d4272004-06-13 20:20:40 +00007065#ifdef FEAT_SYN_HL
7066 /*
7067 * If syntax highlighting is enabled load the highlighting for it.
7068 */
7069 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007070 {
7071 static int recursive = 0;
7072
7073 if (recursive >= 5)
7074 EMSG(_("E679: recursive loop loading syncolor.vim"));
7075 else
7076 {
7077 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007078 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007079 --recursive;
7080 }
7081 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007082#endif
7083}
7084
7085/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007086 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007087 * Return OK for success, FAIL for failure.
7088 */
7089 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007090load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007091{
7092 char_u *buf;
7093 int retval = FAIL;
7094 static int recursive = FALSE;
7095
7096 /* When being called recursively, this is probably because setting
7097 * 'background' caused the highlighting to be reloaded. This means it is
7098 * working, thus we should return OK. */
7099 if (recursive)
7100 return OK;
7101
7102 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007103 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007104 if (buf != NULL)
7105 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007106 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007107 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007108 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007109#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007110 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007111#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007112 }
7113 recursive = FALSE;
7114
7115 return retval;
7116}
7117
7118/*
7119 * Handle the ":highlight .." command.
7120 * When using ":hi clear" this is called recursively for each group with
7121 * "forceit" and "init" both TRUE.
7122 */
7123 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007124do_highlight(
7125 char_u *line,
7126 int forceit,
7127 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007128{
7129 char_u *name_end;
7130 char_u *p;
7131 char_u *linep;
7132 char_u *key_start;
7133 char_u *arg_start;
7134 char_u *key = NULL, *arg = NULL;
7135 long i;
7136 int off;
7137 int len;
7138 int attr;
7139 int id;
7140 int idx;
7141 int dodefault = FALSE;
7142 int doclear = FALSE;
7143 int dolink = FALSE;
7144 int error = FALSE;
7145 int color;
7146 int is_normal_group = FALSE; /* "Normal" group */
7147#ifdef FEAT_GUI_X11
7148 int is_menu_group = FALSE; /* "Menu" group */
7149 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7150 int is_tooltip_group = FALSE; /* "Tooltip" group */
7151 int do_colors = FALSE; /* need to update colors? */
7152#else
7153# define is_menu_group 0
7154# define is_tooltip_group 0
7155#endif
7156
7157 /*
7158 * If no argument, list current highlighting.
7159 */
7160 if (ends_excmd(*line))
7161 {
7162 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7163 /* TODO: only call when the group has attributes set */
7164 highlight_list_one((int)i);
7165 return;
7166 }
7167
7168 /*
7169 * Isolate the name.
7170 */
7171 name_end = skiptowhite(line);
7172 linep = skipwhite(name_end);
7173
7174 /*
7175 * Check for "default" argument.
7176 */
7177 if (STRNCMP(line, "default", name_end - line) == 0)
7178 {
7179 dodefault = TRUE;
7180 line = linep;
7181 name_end = skiptowhite(line);
7182 linep = skipwhite(name_end);
7183 }
7184
7185 /*
7186 * Check for "clear" or "link" argument.
7187 */
7188 if (STRNCMP(line, "clear", name_end - line) == 0)
7189 doclear = TRUE;
7190 if (STRNCMP(line, "link", name_end - line) == 0)
7191 dolink = TRUE;
7192
7193 /*
7194 * ":highlight {group-name}": list highlighting for one group.
7195 */
7196 if (!doclear && !dolink && ends_excmd(*linep))
7197 {
7198 id = syn_namen2id(line, (int)(name_end - line));
7199 if (id == 0)
7200 EMSG2(_("E411: highlight group not found: %s"), line);
7201 else
7202 highlight_list_one(id);
7203 return;
7204 }
7205
7206 /*
7207 * Handle ":highlight link {from} {to}" command.
7208 */
7209 if (dolink)
7210 {
7211 char_u *from_start = linep;
7212 char_u *from_end;
7213 char_u *to_start;
7214 char_u *to_end;
7215 int from_id;
7216 int to_id;
7217
7218 from_end = skiptowhite(from_start);
7219 to_start = skipwhite(from_end);
7220 to_end = skiptowhite(to_start);
7221
7222 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7223 {
7224 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7225 from_start);
7226 return;
7227 }
7228
7229 if (!ends_excmd(*skipwhite(to_end)))
7230 {
7231 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7232 return;
7233 }
7234
7235 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7236 if (STRNCMP(to_start, "NONE", 4) == 0)
7237 to_id = 0;
7238 else
7239 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7240
7241 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7242 {
7243 /*
7244 * Don't allow a link when there already is some highlighting
7245 * for the group, unless '!' is used
7246 */
7247 if (to_id > 0 && !forceit && !init
7248 && hl_has_settings(from_id - 1, dodefault))
7249 {
7250 if (sourcing_name == NULL && !dodefault)
7251 EMSG(_("E414: group has settings, highlight link ignored"));
7252 }
7253 else
7254 {
7255 if (!init)
7256 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7257 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007258#ifdef FEAT_EVAL
7259 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7260#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007261 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007262 }
7263 }
7264
7265 /* Only call highlight_changed() once, after sourcing a syntax file */
7266 need_highlight_changed = TRUE;
7267
7268 return;
7269 }
7270
7271 if (doclear)
7272 {
7273 /*
7274 * ":highlight clear [group]" command.
7275 */
7276 line = linep;
7277 if (ends_excmd(*line))
7278 {
7279#ifdef FEAT_GUI
7280 /* First, we do not destroy the old values, but allocate the new
7281 * ones and update the display. THEN we destroy the old values.
7282 * If we destroy the old values first, then the old values
7283 * (such as GuiFont's or GuiFontset's) will still be displayed but
7284 * invalid because they were free'd.
7285 */
7286 if (gui.in_use)
7287 {
7288# ifdef FEAT_BEVAL_TIP
7289 gui_init_tooltip_font();
7290# endif
7291# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7292 gui_init_menu_font();
7293# endif
7294 }
7295# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7296 gui_mch_def_colors();
7297# endif
7298# ifdef FEAT_GUI_X11
7299# ifdef FEAT_MENU
7300
7301 /* This only needs to be done when there is no Menu highlight
7302 * group defined by default, which IS currently the case.
7303 */
7304 gui_mch_new_menu_colors();
7305# endif
7306 if (gui.in_use)
7307 {
7308 gui_new_scrollbar_colors();
7309# ifdef FEAT_BEVAL
7310 gui_mch_new_tooltip_colors();
7311# endif
7312# ifdef FEAT_MENU
7313 gui_mch_new_menu_font();
7314# endif
7315 }
7316# endif
7317
7318 /* Ok, we're done allocating the new default graphics items.
7319 * The screen should already be refreshed at this point.
7320 * It is now Ok to clear out the old data.
7321 */
7322#endif
7323#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007324 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007325#endif
7326 restore_cterm_colors();
7327
7328 /*
7329 * Clear all default highlight groups and load the defaults.
7330 */
7331 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7332 highlight_clear(idx);
7333 init_highlight(TRUE, TRUE);
7334#ifdef FEAT_GUI
7335 if (gui.in_use)
7336 highlight_gui_started();
7337#endif
7338 highlight_changed();
7339 redraw_later_clear();
7340 return;
7341 }
7342 name_end = skiptowhite(line);
7343 linep = skipwhite(name_end);
7344 }
7345
7346 /*
7347 * Find the group name in the table. If it does not exist yet, add it.
7348 */
7349 id = syn_check_group(line, (int)(name_end - line));
7350 if (id == 0) /* failed (out of memory) */
7351 return;
7352 idx = id - 1; /* index is ID minus one */
7353
7354 /* Return if "default" was used and the group already has settings. */
7355 if (dodefault && hl_has_settings(idx, TRUE))
7356 return;
7357
7358 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7359 is_normal_group = TRUE;
7360#ifdef FEAT_GUI_X11
7361 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7362 is_menu_group = TRUE;
7363 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7364 is_scrollbar_group = TRUE;
7365 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7366 is_tooltip_group = TRUE;
7367#endif
7368
7369 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7370 if (doclear || (forceit && init))
7371 {
7372 highlight_clear(idx);
7373 if (!doclear)
7374 HL_TABLE()[idx].sg_set = 0;
7375 }
7376
7377 if (!doclear)
7378 while (!ends_excmd(*linep))
7379 {
7380 key_start = linep;
7381 if (*linep == '=')
7382 {
7383 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7384 error = TRUE;
7385 break;
7386 }
7387
7388 /*
7389 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7390 * "guibg").
7391 */
7392 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7393 ++linep;
7394 vim_free(key);
7395 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7396 if (key == NULL)
7397 {
7398 error = TRUE;
7399 break;
7400 }
7401 linep = skipwhite(linep);
7402
7403 if (STRCMP(key, "NONE") == 0)
7404 {
7405 if (!init || HL_TABLE()[idx].sg_set == 0)
7406 {
7407 if (!init)
7408 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7409 highlight_clear(idx);
7410 }
7411 continue;
7412 }
7413
7414 /*
7415 * Check for the equal sign.
7416 */
7417 if (*linep != '=')
7418 {
7419 EMSG2(_("E416: missing equal sign: %s"), key_start);
7420 error = TRUE;
7421 break;
7422 }
7423 ++linep;
7424
7425 /*
7426 * Isolate the argument.
7427 */
7428 linep = skipwhite(linep);
7429 if (*linep == '\'') /* guifg='color name' */
7430 {
7431 arg_start = ++linep;
7432 linep = vim_strchr(linep, '\'');
7433 if (linep == NULL)
7434 {
7435 EMSG2(_(e_invarg2), key_start);
7436 error = TRUE;
7437 break;
7438 }
7439 }
7440 else
7441 {
7442 arg_start = linep;
7443 linep = skiptowhite(linep);
7444 }
7445 if (linep == arg_start)
7446 {
7447 EMSG2(_("E417: missing argument: %s"), key_start);
7448 error = TRUE;
7449 break;
7450 }
7451 vim_free(arg);
7452 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7453 if (arg == NULL)
7454 {
7455 error = TRUE;
7456 break;
7457 }
7458 if (*linep == '\'')
7459 ++linep;
7460
7461 /*
7462 * Store the argument.
7463 */
7464 if ( STRCMP(key, "TERM") == 0
7465 || STRCMP(key, "CTERM") == 0
7466 || STRCMP(key, "GUI") == 0)
7467 {
7468 attr = 0;
7469 off = 0;
7470 while (arg[off] != NUL)
7471 {
7472 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7473 {
7474 len = (int)STRLEN(hl_name_table[i]);
7475 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7476 {
7477 attr |= hl_attr_table[i];
7478 off += len;
7479 break;
7480 }
7481 }
7482 if (i < 0)
7483 {
7484 EMSG2(_("E418: Illegal value: %s"), arg);
7485 error = TRUE;
7486 break;
7487 }
7488 if (arg[off] == ',') /* another one follows */
7489 ++off;
7490 }
7491 if (error)
7492 break;
7493 if (*key == 'T')
7494 {
7495 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7496 {
7497 if (!init)
7498 HL_TABLE()[idx].sg_set |= SG_TERM;
7499 HL_TABLE()[idx].sg_term = attr;
7500 }
7501 }
7502 else if (*key == 'C')
7503 {
7504 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7505 {
7506 if (!init)
7507 HL_TABLE()[idx].sg_set |= SG_CTERM;
7508 HL_TABLE()[idx].sg_cterm = attr;
7509 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7510 }
7511 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007512#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007513 else
7514 {
7515 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7516 {
7517 if (!init)
7518 HL_TABLE()[idx].sg_set |= SG_GUI;
7519 HL_TABLE()[idx].sg_gui = attr;
7520 }
7521 }
7522#endif
7523 }
7524 else if (STRCMP(key, "FONT") == 0)
7525 {
7526 /* in non-GUI fonts are simply ignored */
7527#ifdef FEAT_GUI
7528 if (!gui.shell_created)
7529 {
7530 /* GUI not started yet, always accept the name. */
7531 vim_free(HL_TABLE()[idx].sg_font_name);
7532 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7533 }
7534 else
7535 {
7536 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7537# ifdef FEAT_XFONTSET
7538 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7539# endif
7540 /* First, save the current font/fontset.
7541 * Then try to allocate the font/fontset.
7542 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7543 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7544 */
7545
7546 HL_TABLE()[idx].sg_font = NOFONT;
7547# ifdef FEAT_XFONTSET
7548 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7549# endif
7550 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007551 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007552
7553# ifdef FEAT_XFONTSET
7554 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7555 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007556 /* New fontset was accepted. Free the old one, if there
7557 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007558 gui_mch_free_fontset(temp_sg_fontset);
7559 vim_free(HL_TABLE()[idx].sg_font_name);
7560 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7561 }
7562 else
7563 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7564# endif
7565 if (HL_TABLE()[idx].sg_font != NOFONT)
7566 {
7567 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007568 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007569 gui_mch_free_font(temp_sg_font);
7570 vim_free(HL_TABLE()[idx].sg_font_name);
7571 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7572 }
7573 else
7574 HL_TABLE()[idx].sg_font = temp_sg_font;
7575 }
7576#endif
7577 }
7578 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7579 {
7580 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7581 {
7582 if (!init)
7583 HL_TABLE()[idx].sg_set |= SG_CTERM;
7584
7585 /* When setting the foreground color, and previously the "bold"
7586 * flag was set for a light color, reset it now */
7587 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7588 {
7589 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7590 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7591 }
7592
7593 if (VIM_ISDIGIT(*arg))
7594 color = atoi((char *)arg);
7595 else if (STRICMP(arg, "fg") == 0)
7596 {
7597 if (cterm_normal_fg_color)
7598 color = cterm_normal_fg_color - 1;
7599 else
7600 {
7601 EMSG(_("E419: FG color unknown"));
7602 error = TRUE;
7603 break;
7604 }
7605 }
7606 else if (STRICMP(arg, "bg") == 0)
7607 {
7608 if (cterm_normal_bg_color > 0)
7609 color = cterm_normal_bg_color - 1;
7610 else
7611 {
7612 EMSG(_("E420: BG color unknown"));
7613 error = TRUE;
7614 break;
7615 }
7616 }
7617 else
7618 {
7619 static char *(color_names[28]) = {
7620 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7621 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7622 "Gray", "Grey",
7623 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7624 "Blue", "LightBlue", "Green", "LightGreen",
7625 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7626 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7627 static int color_numbers_16[28] = {0, 1, 2, 3,
7628 4, 5, 6, 6,
7629 7, 7,
7630 7, 7, 8, 8,
7631 9, 9, 10, 10,
7632 11, 11, 12, 12, 13,
7633 13, 14, 14, 15, -1};
7634 /* for xterm with 88 colors... */
7635 static int color_numbers_88[28] = {0, 4, 2, 6,
7636 1, 5, 32, 72,
7637 84, 84,
7638 7, 7, 82, 82,
7639 12, 43, 10, 61,
7640 14, 63, 9, 74, 13,
7641 75, 11, 78, 15, -1};
7642 /* for xterm with 256 colors... */
7643 static int color_numbers_256[28] = {0, 4, 2, 6,
7644 1, 5, 130, 130,
7645 248, 248,
7646 7, 7, 242, 242,
7647 12, 81, 10, 121,
7648 14, 159, 9, 224, 13,
7649 225, 11, 229, 15, -1};
7650 /* for terminals with less than 16 colors... */
7651 static int color_numbers_8[28] = {0, 4, 2, 6,
7652 1, 5, 3, 3,
7653 7, 7,
7654 7, 7, 0+8, 0+8,
7655 4+8, 4+8, 2+8, 2+8,
7656 6+8, 6+8, 1+8, 1+8, 5+8,
7657 5+8, 3+8, 3+8, 7+8, -1};
7658#if defined(__QNXNTO__)
7659 static int *color_numbers_8_qansi = color_numbers_8;
7660 /* On qnx, the 8 & 16 color arrays are the same */
7661 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7662 color_numbers_8_qansi = color_numbers_16;
7663#endif
7664
7665 /* reduce calls to STRICMP a bit, it can be slow */
7666 off = TOUPPER_ASC(*arg);
7667 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7668 if (off == color_names[i][0]
7669 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7670 break;
7671 if (i < 0)
7672 {
7673 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7674 error = TRUE;
7675 break;
7676 }
7677
7678 /* Use the _16 table to check if its a valid color name. */
7679 color = color_numbers_16[i];
7680 if (color >= 0)
7681 {
7682 if (t_colors == 8)
7683 {
7684 /* t_Co is 8: use the 8 colors table */
7685#if defined(__QNXNTO__)
7686 color = color_numbers_8_qansi[i];
7687#else
7688 color = color_numbers_8[i];
7689#endif
7690 if (key[5] == 'F')
7691 {
7692 /* set/reset bold attribute to get light foreground
7693 * colors (on some terminals, e.g. "linux") */
7694 if (color & 8)
7695 {
7696 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7697 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7698 }
7699 else
7700 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7701 }
7702 color &= 7; /* truncate to 8 colors */
7703 }
7704 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007705 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007706 {
7707 /*
7708 * Guess: if the termcap entry ends in 'm', it is
7709 * probably an xterm-like terminal. Use the changed
7710 * order for colors.
7711 */
7712 if (*T_CAF != NUL)
7713 p = T_CAF;
7714 else
7715 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007716 if (*p != NUL && (t_colors > 256
7717 || *(p + STRLEN(p) - 1) == 'm'))
7718 {
7719 if (t_colors == 88)
7720 color = color_numbers_88[i];
7721 else if (t_colors >= 256)
7722 color = color_numbers_256[i];
7723 else
7724 color = color_numbers_8[i];
7725 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007726 }
7727 }
7728 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007729 /* Add one to the argument, to avoid zero. Zero is used for
7730 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007731 if (key[5] == 'F')
7732 {
7733 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7734 if (is_normal_group)
7735 {
7736 cterm_normal_fg_color = color + 1;
7737 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7738#ifdef FEAT_GUI
7739 /* Don't do this if the GUI is used. */
7740 if (!gui.in_use && !gui.starting)
7741#endif
7742 {
7743 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007744 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007745 term_fg_color(color);
7746 }
7747 }
7748 }
7749 else
7750 {
7751 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7752 if (is_normal_group)
7753 {
7754 cterm_normal_bg_color = color + 1;
7755#ifdef FEAT_GUI
7756 /* Don't mess with 'background' if the GUI is used. */
7757 if (!gui.in_use && !gui.starting)
7758#endif
7759 {
7760 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007761 if (color >= 0)
7762 {
7763 if (termcap_active)
7764 term_bg_color(color);
7765 if (t_colors < 16)
7766 i = (color == 0 || color == 4);
7767 else
7768 i = (color < 7 || color == 8);
7769 /* Set the 'background' option if the value is
7770 * wrong. */
7771 if (i != (*p_bg == 'd'))
7772 set_option_value((char_u *)"bg", 0L,
7773 i ? (char_u *)"dark"
7774 : (char_u *)"light", 0);
7775 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007776 }
7777 }
7778 }
7779 }
7780 }
7781 else if (STRCMP(key, "GUIFG") == 0)
7782 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007783#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007784 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007786 if (!init)
7787 HL_TABLE()[idx].sg_set |= SG_GUI;
7788
Bram Moolenaar61623362010-07-14 22:04:22 +02007789# ifdef FEAT_GUI
7790 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007791 i = color_name2handle(arg);
7792 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7793 {
7794 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007795# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007796 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7797 if (STRCMP(arg, "NONE"))
7798 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7799 else
7800 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007801# ifdef FEAT_GUI
7802# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007803 if (is_menu_group)
7804 gui.menu_fg_pixel = i;
7805 if (is_scrollbar_group)
7806 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007807# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007808 if (is_tooltip_group)
7809 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007810# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007811 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007812# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007813 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007814# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007816#endif
7817 }
7818 else if (STRCMP(key, "GUIBG") == 0)
7819 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007820#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007821 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007822 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007823 if (!init)
7824 HL_TABLE()[idx].sg_set |= SG_GUI;
7825
Bram Moolenaar61623362010-07-14 22:04:22 +02007826# ifdef FEAT_GUI
7827 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007828 i = color_name2handle(arg);
7829 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7830 {
7831 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007832# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007833 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7834 if (STRCMP(arg, "NONE") != 0)
7835 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7836 else
7837 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007838# ifdef FEAT_GUI
7839# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007840 if (is_menu_group)
7841 gui.menu_bg_pixel = i;
7842 if (is_scrollbar_group)
7843 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007844# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007845 if (is_tooltip_group)
7846 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007847# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007848 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007849# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007850 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007851# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007852 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007853#endif
7854 }
7855 else if (STRCMP(key, "GUISP") == 0)
7856 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007857#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007858 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7859 {
7860 if (!init)
7861 HL_TABLE()[idx].sg_set |= SG_GUI;
7862
Bram Moolenaar61623362010-07-14 22:04:22 +02007863# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007864 i = color_name2handle(arg);
7865 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7866 {
7867 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007868# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007869 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7870 if (STRCMP(arg, "NONE") != 0)
7871 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7872 else
7873 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007874# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007875 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007876# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007878#endif
7879 }
7880 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7881 {
7882 char_u buf[100];
7883 char_u *tname;
7884
7885 if (!init)
7886 HL_TABLE()[idx].sg_set |= SG_TERM;
7887
7888 /*
7889 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007890 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007891 */
7892 if (STRNCMP(arg, "t_", 2) == 0)
7893 {
7894 off = 0;
7895 buf[0] = 0;
7896 while (arg[off] != NUL)
7897 {
7898 /* Isolate one termcap name */
7899 for (len = 0; arg[off + len] &&
7900 arg[off + len] != ','; ++len)
7901 ;
7902 tname = vim_strnsave(arg + off, len);
7903 if (tname == NULL) /* out of memory */
7904 {
7905 error = TRUE;
7906 break;
7907 }
7908 /* lookup the escape sequence for the item */
7909 p = get_term_code(tname);
7910 vim_free(tname);
7911 if (p == NULL) /* ignore non-existing things */
7912 p = (char_u *)"";
7913
7914 /* Append it to the already found stuff */
7915 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7916 {
7917 EMSG2(_("E422: terminal code too long: %s"), arg);
7918 error = TRUE;
7919 break;
7920 }
7921 STRCAT(buf, p);
7922
7923 /* Advance to the next item */
7924 off += len;
7925 if (arg[off] == ',') /* another one follows */
7926 ++off;
7927 }
7928 }
7929 else
7930 {
7931 /*
7932 * Copy characters from arg[] to buf[], translating <> codes.
7933 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007934 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007935 {
7936 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007937 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007938 off += len;
7939 else /* copy as normal char */
7940 buf[off++] = *p++;
7941 }
7942 buf[off] = NUL;
7943 }
7944 if (error)
7945 break;
7946
7947 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7948 p = NULL;
7949 else
7950 p = vim_strsave(buf);
7951 if (key[2] == 'A')
7952 {
7953 vim_free(HL_TABLE()[idx].sg_start);
7954 HL_TABLE()[idx].sg_start = p;
7955 }
7956 else
7957 {
7958 vim_free(HL_TABLE()[idx].sg_stop);
7959 HL_TABLE()[idx].sg_stop = p;
7960 }
7961 }
7962 else
7963 {
7964 EMSG2(_("E423: Illegal argument: %s"), key_start);
7965 error = TRUE;
7966 break;
7967 }
7968
7969 /*
7970 * When highlighting has been given for a group, don't link it.
7971 */
7972 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7973 HL_TABLE()[idx].sg_link = 0;
7974
7975 /*
7976 * Continue with next argument.
7977 */
7978 linep = skipwhite(linep);
7979 }
7980
7981 /*
7982 * If there is an error, and it's a new entry, remove it from the table.
7983 */
7984 if (error && idx == highlight_ga.ga_len)
7985 syn_unadd_group();
7986 else
7987 {
7988 if (is_normal_group)
7989 {
7990 HL_TABLE()[idx].sg_term_attr = 0;
7991 HL_TABLE()[idx].sg_cterm_attr = 0;
7992#ifdef FEAT_GUI
7993 HL_TABLE()[idx].sg_gui_attr = 0;
7994 /*
7995 * Need to update all groups, because they might be using "bg"
7996 * and/or "fg", which have been changed now.
7997 */
7998 if (gui.in_use)
7999 highlight_gui_started();
8000#endif
8001 }
8002#ifdef FEAT_GUI_X11
8003# ifdef FEAT_MENU
8004 else if (is_menu_group)
8005 {
8006 if (gui.in_use && do_colors)
8007 gui_mch_new_menu_colors();
8008 }
8009# endif
8010 else if (is_scrollbar_group)
8011 {
8012 if (gui.in_use && do_colors)
8013 gui_new_scrollbar_colors();
8014 }
8015# ifdef FEAT_BEVAL
8016 else if (is_tooltip_group)
8017 {
8018 if (gui.in_use && do_colors)
8019 gui_mch_new_tooltip_colors();
8020 }
8021# endif
8022#endif
8023 else
8024 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008025#ifdef FEAT_EVAL
8026 HL_TABLE()[idx].sg_scriptID = current_SID;
8027#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008028 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008029 }
8030 vim_free(key);
8031 vim_free(arg);
8032
8033 /* Only call highlight_changed() once, after sourcing a syntax file */
8034 need_highlight_changed = TRUE;
8035}
8036
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008037#if defined(EXITFREE) || defined(PROTO)
8038 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008039free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008040{
8041 int i;
8042
8043 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008044 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008045 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008046 vim_free(HL_TABLE()[i].sg_name);
8047 vim_free(HL_TABLE()[i].sg_name_u);
8048 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008049 ga_clear(&highlight_ga);
8050}
8051#endif
8052
Bram Moolenaar071d4272004-06-13 20:20:40 +00008053/*
8054 * Reset the cterm colors to what they were before Vim was started, if
8055 * possible. Otherwise reset them to zero.
8056 */
8057 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008058restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008059{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008060#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008061 /* Since t_me has been set, this probably means that the user
8062 * wants to use this as default colors. Need to reset default
8063 * background/foreground colors. */
8064 mch_set_normal_colors();
8065#else
8066 cterm_normal_fg_color = 0;
8067 cterm_normal_fg_bold = 0;
8068 cterm_normal_bg_color = 0;
8069#endif
8070}
8071
8072/*
8073 * Return TRUE if highlight group "idx" has any settings.
8074 * When "check_link" is TRUE also check for an existing link.
8075 */
8076 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008077hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008078{
8079 return ( HL_TABLE()[idx].sg_term_attr != 0
8080 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008081 || HL_TABLE()[idx].sg_cterm_fg != 0
8082 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008083#ifdef FEAT_GUI
8084 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008085 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8086 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8087 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8088 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008089#endif
8090 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8091}
8092
8093/*
8094 * Clear highlighting for one group.
8095 */
8096 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008097highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008098{
8099 HL_TABLE()[idx].sg_term = 0;
8100 vim_free(HL_TABLE()[idx].sg_start);
8101 HL_TABLE()[idx].sg_start = NULL;
8102 vim_free(HL_TABLE()[idx].sg_stop);
8103 HL_TABLE()[idx].sg_stop = NULL;
8104 HL_TABLE()[idx].sg_term_attr = 0;
8105 HL_TABLE()[idx].sg_cterm = 0;
8106 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8107 HL_TABLE()[idx].sg_cterm_fg = 0;
8108 HL_TABLE()[idx].sg_cterm_bg = 0;
8109 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008110#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008111 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008112 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8113 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8115 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008116 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8117 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008118#endif
8119#ifdef FEAT_GUI
8120 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8121 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8122 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008123 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8124 HL_TABLE()[idx].sg_font = NOFONT;
8125# ifdef FEAT_XFONTSET
8126 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8127 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8128# endif
8129 vim_free(HL_TABLE()[idx].sg_font_name);
8130 HL_TABLE()[idx].sg_font_name = NULL;
8131 HL_TABLE()[idx].sg_gui_attr = 0;
8132#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008133#ifdef FEAT_EVAL
8134 /* Clear the script ID only when there is no link, since that is not
8135 * cleared. */
8136 if (HL_TABLE()[idx].sg_link == 0)
8137 HL_TABLE()[idx].sg_scriptID = 0;
8138#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008139}
8140
8141#if defined(FEAT_GUI) || defined(PROTO)
8142/*
8143 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008144 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145 * "Tooltip" colors.
8146 */
8147 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008148set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149{
8150 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008151 &gui.norm_pixel, &gui.back_pixel,
8152 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008153 {
8154 gui_mch_new_colors();
8155 must_redraw = CLEAR;
8156 }
8157#ifdef FEAT_GUI_X11
8158 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008159 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8160 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008161 {
8162# ifdef FEAT_MENU
8163 gui_mch_new_menu_colors();
8164# endif
8165 must_redraw = CLEAR;
8166 }
8167# ifdef FEAT_BEVAL
8168 if (set_group_colors((char_u *)"Tooltip",
8169 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8170 FALSE, FALSE, TRUE))
8171 {
8172# ifdef FEAT_TOOLBAR
8173 gui_mch_new_tooltip_colors();
8174# endif
8175 must_redraw = CLEAR;
8176 }
8177#endif
8178 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008179 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8180 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008181 {
8182 gui_new_scrollbar_colors();
8183 must_redraw = CLEAR;
8184 }
8185#endif
8186}
8187
8188/*
8189 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8190 */
8191 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008192set_group_colors(
8193 char_u *name,
8194 guicolor_T *fgp,
8195 guicolor_T *bgp,
8196 int do_menu,
8197 int use_norm,
8198 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008199{
8200 int idx;
8201
8202 idx = syn_name2id(name) - 1;
8203 if (idx >= 0)
8204 {
8205 gui_do_one_color(idx, do_menu, do_tooltip);
8206
8207 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8208 *fgp = HL_TABLE()[idx].sg_gui_fg;
8209 else if (use_norm)
8210 *fgp = gui.def_norm_pixel;
8211 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8212 *bgp = HL_TABLE()[idx].sg_gui_bg;
8213 else if (use_norm)
8214 *bgp = gui.def_back_pixel;
8215 return TRUE;
8216 }
8217 return FALSE;
8218}
8219
8220/*
8221 * Get the font of the "Normal" group.
8222 * Returns "" when it's not found or not set.
8223 */
8224 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008225hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226{
8227 int id;
8228 char_u *s;
8229
8230 id = syn_name2id((char_u *)"Normal");
8231 if (id > 0)
8232 {
8233 s = HL_TABLE()[id - 1].sg_font_name;
8234 if (s != NULL)
8235 return s;
8236 }
8237 return (char_u *)"";
8238}
8239
8240/*
8241 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8242 * actually chosen to be used.
8243 */
8244 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008245hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008246{
8247 int id;
8248
8249 id = syn_name2id((char_u *)"Normal");
8250 if (id > 0)
8251 {
8252 vim_free(HL_TABLE()[id - 1].sg_font_name);
8253 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8254 }
8255}
8256
8257/*
8258 * Set background color for "Normal" group. Called by gui_set_bg_color()
8259 * when the color is known.
8260 */
8261 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008262hl_set_bg_color_name(
8263 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008264{
8265 int id;
8266
8267 if (name != NULL)
8268 {
8269 id = syn_name2id((char_u *)"Normal");
8270 if (id > 0)
8271 {
8272 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8273 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8274 }
8275 }
8276}
8277
8278/*
8279 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8280 * when the color is known.
8281 */
8282 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008283hl_set_fg_color_name(
8284 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008285{
8286 int id;
8287
8288 if (name != NULL)
8289 {
8290 id = syn_name2id((char_u *)"Normal");
8291 if (id > 0)
8292 {
8293 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8294 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8295 }
8296 }
8297}
8298
8299/*
8300 * Return the handle for a color name.
8301 * Returns INVALCOLOR when failed.
8302 */
8303 static guicolor_T
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008304color_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008305{
8306 if (STRCMP(name, "NONE") == 0)
8307 return INVALCOLOR;
8308
8309 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8310 return gui.norm_pixel;
8311 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8312 return gui.back_pixel;
8313
8314 return gui_get_color(name);
8315}
8316
8317/*
8318 * Return the handle for a font name.
8319 * Returns NOFONT when failed.
8320 */
8321 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008322font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008323{
8324 if (STRCMP(name, "NONE") == 0)
8325 return NOFONT;
8326
8327 return gui_mch_get_font(name, TRUE);
8328}
8329
8330# ifdef FEAT_XFONTSET
8331/*
8332 * Return the handle for a fontset name.
8333 * Returns NOFONTSET when failed.
8334 */
8335 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008336fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008337{
8338 if (STRCMP(name, "NONE") == 0)
8339 return NOFONTSET;
8340
8341 return gui_mch_get_fontset(name, TRUE, fixed_width);
8342}
8343# endif
8344
8345/*
8346 * Get the font or fontset for one highlight group.
8347 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008348 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008349hl_do_font(
8350 int idx,
8351 char_u *arg,
8352 int do_normal, /* set normal font */
8353 int do_menu UNUSED, /* set menu font */
8354 int do_tooltip UNUSED, /* set tooltip font */
8355 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008356{
8357# ifdef FEAT_XFONTSET
8358 /* If 'guifontset' is not empty, first try using the name as a
8359 * fontset. If that doesn't work, use it as a font name. */
8360 if (*p_guifontset != NUL
8361# ifdef FONTSET_ALWAYS
8362 || do_menu
8363# endif
8364# ifdef FEAT_BEVAL_TIP
8365 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8366 || do_tooltip
8367# endif
8368 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008369 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008370 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008371 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008372 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8373# ifdef FONTSET_ALWAYS
8374 || do_menu
8375# endif
8376# ifdef FEAT_BEVAL_TIP
8377 || do_tooltip
8378# endif
8379 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008380 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008381 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8382 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008383 /* If it worked and it's the Normal group, use it as the normal
8384 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008385 if (do_normal)
8386 gui_init_font(arg, TRUE);
8387# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8388 if (do_menu)
8389 {
8390# ifdef FONTSET_ALWAYS
8391 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8392# else
8393 /* YIKES! This is a bug waiting to crash the program */
8394 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8395# endif
8396 gui_mch_new_menu_font();
8397 }
8398# ifdef FEAT_BEVAL
8399 if (do_tooltip)
8400 {
8401 /* The Athena widget set cannot currently handle switching between
8402 * displaying a single font and a fontset.
8403 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008404 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008405 * XFontStruct is used.
8406 */
8407 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8408 gui_mch_new_tooltip_font();
8409 }
8410# endif
8411# endif
8412 }
8413 else
8414# endif
8415 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008416 if (free_font)
8417 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008418 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8419 /* If it worked and it's the Normal group, use it as the
8420 * normal font. Same for the Menu group. */
8421 if (HL_TABLE()[idx].sg_font != NOFONT)
8422 {
8423 if (do_normal)
8424 gui_init_font(arg, FALSE);
8425#ifndef FONTSET_ALWAYS
8426# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8427 if (do_menu)
8428 {
8429 gui.menu_font = HL_TABLE()[idx].sg_font;
8430 gui_mch_new_menu_font();
8431 }
8432# endif
8433#endif
8434 }
8435 }
8436}
8437
8438#endif /* FEAT_GUI */
8439
8440/*
8441 * Table with the specifications for an attribute number.
8442 * Note that this table is used by ALL buffers. This is required because the
8443 * GUI can redraw at any time for any buffer.
8444 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008445static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008446
8447#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8448
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008449static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450
8451#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8452
8453#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008454static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008455
8456#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8457#endif
8458
8459/*
8460 * Return the attr number for a set of colors and font.
8461 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8462 * if the combination is new.
8463 * Return 0 for error (no more room).
8464 */
8465 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008466get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008467{
8468 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008469 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008470 static int recursive = FALSE;
8471
8472 /*
8473 * Init the table, in case it wasn't done yet.
8474 */
8475 table->ga_itemsize = sizeof(attrentry_T);
8476 table->ga_growsize = 7;
8477
8478 /*
8479 * Try to find an entry with the same specifications.
8480 */
8481 for (i = 0; i < table->ga_len; ++i)
8482 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008483 taep = &(((attrentry_T *)table->ga_data)[i]);
8484 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008485 && (
8486#ifdef FEAT_GUI
8487 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008488 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8489 && aep->ae_u.gui.bg_color
8490 == taep->ae_u.gui.bg_color
8491 && aep->ae_u.gui.sp_color
8492 == taep->ae_u.gui.sp_color
8493 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008494# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008495 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008496# endif
8497 ))
8498 ||
8499#endif
8500 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008501 && (aep->ae_u.term.start == NULL)
8502 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008503 && (aep->ae_u.term.start == NULL
8504 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008505 taep->ae_u.term.start) == 0)
8506 && (aep->ae_u.term.stop == NULL)
8507 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008508 && (aep->ae_u.term.stop == NULL
8509 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008510 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008511 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008512 && aep->ae_u.cterm.fg_color
8513 == taep->ae_u.cterm.fg_color
8514 && aep->ae_u.cterm.bg_color
8515 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008516 ))
8517
8518 return i + ATTR_OFF;
8519 }
8520
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008521 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008522 {
8523 /*
8524 * Running out of attribute entries! remove all attributes, and
8525 * compute new ones for all groups.
8526 * When called recursively, we are really out of numbers.
8527 */
8528 if (recursive)
8529 {
8530 EMSG(_("E424: Too many different highlighting attributes in use"));
8531 return 0;
8532 }
8533 recursive = TRUE;
8534
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008535 clear_hl_tables();
8536
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537 must_redraw = CLEAR;
8538
8539 for (i = 0; i < highlight_ga.ga_len; ++i)
8540 set_hl_attr(i);
8541
8542 recursive = FALSE;
8543 }
8544
8545 /*
8546 * This is a new combination of colors and font, add an entry.
8547 */
8548 if (ga_grow(table, 1) == FAIL)
8549 return 0;
8550
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008551 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8552 vim_memset(taep, 0, sizeof(attrentry_T));
8553 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008554#ifdef FEAT_GUI
8555 if (table == &gui_attr_table)
8556 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008557 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8558 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8559 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8560 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008561# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008562 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008563# endif
8564 }
8565#endif
8566 if (table == &term_attr_table)
8567 {
8568 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008569 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008570 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008571 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008572 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008573 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008574 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008575 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008576 }
8577 else if (table == &cterm_attr_table)
8578 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008579 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8580 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008581 }
8582 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583 return (table->ga_len - 1 + ATTR_OFF);
8584}
8585
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008586/*
8587 * Clear all highlight tables.
8588 */
8589 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008590clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008591{
8592 int i;
8593 attrentry_T *taep;
8594
8595#ifdef FEAT_GUI
8596 ga_clear(&gui_attr_table);
8597#endif
8598 for (i = 0; i < term_attr_table.ga_len; ++i)
8599 {
8600 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8601 vim_free(taep->ae_u.term.start);
8602 vim_free(taep->ae_u.term.stop);
8603 }
8604 ga_clear(&term_attr_table);
8605 ga_clear(&cterm_attr_table);
8606}
8607
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008608#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008609/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008610 * Combine special attributes (e.g., for spelling) with other attributes
8611 * (e.g., for syntax highlighting).
8612 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008613 * This creates a new group when required.
8614 * Since we expect there to be few spelling mistakes we don't cache the
8615 * result.
8616 * Return the resulting attributes.
8617 */
8618 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008619hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008620{
8621 attrentry_T *char_aep = NULL;
8622 attrentry_T *spell_aep;
8623 attrentry_T new_en;
8624
8625 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008626 return prim_attr;
8627 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8628 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008629#ifdef FEAT_GUI
8630 if (gui.in_use)
8631 {
8632 if (char_attr > HL_ALL)
8633 char_aep = syn_gui_attr2entry(char_attr);
8634 if (char_aep != NULL)
8635 new_en = *char_aep;
8636 else
8637 {
8638 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008639 new_en.ae_u.gui.fg_color = INVALCOLOR;
8640 new_en.ae_u.gui.bg_color = INVALCOLOR;
8641 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008642 if (char_attr <= HL_ALL)
8643 new_en.ae_attr = char_attr;
8644 }
8645
Bram Moolenaar30abd282005-06-22 22:35:10 +00008646 if (prim_attr <= HL_ALL)
8647 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008648 else
8649 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008650 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008651 if (spell_aep != NULL)
8652 {
8653 new_en.ae_attr |= spell_aep->ae_attr;
8654 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8655 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8656 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8657 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8658 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8659 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8660 if (spell_aep->ae_u.gui.font != NOFONT)
8661 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8662# ifdef FEAT_XFONTSET
8663 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8664 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8665# endif
8666 }
8667 }
8668 return get_attr_entry(&gui_attr_table, &new_en);
8669 }
8670#endif
8671
8672 if (t_colors > 1)
8673 {
8674 if (char_attr > HL_ALL)
8675 char_aep = syn_cterm_attr2entry(char_attr);
8676 if (char_aep != NULL)
8677 new_en = *char_aep;
8678 else
8679 {
8680 vim_memset(&new_en, 0, sizeof(new_en));
8681 if (char_attr <= HL_ALL)
8682 new_en.ae_attr = char_attr;
8683 }
8684
Bram Moolenaar30abd282005-06-22 22:35:10 +00008685 if (prim_attr <= HL_ALL)
8686 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008687 else
8688 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008689 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008690 if (spell_aep != NULL)
8691 {
8692 new_en.ae_attr |= spell_aep->ae_attr;
8693 if (spell_aep->ae_u.cterm.fg_color > 0)
8694 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8695 if (spell_aep->ae_u.cterm.bg_color > 0)
8696 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8697 }
8698 }
8699 return get_attr_entry(&cterm_attr_table, &new_en);
8700 }
8701
8702 if (char_attr > HL_ALL)
8703 char_aep = syn_term_attr2entry(char_attr);
8704 if (char_aep != NULL)
8705 new_en = *char_aep;
8706 else
8707 {
8708 vim_memset(&new_en, 0, sizeof(new_en));
8709 if (char_attr <= HL_ALL)
8710 new_en.ae_attr = char_attr;
8711 }
8712
Bram Moolenaar30abd282005-06-22 22:35:10 +00008713 if (prim_attr <= HL_ALL)
8714 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008715 else
8716 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008717 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008718 if (spell_aep != NULL)
8719 {
8720 new_en.ae_attr |= spell_aep->ae_attr;
8721 if (spell_aep->ae_u.term.start != NULL)
8722 {
8723 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8724 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8725 }
8726 }
8727 }
8728 return get_attr_entry(&term_attr_table, &new_en);
8729}
8730#endif
8731
Bram Moolenaar071d4272004-06-13 20:20:40 +00008732#ifdef FEAT_GUI
8733
8734 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008735syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008736{
8737 attr -= ATTR_OFF;
8738 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8739 return NULL;
8740 return &(GUI_ATTR_ENTRY(attr));
8741}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008742#endif /* FEAT_GUI */
8743
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008744/*
8745 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8746 * Only to be used when "attr" > HL_ALL.
8747 */
8748 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008749syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008750{
8751 attrentry_T *aep;
8752
8753#ifdef FEAT_GUI
8754 if (gui.in_use)
8755 aep = syn_gui_attr2entry(attr);
8756 else
8757#endif
8758 if (t_colors > 1)
8759 aep = syn_cterm_attr2entry(attr);
8760 else
8761 aep = syn_term_attr2entry(attr);
8762
8763 if (aep == NULL) /* highlighting not set */
8764 return 0;
8765 return aep->ae_attr;
8766}
8767
8768
Bram Moolenaar071d4272004-06-13 20:20:40 +00008769 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008770syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008771{
8772 attr -= ATTR_OFF;
8773 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8774 return NULL;
8775 return &(TERM_ATTR_ENTRY(attr));
8776}
8777
8778 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008779syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008780{
8781 attr -= ATTR_OFF;
8782 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8783 return NULL;
8784 return &(CTERM_ATTR_ENTRY(attr));
8785}
8786
8787#define LIST_ATTR 1
8788#define LIST_STRING 2
8789#define LIST_INT 3
8790
8791 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008792highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008793{
8794 struct hl_group *sgp;
8795 int didh = FALSE;
8796
8797 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8798
8799 didh = highlight_list_arg(id, didh, LIST_ATTR,
8800 sgp->sg_term, NULL, "term");
8801 didh = highlight_list_arg(id, didh, LIST_STRING,
8802 0, sgp->sg_start, "start");
8803 didh = highlight_list_arg(id, didh, LIST_STRING,
8804 0, sgp->sg_stop, "stop");
8805
8806 didh = highlight_list_arg(id, didh, LIST_ATTR,
8807 sgp->sg_cterm, NULL, "cterm");
8808 didh = highlight_list_arg(id, didh, LIST_INT,
8809 sgp->sg_cterm_fg, NULL, "ctermfg");
8810 didh = highlight_list_arg(id, didh, LIST_INT,
8811 sgp->sg_cterm_bg, NULL, "ctermbg");
8812
Bram Moolenaar61623362010-07-14 22:04:22 +02008813#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008814 didh = highlight_list_arg(id, didh, LIST_ATTR,
8815 sgp->sg_gui, NULL, "gui");
8816 didh = highlight_list_arg(id, didh, LIST_STRING,
8817 0, sgp->sg_gui_fg_name, "guifg");
8818 didh = highlight_list_arg(id, didh, LIST_STRING,
8819 0, sgp->sg_gui_bg_name, "guibg");
8820 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008821 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008822#endif
8823#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008824 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008825 0, sgp->sg_font_name, "font");
8826#endif
8827
Bram Moolenaar661b1822005-07-28 22:36:45 +00008828 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008829 {
8830 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008831 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008832 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8833 msg_putchar(' ');
8834 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8835 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008836
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008837 if (!didh)
8838 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008839#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008840 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008841 last_set_msg(sgp->sg_scriptID);
8842#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008843}
8844
8845 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008846highlight_list_arg(
8847 int id,
8848 int didh,
8849 int type,
8850 int iarg,
8851 char_u *sarg,
8852 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008853{
8854 char_u buf[100];
8855 char_u *ts;
8856 int i;
8857
Bram Moolenaar661b1822005-07-28 22:36:45 +00008858 if (got_int)
8859 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008860 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8861 {
8862 ts = buf;
8863 if (type == LIST_INT)
8864 sprintf((char *)buf, "%d", iarg - 1);
8865 else if (type == LIST_STRING)
8866 ts = sarg;
8867 else /* type == LIST_ATTR */
8868 {
8869 buf[0] = NUL;
8870 for (i = 0; hl_attr_table[i] != 0; ++i)
8871 {
8872 if (iarg & hl_attr_table[i])
8873 {
8874 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008875 vim_strcat(buf, (char_u *)",", 100);
8876 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008877 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8878 }
8879 }
8880 }
8881
8882 (void)syn_list_header(didh,
8883 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8884 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008885 if (!got_int)
8886 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008887 if (*name != NUL)
8888 {
8889 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8890 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8891 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008892 msg_outtrans(ts);
8893 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008894 }
8895 return didh;
8896}
8897
8898#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8899/*
8900 * Return "1" if highlight group "id" has attribute "flag".
8901 * Return NULL otherwise.
8902 */
8903 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008904highlight_has_attr(
8905 int id,
8906 int flag,
8907 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008908{
8909 int attr;
8910
8911 if (id <= 0 || id > highlight_ga.ga_len)
8912 return NULL;
8913
Bram Moolenaar61623362010-07-14 22:04:22 +02008914#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008915 if (modec == 'g')
8916 attr = HL_TABLE()[id - 1].sg_gui;
8917 else
8918#endif
8919 if (modec == 'c')
8920 attr = HL_TABLE()[id - 1].sg_cterm;
8921 else
8922 attr = HL_TABLE()[id - 1].sg_term;
8923
8924 if (attr & flag)
8925 return (char_u *)"1";
8926 return NULL;
8927}
8928#endif
8929
8930#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8931/*
8932 * Return color name of highlight group "id".
8933 */
8934 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008935highlight_color(
8936 int id,
8937 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8938 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008939{
8940 static char_u name[20];
8941 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008942 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008943 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008944 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008945
8946 if (id <= 0 || id > highlight_ga.ga_len)
8947 return NULL;
8948
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008949 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008950 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008951 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008952 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008953 font = TRUE;
8954 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008955 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008956 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8957 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008958 if (modec == 'g')
8959 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008960# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008961 /* return font name */
8962 if (font)
8963 return HL_TABLE()[id - 1].sg_font_name;
8964
Bram Moolenaar071d4272004-06-13 20:20:40 +00008965 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008966 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008967 {
8968 guicolor_T color;
8969 long_u rgb;
8970 static char_u buf[10];
8971
8972 if (fg)
8973 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008974 else if (sp)
8975 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008976 else
8977 color = HL_TABLE()[id - 1].sg_gui_bg;
8978 if (color == INVALCOLOR)
8979 return NULL;
8980 rgb = gui_mch_get_rgb(color);
8981 sprintf((char *)buf, "#%02x%02x%02x",
8982 (unsigned)(rgb >> 16),
8983 (unsigned)(rgb >> 8) & 255,
8984 (unsigned)rgb & 255);
8985 return buf;
8986 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008987#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008988 if (fg)
8989 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008990 if (sp)
8991 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008992 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8993 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008994 if (font || sp)
8995 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008996 if (modec == 'c')
8997 {
8998 if (fg)
8999 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9000 else
9001 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009002 if (n < 0)
9003 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009004 sprintf((char *)name, "%d", n);
9005 return name;
9006 }
9007 /* term doesn't have color */
9008 return NULL;
9009}
9010#endif
9011
9012#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
9013 || defined(PROTO)
9014/*
9015 * Return color name of highlight group "id" as RGB value.
9016 */
9017 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009018highlight_gui_color_rgb(
9019 int id,
9020 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009021{
9022 guicolor_T color;
9023
9024 if (id <= 0 || id > highlight_ga.ga_len)
9025 return 0L;
9026
9027 if (fg)
9028 color = HL_TABLE()[id - 1].sg_gui_fg;
9029 else
9030 color = HL_TABLE()[id - 1].sg_gui_bg;
9031
9032 if (color == INVALCOLOR)
9033 return 0L;
9034
9035 return gui_mch_get_rgb(color);
9036}
9037#endif
9038
9039/*
9040 * Output the syntax list header.
9041 * Return TRUE when started a new line.
9042 */
9043 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009044syn_list_header(
9045 int did_header, /* did header already */
9046 int outlen, /* length of string that comes */
9047 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009048{
9049 int endcol = 19;
9050 int newline = TRUE;
9051
9052 if (!did_header)
9053 {
9054 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009055 if (got_int)
9056 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009057 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9058 endcol = 15;
9059 }
9060 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009061 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009062 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009063 if (got_int)
9064 return TRUE;
9065 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009066 else
9067 {
9068 if (msg_col >= endcol) /* wrap around is like starting a new line */
9069 newline = FALSE;
9070 }
9071
9072 if (msg_col >= endcol) /* output at least one space */
9073 endcol = msg_col + 1;
9074 if (Columns <= endcol) /* avoid hang for tiny window */
9075 endcol = Columns - 1;
9076
9077 msg_advance(endcol);
9078
9079 /* Show "xxx" with the attributes. */
9080 if (!did_header)
9081 {
9082 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9083 msg_putchar(' ');
9084 }
9085
9086 return newline;
9087}
9088
9089/*
9090 * Set the attribute numbers for a highlight group.
9091 * Called after one of the attributes has changed.
9092 */
9093 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009094set_hl_attr(
9095 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009096{
9097 attrentry_T at_en;
9098 struct hl_group *sgp = HL_TABLE() + idx;
9099
9100 /* The "Normal" group doesn't need an attribute number */
9101 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9102 return;
9103
9104#ifdef FEAT_GUI
9105 /*
9106 * For the GUI mode: If there are other than "normal" highlighting
9107 * attributes, need to allocate an attr number.
9108 */
9109 if (sgp->sg_gui_fg == INVALCOLOR
9110 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009111 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009112 && sgp->sg_font == NOFONT
9113# ifdef FEAT_XFONTSET
9114 && sgp->sg_fontset == NOFONTSET
9115# endif
9116 )
9117 {
9118 sgp->sg_gui_attr = sgp->sg_gui;
9119 }
9120 else
9121 {
9122 at_en.ae_attr = sgp->sg_gui;
9123 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9124 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009125 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009126 at_en.ae_u.gui.font = sgp->sg_font;
9127# ifdef FEAT_XFONTSET
9128 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9129# endif
9130 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9131 }
9132#endif
9133 /*
9134 * For the term mode: If there are other than "normal" highlighting
9135 * attributes, need to allocate an attr number.
9136 */
9137 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9138 sgp->sg_term_attr = sgp->sg_term;
9139 else
9140 {
9141 at_en.ae_attr = sgp->sg_term;
9142 at_en.ae_u.term.start = sgp->sg_start;
9143 at_en.ae_u.term.stop = sgp->sg_stop;
9144 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9145 }
9146
9147 /*
9148 * For the color term mode: If there are other than "normal"
9149 * highlighting attributes, need to allocate an attr number.
9150 */
9151 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9152 sgp->sg_cterm_attr = sgp->sg_cterm;
9153 else
9154 {
9155 at_en.ae_attr = sgp->sg_cterm;
9156 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9157 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9158 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9159 }
9160}
9161
9162/*
9163 * Lookup a highlight group name and return it's ID.
9164 * If it is not found, 0 is returned.
9165 */
9166 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009167syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009168{
9169 int i;
9170 char_u name_u[200];
9171
9172 /* Avoid using stricmp() too much, it's slow on some systems */
9173 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9174 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009175 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009176 vim_strup(name_u);
9177 for (i = highlight_ga.ga_len; --i >= 0; )
9178 if (HL_TABLE()[i].sg_name_u != NULL
9179 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9180 break;
9181 return i + 1;
9182}
9183
9184#if defined(FEAT_EVAL) || defined(PROTO)
9185/*
9186 * Return TRUE if highlight group "name" exists.
9187 */
9188 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009189highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009190{
9191 return (syn_name2id(name) > 0);
9192}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009193
9194# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9195/*
9196 * Return the name of highlight group "id".
9197 * When not a valid ID return an empty string.
9198 */
9199 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009200syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009201{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009202 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009203 return (char_u *)"";
9204 return HL_TABLE()[id - 1].sg_name;
9205}
9206# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009207#endif
9208
9209/*
9210 * Like syn_name2id(), but take a pointer + length argument.
9211 */
9212 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009213syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009214{
9215 char_u *name;
9216 int id = 0;
9217
9218 name = vim_strnsave(linep, len);
9219 if (name != NULL)
9220 {
9221 id = syn_name2id(name);
9222 vim_free(name);
9223 }
9224 return id;
9225}
9226
9227/*
9228 * Find highlight group name in the table and return it's ID.
9229 * The argument is a pointer to the name and the length of the name.
9230 * If it doesn't exist yet, a new entry is created.
9231 * Return 0 for failure.
9232 */
9233 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009234syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009235{
9236 int id;
9237 char_u *name;
9238
9239 name = vim_strnsave(pp, len);
9240 if (name == NULL)
9241 return 0;
9242
9243 id = syn_name2id(name);
9244 if (id == 0) /* doesn't exist yet */
9245 id = syn_add_group(name);
9246 else
9247 vim_free(name);
9248 return id;
9249}
9250
9251/*
9252 * Add new highlight group and return it's ID.
9253 * "name" must be an allocated string, it will be consumed.
9254 * Return 0 for failure.
9255 */
9256 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009257syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009258{
9259 char_u *p;
9260
9261 /* Check that the name is ASCII letters, digits and underscore. */
9262 for (p = name; *p != NUL; ++p)
9263 {
9264 if (!vim_isprintc(*p))
9265 {
9266 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009267 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009268 return 0;
9269 }
9270 else if (!ASCII_ISALNUM(*p) && *p != '_')
9271 {
9272 /* This is an error, but since there previously was no check only
9273 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009274 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009275 MSG(_("W18: Invalid character in group name"));
9276 break;
9277 }
9278 }
9279
9280 /*
9281 * First call for this growarray: init growing array.
9282 */
9283 if (highlight_ga.ga_data == NULL)
9284 {
9285 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9286 highlight_ga.ga_growsize = 10;
9287 }
9288
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009289 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009290 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009291 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009292 vim_free(name);
9293 return 0;
9294 }
9295
Bram Moolenaar071d4272004-06-13 20:20:40 +00009296 /*
9297 * Make room for at least one other syntax_highlight entry.
9298 */
9299 if (ga_grow(&highlight_ga, 1) == FAIL)
9300 {
9301 vim_free(name);
9302 return 0;
9303 }
9304
9305 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9306 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9307 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9308#ifdef FEAT_GUI
9309 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9310 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009311 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009312#endif
9313 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009314
9315 return highlight_ga.ga_len; /* ID is index plus one */
9316}
9317
9318/*
9319 * When, just after calling syn_add_group(), an error is discovered, this
9320 * function deletes the new name.
9321 */
9322 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009323syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009324{
9325 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009326 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9327 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9328}
9329
9330/*
9331 * Translate a group ID to highlight attributes.
9332 */
9333 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009334syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009335{
9336 int attr;
9337 struct hl_group *sgp;
9338
9339 hl_id = syn_get_final_id(hl_id);
9340 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9341
9342#ifdef FEAT_GUI
9343 /*
9344 * Only use GUI attr when the GUI is being used.
9345 */
9346 if (gui.in_use)
9347 attr = sgp->sg_gui_attr;
9348 else
9349#endif
9350 if (t_colors > 1)
9351 attr = sgp->sg_cterm_attr;
9352 else
9353 attr = sgp->sg_term_attr;
9354
9355 return attr;
9356}
9357
9358#ifdef FEAT_GUI
9359/*
9360 * Get the GUI colors and attributes for a group ID.
9361 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9362 */
9363 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009364syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009365{
9366 struct hl_group *sgp;
9367
9368 hl_id = syn_get_final_id(hl_id);
9369 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9370
9371 *fgp = sgp->sg_gui_fg;
9372 *bgp = sgp->sg_gui_bg;
9373 return sgp->sg_gui;
9374}
9375#endif
9376
9377/*
9378 * Translate a group ID to the final group ID (following links).
9379 */
9380 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009381syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009382{
9383 int count;
9384 struct hl_group *sgp;
9385
9386 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9387 return 0; /* Can be called from eval!! */
9388
9389 /*
9390 * Follow links until there is no more.
9391 * Look out for loops! Break after 100 links.
9392 */
9393 for (count = 100; --count >= 0; )
9394 {
9395 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9396 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9397 break;
9398 hl_id = sgp->sg_link;
9399 }
9400
9401 return hl_id;
9402}
9403
9404#ifdef FEAT_GUI
9405/*
9406 * Call this function just after the GUI has started.
9407 * It finds the font and color handles for the highlighting groups.
9408 */
9409 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009410highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009411{
9412 int idx;
9413
9414 /* First get the colors from the "Normal" and "Menu" group, if set */
9415 set_normal_colors();
9416
9417 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9418 gui_do_one_color(idx, FALSE, FALSE);
9419
9420 highlight_changed();
9421}
9422
9423 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009424gui_do_one_color(
9425 int idx,
9426 int do_menu, /* TRUE: might set the menu font */
9427 int do_tooltip) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009428{
9429 int didit = FALSE;
9430
9431 if (HL_TABLE()[idx].sg_font_name != NULL)
9432 {
9433 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009434 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009435 didit = TRUE;
9436 }
9437 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9438 {
9439 HL_TABLE()[idx].sg_gui_fg =
9440 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9441 didit = TRUE;
9442 }
9443 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9444 {
9445 HL_TABLE()[idx].sg_gui_bg =
9446 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9447 didit = TRUE;
9448 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009449 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9450 {
9451 HL_TABLE()[idx].sg_gui_sp =
9452 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9453 didit = TRUE;
9454 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009455 if (didit) /* need to get a new attr number */
9456 set_hl_attr(idx);
9457}
9458
9459#endif
9460
9461/*
9462 * Translate the 'highlight' option into attributes in highlight_attr[] and
9463 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9464 * corresponding highlights to use on top of HLF_SNC is computed.
9465 * Called only when the 'highlight' option has been changed and upon first
9466 * screen redraw after any :highlight command.
9467 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9468 */
9469 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009470highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009471{
9472 int hlf;
9473 int i;
9474 char_u *p;
9475 int attr;
9476 char_u *end;
9477 int id;
9478#ifdef USER_HIGHLIGHT
9479 char_u userhl[10];
9480# ifdef FEAT_STL_OPT
9481 int id_SNC = -1;
9482 int id_S = -1;
9483 int hlcnt;
9484# endif
9485#endif
9486 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9487
9488 need_highlight_changed = FALSE;
9489
9490 /*
9491 * Clear all attributes.
9492 */
9493 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9494 highlight_attr[hlf] = 0;
9495
9496 /*
9497 * First set all attributes to their default value.
9498 * Then use the attributes from the 'highlight' option.
9499 */
9500 for (i = 0; i < 2; ++i)
9501 {
9502 if (i)
9503 p = p_hl;
9504 else
9505 p = get_highlight_default();
9506 if (p == NULL) /* just in case */
9507 continue;
9508
9509 while (*p)
9510 {
9511 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9512 if (hl_flags[hlf] == *p)
9513 break;
9514 ++p;
9515 if (hlf == (int)HLF_COUNT || *p == NUL)
9516 return FAIL;
9517
9518 /*
9519 * Allow several hl_flags to be combined, like "bu" for
9520 * bold-underlined.
9521 */
9522 attr = 0;
9523 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9524 {
9525 if (vim_iswhite(*p)) /* ignore white space */
9526 continue;
9527
9528 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9529 return FAIL;
9530
9531 switch (*p)
9532 {
9533 case 'b': attr |= HL_BOLD;
9534 break;
9535 case 'i': attr |= HL_ITALIC;
9536 break;
9537 case '-':
9538 case 'n': /* no highlighting */
9539 break;
9540 case 'r': attr |= HL_INVERSE;
9541 break;
9542 case 's': attr |= HL_STANDOUT;
9543 break;
9544 case 'u': attr |= HL_UNDERLINE;
9545 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009546 case 'c': attr |= HL_UNDERCURL;
9547 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009548 case ':': ++p; /* highlight group name */
9549 if (attr || *p == NUL) /* no combinations */
9550 return FAIL;
9551 end = vim_strchr(p, ',');
9552 if (end == NULL)
9553 end = p + STRLEN(p);
9554 id = syn_check_group(p, (int)(end - p));
9555 if (id == 0)
9556 return FAIL;
9557 attr = syn_id2attr(id);
9558 p = end - 1;
9559#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9560 if (hlf == (int)HLF_SNC)
9561 id_SNC = syn_get_final_id(id);
9562 else if (hlf == (int)HLF_S)
9563 id_S = syn_get_final_id(id);
9564#endif
9565 break;
9566 default: return FAIL;
9567 }
9568 }
9569 highlight_attr[hlf] = attr;
9570
9571 p = skip_to_option_part(p); /* skip comma and spaces */
9572 }
9573 }
9574
9575#ifdef USER_HIGHLIGHT
9576 /* Setup the user highlights
9577 *
9578 * Temporarily utilize 10 more hl entries. Have to be in there
9579 * simultaneously in case of table overflows in get_attr_entry()
9580 */
9581# ifdef FEAT_STL_OPT
9582 if (ga_grow(&highlight_ga, 10) == FAIL)
9583 return FAIL;
9584 hlcnt = highlight_ga.ga_len;
9585 if (id_S == 0)
9586 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009587 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009588 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9589 id_S = hlcnt + 10;
9590 }
9591# endif
9592 for (i = 0; i < 9; i++)
9593 {
9594 sprintf((char *)userhl, "User%d", i + 1);
9595 id = syn_name2id(userhl);
9596 if (id == 0)
9597 {
9598 highlight_user[i] = 0;
9599# ifdef FEAT_STL_OPT
9600 highlight_stlnc[i] = 0;
9601# endif
9602 }
9603 else
9604 {
9605# ifdef FEAT_STL_OPT
9606 struct hl_group *hlt = HL_TABLE();
9607# endif
9608
9609 highlight_user[i] = syn_id2attr(id);
9610# ifdef FEAT_STL_OPT
9611 if (id_SNC == 0)
9612 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009613 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009614 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9615 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009616# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009617 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9618# endif
9619 }
9620 else
9621 mch_memmove(&hlt[hlcnt + i],
9622 &hlt[id_SNC - 1],
9623 sizeof(struct hl_group));
9624 hlt[hlcnt + i].sg_link = 0;
9625
9626 /* Apply difference between UserX and HLF_S to HLF_SNC */
9627 hlt[hlcnt + i].sg_term ^=
9628 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9629 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9630 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9631 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9632 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9633 hlt[hlcnt + i].sg_cterm ^=
9634 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9635 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9636 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9637 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9638 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009639# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009640 hlt[hlcnt + i].sg_gui ^=
9641 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009642# endif
9643# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009644 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9645 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9646 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9647 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009648 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9649 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009650 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9651 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9652# ifdef FEAT_XFONTSET
9653 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9654 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9655# endif
9656# endif
9657 highlight_ga.ga_len = hlcnt + i + 1;
9658 set_hl_attr(hlcnt + i); /* At long last we can apply */
9659 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9660# endif
9661 }
9662 }
9663# ifdef FEAT_STL_OPT
9664 highlight_ga.ga_len = hlcnt;
9665# endif
9666
9667#endif /* USER_HIGHLIGHT */
9668
9669 return OK;
9670}
9671
Bram Moolenaar4f688582007-07-24 12:34:30 +00009672#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009673
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009674static void highlight_list(void);
9675static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009676
9677/*
9678 * Handle command line completion for :highlight command.
9679 */
9680 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009681set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009682{
9683 char_u *p;
9684
9685 /* Default: expand group names */
9686 xp->xp_context = EXPAND_HIGHLIGHT;
9687 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009688 include_link = 2;
9689 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009690
9691 /* (part of) subcommand already typed */
9692 if (*arg != NUL)
9693 {
9694 p = skiptowhite(arg);
9695 if (*p != NUL) /* past "default" or group name */
9696 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009697 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009698 if (STRNCMP("default", arg, p - arg) == 0)
9699 {
9700 arg = skipwhite(p);
9701 xp->xp_pattern = arg;
9702 p = skiptowhite(arg);
9703 }
9704 if (*p != NUL) /* past group name */
9705 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009706 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009707 if (arg[1] == 'i' && arg[0] == 'N')
9708 highlight_list();
9709 if (STRNCMP("link", arg, p - arg) == 0
9710 || STRNCMP("clear", arg, p - arg) == 0)
9711 {
9712 xp->xp_pattern = skipwhite(p);
9713 p = skiptowhite(xp->xp_pattern);
9714 if (*p != NUL) /* past first group name */
9715 {
9716 xp->xp_pattern = skipwhite(p);
9717 p = skiptowhite(xp->xp_pattern);
9718 }
9719 }
9720 if (*p != NUL) /* past group name(s) */
9721 xp->xp_context = EXPAND_NOTHING;
9722 }
9723 }
9724 }
9725}
9726
9727/*
9728 * List highlighting matches in a nice way.
9729 */
9730 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009731highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009732{
9733 int i;
9734
9735 for (i = 10; --i >= 0; )
9736 highlight_list_two(i, hl_attr(HLF_D));
9737 for (i = 40; --i >= 0; )
9738 highlight_list_two(99, 0);
9739}
9740
9741 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009742highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009743{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009744 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009745 msg_clr_eos();
9746 out_flush();
9747 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9748}
9749
9750#endif /* FEAT_CMDL_COMPL */
9751
9752#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9753 || defined(FEAT_SIGNS) || defined(PROTO)
9754/*
9755 * Function given to ExpandGeneric() to obtain the list of group names.
9756 * Also used for synIDattr() function.
9757 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009758 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009759get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009760{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009761#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009762 if (idx == highlight_ga.ga_len && include_none != 0)
9763 return (char_u *)"none";
9764 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009765 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009766 if (idx == highlight_ga.ga_len + include_none + include_default
9767 && include_link != 0)
9768 return (char_u *)"link";
9769 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9770 && include_link != 0)
9771 return (char_u *)"clear";
9772#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009773 if (idx < 0 || idx >= highlight_ga.ga_len)
9774 return NULL;
9775 return HL_TABLE()[idx].sg_name;
9776}
9777#endif
9778
Bram Moolenaar4f688582007-07-24 12:34:30 +00009779#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009780/*
9781 * Free all the highlight group fonts.
9782 * Used when quitting for systems which need it.
9783 */
9784 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009785free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009786{
9787 int idx;
9788
9789 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9790 {
9791 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9792 HL_TABLE()[idx].sg_font = NOFONT;
9793# ifdef FEAT_XFONTSET
9794 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9795 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9796# endif
9797 }
9798
9799 gui_mch_free_font(gui.norm_font);
9800# ifdef FEAT_XFONTSET
9801 gui_mch_free_fontset(gui.fontset);
9802# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009803# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009804 gui_mch_free_font(gui.bold_font);
9805 gui_mch_free_font(gui.ital_font);
9806 gui_mch_free_font(gui.boldital_font);
9807# endif
9808}
9809#endif
9810
9811/**************************************
9812 * End of Highlighting stuff *
9813 **************************************/