blob: a43f4a63a0da80aac0c5aa98bb72623425e3bbc6 [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 Moolenaarb681be12016-03-31 23:02:16 +0200512 if (syn_block != wp->w_s
513 || syn_buf != wp->w_buffer
514 || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 {
516 invalidate_current_state();
517 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200518 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000520 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 syn_win = wp;
522
523 /*
524 * Allocate syntax stack when needed.
525 */
526 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200527 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000528 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200529 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530
531 /*
532 * If the state of the end of the previous line is useful, store it.
533 */
534 if (VALID_STATE(&current_state)
535 && current_lnum < lnum
536 && current_lnum < syn_buf->b_ml.ml_line_count)
537 {
538 (void)syn_finish_line(FALSE);
539 if (!current_state_stored)
540 {
541 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000542 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 }
544
545 /*
546 * If the current_lnum is now the same as "lnum", keep the current
547 * state (this happens very often!). Otherwise invalidate
548 * current_state and figure it out below.
549 */
550 if (current_lnum != lnum)
551 invalidate_current_state();
552 }
553 else
554 invalidate_current_state();
555
556 /*
557 * Try to synchronize from a saved state in b_sst_array[].
558 * Only do this if lnum is not before and not to far beyond a saved state.
559 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200560 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561 {
562 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200563 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000564 {
565 if (p->sst_lnum > lnum)
566 break;
567 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
568 {
569 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200570 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571 last_min_valid = p;
572 }
573 }
574 if (last_min_valid != NULL)
575 load_current_state(last_min_valid);
576 }
577
578 /*
579 * If "lnum" is before or far beyond a line with a saved state, need to
580 * re-synchronize.
581 */
582 if (INVALID_STATE(&current_state))
583 {
584 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200585 if (current_lnum == 1)
586 /* First line is always valid, no matter "minlines". */
587 first_stored = 1;
588 else
589 /* Need to parse "minlines" lines before state can be considered
590 * valid to store. */
591 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592 }
593 else
594 first_stored = current_lnum;
595
596 /*
597 * Advance from the sync point or saved state until the current line.
598 * Save some entries for syncing with later on.
599 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200600 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000601 dist = 999999;
602 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200603 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604 while (current_lnum < lnum)
605 {
606 syn_start_line();
607 (void)syn_finish_line(FALSE);
608 ++current_lnum;
609
610 /* If we parsed at least "minlines" lines or started at a valid
611 * state, the current state is considered valid. */
612 if (current_lnum >= first_stored)
613 {
614 /* Check if the saved state entry is for the current line and is
615 * equal to the current state. If so, then validate all saved
616 * states that depended on a change before the parsed line. */
617 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000618 prev = syn_stack_find_entry(current_lnum - 1);
619 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200620 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000622 sp = prev;
623 while (sp != NULL && sp->sst_lnum < current_lnum)
624 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 if (sp != NULL
626 && sp->sst_lnum == current_lnum
627 && syn_stack_equal(sp))
628 {
629 parsed_lnum = current_lnum;
630 prev = sp;
631 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
632 {
633 if (sp->sst_lnum <= lnum)
634 /* valid state before desired line, use this one */
635 prev = sp;
636 else if (sp->sst_change_lnum == 0)
637 /* past saved states depending on change, break here. */
638 break;
639 sp->sst_change_lnum = 0;
640 sp = sp->sst_next;
641 }
642 load_current_state(prev);
643 }
644 /* Store the state at this line when it's the first one, the line
645 * where we start parsing, or some distance from the previously
646 * saved state. But only when parsed at least 'minlines'. */
647 else if (prev == NULL
648 || current_lnum == lnum
649 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000650 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000651 }
652
653 /* This can take a long time: break when CTRL-C pressed. The current
654 * state will be wrong then. */
655 line_breakcheck();
656 if (got_int)
657 {
658 current_lnum = lnum;
659 break;
660 }
661 }
662
663 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000664}
665
666/*
667 * We cannot simply discard growarrays full of state_items or buf_states; we
668 * have to manually release their extmatch pointers first.
669 */
670 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100671clear_syn_state(synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672{
673 int i;
674 garray_T *gap;
675
676 if (p->sst_stacksize > SST_FIX_STATES)
677 {
678 gap = &(p->sst_union.sst_ga);
679 for (i = 0; i < gap->ga_len; i++)
680 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
681 ga_clear(gap);
682 }
683 else
684 {
685 for (i = 0; i < p->sst_stacksize; i++)
686 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
687 }
688}
689
690/*
691 * Cleanup the current_state stack.
692 */
693 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100694clear_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000695{
696 int i;
697 stateitem_T *sip;
698
699 sip = (stateitem_T *)(current_state.ga_data);
700 for (i = 0; i < current_state.ga_len; i++)
701 unref_extmatch(sip[i].si_extmatch);
702 ga_clear(&current_state);
703}
704
705/*
706 * Try to find a synchronisation point for line "lnum".
707 *
708 * This sets current_lnum and the current state. One of three methods is
709 * used:
710 * 1. Search backwards for the end of a C-comment.
711 * 2. Search backwards for given sync patterns.
712 * 3. Simply start on a given number of lines above "lnum".
713 */
714 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100715syn_sync(
716 win_T *wp,
717 linenr_T start_lnum,
718 synstate_T *last_valid)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719{
720 buf_T *curbuf_save;
721 win_T *curwin_save;
722 pos_T cursor_save;
723 int idx;
724 linenr_T lnum;
725 linenr_T end_lnum;
726 linenr_T break_lnum;
727 int had_sync_point;
728 stateitem_T *cur_si;
729 synpat_T *spp;
730 char_u *line;
731 int found_flags = 0;
732 int found_match_idx = 0;
733 linenr_T found_current_lnum = 0;
734 int found_current_col= 0;
735 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000736 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737
738 /*
739 * Clear any current state that might be hanging around.
740 */
741 invalidate_current_state();
742
743 /*
744 * Start at least "minlines" back. Default starting point for parsing is
745 * there.
746 * Start further back, to avoid that scrolling backwards will result in
747 * resyncing for every line. Now it resyncs only one out of N lines,
748 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
749 * Watch out for overflow when minlines is MAXLNUM.
750 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200751 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752 start_lnum = 1;
753 else
754 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200755 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200757 else if (syn_block->b_syn_sync_minlines < 10)
758 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200760 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
761 if (syn_block->b_syn_sync_maxlines != 0
762 && lnum > syn_block->b_syn_sync_maxlines)
763 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 if (lnum >= start_lnum)
765 start_lnum = 1;
766 else
767 start_lnum -= lnum;
768 }
769 current_lnum = start_lnum;
770
771 /*
772 * 1. Search backwards for the end of a C-style comment.
773 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200774 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775 {
776 /* Need to make syn_buf the current buffer for a moment, to be able to
777 * use find_start_comment(). */
778 curwin_save = curwin;
779 curwin = wp;
780 curbuf_save = curbuf;
781 curbuf = syn_buf;
782
783 /*
784 * Skip lines that end in a backslash.
785 */
786 for ( ; start_lnum > 1; --start_lnum)
787 {
788 line = ml_get(start_lnum - 1);
789 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
790 break;
791 }
792 current_lnum = start_lnum;
793
794 /* set cursor to start of search */
795 cursor_save = wp->w_cursor;
796 wp->w_cursor.lnum = start_lnum;
797 wp->w_cursor.col = 0;
798
799 /*
800 * If the line is inside a comment, need to find the syntax item that
801 * defines the comment.
802 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
803 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200804 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000805 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200806 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
807 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
808 == syn_block->b_syn_sync_id
809 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000810 {
811 validate_current_state();
812 if (push_current_state(idx) == OK)
813 update_si_attr(current_state.ga_len - 1);
814 break;
815 }
816 }
817
818 /* restore cursor and buffer */
819 wp->w_cursor = cursor_save;
820 curwin = curwin_save;
821 curbuf = curbuf_save;
822 }
823
824 /*
825 * 2. Search backwards for given sync patterns.
826 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200827 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000828 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200829 if (syn_block->b_syn_sync_maxlines != 0
830 && start_lnum > syn_block->b_syn_sync_maxlines)
831 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 else
833 break_lnum = 0;
834
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000835 found_m_endpos.lnum = 0;
836 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000837 end_lnum = start_lnum;
838 lnum = start_lnum;
839 while (--lnum > break_lnum)
840 {
841 /* This can take a long time: break when CTRL-C pressed. */
842 line_breakcheck();
843 if (got_int)
844 {
845 invalidate_current_state();
846 current_lnum = start_lnum;
847 break;
848 }
849
850 /* Check if we have run into a valid saved state stack now. */
851 if (last_valid != NULL && lnum == last_valid->sst_lnum)
852 {
853 load_current_state(last_valid);
854 break;
855 }
856
857 /*
858 * Check if the previous line has the line-continuation pattern.
859 */
860 if (lnum > 1 && syn_match_linecont(lnum - 1))
861 continue;
862
863 /*
864 * Start with nothing on the state stack
865 */
866 validate_current_state();
867
868 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
869 {
870 syn_start_line();
871 for (;;)
872 {
873 had_sync_point = syn_finish_line(TRUE);
874 /*
875 * When a sync point has been found, remember where, and
876 * continue to look for another one, further on in the line.
877 */
878 if (had_sync_point && current_state.ga_len)
879 {
880 cur_si = &CUR_STATE(current_state.ga_len - 1);
881 if (cur_si->si_m_endpos.lnum > start_lnum)
882 {
883 /* ignore match that goes to after where started */
884 current_lnum = end_lnum;
885 break;
886 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000887 if (cur_si->si_idx < 0)
888 {
889 /* Cannot happen? */
890 found_flags = 0;
891 found_match_idx = KEYWORD_IDX;
892 }
893 else
894 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200895 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000896 found_flags = spp->sp_flags;
897 found_match_idx = spp->sp_sync_idx;
898 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000899 found_current_lnum = current_lnum;
900 found_current_col = current_col;
901 found_m_endpos = cur_si->si_m_endpos;
902 /*
903 * Continue after the match (be aware of a zero-length
904 * match).
905 */
906 if (found_m_endpos.lnum > current_lnum)
907 {
908 current_lnum = found_m_endpos.lnum;
909 current_col = found_m_endpos.col;
910 if (current_lnum >= end_lnum)
911 break;
912 }
913 else if (found_m_endpos.col > current_col)
914 current_col = found_m_endpos.col;
915 else
916 ++current_col;
917
918 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000919 * an item that ends here, need to do that now. Be
920 * careful not to go past the NUL. */
921 prev_current_col = current_col;
922 if (syn_getcurline()[current_col] != NUL)
923 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000924 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000925 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000926 }
927 else
928 break;
929 }
930 }
931
932 /*
933 * If a sync point was encountered, break here.
934 */
935 if (found_flags)
936 {
937 /*
938 * Put the item that was specified by the sync point on the
939 * state stack. If there was no item specified, make the
940 * state stack empty.
941 */
942 clear_current_state();
943 if (found_match_idx >= 0
944 && push_current_state(found_match_idx) == OK)
945 update_si_attr(current_state.ga_len - 1);
946
947 /*
948 * When using "grouphere", continue from the sync point
949 * match, until the end of the line. Parsing starts at
950 * the next line.
951 * For "groupthere" the parsing starts at start_lnum.
952 */
953 if (found_flags & HL_SYNC_HERE)
954 {
955 if (current_state.ga_len)
956 {
957 cur_si = &CUR_STATE(current_state.ga_len - 1);
958 cur_si->si_h_startpos.lnum = found_current_lnum;
959 cur_si->si_h_startpos.col = found_current_col;
960 update_si_end(cur_si, (int)current_col, TRUE);
961 check_keepend();
962 }
963 current_col = found_m_endpos.col;
964 current_lnum = found_m_endpos.lnum;
965 (void)syn_finish_line(FALSE);
966 ++current_lnum;
967 }
968 else
969 current_lnum = start_lnum;
970
971 break;
972 }
973
974 end_lnum = lnum;
975 invalidate_current_state();
976 }
977
978 /* Ran into start of the file or exceeded maximum number of lines */
979 if (lnum <= break_lnum)
980 {
981 invalidate_current_state();
982 current_lnum = break_lnum + 1;
983 }
984 }
985
986 validate_current_state();
987}
988
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100989 static void
990save_chartab(char_u *chartab)
991{
992 if (syn_block->b_syn_isk != empty_option)
993 {
994 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
995 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
996 (size_t)32);
997 }
998}
999
1000 static void
1001restore_chartab(char_u *chartab)
1002{
1003 if (syn_win->w_s->b_syn_isk != empty_option)
1004 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1005}
1006
Bram Moolenaar071d4272004-06-13 20:20:40 +00001007/*
1008 * Return TRUE if the line-continuation pattern matches in line "lnum".
1009 */
1010 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001011syn_match_linecont(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012{
1013 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001014 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001015 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016
Bram Moolenaar860cae12010-06-05 23:22:07 +02001017 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001019 /* use syntax iskeyword option */
1020 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001021 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1022 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001023 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001024 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001025 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001026 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001027 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001028 }
1029 return FALSE;
1030}
1031
1032/*
1033 * Prepare the current state for the start of a line.
1034 */
1035 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001036syn_start_line(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037{
1038 current_finished = FALSE;
1039 current_col = 0;
1040
1041 /*
1042 * Need to update the end of a start/skip/end that continues from the
1043 * previous line and regions that have "keepend".
1044 */
1045 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001046 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001047 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001048 check_state_ends();
1049 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050
1051 next_match_idx = -1;
1052 ++current_line_id;
1053}
1054
1055/*
1056 * Check for items in the stack that need their end updated.
1057 * When "startofline" is TRUE the last item is always updated.
1058 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1059 */
1060 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001061syn_update_ends(int startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001062{
1063 stateitem_T *cur_si;
1064 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001065 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066
1067 if (startofline)
1068 {
1069 /* Check for a match carried over from a previous line with a
1070 * contained region. The match ends as soon as the region ends. */
1071 for (i = 0; i < current_state.ga_len; ++i)
1072 {
1073 cur_si = &CUR_STATE(i);
1074 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001075 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076 == SPTYPE_MATCH
1077 && cur_si->si_m_endpos.lnum < current_lnum)
1078 {
1079 cur_si->si_flags |= HL_MATCHCONT;
1080 cur_si->si_m_endpos.lnum = 0;
1081 cur_si->si_m_endpos.col = 0;
1082 cur_si->si_h_endpos = cur_si->si_m_endpos;
1083 cur_si->si_ends = TRUE;
1084 }
1085 }
1086 }
1087
1088 /*
1089 * Need to update the end of a start/skip/end that continues from the
1090 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001091 * influence contained items. If we've just removed "extend"
1092 * (startofline == 0) then we should update ends of normal regions
1093 * contained inside "keepend" because "extend" could have extended
1094 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001095 * Then check for items ending in column 0.
1096 */
1097 i = current_state.ga_len - 1;
1098 if (keepend_level >= 0)
1099 for ( ; i > keepend_level; --i)
1100 if (CUR_STATE(i).si_flags & HL_EXTEND)
1101 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001102
1103 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001104 for ( ; i < current_state.ga_len; ++i)
1105 {
1106 cur_si = &CUR_STATE(i);
1107 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001108 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001109 || (i == current_state.ga_len - 1 && startofline))
1110 {
1111 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1112 cur_si->si_h_startpos.lnum = current_lnum;
1113
1114 if (!(cur_si->si_flags & HL_MATCHCONT))
1115 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001116
1117 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1118 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119 }
1120 }
1121 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122}
1123
1124/****************************************
1125 * Handling of the state stack cache.
1126 */
1127
1128/*
1129 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1130 *
1131 * To speed up syntax highlighting, the state stack for the start of some
1132 * lines is cached. These entries can be used to start parsing at that point.
1133 *
1134 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1135 * valid entries. b_sst_first points to the first one, then follow sst_next.
1136 * The entries are sorted on line number. The first entry is often for line 2
1137 * (line 1 always starts with an empty stack).
1138 * There is also a list for free entries. This construction is used to avoid
1139 * having to allocate and free memory blocks too often.
1140 *
1141 * When making changes to the buffer, this is logged in b_mod_*. When calling
1142 * update_screen() to update the display, it will call
1143 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1144 * entries. The entries which are inside the changed area are removed,
1145 * because they must be recomputed. Entries below the changed have their line
1146 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1147 * set to indicate that a check must be made if the changed lines would change
1148 * the cached entry.
1149 *
1150 * When later displaying lines, an entry is stored for each line. Displayed
1151 * lines are likely to be displayed again, in which case the state at the
1152 * start of the line is needed.
1153 * For not displayed lines, an entry is stored for every so many lines. These
1154 * entries will be used e.g., when scrolling backwards. The distance between
1155 * entries depends on the number of lines in the buffer. For small buffers
1156 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1157 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1158 */
1159
Bram Moolenaar860cae12010-06-05 23:22:07 +02001160 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001161syn_stack_free_block(synblock_T *block)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001162{
1163 synstate_T *p;
1164
1165 if (block->b_sst_array != NULL)
1166 {
1167 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1168 clear_syn_state(p);
1169 vim_free(block->b_sst_array);
1170 block->b_sst_array = NULL;
1171 block->b_sst_len = 0;
1172 }
1173}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174/*
1175 * Free b_sst_array[] for buffer "buf".
1176 * Used when syntax items changed to force resyncing everywhere.
1177 */
1178 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001179syn_stack_free_all(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001180{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 win_T *wp;
1182
Bram Moolenaar860cae12010-06-05 23:22:07 +02001183 syn_stack_free_block(block);
1184
1185
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186#ifdef FEAT_FOLDING
1187 /* When using "syntax" fold method, must update all folds. */
1188 FOR_ALL_WINDOWS(wp)
1189 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001190 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 foldUpdateAll(wp);
1192 }
1193#endif
1194}
1195
1196/*
1197 * Allocate the syntax state stack for syn_buf when needed.
1198 * If the number of entries in b_sst_array[] is much too big or a bit too
1199 * small, reallocate it.
1200 * Also used to allocate b_sst_array[] for the first time.
1201 */
1202 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001203syn_stack_alloc(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204{
1205 long len;
1206 synstate_T *to, *from;
1207 synstate_T *sstp;
1208
1209 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1210 if (len < SST_MIN_ENTRIES)
1211 len = SST_MIN_ENTRIES;
1212 else if (len > SST_MAX_ENTRIES)
1213 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001214 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 {
1216 /* Allocate 50% too much, to avoid reallocating too often. */
1217 len = syn_buf->b_ml.ml_line_count;
1218 len = (len + len / 2) / SST_DIST + Rows * 2;
1219 if (len < SST_MIN_ENTRIES)
1220 len = SST_MIN_ENTRIES;
1221 else if (len > SST_MAX_ENTRIES)
1222 len = SST_MAX_ENTRIES;
1223
Bram Moolenaar860cae12010-06-05 23:22:07 +02001224 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225 {
1226 /* When shrinking the array, cleanup the existing stack.
1227 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229 && syn_stack_cleanup())
1230 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001231 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1232 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 }
1234
1235 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1236 if (sstp == NULL) /* out of memory! */
1237 return;
1238
1239 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 {
1242 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 from = from->sst_next)
1245 {
1246 ++to;
1247 *to = *from;
1248 to->sst_next = to + 1;
1249 }
1250 }
1251 if (to != sstp - 1)
1252 {
1253 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001254 syn_block->b_sst_first = sstp;
1255 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 }
1257 else
1258 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001259 syn_block->b_sst_first = NULL;
1260 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 }
1262
1263 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 while (++to < sstp + len)
1266 to->sst_next = to + 1;
1267 (sstp + len - 1)->sst_next = NULL;
1268
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 vim_free(syn_block->b_sst_array);
1270 syn_block->b_sst_array = sstp;
1271 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272 }
1273}
1274
1275/*
1276 * Check for changes in a buffer to affect stored syntax states. Uses the
1277 * b_mod_* fields.
1278 * Called from update_screen(), before screen is being updated, once for each
1279 * displayed buffer.
1280 */
1281 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001282syn_stack_apply_changes(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001283{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 win_T *wp;
1285
1286 syn_stack_apply_changes_block(&buf->b_s, buf);
1287
1288 FOR_ALL_WINDOWS(wp)
1289 {
1290 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1291 syn_stack_apply_changes_block(wp->w_s, buf);
1292 }
1293}
1294
1295 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001296syn_stack_apply_changes_block(synblock_T *block, buf_T *buf)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001297{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 synstate_T *p, *prev, *np;
1299 linenr_T n;
1300
Bram Moolenaar860cae12010-06-05 23:22:07 +02001301 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302 return;
1303
1304 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001305 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001307 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 {
1309 n = p->sst_lnum + buf->b_mod_xlines;
1310 if (n <= buf->b_mod_bot)
1311 {
1312 /* this state is inside the changed area, remove it */
1313 np = p->sst_next;
1314 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001315 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316 else
1317 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001318 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 p = np;
1320 continue;
1321 }
1322 /* This state is below the changed area. Remember the line
1323 * that needs to be parsed before this entry can be made valid
1324 * again. */
1325 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1326 {
1327 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1328 p->sst_change_lnum += buf->b_mod_xlines;
1329 else
1330 p->sst_change_lnum = buf->b_mod_top;
1331 }
1332 if (p->sst_change_lnum == 0
1333 || p->sst_change_lnum < buf->b_mod_bot)
1334 p->sst_change_lnum = buf->b_mod_bot;
1335
1336 p->sst_lnum = n;
1337 }
1338 prev = p;
1339 p = p->sst_next;
1340 }
1341}
1342
1343/*
1344 * Reduce the number of entries in the state stack for syn_buf.
1345 * Returns TRUE if at least one entry was freed.
1346 */
1347 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001348syn_stack_cleanup(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001349{
1350 synstate_T *p, *prev;
1351 disptick_T tick;
1352 int above;
1353 int dist;
1354 int retval = FALSE;
1355
Bram Moolenaar860cae12010-06-05 23:22:07 +02001356 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001357 return retval;
1358
1359 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001360 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001361 dist = 999999;
1362 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001363 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364
1365 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001366 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001367 * be removed. Set "above" when the "tick" for the oldest entry is above
1368 * "b_sst_lasttick" (the display tick wraps around).
1369 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001370 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001372 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1374 {
1375 if (prev->sst_lnum + dist > p->sst_lnum)
1376 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001377 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001378 {
1379 if (!above || p->sst_tick < tick)
1380 tick = p->sst_tick;
1381 above = TRUE;
1382 }
1383 else if (!above && p->sst_tick < tick)
1384 tick = p->sst_tick;
1385 }
1386 }
1387
1388 /*
1389 * Go through the list to make the entries for the oldest tick at an
1390 * interval of several lines.
1391 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001392 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1394 {
1395 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1396 {
1397 /* Move this entry from used list to free list */
1398 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001399 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400 p = prev;
1401 retval = TRUE;
1402 }
1403 }
1404 return retval;
1405}
1406
1407/*
1408 * Free the allocated memory for a syn_state item.
1409 * Move the entry into the free list.
1410 */
1411 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001412syn_stack_free_entry(synblock_T *block, synstate_T *p)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413{
1414 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001415 p->sst_next = block->b_sst_firstfree;
1416 block->b_sst_firstfree = p;
1417 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418}
1419
1420/*
1421 * Find an entry in the list of state stacks at or before "lnum".
1422 * Returns NULL when there is no entry or the first entry is after "lnum".
1423 */
1424 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001425syn_stack_find_entry(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426{
1427 synstate_T *p, *prev;
1428
1429 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001430 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431 {
1432 if (p->sst_lnum == lnum)
1433 return p;
1434 if (p->sst_lnum > lnum)
1435 break;
1436 }
1437 return prev;
1438}
1439
1440/*
1441 * Try saving the current state in b_sst_array[].
1442 * The current state must be valid for the start of the current_lnum line!
1443 */
1444 static synstate_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001445store_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446{
1447 int i;
1448 synstate_T *p;
1449 bufstate_T *bp;
1450 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001451 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001452
1453 /*
1454 * If the current state contains a start or end pattern that continues
1455 * from the previous line, we can't use it. Don't store it then.
1456 */
1457 for (i = current_state.ga_len - 1; i >= 0; --i)
1458 {
1459 cur_si = &CUR_STATE(i);
1460 if (cur_si->si_h_startpos.lnum >= current_lnum
1461 || cur_si->si_m_endpos.lnum >= current_lnum
1462 || cur_si->si_h_endpos.lnum >= current_lnum
1463 || (cur_si->si_end_idx
1464 && cur_si->si_eoe_pos.lnum >= current_lnum))
1465 break;
1466 }
1467 if (i >= 0)
1468 {
1469 if (sp != NULL)
1470 {
1471 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001472 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001473 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001474 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475 else
1476 {
1477 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001478 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 if (p->sst_next == sp)
1480 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001481 if (p != NULL) /* just in case */
1482 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001484 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 sp = NULL;
1486 }
1487 }
1488 else if (sp == NULL || sp->sst_lnum != current_lnum)
1489 {
1490 /*
1491 * Add a new entry
1492 */
1493 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001494 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 {
1496 (void)syn_stack_cleanup();
1497 /* "sp" may have been moved to the freelist now */
1498 sp = syn_stack_find_entry(current_lnum);
1499 }
1500 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001501 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 sp = NULL;
1503 else
1504 {
1505 /* Take the first item from the free list and put it in the used
1506 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001507 p = syn_block->b_sst_firstfree;
1508 syn_block->b_sst_firstfree = p->sst_next;
1509 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001510 if (sp == NULL)
1511 {
1512 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001513 p->sst_next = syn_block->b_sst_first;
1514 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 }
1516 else
1517 {
1518 /* insert in list after *sp */
1519 p->sst_next = sp->sst_next;
1520 sp->sst_next = p;
1521 }
1522 sp = p;
1523 sp->sst_stacksize = 0;
1524 sp->sst_lnum = current_lnum;
1525 }
1526 }
1527 if (sp != NULL)
1528 {
1529 /* When overwriting an existing state stack, clear it first */
1530 clear_syn_state(sp);
1531 sp->sst_stacksize = current_state.ga_len;
1532 if (current_state.ga_len > SST_FIX_STATES)
1533 {
1534 /* Need to clear it, might be something remaining from when the
1535 * length was less than SST_FIX_STATES. */
1536 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1537 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1538 sp->sst_stacksize = 0;
1539 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001540 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1542 }
1543 else
1544 bp = sp->sst_union.sst_stack;
1545 for (i = 0; i < sp->sst_stacksize; ++i)
1546 {
1547 bp[i].bs_idx = CUR_STATE(i).si_idx;
1548 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001549#ifdef FEAT_CONCEAL
1550 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1551 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1552#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1554 }
1555 sp->sst_next_flags = current_next_flags;
1556 sp->sst_next_list = current_next_list;
1557 sp->sst_tick = display_tick;
1558 sp->sst_change_lnum = 0;
1559 }
1560 current_state_stored = TRUE;
1561 return sp;
1562}
1563
1564/*
1565 * Copy a state stack from "from" in b_sst_array[] to current_state;
1566 */
1567 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001568load_current_state(synstate_T *from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001569{
1570 int i;
1571 bufstate_T *bp;
1572
1573 clear_current_state();
1574 validate_current_state();
1575 keepend_level = -1;
1576 if (from->sst_stacksize
1577 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1578 {
1579 if (from->sst_stacksize > SST_FIX_STATES)
1580 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1581 else
1582 bp = from->sst_union.sst_stack;
1583 for (i = 0; i < from->sst_stacksize; ++i)
1584 {
1585 CUR_STATE(i).si_idx = bp[i].bs_idx;
1586 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001587#ifdef FEAT_CONCEAL
1588 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1589 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1590#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1592 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1593 keepend_level = i;
1594 CUR_STATE(i).si_ends = FALSE;
1595 CUR_STATE(i).si_m_lnum = 0;
1596 if (CUR_STATE(i).si_idx >= 0)
1597 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001598 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 else
1600 CUR_STATE(i).si_next_list = NULL;
1601 update_si_attr(i);
1602 }
1603 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604 }
1605 current_next_list = from->sst_next_list;
1606 current_next_flags = from->sst_next_flags;
1607 current_lnum = from->sst_lnum;
1608}
1609
1610/*
1611 * Compare saved state stack "*sp" with the current state.
1612 * Return TRUE when they are equal.
1613 */
1614 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001615syn_stack_equal(synstate_T *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616{
1617 int i, j;
1618 bufstate_T *bp;
1619 reg_extmatch_T *six, *bsx;
1620
1621 /* First a quick check if the stacks have the same size end nextlist. */
1622 if (sp->sst_stacksize == current_state.ga_len
1623 && sp->sst_next_list == current_next_list)
1624 {
1625 /* Need to compare all states on both stacks. */
1626 if (sp->sst_stacksize > SST_FIX_STATES)
1627 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1628 else
1629 bp = sp->sst_union.sst_stack;
1630
1631 for (i = current_state.ga_len; --i >= 0; )
1632 {
1633 /* If the item has another index the state is different. */
1634 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1635 break;
1636 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1637 {
1638 /* When the extmatch pointers are different, the strings in
1639 * them can still be the same. Check if the extmatch
1640 * references are equal. */
1641 bsx = bp[i].bs_extmatch;
1642 six = CUR_STATE(i).si_extmatch;
1643 /* If one of the extmatch pointers is NULL the states are
1644 * different. */
1645 if (bsx == NULL || six == NULL)
1646 break;
1647 for (j = 0; j < NSUBEXP; ++j)
1648 {
1649 /* Check each referenced match string. They must all be
1650 * equal. */
1651 if (bsx->matches[j] != six->matches[j])
1652 {
1653 /* If the pointer is different it can still be the
1654 * same text. Compare the strings, ignore case when
1655 * the start item has the sp_ic flag set. */
1656 if (bsx->matches[j] == NULL
1657 || six->matches[j] == NULL)
1658 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001659 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660 ? MB_STRICMP(bsx->matches[j],
1661 six->matches[j]) != 0
1662 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1663 break;
1664 }
1665 }
1666 if (j != NSUBEXP)
1667 break;
1668 }
1669 }
1670 if (i < 0)
1671 return TRUE;
1672 }
1673 return FALSE;
1674}
1675
1676/*
1677 * We stop parsing syntax above line "lnum". If the stored state at or below
1678 * this line depended on a change before it, it now depends on the line below
1679 * the last parsed line.
1680 * The window looks like this:
1681 * line which changed
1682 * displayed line
1683 * displayed line
1684 * lnum -> line below window
1685 */
1686 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001687syntax_end_parsing(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688{
1689 synstate_T *sp;
1690
1691 sp = syn_stack_find_entry(lnum);
1692 if (sp != NULL && sp->sst_lnum < lnum)
1693 sp = sp->sst_next;
1694
1695 if (sp != NULL && sp->sst_change_lnum != 0)
1696 sp->sst_change_lnum = lnum;
1697}
1698
1699/*
1700 * End of handling of the state stack.
1701 ****************************************/
1702
1703 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001704invalidate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705{
1706 clear_current_state();
1707 current_state.ga_itemsize = 0; /* mark current_state invalid */
1708 current_next_list = NULL;
1709 keepend_level = -1;
1710}
1711
1712 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001713validate_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001714{
1715 current_state.ga_itemsize = sizeof(stateitem_T);
1716 current_state.ga_growsize = 3;
1717}
1718
1719/*
1720 * Return TRUE if the syntax at start of lnum changed since last time.
1721 * This will only be called just after get_syntax_attr() for the previous
1722 * line, to check if the next line needs to be redrawn too.
1723 */
1724 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001725syntax_check_changed(linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001726{
1727 int retval = TRUE;
1728 synstate_T *sp;
1729
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730 /*
1731 * Check the state stack when:
1732 * - lnum is just below the previously syntaxed line.
1733 * - lnum is not before the lines with saved states.
1734 * - lnum is not past the lines with saved states.
1735 * - lnum is at or before the last changed line.
1736 */
1737 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1738 {
1739 sp = syn_stack_find_entry(lnum);
1740 if (sp != NULL && sp->sst_lnum == lnum)
1741 {
1742 /*
1743 * finish the previous line (needed when not all of the line was
1744 * drawn)
1745 */
1746 (void)syn_finish_line(FALSE);
1747
1748 /*
1749 * Compare the current state with the previously saved state of
1750 * the line.
1751 */
1752 if (syn_stack_equal(sp))
1753 retval = FALSE;
1754
1755 /*
1756 * Store the current state in b_sst_array[] for later use.
1757 */
1758 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001759 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001760 }
1761 }
1762
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763 return retval;
1764}
1765
1766/*
1767 * Finish the current line.
1768 * This doesn't return any attributes, it only gets the state at the end of
1769 * the line. It can start anywhere in the line, as long as the current state
1770 * is valid.
1771 */
1772 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001773syn_finish_line(
1774 int syncing) /* called for syncing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775{
1776 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001777 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778
1779 if (!current_finished)
1780 {
1781 while (!current_finished)
1782 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001783 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 /*
1785 * When syncing, and found some item, need to check the item.
1786 */
1787 if (syncing && current_state.ga_len)
1788 {
1789 /*
1790 * Check for match with sync item.
1791 */
1792 cur_si = &CUR_STATE(current_state.ga_len - 1);
1793 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001794 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1796 return TRUE;
1797
1798 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001799 * that ends here, need to do that now. Be careful not to go
1800 * past the NUL. */
1801 prev_current_col = current_col;
1802 if (syn_getcurline()[current_col] != NUL)
1803 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001805 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001806 }
1807 ++current_col;
1808 }
1809 }
1810 return FALSE;
1811}
1812
1813/*
1814 * Return highlight attributes for next character.
1815 * Must first call syntax_start() once for the line.
1816 * "col" is normally 0 for the first use in a line, and increments by one each
1817 * time. It's allowed to skip characters and to stop before the end of the
1818 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001819 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1820 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821 */
1822 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001823get_syntax_attr(
1824 colnr_T col,
1825 int *can_spell,
1826 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827{
1828 int attr = 0;
1829
Bram Moolenaar349955a2007-08-14 21:07:36 +00001830 if (can_spell != NULL)
1831 /* Default: Only do spelling when there is no @Spell cluster or when
1832 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001833 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1834 ? (syn_block->b_spell_cluster_id == 0)
1835 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001836
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001838 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839 return 0;
1840
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001841 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001842 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001843 {
1844 clear_current_state();
1845#ifdef FEAT_EVAL
1846 current_id = 0;
1847 current_trans_id = 0;
1848#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001849#ifdef FEAT_CONCEAL
1850 current_flags = 0;
1851#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001852 return 0;
1853 }
1854
Bram Moolenaar071d4272004-06-13 20:20:40 +00001855 /* Make sure current_state is valid */
1856 if (INVALID_STATE(&current_state))
1857 validate_current_state();
1858
1859 /*
1860 * Skip from the current column to "col", get the attributes for "col".
1861 */
1862 while (current_col <= col)
1863 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001864 attr = syn_current_attr(FALSE, TRUE, can_spell,
1865 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001866 ++current_col;
1867 }
1868
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869 return attr;
1870}
1871
1872/*
1873 * Get syntax attributes for current_lnum, current_col.
1874 */
1875 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01001876syn_current_attr(
1877 int syncing, /* When 1: called for syncing */
1878 int displaying, /* result will be displayed */
1879 int *can_spell, /* return: do spell checking */
1880 int keep_state) /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001881{
1882 int syn_id;
1883 lpos_T endpos; /* was: char_u *endp; */
1884 lpos_T hl_startpos; /* was: int hl_startcol; */
1885 lpos_T hl_endpos;
1886 lpos_T eos_pos; /* end-of-start match (start region) */
1887 lpos_T eoe_pos; /* end-of-end pattern */
1888 int end_idx; /* group ID for end pattern */
1889 int idx;
1890 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001891 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 int startcol;
1893 int endcol;
1894 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001895 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896 short *next_list;
1897 int found_match; /* found usable match */
1898 static int try_next_column = FALSE; /* must try in next col */
1899 int do_keywords;
1900 regmmatch_T regmatch;
1901 lpos_T pos;
1902 int lc_col;
1903 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001904 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905 char_u *line; /* current line. NOTE: becomes invalid after
1906 looking for a pattern match! */
1907
1908 /* variables for zero-width matches that have a "nextgroup" argument */
1909 int keep_next_list;
1910 int zero_width_next_list = FALSE;
1911 garray_T zero_width_next_ga;
1912
1913 /*
1914 * No character, no attributes! Past end of line?
1915 * Do try matching with an empty line (could be the start of a region).
1916 */
1917 line = syn_getcurline();
1918 if (line[current_col] == NUL && current_col != 0)
1919 {
1920 /*
1921 * If we found a match after the last column, use it.
1922 */
1923 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1924 && next_match_col != MAXCOL)
1925 (void)push_next_match(NULL);
1926
1927 current_finished = TRUE;
1928 current_state_stored = FALSE;
1929 return 0;
1930 }
1931
1932 /* if the current or next character is NUL, we will finish the line now */
1933 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1934 {
1935 current_finished = TRUE;
1936 current_state_stored = FALSE;
1937 }
1938
1939 /*
1940 * When in the previous column there was a match but it could not be used
1941 * (empty match or already matched in this column) need to try again in
1942 * the next column.
1943 */
1944 if (try_next_column)
1945 {
1946 next_match_idx = -1;
1947 try_next_column = FALSE;
1948 }
1949
1950 /* Only check for keywords when not syncing and there are some. */
1951 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001952 && (syn_block->b_keywtab.ht_used > 0
1953 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001954
1955 /* Init the list of zero-width matches with a nextlist. This is used to
1956 * avoid matching the same item in the same position twice. */
1957 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1958
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001959 /* use syntax iskeyword option */
1960 save_chartab(buf_chartab);
1961
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 /*
1963 * Repeat matching keywords and patterns, to find contained items at the
1964 * same column. This stops when there are no extra matches at the current
1965 * column.
1966 */
1967 do
1968 {
1969 found_match = FALSE;
1970 keep_next_list = FALSE;
1971 syn_id = 0;
1972
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001973
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974 /*
1975 * 1. Check for a current state.
1976 * Only when there is no current state, or if the current state may
1977 * contain other things, we need to check for keywords and patterns.
1978 * Always need to check for contained items if some item has the
1979 * "containedin" argument (takes extra time!).
1980 */
1981 if (current_state.ga_len)
1982 cur_si = &CUR_STATE(current_state.ga_len - 1);
1983 else
1984 cur_si = NULL;
1985
Bram Moolenaar860cae12010-06-05 23:22:07 +02001986 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987 || cur_si->si_cont_list != NULL)
1988 {
1989 /*
1990 * 2. Check for keywords, if on a keyword char after a non-keyword
1991 * char. Don't do this when syncing.
1992 */
1993 if (do_keywords)
1994 {
1995 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001996 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001997 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001998 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999#ifdef FEAT_MBYTE
2000 - (has_mbyte
2001 ? (*mb_head_off)(line, line + current_col - 1)
2002 : 0)
2003#endif
2004 , syn_buf)))
2005 {
2006 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002007 &endcol, &flags, &next_list, cur_si,
2008 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002009 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 {
2011 if (push_current_state(KEYWORD_IDX) == OK)
2012 {
2013 cur_si = &CUR_STATE(current_state.ga_len - 1);
2014 cur_si->si_m_startcol = current_col;
2015 cur_si->si_h_startpos.lnum = current_lnum;
2016 cur_si->si_h_startpos.col = 0; /* starts right away */
2017 cur_si->si_m_endpos.lnum = current_lnum;
2018 cur_si->si_m_endpos.col = endcol;
2019 cur_si->si_h_endpos.lnum = current_lnum;
2020 cur_si->si_h_endpos.col = endcol;
2021 cur_si->si_ends = TRUE;
2022 cur_si->si_end_idx = 0;
2023 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002024#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002025 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002026 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002027 if (current_state.ga_len > 1)
2028 cur_si->si_flags |=
2029 CUR_STATE(current_state.ga_len - 2).si_flags
2030 & HL_CONCEAL;
2031#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002032 cur_si->si_id = syn_id;
2033 cur_si->si_trans_id = syn_id;
2034 if (flags & HL_TRANSP)
2035 {
2036 if (current_state.ga_len < 2)
2037 {
2038 cur_si->si_attr = 0;
2039 cur_si->si_trans_id = 0;
2040 }
2041 else
2042 {
2043 cur_si->si_attr = CUR_STATE(
2044 current_state.ga_len - 2).si_attr;
2045 cur_si->si_trans_id = CUR_STATE(
2046 current_state.ga_len - 2).si_trans_id;
2047 }
2048 }
2049 else
2050 cur_si->si_attr = syn_id2attr(syn_id);
2051 cur_si->si_cont_list = NULL;
2052 cur_si->si_next_list = next_list;
2053 check_keepend();
2054 }
2055 else
2056 vim_free(next_list);
2057 }
2058 }
2059 }
2060
2061 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002062 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002064 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002065 {
2066 /*
2067 * If we didn't check for a match yet, or we are past it, check
2068 * for any match with a pattern.
2069 */
2070 if (next_match_idx < 0 || next_match_col < (int)current_col)
2071 {
2072 /*
2073 * Check all relevant patterns for a match at this
2074 * position. This is complicated, because matching with a
2075 * pattern takes quite a bit of time, thus we want to
2076 * avoid doing it when it's not needed.
2077 */
2078 next_match_idx = 0; /* no match in this line yet */
2079 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002080 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002081 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002082 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 if ( spp->sp_syncing == syncing
2084 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2085 && (spp->sp_type == SPTYPE_MATCH
2086 || spp->sp_type == SPTYPE_START)
2087 && (current_next_list != NULL
2088 ? in_id_list(NULL, current_next_list,
2089 &spp->sp_syn, 0)
2090 : (cur_si == NULL
2091 ? !(spp->sp_flags & HL_CONTAINED)
2092 : in_id_list(cur_si,
2093 cur_si->si_cont_list, &spp->sp_syn,
2094 spp->sp_flags & HL_CONTAINED))))
2095 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002096 int r;
2097
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 /* If we already tried matching in this line, and
2099 * there isn't a match before next_match_col, skip
2100 * this item. */
2101 if (spp->sp_line_id == current_line_id
2102 && spp->sp_startcol >= next_match_col)
2103 continue;
2104 spp->sp_line_id = current_line_id;
2105
2106 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2107 if (lc_col < 0)
2108 lc_col = 0;
2109
2110 regmatch.rmm_ic = spp->sp_ic;
2111 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002112 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002113 current_lnum,
2114 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002115 IF_SYN_TIME(&spp->sp_time));
2116 spp->sp_prog = regmatch.regprog;
2117 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002118 {
2119 /* no match in this line, try another one */
2120 spp->sp_startcol = MAXCOL;
2121 continue;
2122 }
2123
2124 /*
2125 * Compute the first column of the match.
2126 */
2127 syn_add_start_off(&pos, &regmatch,
2128 spp, SPO_MS_OFF, -1);
2129 if (pos.lnum > current_lnum)
2130 {
2131 /* must have used end of match in a next line,
2132 * we can't handle that */
2133 spp->sp_startcol = MAXCOL;
2134 continue;
2135 }
2136 startcol = pos.col;
2137
2138 /* remember the next column where this pattern
2139 * matches in the current line */
2140 spp->sp_startcol = startcol;
2141
2142 /*
2143 * If a previously found match starts at a lower
2144 * column number, don't use this one.
2145 */
2146 if (startcol >= next_match_col)
2147 continue;
2148
2149 /*
2150 * If we matched this pattern at this position
2151 * before, skip it. Must retry in the next
2152 * column, because it may match from there.
2153 */
2154 if (did_match_already(idx, &zero_width_next_ga))
2155 {
2156 try_next_column = TRUE;
2157 continue;
2158 }
2159
2160 endpos.lnum = regmatch.endpos[0].lnum;
2161 endpos.col = regmatch.endpos[0].col;
2162
2163 /* Compute the highlight start. */
2164 syn_add_start_off(&hl_startpos, &regmatch,
2165 spp, SPO_HS_OFF, -1);
2166
2167 /* Compute the region start. */
2168 /* Default is to use the end of the match. */
2169 syn_add_end_off(&eos_pos, &regmatch,
2170 spp, SPO_RS_OFF, 0);
2171
2172 /*
2173 * Grab the external submatches before they get
2174 * overwritten. Reference count doesn't change.
2175 */
2176 unref_extmatch(cur_extmatch);
2177 cur_extmatch = re_extmatch_out;
2178 re_extmatch_out = NULL;
2179
2180 flags = 0;
2181 eoe_pos.lnum = 0; /* avoid warning */
2182 eoe_pos.col = 0;
2183 end_idx = 0;
2184 hl_endpos.lnum = 0;
2185
2186 /*
2187 * For a "oneline" the end must be found in the
2188 * same line too. Search for it after the end of
2189 * the match with the start pattern. Set the
2190 * resulting end positions at the same time.
2191 */
2192 if (spp->sp_type == SPTYPE_START
2193 && (spp->sp_flags & HL_ONELINE))
2194 {
2195 lpos_T startpos;
2196
2197 startpos = endpos;
2198 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2199 &flags, &eoe_pos, &end_idx, cur_extmatch);
2200 if (endpos.lnum == 0)
2201 continue; /* not found */
2202 }
2203
2204 /*
2205 * For a "match" the size must be > 0 after the
2206 * end offset needs has been added. Except when
2207 * syncing.
2208 */
2209 else if (spp->sp_type == SPTYPE_MATCH)
2210 {
2211 syn_add_end_off(&hl_endpos, &regmatch, spp,
2212 SPO_HE_OFF, 0);
2213 syn_add_end_off(&endpos, &regmatch, spp,
2214 SPO_ME_OFF, 0);
2215 if (endpos.lnum == current_lnum
2216 && (int)endpos.col + syncing < startcol)
2217 {
2218 /*
2219 * If an empty string is matched, may need
2220 * to try matching again at next column.
2221 */
2222 if (regmatch.startpos[0].col
2223 == regmatch.endpos[0].col)
2224 try_next_column = TRUE;
2225 continue;
2226 }
2227 }
2228
2229 /*
2230 * keep the best match so far in next_match_*
2231 */
2232 /* Highlighting must start after startpos and end
2233 * before endpos. */
2234 if (hl_startpos.lnum == current_lnum
2235 && (int)hl_startpos.col < startcol)
2236 hl_startpos.col = startcol;
2237 limit_pos_zero(&hl_endpos, &endpos);
2238
2239 next_match_idx = idx;
2240 next_match_col = startcol;
2241 next_match_m_endpos = endpos;
2242 next_match_h_endpos = hl_endpos;
2243 next_match_h_startpos = hl_startpos;
2244 next_match_flags = flags;
2245 next_match_eos_pos = eos_pos;
2246 next_match_eoe_pos = eoe_pos;
2247 next_match_end_idx = end_idx;
2248 unref_extmatch(next_match_extmatch);
2249 next_match_extmatch = cur_extmatch;
2250 cur_extmatch = NULL;
2251 }
2252 }
2253 }
2254
2255 /*
2256 * If we found a match at the current column, use it.
2257 */
2258 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2259 {
2260 synpat_T *lspp;
2261
2262 /* When a zero-width item matched which has a nextgroup,
2263 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002264 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002265 if (next_match_m_endpos.lnum == current_lnum
2266 && next_match_m_endpos.col == current_col
2267 && lspp->sp_next_list != NULL)
2268 {
2269 current_next_list = lspp->sp_next_list;
2270 current_next_flags = lspp->sp_flags;
2271 keep_next_list = TRUE;
2272 zero_width_next_list = TRUE;
2273
2274 /* Add the index to a list, so that we can check
2275 * later that we don't match it again (and cause an
2276 * endless loop). */
2277 if (ga_grow(&zero_width_next_ga, 1) == OK)
2278 {
2279 ((int *)(zero_width_next_ga.ga_data))
2280 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 }
2282 next_match_idx = -1;
2283 }
2284 else
2285 cur_si = push_next_match(cur_si);
2286 found_match = TRUE;
2287 }
2288 }
2289 }
2290
2291 /*
2292 * Handle searching for nextgroup match.
2293 */
2294 if (current_next_list != NULL && !keep_next_list)
2295 {
2296 /*
2297 * If a nextgroup was not found, continue looking for one if:
2298 * - this is an empty line and the "skipempty" option was given
2299 * - we are on white space and the "skipwhite" option was given
2300 */
2301 if (!found_match)
2302 {
2303 line = syn_getcurline();
2304 if (((current_next_flags & HL_SKIPWHITE)
2305 && vim_iswhite(line[current_col]))
2306 || ((current_next_flags & HL_SKIPEMPTY)
2307 && *line == NUL))
2308 break;
2309 }
2310
2311 /*
2312 * If a nextgroup was found: Use it, and continue looking for
2313 * contained matches.
2314 * If a nextgroup was not found: Continue looking for a normal
2315 * match.
2316 * When did set current_next_list for a zero-width item and no
2317 * match was found don't loop (would get stuck).
2318 */
2319 current_next_list = NULL;
2320 next_match_idx = -1;
2321 if (!zero_width_next_list)
2322 found_match = TRUE;
2323 }
2324
2325 } while (found_match);
2326
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002327 restore_chartab(buf_chartab);
2328
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 /*
2330 * Use attributes from the current state, if within its highlighting.
2331 * If not, use attributes from the current-but-one state, etc.
2332 */
2333 current_attr = 0;
2334#ifdef FEAT_EVAL
2335 current_id = 0;
2336 current_trans_id = 0;
2337#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002338#ifdef FEAT_CONCEAL
2339 current_flags = 0;
2340#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002341 if (cur_si != NULL)
2342 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002343#ifndef FEAT_EVAL
2344 int current_trans_id = 0;
2345#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2347 {
2348 sip = &CUR_STATE(idx);
2349 if ((current_lnum > sip->si_h_startpos.lnum
2350 || (current_lnum == sip->si_h_startpos.lnum
2351 && current_col >= sip->si_h_startpos.col))
2352 && (sip->si_h_endpos.lnum == 0
2353 || current_lnum < sip->si_h_endpos.lnum
2354 || (current_lnum == sip->si_h_endpos.lnum
2355 && current_col < sip->si_h_endpos.col)))
2356 {
2357 current_attr = sip->si_attr;
2358#ifdef FEAT_EVAL
2359 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002361 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362#ifdef FEAT_CONCEAL
2363 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002364 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002365 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002366#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 break;
2368 }
2369 }
2370
Bram Moolenaar217ad922005-03-20 22:37:15 +00002371 if (can_spell != NULL)
2372 {
2373 struct sp_syn sps;
2374
2375 /*
2376 * set "can_spell" to TRUE if spell checking is supposed to be
2377 * done in the current item.
2378 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002379 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002380 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002381 /* There is no @Spell cluster: Do spelling for items without
2382 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002383 if (syn_block->b_nospell_cluster_id == 0
2384 || current_trans_id == 0)
2385 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002386 else
2387 {
2388 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002389 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002390 sps.cont_in_list = NULL;
2391 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2392 }
2393 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002394 else
2395 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002396 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002397 * the @Spell cluster. But not when @NoSpell is also there.
2398 * At the toplevel only spell check when ":syn spell toplevel"
2399 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002400 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002401 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002402 else
2403 {
2404 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002405 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002406 sps.cont_in_list = NULL;
2407 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2408
Bram Moolenaar860cae12010-06-05 23:22:07 +02002409 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002410 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002411 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002412 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2413 *can_spell = FALSE;
2414 }
2415 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002416 }
2417 }
2418
2419
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420 /*
2421 * Check for end of current state (and the states before it) at the
2422 * next column. Don't do this for syncing, because we would miss a
2423 * single character match.
2424 * First check if the current state ends at the current column. It
2425 * may be for an empty match and a containing item might end in the
2426 * current column.
2427 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002428 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429 {
2430 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002431 if (current_state.ga_len > 0
2432 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 {
2434 ++current_col;
2435 check_state_ends();
2436 --current_col;
2437 }
2438 }
2439 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002440 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002441 /* Default: Only do spelling when there is no @Spell cluster or when
2442 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002443 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2444 ? (syn_block->b_spell_cluster_id == 0)
2445 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002447 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 if (current_next_list != NULL
2449 && syn_getcurline()[current_col + 1] == NUL
2450 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2451 current_next_list = NULL;
2452
2453 if (zero_width_next_ga.ga_len > 0)
2454 ga_clear(&zero_width_next_ga);
2455
2456 /* No longer need external matches. But keep next_match_extmatch. */
2457 unref_extmatch(re_extmatch_out);
2458 re_extmatch_out = NULL;
2459 unref_extmatch(cur_extmatch);
2460
2461 return current_attr;
2462}
2463
2464
2465/*
2466 * Check if we already matched pattern "idx" at the current column.
2467 */
2468 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002469did_match_already(int idx, garray_T *gap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470{
2471 int i;
2472
2473 for (i = current_state.ga_len; --i >= 0; )
2474 if (CUR_STATE(i).si_m_startcol == (int)current_col
2475 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2476 && CUR_STATE(i).si_idx == idx)
2477 return TRUE;
2478
2479 /* Zero-width matches with a nextgroup argument are not put on the syntax
2480 * stack, and can only be matched once anyway. */
2481 for (i = gap->ga_len; --i >= 0; )
2482 if (((int *)(gap->ga_data))[i] == idx)
2483 return TRUE;
2484
2485 return FALSE;
2486}
2487
2488/*
2489 * Push the next match onto the stack.
2490 */
2491 static stateitem_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002492push_next_match(stateitem_T *cur_si)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002493{
2494 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002495#ifdef FEAT_CONCEAL
2496 int save_flags;
2497#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498
Bram Moolenaar860cae12010-06-05 23:22:07 +02002499 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002500
2501 /*
2502 * Push the item in current_state stack;
2503 */
2504 if (push_current_state(next_match_idx) == OK)
2505 {
2506 /*
2507 * If it's a start-skip-end type that crosses lines, figure out how
2508 * much it continues in this line. Otherwise just fill in the length.
2509 */
2510 cur_si = &CUR_STATE(current_state.ga_len - 1);
2511 cur_si->si_h_startpos = next_match_h_startpos;
2512 cur_si->si_m_startcol = current_col;
2513 cur_si->si_m_lnum = current_lnum;
2514 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002515#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002516 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002517 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002518 if (current_state.ga_len > 1)
2519 cur_si->si_flags |=
2520 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2521#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522 cur_si->si_next_list = spp->sp_next_list;
2523 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2524 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2525 {
2526 /* Try to find the end pattern in the current line */
2527 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2528 check_keepend();
2529 }
2530 else
2531 {
2532 cur_si->si_m_endpos = next_match_m_endpos;
2533 cur_si->si_h_endpos = next_match_h_endpos;
2534 cur_si->si_ends = TRUE;
2535 cur_si->si_flags |= next_match_flags;
2536 cur_si->si_eoe_pos = next_match_eoe_pos;
2537 cur_si->si_end_idx = next_match_end_idx;
2538 }
2539 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2540 keepend_level = current_state.ga_len - 1;
2541 check_keepend();
2542 update_si_attr(current_state.ga_len - 1);
2543
Bram Moolenaar860cae12010-06-05 23:22:07 +02002544#ifdef FEAT_CONCEAL
2545 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2546#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547 /*
2548 * If the start pattern has another highlight group, push another item
2549 * on the stack for the start pattern.
2550 */
2551 if ( spp->sp_type == SPTYPE_START
2552 && spp->sp_syn_match_id != 0
2553 && push_current_state(next_match_idx) == OK)
2554 {
2555 cur_si = &CUR_STATE(current_state.ga_len - 1);
2556 cur_si->si_h_startpos = next_match_h_startpos;
2557 cur_si->si_m_startcol = current_col;
2558 cur_si->si_m_lnum = current_lnum;
2559 cur_si->si_m_endpos = next_match_eos_pos;
2560 cur_si->si_h_endpos = next_match_eos_pos;
2561 cur_si->si_ends = TRUE;
2562 cur_si->si_end_idx = 0;
2563 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002564#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002565 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002566 cur_si->si_flags |= save_flags;
2567 if (cur_si->si_flags & HL_CONCEALENDS)
2568 cur_si->si_flags |= HL_CONCEAL;
2569#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 cur_si->si_next_list = NULL;
2571 check_keepend();
2572 update_si_attr(current_state.ga_len - 1);
2573 }
2574 }
2575
2576 next_match_idx = -1; /* try other match next time */
2577
2578 return cur_si;
2579}
2580
2581/*
2582 * Check for end of current state (and the states before it).
2583 */
2584 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002585check_state_ends(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002586{
2587 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002588 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589
2590 cur_si = &CUR_STATE(current_state.ga_len - 1);
2591 for (;;)
2592 {
2593 if (cur_si->si_ends
2594 && (cur_si->si_m_endpos.lnum < current_lnum
2595 || (cur_si->si_m_endpos.lnum == current_lnum
2596 && cur_si->si_m_endpos.col <= current_col)))
2597 {
2598 /*
2599 * If there is an end pattern group ID, highlight the end pattern
2600 * now. No need to pop the current item from the stack.
2601 * Only do this if the end pattern continues beyond the current
2602 * position.
2603 */
2604 if (cur_si->si_end_idx
2605 && (cur_si->si_eoe_pos.lnum > current_lnum
2606 || (cur_si->si_eoe_pos.lnum == current_lnum
2607 && cur_si->si_eoe_pos.col > current_col)))
2608 {
2609 cur_si->si_idx = cur_si->si_end_idx;
2610 cur_si->si_end_idx = 0;
2611 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2612 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2613 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002614#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002615 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002616 if (cur_si->si_flags & HL_CONCEALENDS)
2617 cur_si->si_flags |= HL_CONCEAL;
2618#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002619 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002620
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002621 /* nextgroup= should not match in the end pattern */
2622 current_next_list = NULL;
2623
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002624 /* what matches next may be different now, clear it */
2625 next_match_idx = 0;
2626 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002627 break;
2628 }
2629 else
2630 {
2631 /* handle next_list, unless at end of line and no "skipnl" or
2632 * "skipempty" */
2633 current_next_list = cur_si->si_next_list;
2634 current_next_flags = cur_si->si_flags;
2635 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2636 && syn_getcurline()[current_col] == NUL)
2637 current_next_list = NULL;
2638
2639 /* When the ended item has "extend", another item with
2640 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002641 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642
2643 pop_current_state();
2644
2645 if (current_state.ga_len == 0)
2646 break;
2647
Bram Moolenaar81993f42008-01-11 20:27:45 +00002648 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 {
2650 syn_update_ends(FALSE);
2651 if (current_state.ga_len == 0)
2652 break;
2653 }
2654
2655 cur_si = &CUR_STATE(current_state.ga_len - 1);
2656
2657 /*
2658 * Only for a region the search for the end continues after
2659 * the end of the contained item. If the contained match
2660 * included the end-of-line, break here, the region continues.
2661 * Don't do this when:
2662 * - "keepend" is used for the contained item
2663 * - not at the end of the line (could be end="x$"me=e-1).
2664 * - "excludenl" is used (HL_HAS_EOL won't be set)
2665 */
2666 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002667 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668 == SPTYPE_START
2669 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2670 {
2671 update_si_end(cur_si, (int)current_col, TRUE);
2672 check_keepend();
2673 if ((current_next_flags & HL_HAS_EOL)
2674 && keepend_level < 0
2675 && syn_getcurline()[current_col] == NUL)
2676 break;
2677 }
2678 }
2679 }
2680 else
2681 break;
2682 }
2683}
2684
2685/*
2686 * Update an entry in the current_state stack for a match or region. This
2687 * fills in si_attr, si_next_list and si_cont_list.
2688 */
2689 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002690update_si_attr(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691{
2692 stateitem_T *sip = &CUR_STATE(idx);
2693 synpat_T *spp;
2694
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002695 /* This should not happen... */
2696 if (sip->si_idx < 0)
2697 return;
2698
Bram Moolenaar860cae12010-06-05 23:22:07 +02002699 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700 if (sip->si_flags & HL_MATCH)
2701 sip->si_id = spp->sp_syn_match_id;
2702 else
2703 sip->si_id = spp->sp_syn.id;
2704 sip->si_attr = syn_id2attr(sip->si_id);
2705 sip->si_trans_id = sip->si_id;
2706 if (sip->si_flags & HL_MATCH)
2707 sip->si_cont_list = NULL;
2708 else
2709 sip->si_cont_list = spp->sp_cont_list;
2710
2711 /*
2712 * For transparent items, take attr from outer item.
2713 * Also take cont_list, if there is none.
2714 * Don't do this for the matchgroup of a start or end pattern.
2715 */
2716 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2717 {
2718 if (idx == 0)
2719 {
2720 sip->si_attr = 0;
2721 sip->si_trans_id = 0;
2722 if (sip->si_cont_list == NULL)
2723 sip->si_cont_list = ID_LIST_ALL;
2724 }
2725 else
2726 {
2727 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2728 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002729 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2730 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 if (sip->si_cont_list == NULL)
2732 {
2733 sip->si_flags |= HL_TRANS_CONT;
2734 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2735 }
2736 }
2737 }
2738}
2739
2740/*
2741 * Check the current stack for patterns with "keepend" flag.
2742 * Propagate the match-end to contained items, until a "skipend" item is found.
2743 */
2744 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002745check_keepend(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746{
2747 int i;
2748 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002749 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750 stateitem_T *sip;
2751
2752 /*
2753 * This check can consume a lot of time; only do it from the level where
2754 * there really is a keepend.
2755 */
2756 if (keepend_level < 0)
2757 return;
2758
2759 /*
2760 * Find the last index of an "extend" item. "keepend" items before that
2761 * won't do anything. If there is no "extend" item "i" will be
2762 * "keepend_level" and all "keepend" items will work normally.
2763 */
2764 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2765 if (CUR_STATE(i).si_flags & HL_EXTEND)
2766 break;
2767
2768 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002769 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002770 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002771 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 for ( ; i < current_state.ga_len; ++i)
2773 {
2774 sip = &CUR_STATE(i);
2775 if (maxpos.lnum != 0)
2776 {
2777 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002778 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002779 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2780 sip->si_ends = TRUE;
2781 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002782 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2783 {
2784 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002785 || maxpos.lnum > sip->si_m_endpos.lnum
2786 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002787 && maxpos.col > sip->si_m_endpos.col))
2788 maxpos = sip->si_m_endpos;
2789 if (maxpos_h.lnum == 0
2790 || maxpos_h.lnum > sip->si_h_endpos.lnum
2791 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2792 && maxpos_h.col > sip->si_h_endpos.col))
2793 maxpos_h = sip->si_h_endpos;
2794 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 }
2796}
2797
2798/*
2799 * Update an entry in the current_state stack for a start-skip-end pattern.
2800 * This finds the end of the current item, if it's in the current line.
2801 *
2802 * Return the flags for the matched END.
2803 */
2804 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002805update_si_end(
2806 stateitem_T *sip,
2807 int startcol, /* where to start searching for the end */
2808 int force) /* when TRUE overrule a previous end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809{
2810 lpos_T startpos;
2811 lpos_T endpos;
2812 lpos_T hl_endpos;
2813 lpos_T end_endpos;
2814 int end_idx;
2815
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002816 /* return quickly for a keyword */
2817 if (sip->si_idx < 0)
2818 return;
2819
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 /* Don't update when it's already done. Can be a match of an end pattern
2821 * that started in a previous line. Watch out: can also be a "keepend"
2822 * from a containing item. */
2823 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2824 return;
2825
2826 /*
2827 * We need to find the end of the region. It may continue in the next
2828 * line.
2829 */
2830 end_idx = 0;
2831 startpos.lnum = current_lnum;
2832 startpos.col = startcol;
2833 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2834 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2835
2836 if (endpos.lnum == 0)
2837 {
2838 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002839 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002840 {
2841 /* a "oneline" never continues in the next line */
2842 sip->si_ends = TRUE;
2843 sip->si_m_endpos.lnum = current_lnum;
2844 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2845 }
2846 else
2847 {
2848 /* continues in the next line */
2849 sip->si_ends = FALSE;
2850 sip->si_m_endpos.lnum = 0;
2851 }
2852 sip->si_h_endpos = sip->si_m_endpos;
2853 }
2854 else
2855 {
2856 /* match within this line */
2857 sip->si_m_endpos = endpos;
2858 sip->si_h_endpos = hl_endpos;
2859 sip->si_eoe_pos = end_endpos;
2860 sip->si_ends = TRUE;
2861 sip->si_end_idx = end_idx;
2862 }
2863}
2864
2865/*
2866 * Add a new state to the current state stack.
2867 * It is cleared and the index set to "idx".
2868 * Return FAIL if it's not possible (out of memory).
2869 */
2870 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002871push_current_state(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002872{
2873 if (ga_grow(&current_state, 1) == FAIL)
2874 return FAIL;
2875 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2876 CUR_STATE(current_state.ga_len).si_idx = idx;
2877 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 return OK;
2879}
2880
2881/*
2882 * Remove a state from the current_state stack.
2883 */
2884 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002885pop_current_state(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886{
2887 if (current_state.ga_len)
2888 {
2889 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2890 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002891 }
2892 /* after the end of a pattern, try matching a keyword or pattern */
2893 next_match_idx = -1;
2894
2895 /* if first state with "keepend" is popped, reset keepend_level */
2896 if (keepend_level >= current_state.ga_len)
2897 keepend_level = -1;
2898}
2899
2900/*
2901 * Find the end of a start/skip/end syntax region after "startpos".
2902 * Only checks one line.
2903 * Also handles a match item that continued from a previous line.
2904 * If not found, the syntax item continues in the next line. m_endpos->lnum
2905 * will be 0.
2906 * If found, the end of the region and the end of the highlighting is
2907 * computed.
2908 */
2909 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01002910find_endpos(
2911 int idx, /* index of the pattern */
2912 lpos_T *startpos, /* where to start looking for an END match */
2913 lpos_T *m_endpos, /* return: end of match */
2914 lpos_T *hl_endpos, /* return: end of highlighting */
2915 long *flagsp, /* return: flags of matching END */
2916 lpos_T *end_endpos, /* return: end of end pattern match */
2917 int *end_idx, /* return: group ID for end pat. match, or 0 */
2918 reg_extmatch_T *start_ext) /* submatches from the start pattern */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002919{
2920 colnr_T matchcol;
2921 synpat_T *spp, *spp_skip;
2922 int start_idx;
2923 int best_idx;
2924 regmmatch_T regmatch;
2925 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2926 lpos_T pos;
2927 char_u *line;
2928 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002929 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002931 /* just in case we are invoked for a keyword */
2932 if (idx < 0)
2933 return;
2934
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 /*
2936 * Check for being called with a START pattern.
2937 * Can happen with a match that continues to the next line, because it
2938 * contained a region.
2939 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002940 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 if (spp->sp_type != SPTYPE_START)
2942 {
2943 *hl_endpos = *startpos;
2944 return;
2945 }
2946
2947 /*
2948 * Find the SKIP or first END pattern after the last START pattern.
2949 */
2950 for (;;)
2951 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002952 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953 if (spp->sp_type != SPTYPE_START)
2954 break;
2955 ++idx;
2956 }
2957
2958 /*
2959 * Lookup the SKIP pattern (if present)
2960 */
2961 if (spp->sp_type == SPTYPE_SKIP)
2962 {
2963 spp_skip = spp;
2964 ++idx;
2965 }
2966 else
2967 spp_skip = NULL;
2968
2969 /* Setup external matches for syn_regexec(). */
2970 unref_extmatch(re_extmatch_in);
2971 re_extmatch_in = ref_extmatch(start_ext);
2972
2973 matchcol = startpos->col; /* start looking for a match at sstart */
2974 start_idx = idx; /* remember the first END pattern. */
2975 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002976
2977 /* use syntax iskeyword option */
2978 save_chartab(buf_chartab);
2979
Bram Moolenaar071d4272004-06-13 20:20:40 +00002980 for (;;)
2981 {
2982 /*
2983 * Find end pattern that matches first after "matchcol".
2984 */
2985 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002986 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002987 {
2988 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002989 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002990
Bram Moolenaar860cae12010-06-05 23:22:07 +02002991 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002992 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2993 break;
2994 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2995 if (lc_col < 0)
2996 lc_col = 0;
2997
2998 regmatch.rmm_ic = spp->sp_ic;
2999 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003000 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3001 IF_SYN_TIME(&spp->sp_time));
3002 spp->sp_prog = regmatch.regprog;
3003 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003004 {
3005 if (best_idx == -1 || regmatch.startpos[0].col
3006 < best_regmatch.startpos[0].col)
3007 {
3008 best_idx = idx;
3009 best_regmatch.startpos[0] = regmatch.startpos[0];
3010 best_regmatch.endpos[0] = regmatch.endpos[0];
3011 }
3012 }
3013 }
3014
3015 /*
3016 * If all end patterns have been tried, and there is no match, the
3017 * item continues until end-of-line.
3018 */
3019 if (best_idx == -1)
3020 break;
3021
3022 /*
3023 * If the skip pattern matches before the end pattern,
3024 * continue searching after the skip pattern.
3025 */
3026 if (spp_skip != NULL)
3027 {
3028 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003029 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030
3031 if (lc_col < 0)
3032 lc_col = 0;
3033 regmatch.rmm_ic = spp_skip->sp_ic;
3034 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003035 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3036 IF_SYN_TIME(&spp_skip->sp_time));
3037 spp_skip->sp_prog = regmatch.regprog;
3038 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 <= best_regmatch.startpos[0].col)
3040 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003041 int line_len;
3042
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043 /* Add offset to skip pattern match */
3044 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3045
3046 /* If the skip pattern goes on to the next line, there is no
3047 * match with an end pattern in this line. */
3048 if (pos.lnum > startpos->lnum)
3049 break;
3050
3051 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003052 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053
3054 /* take care of an empty match or negative offset */
3055 if (pos.col <= matchcol)
3056 ++matchcol;
3057 else if (pos.col <= regmatch.endpos[0].col)
3058 matchcol = pos.col;
3059 else
3060 /* Be careful not to jump over the NUL at the end-of-line */
3061 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003062 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063 ++matchcol)
3064 ;
3065
3066 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003067 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068 break;
3069
3070 continue; /* start with first end pattern again */
3071 }
3072 }
3073
3074 /*
3075 * Match from start pattern to end pattern.
3076 * Correct for match and highlight offset of end pattern.
3077 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003078 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003079 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3080 /* can't end before the start */
3081 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3082 m_endpos->col = startpos->col;
3083
3084 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3085 /* can't end before the start */
3086 if (end_endpos->lnum == startpos->lnum
3087 && end_endpos->col < startpos->col)
3088 end_endpos->col = startpos->col;
3089 /* can't end after the match */
3090 limit_pos(end_endpos, m_endpos);
3091
3092 /*
3093 * If the end group is highlighted differently, adjust the pointers.
3094 */
3095 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3096 {
3097 *end_idx = best_idx;
3098 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3099 {
3100 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3101 hl_endpos->col = best_regmatch.endpos[0].col;
3102 }
3103 else
3104 {
3105 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3106 hl_endpos->col = best_regmatch.startpos[0].col;
3107 }
3108 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3109
3110 /* can't end before the start */
3111 if (hl_endpos->lnum == startpos->lnum
3112 && hl_endpos->col < startpos->col)
3113 hl_endpos->col = startpos->col;
3114 limit_pos(hl_endpos, m_endpos);
3115
3116 /* now the match ends where the highlighting ends, it is turned
3117 * into the matchgroup for the end */
3118 *m_endpos = *hl_endpos;
3119 }
3120 else
3121 {
3122 *end_idx = 0;
3123 *hl_endpos = *end_endpos;
3124 }
3125
3126 *flagsp = spp->sp_flags;
3127
3128 had_match = TRUE;
3129 break;
3130 }
3131
3132 /* no match for an END pattern in this line */
3133 if (!had_match)
3134 m_endpos->lnum = 0;
3135
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003136 restore_chartab(buf_chartab);
3137
Bram Moolenaar071d4272004-06-13 20:20:40 +00003138 /* Remove external matches. */
3139 unref_extmatch(re_extmatch_in);
3140 re_extmatch_in = NULL;
3141}
3142
3143/*
3144 * Limit "pos" not to be after "limit".
3145 */
3146 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003147limit_pos(lpos_T *pos, lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003148{
3149 if (pos->lnum > limit->lnum)
3150 *pos = *limit;
3151 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3152 pos->col = limit->col;
3153}
3154
3155/*
3156 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3157 */
3158 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003159limit_pos_zero(
3160 lpos_T *pos,
3161 lpos_T *limit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162{
3163 if (pos->lnum == 0)
3164 *pos = *limit;
3165 else
3166 limit_pos(pos, limit);
3167}
3168
3169/*
3170 * Add offset to matched text for end of match or highlight.
3171 */
3172 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003173syn_add_end_off(
3174 lpos_T *result, /* returned position */
3175 regmmatch_T *regmatch, /* start/end of match */
3176 synpat_T *spp, /* matched pattern */
3177 int idx, /* index of offset */
3178 int extra) /* extra chars for offset to start */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003179{
3180 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003181 int off;
3182 char_u *base;
3183 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184
3185 if (spp->sp_off_flags & (1 << idx))
3186 {
3187 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003188 col = regmatch->startpos[0].col;
3189 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190 }
3191 else
3192 {
3193 result->lnum = regmatch->endpos[0].lnum;
3194 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003195 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003197 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3198 * is a matchgroup. Watch out for match with last NL in the buffer. */
3199 if (result->lnum > syn_buf->b_ml.ml_line_count)
3200 col = 0;
3201 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003202 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003203 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3204 p = base + col;
3205 if (off > 0)
3206 {
3207 while (off-- > 0 && *p != NUL)
3208 mb_ptr_adv(p);
3209 }
3210 else if (off < 0)
3211 {
3212 while (off++ < 0 && base < p)
3213 mb_ptr_back(base, p);
3214 }
3215 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003216 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218}
3219
3220/*
3221 * Add offset to matched text for start of match or highlight.
3222 * Avoid resulting column to become negative.
3223 */
3224 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003225syn_add_start_off(
3226 lpos_T *result, /* returned position */
3227 regmmatch_T *regmatch, /* start/end of match */
3228 synpat_T *spp,
3229 int idx,
3230 int extra) /* extra chars for offset to end */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003231{
3232 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003233 int off;
3234 char_u *base;
3235 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236
3237 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3238 {
3239 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003240 col = regmatch->endpos[0].col;
3241 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003242 }
3243 else
3244 {
3245 result->lnum = regmatch->startpos[0].lnum;
3246 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003247 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003249 if (result->lnum > syn_buf->b_ml.ml_line_count)
3250 {
3251 /* a "\n" at the end of the pattern may take us below the last line */
3252 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003253 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003254 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003255 if (off != 0)
3256 {
3257 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3258 p = base + col;
3259 if (off > 0)
3260 {
3261 while (off-- && *p != NUL)
3262 mb_ptr_adv(p);
3263 }
3264 else if (off < 0)
3265 {
3266 while (off++ && base < p)
3267 mb_ptr_back(base, p);
3268 }
3269 col = (int)(p - base);
3270 }
3271 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272}
3273
3274/*
3275 * Get current line in syntax buffer.
3276 */
3277 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003278syn_getcurline(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003279{
3280 return ml_get_buf(syn_buf, current_lnum, FALSE);
3281}
3282
3283/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003284 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285 * Returns TRUE when there is a match.
3286 */
3287 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003288syn_regexec(
3289 regmmatch_T *rmp,
3290 linenr_T lnum,
3291 colnr_T col,
3292 syn_time_T *st UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003294 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003295#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003296 proftime_T pt;
3297
3298 if (syn_time_on)
3299 profile_start(&pt);
3300#endif
3301
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003302 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003303 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3304
Bram Moolenaarf7512552013-06-06 14:55:19 +02003305#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003306 if (syn_time_on)
3307 {
3308 profile_end(&pt);
3309 profile_add(&st->total, &pt);
3310 if (profile_cmp(&pt, &st->slowest) < 0)
3311 st->slowest = pt;
3312 ++st->count;
3313 if (r > 0)
3314 ++st->match;
3315 }
3316#endif
3317
3318 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319 {
3320 rmp->startpos[0].lnum += lnum;
3321 rmp->endpos[0].lnum += lnum;
3322 return TRUE;
3323 }
3324 return FALSE;
3325}
3326
3327/*
3328 * Check one position in a line for a matching keyword.
3329 * The caller must check if a keyword can start at startcol.
3330 * Return it's ID if found, 0 otherwise.
3331 */
3332 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003333check_keyword_id(
3334 char_u *line,
3335 int startcol, /* position in line to check for keyword */
3336 int *endcolp, /* return: character after found keyword */
3337 long *flagsp, /* return: flags of matching keyword */
3338 short **next_listp, /* return: next_list of matching keyword */
3339 stateitem_T *cur_si, /* item at the top of the stack */
3340 int *ccharp UNUSED) /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003342 keyentry_T *kp;
3343 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003344 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003345 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003347 hashtab_T *ht;
3348 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003349
3350 /* Find first character after the keyword. First character was already
3351 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003352 kwp = line + startcol;
3353 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354 do
3355 {
3356#ifdef FEAT_MBYTE
3357 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003358 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 else
3360#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003361 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003362 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003363 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364
Bram Moolenaardad6b692005-01-25 22:14:34 +00003365 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 return 0;
3367
3368 /*
3369 * Must make a copy of the keyword, so we can add a NUL and make it
3370 * lowercase.
3371 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003372 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373
3374 /*
3375 * Try twice:
3376 * 1. matching case
3377 * 2. ignoring case
3378 */
3379 for (round = 1; round <= 2; ++round)
3380 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003381 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003382 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003384 if (round == 2) /* ignore case */
3385 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386
3387 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 * Find keywords that match. There can be several with different
3389 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390 * When current_next_list is non-zero accept only that group, otherwise:
3391 * Accept a not-contained keyword at toplevel.
3392 * Accept a keyword at other levels only if it is in the contains list.
3393 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003394 hi = hash_find(ht, keyword);
3395 if (!HASHITEM_EMPTY(hi))
3396 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003398 if (current_next_list != 0
3399 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3400 : (cur_si == NULL
3401 ? !(kp->flags & HL_CONTAINED)
3402 : in_id_list(cur_si, cur_si->si_cont_list,
3403 &kp->k_syn, kp->flags & HL_CONTAINED)))
3404 {
3405 *endcolp = startcol + kwlen;
3406 *flagsp = kp->flags;
3407 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408#ifdef FEAT_CONCEAL
3409 *ccharp = kp->k_char;
3410#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003411 return kp->k_syn.id;
3412 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413 }
3414 }
3415 return 0;
3416}
3417
3418/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003419 * Handle ":syntax conceal" command.
3420 */
3421 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003422syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423{
3424#ifdef FEAT_CONCEAL
3425 char_u *arg = eap->arg;
3426 char_u *next;
3427
3428 eap->nextcmd = find_nextcmd(arg);
3429 if (eap->skip)
3430 return;
3431
3432 next = skiptowhite(arg);
3433 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3434 curwin->w_s->b_syn_conceal = TRUE;
3435 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3436 curwin->w_s->b_syn_conceal = FALSE;
3437 else
3438 EMSG2(_("E390: Illegal argument: %s"), arg);
3439#endif
3440}
3441
3442/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 * Handle ":syntax case" command.
3444 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003446syn_cmd_case(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447{
3448 char_u *arg = eap->arg;
3449 char_u *next;
3450
3451 eap->nextcmd = find_nextcmd(arg);
3452 if (eap->skip)
3453 return;
3454
3455 next = skiptowhite(arg);
3456 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003457 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003459 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460 else
3461 EMSG2(_("E390: Illegal argument: %s"), arg);
3462}
3463
3464/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003465 * Handle ":syntax spell" command.
3466 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003467 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003468syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003469{
3470 char_u *arg = eap->arg;
3471 char_u *next;
3472
3473 eap->nextcmd = find_nextcmd(arg);
3474 if (eap->skip)
3475 return;
3476
3477 next = skiptowhite(arg);
3478 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003480 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003481 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003482 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003483 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003484 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003485 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003486 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003487 return;
3488 }
3489
3490 /* assume spell checking changed, force a redraw */
3491 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003492}
3493
3494/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003495 * Handle ":syntax iskeyword" command.
3496 */
3497 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003498syn_cmd_iskeyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003499{
3500 char_u *arg = eap->arg;
3501 char_u save_chartab[32];
3502 char_u *save_isk;
3503
3504 if (eap->skip)
3505 return;
3506
3507 arg = skipwhite(arg);
3508 if (*arg == NUL)
3509 {
3510 MSG_PUTS("\n");
3511 MSG_PUTS(_("syntax iskeyword "));
3512 if (curwin->w_s->b_syn_isk != empty_option)
3513 msg_outtrans(curwin->w_s->b_syn_isk);
3514 else
3515 msg_outtrans((char_u *)"not set");
3516 }
3517 else
3518 {
3519 if (STRNICMP(arg, "clear", 5) == 0)
3520 {
3521 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3522 (size_t)32);
3523 clear_string_option(&curwin->w_s->b_syn_isk);
3524 }
3525 else
3526 {
3527 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3528 save_isk = curbuf->b_p_isk;
3529 curbuf->b_p_isk = vim_strsave(arg);
3530
3531 buf_init_chartab(curbuf, FALSE);
3532 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3533 (size_t)32);
3534 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3535 clear_string_option(&curwin->w_s->b_syn_isk);
3536 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3537 curbuf->b_p_isk = save_isk;
3538 }
3539 }
3540 redraw_win_later(curwin, NOT_VALID);
3541}
3542
3543/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003544 * Clear all syntax info for one buffer.
3545 */
3546 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003547syntax_clear(synblock_T *block)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548{
3549 int i;
3550
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551 block->b_syn_error = FALSE; /* clear previous error */
3552 block->b_syn_ic = FALSE; /* Use case, by default */
3553 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3554 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003555
3556 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003557 clear_keywtab(&block->b_keywtab);
3558 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559
3560 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003561 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3562 syn_clear_pattern(block, i);
3563 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564
3565 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003566 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3567 syn_clear_cluster(block, i);
3568 ga_clear(&block->b_syn_clusters);
3569 block->b_spell_cluster_id = 0;
3570 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571
Bram Moolenaar860cae12010-06-05 23:22:07 +02003572 block->b_syn_sync_flags = 0;
3573 block->b_syn_sync_minlines = 0;
3574 block->b_syn_sync_maxlines = 0;
3575 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576
Bram Moolenaar473de612013-06-08 18:19:48 +02003577 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578 block->b_syn_linecont_prog = NULL;
3579 vim_free(block->b_syn_linecont_pat);
3580 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003581#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003582 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003584 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003585
3586 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003587 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003589
3590 /* Reset the counter for ":syn include" */
3591 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592}
3593
3594/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003595 * Get rid of ownsyntax for window "wp".
3596 */
3597 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003598reset_synblock(win_T *wp)
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003599{
3600 if (wp->w_s != &wp->w_buffer->b_s)
3601 {
3602 syntax_clear(wp->w_s);
3603 vim_free(wp->w_s);
3604 wp->w_s = &wp->w_buffer->b_s;
3605 }
3606}
3607
3608/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609 * Clear syncing info for one buffer.
3610 */
3611 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003612syntax_sync_clear(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613{
3614 int i;
3615
3616 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3618 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3619 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620
Bram Moolenaar860cae12010-06-05 23:22:07 +02003621 curwin->w_s->b_syn_sync_flags = 0;
3622 curwin->w_s->b_syn_sync_minlines = 0;
3623 curwin->w_s->b_syn_sync_maxlines = 0;
3624 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625
Bram Moolenaar473de612013-06-08 18:19:48 +02003626 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003627 curwin->w_s->b_syn_linecont_prog = NULL;
3628 vim_free(curwin->w_s->b_syn_linecont_pat);
3629 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003630 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003632 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633}
3634
3635/*
3636 * Remove one pattern from the buffer's pattern list.
3637 */
3638 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003639syn_remove_pattern(
3640 synblock_T *block,
3641 int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642{
3643 synpat_T *spp;
3644
Bram Moolenaar860cae12010-06-05 23:22:07 +02003645 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003646#ifdef FEAT_FOLDING
3647 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3653 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654}
3655
3656/*
3657 * Clear and free one syntax pattern. When clearing all, must be called from
3658 * last to first!
3659 */
3660 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003661syn_clear_pattern(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003663 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003664 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003666 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003668 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3669 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3670 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671 }
3672}
3673
3674/*
3675 * Clear and free one syntax cluster.
3676 */
3677 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003678syn_clear_cluster(synblock_T *block, int i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003680 vim_free(SYN_CLSTR(block)[i].scl_name);
3681 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3682 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683}
3684
3685/*
3686 * Handle ":syntax clear" command.
3687 */
3688 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003689syn_cmd_clear(exarg_T *eap, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690{
3691 char_u *arg = eap->arg;
3692 char_u *arg_end;
3693 int id;
3694
3695 eap->nextcmd = find_nextcmd(arg);
3696 if (eap->skip)
3697 return;
3698
3699 /*
3700 * We have to disable this within ":syn include @group filename",
3701 * because otherwise @group would get deleted.
3702 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3703 * clear".
3704 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003705 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706 return;
3707
3708 if (ends_excmd(*arg))
3709 {
3710 /*
3711 * No argument: Clear all syntax items.
3712 */
3713 if (syncing)
3714 syntax_sync_clear();
3715 else
3716 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 syntax_clear(curwin->w_s);
3718 if (curwin->w_s == &curwin->w_buffer->b_s)
3719 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003720 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 }
3722 }
3723 else
3724 {
3725 /*
3726 * Clear the group IDs that are in the argument.
3727 */
3728 while (!ends_excmd(*arg))
3729 {
3730 arg_end = skiptowhite(arg);
3731 if (*arg == '@')
3732 {
3733 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3734 if (id == 0)
3735 {
3736 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3737 break;
3738 }
3739 else
3740 {
3741 /*
3742 * We can't physically delete a cluster without changing
3743 * the IDs of other clusters, so we do the next best thing
3744 * and make it empty.
3745 */
3746 short scl_id = id - SYNID_CLUSTER;
3747
Bram Moolenaar860cae12010-06-05 23:22:07 +02003748 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3749 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003750 }
3751 }
3752 else
3753 {
3754 id = syn_namen2id(arg, (int)(arg_end - arg));
3755 if (id == 0)
3756 {
3757 EMSG2(_(e_nogroup), arg);
3758 break;
3759 }
3760 else
3761 syn_clear_one(id, syncing);
3762 }
3763 arg = skipwhite(arg_end);
3764 }
3765 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003766 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003767 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768}
3769
3770/*
3771 * Clear one syntax group for the current buffer.
3772 */
3773 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003774syn_clear_one(int id, int syncing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775{
3776 synpat_T *spp;
3777 int idx;
3778
3779 /* Clear keywords only when not ":syn sync clear group-name" */
3780 if (!syncing)
3781 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003782 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3783 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003784 }
3785
3786 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003787 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003789 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3791 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003792 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793 }
3794}
3795
3796/*
3797 * Handle ":syntax on" command.
3798 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003799 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003800syn_cmd_on(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801{
3802 syn_cmd_onoff(eap, "syntax");
3803}
3804
3805/*
3806 * Handle ":syntax enable" command.
3807 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003808 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003809syn_cmd_enable(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003810{
3811 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3812 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003813 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814}
3815
3816/*
3817 * Handle ":syntax reset" command.
3818 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003819 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003820syn_cmd_reset(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821{
3822 eap->nextcmd = check_nextcmd(eap->arg);
3823 if (!eap->skip)
3824 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003825 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3827 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003828 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 }
3830}
3831
3832/*
3833 * Handle ":syntax manual" command.
3834 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003836syn_cmd_manual(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837{
3838 syn_cmd_onoff(eap, "manual");
3839}
3840
3841/*
3842 * Handle ":syntax off" command.
3843 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003845syn_cmd_off(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846{
3847 syn_cmd_onoff(eap, "nosyntax");
3848}
3849
3850 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003851syn_cmd_onoff(exarg_T *eap, char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852{
3853 char_u buf[100];
3854
3855 eap->nextcmd = check_nextcmd(eap->arg);
3856 if (!eap->skip)
3857 {
3858 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003859 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 do_cmdline_cmd(buf);
3861 }
3862}
3863
3864/*
3865 * Handle ":syntax [list]" command: list current syntax words.
3866 */
3867 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003868syn_cmd_list(
3869 exarg_T *eap,
3870 int syncing) /* when TRUE: list syncing items */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871{
3872 char_u *arg = eap->arg;
3873 int id;
3874 char_u *arg_end;
3875
3876 eap->nextcmd = find_nextcmd(arg);
3877 if (eap->skip)
3878 return;
3879
Bram Moolenaar860cae12010-06-05 23:22:07 +02003880 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003882 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 return;
3884 }
3885
3886 if (syncing)
3887 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003888 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003889 {
3890 MSG_PUTS(_("syncing on C-style comments"));
3891 syn_lines_msg();
3892 syn_match_msg();
3893 return;
3894 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003895 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003897 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 MSG_PUTS(_("no syncing"));
3899 else
3900 {
3901 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003902 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903 MSG_PUTS(_(" lines before top line"));
3904 syn_match_msg();
3905 }
3906 return;
3907 }
3908 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003909 if (curwin->w_s->b_syn_sync_minlines > 0
3910 || curwin->w_s->b_syn_sync_maxlines > 0
3911 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003912 {
3913 MSG_PUTS(_("\nsyncing on items"));
3914 syn_lines_msg();
3915 syn_match_msg();
3916 }
3917 }
3918 else
3919 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3920 if (ends_excmd(*arg))
3921 {
3922 /*
3923 * No argument: List all group IDs and all syntax clusters.
3924 */
3925 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3926 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 syn_list_cluster(id);
3929 }
3930 else
3931 {
3932 /*
3933 * List the group IDs and syntax clusters that are in the argument.
3934 */
3935 while (!ends_excmd(*arg) && !got_int)
3936 {
3937 arg_end = skiptowhite(arg);
3938 if (*arg == '@')
3939 {
3940 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3941 if (id == 0)
3942 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3943 else
3944 syn_list_cluster(id - SYNID_CLUSTER);
3945 }
3946 else
3947 {
3948 id = syn_namen2id(arg, (int)(arg_end - arg));
3949 if (id == 0)
3950 EMSG2(_(e_nogroup), arg);
3951 else
3952 syn_list_one(id, syncing, TRUE);
3953 }
3954 arg = skipwhite(arg_end);
3955 }
3956 }
3957 eap->nextcmd = check_nextcmd(arg);
3958}
3959
3960 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003961syn_lines_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003962{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003963 if (curwin->w_s->b_syn_sync_maxlines > 0
3964 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965 {
3966 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003967 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 {
3969 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003970 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3971 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003972 MSG_PUTS(", ");
3973 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003974 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 {
3976 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003977 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003978 }
3979 MSG_PUTS(_(" lines before top line"));
3980 }
3981}
3982
3983 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01003984syn_match_msg(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003986 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 {
3988 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003989 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 MSG_PUTS(_(" line breaks"));
3991 }
3992}
3993
3994static int last_matchgroup;
3995
3996struct name_list
3997{
3998 int flag;
3999 char *name;
4000};
4001
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01004002static void syn_list_flags(struct name_list *nl, int flags, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003
4004/*
4005 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4006 */
4007 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004008syn_list_one(
4009 int id,
4010 int syncing, /* when TRUE: list syncing items */
4011 int link_only) /* when TRUE; list link-only too */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004012{
4013 int attr;
4014 int idx;
4015 int did_header = FALSE;
4016 synpat_T *spp;
4017 static struct name_list namelist1[] =
4018 {
4019 {HL_DISPLAY, "display"},
4020 {HL_CONTAINED, "contained"},
4021 {HL_ONELINE, "oneline"},
4022 {HL_KEEPEND, "keepend"},
4023 {HL_EXTEND, "extend"},
4024 {HL_EXCLUDENL, "excludenl"},
4025 {HL_TRANSP, "transparent"},
4026 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004027#ifdef FEAT_CONCEAL
4028 {HL_CONCEAL, "conceal"},
4029 {HL_CONCEALENDS, "concealends"},
4030#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 {0, NULL}
4032 };
4033 static struct name_list namelist2[] =
4034 {
4035 {HL_SKIPWHITE, "skipwhite"},
4036 {HL_SKIPNL, "skipnl"},
4037 {HL_SKIPEMPTY, "skipempty"},
4038 {0, NULL}
4039 };
4040
4041 attr = hl_attr(HLF_D); /* highlight like directories */
4042
4043 /* list the keywords for "id" */
4044 if (!syncing)
4045 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004046 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4047 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048 did_header, attr);
4049 }
4050
4051 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004052 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004054 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004055 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4056 continue;
4057
4058 (void)syn_list_header(did_header, 999, id);
4059 did_header = TRUE;
4060 last_matchgroup = 0;
4061 if (spp->sp_type == SPTYPE_MATCH)
4062 {
4063 put_pattern("match", ' ', spp, attr);
4064 msg_putchar(' ');
4065 }
4066 else if (spp->sp_type == SPTYPE_START)
4067 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004068 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4069 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4070 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4071 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4072 while (idx < curwin->w_s->b_syn_patterns.ga_len
4073 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4074 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004075 --idx;
4076 msg_putchar(' ');
4077 }
4078 syn_list_flags(namelist1, spp->sp_flags, attr);
4079
4080 if (spp->sp_cont_list != NULL)
4081 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4082
4083 if (spp->sp_syn.cont_in_list != NULL)
4084 put_id_list((char_u *)"containedin",
4085 spp->sp_syn.cont_in_list, attr);
4086
4087 if (spp->sp_next_list != NULL)
4088 {
4089 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4090 syn_list_flags(namelist2, spp->sp_flags, attr);
4091 }
4092 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4093 {
4094 if (spp->sp_flags & HL_SYNC_HERE)
4095 msg_puts_attr((char_u *)"grouphere", attr);
4096 else
4097 msg_puts_attr((char_u *)"groupthere", attr);
4098 msg_putchar(' ');
4099 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004100 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4102 else
4103 MSG_PUTS("NONE");
4104 msg_putchar(' ');
4105 }
4106 }
4107
4108 /* list the link, if there is one */
4109 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4110 {
4111 (void)syn_list_header(did_header, 999, id);
4112 msg_puts_attr((char_u *)"links to", attr);
4113 msg_putchar(' ');
4114 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4115 }
4116}
4117
4118 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004119syn_list_flags(struct name_list *nlist, int flags, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004120{
4121 int i;
4122
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004123 for (i = 0; nlist[i].flag != 0; ++i)
4124 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004125 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004126 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004127 msg_putchar(' ');
4128 }
4129}
4130
4131/*
4132 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4133 */
4134 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004135syn_list_cluster(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004136{
4137 int endcol = 15;
4138
4139 /* slight hack: roughly duplicate the guts of syn_list_header() */
4140 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004141 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142
4143 if (msg_col >= endcol) /* output at least one space */
4144 endcol = msg_col + 1;
4145 if (Columns <= endcol) /* avoid hang for tiny window */
4146 endcol = Columns - 1;
4147
4148 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004149 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004151 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 hl_attr(HLF_D));
4153 }
4154 else
4155 {
4156 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4157 msg_puts((char_u *)"=NONE");
4158 }
4159}
4160
4161 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004162put_id_list(char_u *name, short *list, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004163{
4164 short *p;
4165
4166 msg_puts_attr(name, attr);
4167 msg_putchar('=');
4168 for (p = list; *p; ++p)
4169 {
4170 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4171 {
4172 if (p[1])
4173 MSG_PUTS("ALLBUT");
4174 else
4175 MSG_PUTS("ALL");
4176 }
4177 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4178 {
4179 MSG_PUTS("TOP");
4180 }
4181 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4182 {
4183 MSG_PUTS("CONTAINED");
4184 }
4185 else if (*p >= SYNID_CLUSTER)
4186 {
4187 short scl_id = *p - SYNID_CLUSTER;
4188
4189 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004190 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004191 }
4192 else
4193 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4194 if (p[1])
4195 msg_putchar(',');
4196 }
4197 msg_putchar(' ');
4198}
4199
4200 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004201put_pattern(
4202 char *s,
4203 int c,
4204 synpat_T *spp,
4205 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206{
4207 long n;
4208 int mask;
4209 int first;
4210 static char *sepchars = "/+=-#@\"|'^&";
4211 int i;
4212
4213 /* May have to write "matchgroup=group" */
4214 if (last_matchgroup != spp->sp_syn_match_id)
4215 {
4216 last_matchgroup = spp->sp_syn_match_id;
4217 msg_puts_attr((char_u *)"matchgroup", attr);
4218 msg_putchar('=');
4219 if (last_matchgroup == 0)
4220 msg_outtrans((char_u *)"NONE");
4221 else
4222 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4223 msg_putchar(' ');
4224 }
4225
4226 /* output the name of the pattern and an '=' or ' ' */
4227 msg_puts_attr((char_u *)s, attr);
4228 msg_putchar(c);
4229
4230 /* output the pattern, in between a char that is not in the pattern */
4231 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4232 if (sepchars[++i] == NUL)
4233 {
4234 i = 0; /* no good char found, just use the first one */
4235 break;
4236 }
4237 msg_putchar(sepchars[i]);
4238 msg_outtrans(spp->sp_pattern);
4239 msg_putchar(sepchars[i]);
4240
4241 /* output any pattern options */
4242 first = TRUE;
4243 for (i = 0; i < SPO_COUNT; ++i)
4244 {
4245 mask = (1 << i);
4246 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4247 {
4248 if (!first)
4249 msg_putchar(','); /* separate with commas */
4250 msg_puts((char_u *)spo_name_tab[i]);
4251 n = spp->sp_offsets[i];
4252 if (i != SPO_LC_OFF)
4253 {
4254 if (spp->sp_off_flags & mask)
4255 msg_putchar('s');
4256 else
4257 msg_putchar('e');
4258 if (n > 0)
4259 msg_putchar('+');
4260 }
4261 if (n || i == SPO_LC_OFF)
4262 msg_outnum(n);
4263 first = FALSE;
4264 }
4265 }
4266 msg_putchar(' ');
4267}
4268
4269/*
4270 * List or clear the keywords for one syntax group.
4271 * Return TRUE if the header has been printed.
4272 */
4273 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004274syn_list_keywords(
4275 int id,
4276 hashtab_T *ht,
4277 int did_header, /* header has already been printed */
4278 int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004281 hashitem_T *hi;
4282 keyentry_T *kp;
4283 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 int prev_contained = 0;
4285 short *prev_next_list = NULL;
4286 short *prev_cont_in_list = NULL;
4287 int prev_skipnl = 0;
4288 int prev_skipwhite = 0;
4289 int prev_skipempty = 0;
4290
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 /*
4292 * Unfortunately, this list of keywords is not sorted on alphabet but on
4293 * hash value...
4294 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004295 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 --todo;
4301 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004303 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004305 if (prev_contained != (kp->flags & HL_CONTAINED)
4306 || prev_skipnl != (kp->flags & HL_SKIPNL)
4307 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4308 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4309 || prev_cont_in_list != kp->k_syn.cont_in_list
4310 || prev_next_list != kp->next_list)
4311 outlen = 9999;
4312 else
4313 outlen = (int)STRLEN(kp->keyword);
4314 /* output "contained" and "nextgroup" on each line */
4315 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004317 prev_contained = 0;
4318 prev_next_list = NULL;
4319 prev_cont_in_list = NULL;
4320 prev_skipnl = 0;
4321 prev_skipwhite = 0;
4322 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004324 did_header = TRUE;
4325 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 put_id_list((char_u *)"containedin",
4334 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 if (kp->next_list != prev_next_list)
4339 {
4340 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4341 msg_putchar(' ');
4342 prev_next_list = kp->next_list;
4343 if (kp->flags & HL_SKIPNL)
4344 {
4345 msg_puts_attr((char_u *)"skipnl", attr);
4346 msg_putchar(' ');
4347 prev_skipnl = (kp->flags & HL_SKIPNL);
4348 }
4349 if (kp->flags & HL_SKIPWHITE)
4350 {
4351 msg_puts_attr((char_u *)"skipwhite", attr);
4352 msg_putchar(' ');
4353 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4354 }
4355 if (kp->flags & HL_SKIPEMPTY)
4356 {
4357 msg_puts_attr((char_u *)"skipempty", attr);
4358 msg_putchar(' ');
4359 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4360 }
4361 }
4362 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 }
4365 }
4366 }
4367
4368 return did_header;
4369}
4370
4371 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004372syn_clear_keyword(int id, hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 hashitem_T *hi;
4375 keyentry_T *kp;
4376 keyentry_T *kp_prev;
4377 keyentry_T *kp_next;
4378 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004381 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004382 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004386 --todo;
4387 kp_prev = NULL;
4388 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 if (kp->k_syn.id == id)
4391 {
4392 kp_next = kp->ke_next;
4393 if (kp_prev == NULL)
4394 {
4395 if (kp_next == NULL)
4396 hash_remove(ht, hi);
4397 else
4398 hi->hi_key = KE2HIKEY(kp_next);
4399 }
4400 else
4401 kp_prev->ke_next = kp_next;
4402 vim_free(kp->next_list);
4403 vim_free(kp->k_syn.cont_in_list);
4404 vim_free(kp);
4405 kp = kp_next;
4406 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004407 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004408 {
4409 kp_prev = kp;
4410 kp = kp->ke_next;
4411 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004412 }
4413 }
4414 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004415 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416}
4417
4418/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004419 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 */
4421 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004422clear_keywtab(hashtab_T *ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004424 hashitem_T *hi;
4425 int todo;
4426 keyentry_T *kp;
4427 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004428
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004429 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004430 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004431 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004432 if (!HASHITEM_EMPTY(hi))
4433 {
4434 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004435 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004437 kp_next = kp->ke_next;
4438 vim_free(kp->next_list);
4439 vim_free(kp->k_syn.cont_in_list);
4440 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 hash_clear(ht);
4445 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446}
4447
4448/*
4449 * Add a keyword to the list of keywords.
4450 */
4451 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004452add_keyword(
4453 char_u *name, /* name of keyword */
4454 int id, /* group ID for this keyword */
4455 int flags, /* flags for this keyword */
4456 short *cont_in_list, /* containedin for this keyword */
4457 short *next_list, /* nextgroup for this keyword */
4458 int conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004460 keyentry_T *kp;
4461 hashtab_T *ht;
4462 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004463 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004465 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466
Bram Moolenaar860cae12010-06-05 23:22:07 +02004467 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004468 name_ic = str_foldcase(name, (int)STRLEN(name),
4469 name_folded, MAXKEYWLEN + 1);
4470 else
4471 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004472 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4473 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004475 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004476 kp->k_syn.id = id;
4477 kp->k_syn.inc_tag = current_syn_inc_tag;
4478 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004479 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004480 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004481 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004482 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004483 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484
Bram Moolenaar860cae12010-06-05 23:22:07 +02004485 if (curwin->w_s->b_syn_ic)
4486 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004488 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489
Bram Moolenaardad6b692005-01-25 22:14:34 +00004490 hash = hash_hash(kp->keyword);
4491 hi = hash_lookup(ht, kp->keyword, hash);
4492 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004494 /* new keyword, add to hashtable */
4495 kp->ke_next = NULL;
4496 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 /* keyword already exists, prepend to list */
4501 kp->ke_next = HI2KE(hi);
4502 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504}
4505
4506/*
4507 * Get the start and end of the group name argument.
4508 * Return a pointer to the first argument.
4509 * Return NULL if the end of the command was found instead of further args.
4510 */
4511 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004512get_group_name(
4513 char_u *arg, /* start of the argument */
4514 char_u **name_end) /* pointer to end of the name */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515{
4516 char_u *rest;
4517
4518 *name_end = skiptowhite(arg);
4519 rest = skipwhite(*name_end);
4520
4521 /*
4522 * Check if there are enough arguments. The first argument may be a
4523 * pattern, where '|' is allowed, so only check for NUL.
4524 */
4525 if (ends_excmd(*arg) || *rest == NUL)
4526 return NULL;
4527 return rest;
4528}
4529
4530/*
4531 * Check for syntax command option arguments.
4532 * This can be called at any place in the list of arguments, and just picks
4533 * out the arguments that are known. Can be called several times in a row to
4534 * collect all options in between other arguments.
4535 * Return a pointer to the next argument (which isn't an option).
4536 * Return NULL for any error;
4537 */
4538 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004539get_syn_options(
4540 char_u *arg, /* next argument to be checked */
4541 syn_opt_arg_T *opt, /* various things */
4542 int *conceal_char UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 char_u *gname_start, *gname;
4545 int syn_id;
4546 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004547 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548 int i;
4549 int fidx;
4550 static struct flag
4551 {
4552 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004553 int argtype;
4554 int flags;
4555 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4556 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4557 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4558 {"eExXtTeEnNdD", 0, HL_EXTEND},
4559 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4560 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4561 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4562 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4563 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4564 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4565 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4566 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4567 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004568 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4569 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4570 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004571 {"cCoOnNtTaAiInNsS", 1, 0},
4572 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4573 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004575 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576
4577 if (arg == NULL) /* already detected error */
4578 return NULL;
4579
Bram Moolenaar860cae12010-06-05 23:22:07 +02004580#ifdef FEAT_CONCEAL
4581 if (curwin->w_s->b_syn_conceal)
4582 opt->flags |= HL_CONCEAL;
4583#endif
4584
Bram Moolenaar071d4272004-06-13 20:20:40 +00004585 for (;;)
4586 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004587 /*
4588 * This is used very often when a large number of keywords is defined.
4589 * Need to skip quickly when no option name is found.
4590 * Also avoid tolower(), it's slow.
4591 */
4592 if (strchr(first_letters, *arg) == NULL)
4593 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004594
4595 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4596 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004597 p = flagtab[fidx].name;
4598 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4599 if (arg[len] != p[i] && arg[len] != p[i + 1])
4600 break;
4601 if (p[i] == NUL && (vim_iswhite(arg[len])
4602 || (flagtab[fidx].argtype > 0
4603 ? arg[len] == '='
4604 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004606 if (opt->keyword
4607 && (flagtab[fidx].flags == HL_DISPLAY
4608 || flagtab[fidx].flags == HL_FOLD
4609 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 /* treat "display", "fold" and "extend" as a keyword */
4611 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 break;
4613 }
4614 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004615 if (fidx < 0) /* no match found */
4616 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004618 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004619 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004620 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004621 {
4622 EMSG(_("E395: contains argument not accepted here"));
4623 return NULL;
4624 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004625 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626 return NULL;
4627 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004630 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004631 return NULL;
4632 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004633 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004635 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 return NULL;
4637 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004638 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4639 {
4640#ifdef FEAT_MBYTE
4641 /* cchar=? */
4642 if (has_mbyte)
4643 {
4644# ifdef FEAT_CONCEAL
4645 *conceal_char = mb_ptr2char(arg + 6);
4646# endif
4647 arg += mb_ptr2len(arg + 6) - 1;
4648 }
4649 else
4650#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004651 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004652#ifdef FEAT_CONCEAL
4653 *conceal_char = arg[6];
4654#else
4655 ;
4656#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004657 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004658#ifdef FEAT_CONCEAL
4659 if (!vim_isprintc_strict(*conceal_char))
4660 {
4661 EMSG(_("E844: invalid cchar value"));
4662 return NULL;
4663 }
4664#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004665 arg = skipwhite(arg + 7);
4666 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004668 {
4669 opt->flags |= flagtab[fidx].flags;
4670 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004672 if (flagtab[fidx].flags == HL_SYNC_HERE
4673 || flagtab[fidx].flags == HL_SYNC_THERE)
4674 {
4675 if (opt->sync_idx == NULL)
4676 {
4677 EMSG(_("E393: group[t]here not accepted here"));
4678 return NULL;
4679 }
4680 gname_start = arg;
4681 arg = skiptowhite(arg);
4682 if (gname_start == arg)
4683 return NULL;
4684 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4685 if (gname == NULL)
4686 return NULL;
4687 if (STRCMP(gname, "NONE") == 0)
4688 *opt->sync_idx = NONE_IDX;
4689 else
4690 {
4691 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004692 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4693 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4694 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004695 {
4696 *opt->sync_idx = i;
4697 break;
4698 }
4699 if (i < 0)
4700 {
4701 EMSG2(_("E394: Didn't find region item for %s"), gname);
4702 vim_free(gname);
4703 return NULL;
4704 }
4705 }
4706
4707 vim_free(gname);
4708 arg = skipwhite(arg);
4709 }
4710#ifdef FEAT_FOLDING
4711 else if (flagtab[fidx].flags == HL_FOLD
4712 && foldmethodIsSyntax(curwin))
4713 /* Need to update folds later. */
4714 foldUpdateAll(curwin);
4715#endif
4716 }
4717 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718
4719 return arg;
4720}
4721
4722/*
4723 * Adjustments to syntax item when declared in a ":syn include"'d file.
4724 * Set the contained flag, and if the item is not already contained, add it
4725 * to the specified top-level group, if any.
4726 */
4727 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004728syn_incl_toplevel(int id, int *flagsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004730 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 return;
4732 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004733 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004734 {
4735 /* We have to alloc this, because syn_combine_list() will free it. */
4736 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004737 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738
4739 if (grp_list != NULL)
4740 {
4741 grp_list[0] = id;
4742 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004743 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744 CLUSTER_ADD);
4745 }
4746 }
4747}
4748
4749/*
4750 * Handle ":syntax include [@{group-name}] filename" command.
4751 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004752 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004753syn_cmd_include(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754{
4755 char_u *arg = eap->arg;
4756 int sgl_id = 1;
4757 char_u *group_name_end;
4758 char_u *rest;
4759 char_u *errormsg = NULL;
4760 int prev_toplvl_grp;
4761 int prev_syn_inc_tag;
4762 int source = FALSE;
4763
4764 eap->nextcmd = find_nextcmd(arg);
4765 if (eap->skip)
4766 return;
4767
4768 if (arg[0] == '@')
4769 {
4770 ++arg;
4771 rest = get_group_name(arg, &group_name_end);
4772 if (rest == NULL)
4773 {
4774 EMSG((char_u *)_("E397: Filename required"));
4775 return;
4776 }
4777 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004778 if (sgl_id == 0)
4779 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780 /* separate_nextcmd() and expand_filename() depend on this */
4781 eap->arg = rest;
4782 }
4783
4784 /*
4785 * Everything that's left, up to the next command, should be the
4786 * filename to include.
4787 */
4788 eap->argt |= (XFILE | NOSPC);
4789 separate_nextcmd(eap);
4790 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4791 {
4792 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4793 * file. Need to expand the file name first. In other cases
4794 * ":runtime!" is used. */
4795 source = TRUE;
4796 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4797 {
4798 if (errormsg != NULL)
4799 EMSG(errormsg);
4800 return;
4801 }
4802 }
4803
4804 /*
4805 * Save and restore the existing top-level grouplist id and ":syn
4806 * include" tag around the actual inclusion.
4807 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004808 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4809 {
4810 EMSG((char_u *)_("E847: Too many syntax includes"));
4811 return;
4812 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 prev_syn_inc_tag = current_syn_inc_tag;
4814 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004815 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4816 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004817 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01004818 : source_runtime(eap->arg, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004819 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004820 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 current_syn_inc_tag = prev_syn_inc_tag;
4822}
4823
4824/*
4825 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4826 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004828syn_cmd_keyword(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004829{
4830 char_u *arg = eap->arg;
4831 char_u *group_name_end;
4832 int syn_id;
4833 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004834 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004836 char_u *kw;
4837 syn_opt_arg_T syn_opt_arg;
4838 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004839 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840
4841 rest = get_group_name(arg, &group_name_end);
4842
4843 if (rest != NULL)
4844 {
4845 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004846 if (syn_id != 0)
4847 /* allocate a buffer, for removing backslashes in the keyword */
4848 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004849 if (keyword_copy != NULL)
4850 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004851 syn_opt_arg.flags = 0;
4852 syn_opt_arg.keyword = TRUE;
4853 syn_opt_arg.sync_idx = NULL;
4854 syn_opt_arg.has_cont_list = FALSE;
4855 syn_opt_arg.cont_in_list = NULL;
4856 syn_opt_arg.next_list = NULL;
4857
Bram Moolenaar071d4272004-06-13 20:20:40 +00004858 /*
4859 * The options given apply to ALL keywords, so all options must be
4860 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004861 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004862 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004863 cnt = 0;
4864 p = keyword_copy;
4865 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004866 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004867 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004868 if (rest == NULL || ends_excmd(*rest))
4869 break;
4870 /* Copy the keyword, removing backslashes, and add a NUL. */
4871 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004872 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004873 if (*rest == '\\' && rest[1] != NUL)
4874 ++rest;
4875 *p++ = *rest++;
4876 }
4877 *p++ = NUL;
4878 ++cnt;
4879 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004881 if (!eap->skip)
4882 {
4883 /* Adjust flags for use of ":syn include". */
4884 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4885
4886 /*
4887 * 2: Add an entry for each keyword.
4888 */
4889 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4890 {
4891 for (p = vim_strchr(kw, '['); ; )
4892 {
4893 if (p != NULL)
4894 *p = NUL;
4895 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004896 syn_opt_arg.cont_in_list,
4897 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004898 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004899 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004900 if (p[1] == NUL)
4901 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004902 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004903 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004904 }
4905 if (p[1] == ']')
4906 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004907 if (p[2] != NUL)
4908 {
4909 EMSG3(_("E890: trailing char after ']': %s]%s"),
4910 kw, &p[2]);
4911 goto error;
4912 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004913 kw = p + 1; /* skip over the "]" */
4914 break;
4915 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004916#ifdef FEAT_MBYTE
4917 if (has_mbyte)
4918 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004919 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004920
4921 mch_memmove(p, p + 1, l);
4922 p += l;
4923 }
4924 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004925#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004926 {
4927 p[0] = p[1];
4928 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929 }
4930 }
4931 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004933error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004935 vim_free(syn_opt_arg.cont_in_list);
4936 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004937 }
4938 }
4939
4940 if (rest != NULL)
4941 eap->nextcmd = check_nextcmd(rest);
4942 else
4943 EMSG2(_(e_invarg2), arg);
4944
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004945 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004946 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947}
4948
4949/*
4950 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4951 *
4952 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4953 */
4954 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01004955syn_cmd_match(
4956 exarg_T *eap,
4957 int syncing) /* TRUE for ":syntax sync match .. " */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958{
4959 char_u *arg = eap->arg;
4960 char_u *group_name_end;
4961 char_u *rest;
4962 synpat_T item; /* the item found in the line */
4963 int syn_id;
4964 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004965 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004967 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968
4969 /* Isolate the group name, check for validity */
4970 rest = get_group_name(arg, &group_name_end);
4971
4972 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004973 syn_opt_arg.flags = 0;
4974 syn_opt_arg.keyword = FALSE;
4975 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4976 syn_opt_arg.has_cont_list = TRUE;
4977 syn_opt_arg.cont_list = NULL;
4978 syn_opt_arg.cont_in_list = NULL;
4979 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004980 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981
4982 /* get the pattern. */
4983 init_syn_patterns();
4984 vim_memset(&item, 0, sizeof(item));
4985 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004986 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4987 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004988
4989 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004990 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004991
4992 if (rest != NULL) /* all arguments are valid */
4993 {
4994 /*
4995 * Check for trailing command and illegal trailing arguments.
4996 */
4997 eap->nextcmd = check_nextcmd(rest);
4998 if (!ends_excmd(*rest) || eap->skip)
4999 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005000 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 && (syn_id = syn_check_group(arg,
5002 (int)(group_name_end - arg))) != 0)
5003 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005004 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005 /*
5006 * Store the pattern in the syn_items list
5007 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005008 idx = curwin->w_s->b_syn_patterns.ga_len;
5009 SYN_ITEMS(curwin->w_s)[idx] = item;
5010 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5011 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5012 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5013 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5014 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5015 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5016 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5017 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005018 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005019#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005020 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005021#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005022 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005023 curwin->w_s->b_syn_containedin = TRUE;
5024 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5025 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005026
5027 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005028 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005029 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005031 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005032 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005033#endif
5034
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005035 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005036 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037 return; /* don't free the progs and patterns now */
5038 }
5039 }
5040
5041 /*
5042 * Something failed, free the allocated memory.
5043 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005044 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005045 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005046 vim_free(syn_opt_arg.cont_list);
5047 vim_free(syn_opt_arg.cont_in_list);
5048 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049
5050 if (rest == NULL)
5051 EMSG2(_(e_invarg2), arg);
5052}
5053
5054/*
5055 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5056 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5057 */
5058 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005059syn_cmd_region(
5060 exarg_T *eap,
5061 int syncing) /* TRUE for ":syntax sync region .." */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005062{
5063 char_u *arg = eap->arg;
5064 char_u *group_name_end;
5065 char_u *rest; /* next arg, NULL on error */
5066 char_u *key_end;
5067 char_u *key = NULL;
5068 char_u *p;
5069 int item;
5070#define ITEM_START 0
5071#define ITEM_SKIP 1
5072#define ITEM_END 2
5073#define ITEM_MATCHGROUP 3
5074 struct pat_ptr
5075 {
5076 synpat_T *pp_synp; /* pointer to syn_pattern */
5077 int pp_matchgroup_id; /* matchgroup ID */
5078 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5079 } *(pat_ptrs[3]);
5080 /* patterns found in the line */
5081 struct pat_ptr *ppp;
5082 struct pat_ptr *ppp_next;
5083 int pat_count = 0; /* nr of syn_patterns found */
5084 int syn_id;
5085 int matchgroup_id = 0;
5086 int not_enough = FALSE; /* not enough arguments */
5087 int illegal = FALSE; /* illegal arguments */
5088 int success = FALSE;
5089 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005090 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005091 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005092
5093 /* Isolate the group name, check for validity */
5094 rest = get_group_name(arg, &group_name_end);
5095
5096 pat_ptrs[0] = NULL;
5097 pat_ptrs[1] = NULL;
5098 pat_ptrs[2] = NULL;
5099
5100 init_syn_patterns();
5101
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005102 syn_opt_arg.flags = 0;
5103 syn_opt_arg.keyword = FALSE;
5104 syn_opt_arg.sync_idx = NULL;
5105 syn_opt_arg.has_cont_list = TRUE;
5106 syn_opt_arg.cont_list = NULL;
5107 syn_opt_arg.cont_in_list = NULL;
5108 syn_opt_arg.next_list = NULL;
5109
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 /*
5111 * get the options, patterns and matchgroup.
5112 */
5113 while (rest != NULL && !ends_excmd(*rest))
5114 {
5115 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005116 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 if (rest == NULL || ends_excmd(*rest))
5118 break;
5119
5120 /* must be a pattern or matchgroup then */
5121 key_end = rest;
5122 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5123 ++key_end;
5124 vim_free(key);
5125 key = vim_strnsave_up(rest, (int)(key_end - rest));
5126 if (key == NULL) /* out of memory */
5127 {
5128 rest = NULL;
5129 break;
5130 }
5131 if (STRCMP(key, "MATCHGROUP") == 0)
5132 item = ITEM_MATCHGROUP;
5133 else if (STRCMP(key, "START") == 0)
5134 item = ITEM_START;
5135 else if (STRCMP(key, "END") == 0)
5136 item = ITEM_END;
5137 else if (STRCMP(key, "SKIP") == 0)
5138 {
5139 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5140 {
5141 illegal = TRUE;
5142 break;
5143 }
5144 item = ITEM_SKIP;
5145 }
5146 else
5147 break;
5148 rest = skipwhite(key_end);
5149 if (*rest != '=')
5150 {
5151 rest = NULL;
5152 EMSG2(_("E398: Missing '=': %s"), arg);
5153 break;
5154 }
5155 rest = skipwhite(rest + 1);
5156 if (*rest == NUL)
5157 {
5158 not_enough = TRUE;
5159 break;
5160 }
5161
5162 if (item == ITEM_MATCHGROUP)
5163 {
5164 p = skiptowhite(rest);
5165 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5166 matchgroup_id = 0;
5167 else
5168 {
5169 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5170 if (matchgroup_id == 0)
5171 {
5172 illegal = TRUE;
5173 break;
5174 }
5175 }
5176 rest = skipwhite(p);
5177 }
5178 else
5179 {
5180 /*
5181 * Allocate room for a syn_pattern, and link it in the list of
5182 * syn_patterns for this item, at the start (because the list is
5183 * used from end to start).
5184 */
5185 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5186 if (ppp == NULL)
5187 {
5188 rest = NULL;
5189 break;
5190 }
5191 ppp->pp_next = pat_ptrs[item];
5192 pat_ptrs[item] = ppp;
5193 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5194 if (ppp->pp_synp == NULL)
5195 {
5196 rest = NULL;
5197 break;
5198 }
5199
5200 /*
5201 * Get the syntax pattern and the following offset(s).
5202 */
5203 /* Enable the appropriate \z specials. */
5204 if (item == ITEM_START)
5205 reg_do_extmatch = REX_SET;
5206 else if (item == ITEM_SKIP || item == ITEM_END)
5207 reg_do_extmatch = REX_USE;
5208 rest = get_syn_pattern(rest, ppp->pp_synp);
5209 reg_do_extmatch = 0;
5210 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005211 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5213 ppp->pp_matchgroup_id = matchgroup_id;
5214 ++pat_count;
5215 }
5216 }
5217 vim_free(key);
5218 if (illegal || not_enough)
5219 rest = NULL;
5220
5221 /*
5222 * Must have a "start" and "end" pattern.
5223 */
5224 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5225 pat_ptrs[ITEM_END] == NULL))
5226 {
5227 not_enough = TRUE;
5228 rest = NULL;
5229 }
5230
5231 if (rest != NULL)
5232 {
5233 /*
5234 * Check for trailing garbage or command.
5235 * If OK, add the item.
5236 */
5237 eap->nextcmd = check_nextcmd(rest);
5238 if (!ends_excmd(*rest) || eap->skip)
5239 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005240 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241 && (syn_id = syn_check_group(arg,
5242 (int)(group_name_end - arg))) != 0)
5243 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005244 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005245 /*
5246 * Store the start/skip/end in the syn_items list
5247 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005248 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005249 for (item = ITEM_START; item <= ITEM_END; ++item)
5250 {
5251 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5252 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005253 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5254 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5255 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005256 (item == ITEM_START) ? SPTYPE_START :
5257 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005258 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5259 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005260 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5261 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005262 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005263 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005264#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005265 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005266#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267 if (item == ITEM_START)
5268 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005269 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005270 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005271 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005272 syn_opt_arg.cont_in_list;
5273 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005274 curwin->w_s->b_syn_containedin = TRUE;
5275 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005276 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005277 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005278 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005279 ++idx;
5280#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005281 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005282 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005283#endif
5284 }
5285 }
5286
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005287 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005288 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005289 success = TRUE; /* don't free the progs and patterns now */
5290 }
5291 }
5292
5293 /*
5294 * Free the allocated memory.
5295 */
5296 for (item = ITEM_START; item <= ITEM_END; ++item)
5297 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5298 {
5299 if (!success)
5300 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005301 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005302 vim_free(ppp->pp_synp->sp_pattern);
5303 }
5304 vim_free(ppp->pp_synp);
5305 ppp_next = ppp->pp_next;
5306 vim_free(ppp);
5307 }
5308
5309 if (!success)
5310 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005311 vim_free(syn_opt_arg.cont_list);
5312 vim_free(syn_opt_arg.cont_in_list);
5313 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 if (not_enough)
5315 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5316 else if (illegal || rest == NULL)
5317 EMSG2(_(e_invarg2), arg);
5318 }
5319}
5320
5321/*
5322 * A simple syntax group ID comparison function suitable for use in qsort()
5323 */
5324 static int
5325#ifdef __BORLANDC__
5326_RTLENTRYF
5327#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005328syn_compare_stub(const void *v1, const void *v2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005329{
5330 const short *s1 = v1;
5331 const short *s2 = v2;
5332
5333 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5334}
5335
5336/*
5337 * Combines lists of syntax clusters.
5338 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5339 */
5340 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005341syn_combine_list(short **clstr1, short **clstr2, int list_op)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005342{
5343 int count1 = 0;
5344 int count2 = 0;
5345 short *g1;
5346 short *g2;
5347 short *clstr = NULL;
5348 int count;
5349 int round;
5350
5351 /*
5352 * Handle degenerate cases.
5353 */
5354 if (*clstr2 == NULL)
5355 return;
5356 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5357 {
5358 if (list_op == CLUSTER_REPLACE)
5359 vim_free(*clstr1);
5360 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5361 *clstr1 = *clstr2;
5362 else
5363 vim_free(*clstr2);
5364 return;
5365 }
5366
5367 for (g1 = *clstr1; *g1; g1++)
5368 ++count1;
5369 for (g2 = *clstr2; *g2; g2++)
5370 ++count2;
5371
5372 /*
5373 * For speed purposes, sort both lists.
5374 */
5375 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5376 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5377
5378 /*
5379 * We proceed in two passes; in round 1, we count the elements to place
5380 * in the new list, and in round 2, we allocate and populate the new
5381 * list. For speed, we use a mergesort-like method, adding the smaller
5382 * of the current elements in each list to the new list.
5383 */
5384 for (round = 1; round <= 2; round++)
5385 {
5386 g1 = *clstr1;
5387 g2 = *clstr2;
5388 count = 0;
5389
5390 /*
5391 * First, loop through the lists until one of them is empty.
5392 */
5393 while (*g1 && *g2)
5394 {
5395 /*
5396 * We always want to add from the first list.
5397 */
5398 if (*g1 < *g2)
5399 {
5400 if (round == 2)
5401 clstr[count] = *g1;
5402 count++;
5403 g1++;
5404 continue;
5405 }
5406 /*
5407 * We only want to add from the second list if we're adding the
5408 * lists.
5409 */
5410 if (list_op == CLUSTER_ADD)
5411 {
5412 if (round == 2)
5413 clstr[count] = *g2;
5414 count++;
5415 }
5416 if (*g1 == *g2)
5417 g1++;
5418 g2++;
5419 }
5420
5421 /*
5422 * Now add the leftovers from whichever list didn't get finished
5423 * first. As before, we only want to add from the second list if
5424 * we're adding the lists.
5425 */
5426 for (; *g1; g1++, count++)
5427 if (round == 2)
5428 clstr[count] = *g1;
5429 if (list_op == CLUSTER_ADD)
5430 for (; *g2; g2++, count++)
5431 if (round == 2)
5432 clstr[count] = *g2;
5433
5434 if (round == 1)
5435 {
5436 /*
5437 * If the group ended up empty, we don't need to allocate any
5438 * space for it.
5439 */
5440 if (count == 0)
5441 {
5442 clstr = NULL;
5443 break;
5444 }
5445 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5446 if (clstr == NULL)
5447 break;
5448 clstr[count] = 0;
5449 }
5450 }
5451
5452 /*
5453 * Finally, put the new list in place.
5454 */
5455 vim_free(*clstr1);
5456 vim_free(*clstr2);
5457 *clstr1 = clstr;
5458}
5459
5460/*
5461 * Lookup a syntax cluster name and return it's ID.
5462 * If it is not found, 0 is returned.
5463 */
5464 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005465syn_scl_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005466{
5467 int i;
5468 char_u *name_u;
5469
5470 /* Avoid using stricmp() too much, it's slow on some systems */
5471 name_u = vim_strsave_up(name);
5472 if (name_u == NULL)
5473 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005474 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5475 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5476 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477 break;
5478 vim_free(name_u);
5479 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5480}
5481
5482/*
5483 * Like syn_scl_name2id(), but take a pointer + length argument.
5484 */
5485 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005486syn_scl_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487{
5488 char_u *name;
5489 int id = 0;
5490
5491 name = vim_strnsave(linep, len);
5492 if (name != NULL)
5493 {
5494 id = syn_scl_name2id(name);
5495 vim_free(name);
5496 }
5497 return id;
5498}
5499
5500/*
5501 * Find syntax cluster name in the table and return it's ID.
5502 * The argument is a pointer to the name and the length of the name.
5503 * If it doesn't exist yet, a new entry is created.
5504 * Return 0 for failure.
5505 */
5506 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005507syn_check_cluster(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005508{
5509 int id;
5510 char_u *name;
5511
5512 name = vim_strnsave(pp, len);
5513 if (name == NULL)
5514 return 0;
5515
5516 id = syn_scl_name2id(name);
5517 if (id == 0) /* doesn't exist yet */
5518 id = syn_add_cluster(name);
5519 else
5520 vim_free(name);
5521 return id;
5522}
5523
5524/*
5525 * Add new syntax cluster and return it's ID.
5526 * "name" must be an allocated string, it will be consumed.
5527 * Return 0 for failure.
5528 */
5529 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005530syn_add_cluster(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005531{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005532 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005533
5534 /*
5535 * First call for this growarray: init growing array.
5536 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005537 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005539 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5540 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541 }
5542
Bram Moolenaar42431a72011-04-01 14:44:59 +02005543 len = curwin->w_s->b_syn_clusters.ga_len;
5544 if (len >= MAX_CLUSTER_ID)
5545 {
5546 EMSG((char_u *)_("E848: Too many syntax clusters"));
5547 vim_free(name);
5548 return 0;
5549 }
5550
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551 /*
5552 * Make room for at least one other cluster entry.
5553 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005554 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 {
5556 vim_free(name);
5557 return 0;
5558 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559
Bram Moolenaar860cae12010-06-05 23:22:07 +02005560 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5561 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5562 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5563 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5564 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565
Bram Moolenaar217ad922005-03-20 22:37:15 +00005566 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005567 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005568 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005569 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005570
Bram Moolenaar071d4272004-06-13 20:20:40 +00005571 return len + SYNID_CLUSTER;
5572}
5573
5574/*
5575 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5576 * [add={groupname},..] [remove={groupname},..]".
5577 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005578 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005579syn_cmd_cluster(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580{
5581 char_u *arg = eap->arg;
5582 char_u *group_name_end;
5583 char_u *rest;
5584 int scl_id;
5585 short *clstr_list;
5586 int got_clstr = FALSE;
5587 int opt_len;
5588 int list_op;
5589
5590 eap->nextcmd = find_nextcmd(arg);
5591 if (eap->skip)
5592 return;
5593
5594 rest = get_group_name(arg, &group_name_end);
5595
5596 if (rest != NULL)
5597 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005598 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5599 if (scl_id == 0)
5600 return;
5601 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602
5603 for (;;)
5604 {
5605 if (STRNICMP(rest, "add", 3) == 0
5606 && (vim_iswhite(rest[3]) || rest[3] == '='))
5607 {
5608 opt_len = 3;
5609 list_op = CLUSTER_ADD;
5610 }
5611 else if (STRNICMP(rest, "remove", 6) == 0
5612 && (vim_iswhite(rest[6]) || rest[6] == '='))
5613 {
5614 opt_len = 6;
5615 list_op = CLUSTER_SUBTRACT;
5616 }
5617 else if (STRNICMP(rest, "contains", 8) == 0
5618 && (vim_iswhite(rest[8]) || rest[8] == '='))
5619 {
5620 opt_len = 8;
5621 list_op = CLUSTER_REPLACE;
5622 }
5623 else
5624 break;
5625
5626 clstr_list = NULL;
5627 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5628 {
5629 EMSG2(_(e_invarg2), rest);
5630 break;
5631 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005632 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005633 &clstr_list, list_op);
5634 got_clstr = TRUE;
5635 }
5636
5637 if (got_clstr)
5638 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005639 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005640 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641 }
5642 }
5643
5644 if (!got_clstr)
5645 EMSG(_("E400: No cluster specified"));
5646 if (rest == NULL || !ends_excmd(*rest))
5647 EMSG2(_(e_invarg2), arg);
5648}
5649
5650/*
5651 * On first call for current buffer: Init growing array.
5652 */
5653 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005654init_syn_patterns(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005655{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005656 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5657 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658}
5659
5660/*
5661 * Get one pattern for a ":syntax match" or ":syntax region" command.
5662 * Stores the pattern and program in a synpat_T.
5663 * Returns a pointer to the next argument, or NULL in case of an error.
5664 */
5665 static char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005666get_syn_pattern(char_u *arg, synpat_T *ci)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005667{
5668 char_u *end;
5669 int *p;
5670 int idx;
5671 char_u *cpo_save;
5672
5673 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005674 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 return NULL;
5676
5677 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5678 if (*end != *arg) /* end delimiter not found */
5679 {
5680 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5681 return NULL;
5682 }
5683 /* store the pattern and compiled regexp program */
5684 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5685 return NULL;
5686
5687 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5688 cpo_save = p_cpo;
5689 p_cpo = (char_u *)"";
5690 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5691 p_cpo = cpo_save;
5692
5693 if (ci->sp_prog == NULL)
5694 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005695 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005696#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005697 syn_clear_time(&ci->sp_time);
5698#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699
5700 /*
5701 * Check for a match, highlight or region offset.
5702 */
5703 ++end;
5704 do
5705 {
5706 for (idx = SPO_COUNT; --idx >= 0; )
5707 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5708 break;
5709 if (idx >= 0)
5710 {
5711 p = &(ci->sp_offsets[idx]);
5712 if (idx != SPO_LC_OFF)
5713 switch (end[3])
5714 {
5715 case 's': break;
5716 case 'b': break;
5717 case 'e': idx += SPO_COUNT; break;
5718 default: idx = -1; break;
5719 }
5720 if (idx >= 0)
5721 {
5722 ci->sp_off_flags |= (1 << idx);
5723 if (idx == SPO_LC_OFF) /* lc=99 */
5724 {
5725 end += 3;
5726 *p = getdigits(&end);
5727
5728 /* "lc=" offset automatically sets "ms=" offset */
5729 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5730 {
5731 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5732 ci->sp_offsets[SPO_MS_OFF] = *p;
5733 }
5734 }
5735 else /* yy=x+99 */
5736 {
5737 end += 4;
5738 if (*end == '+')
5739 {
5740 ++end;
5741 *p = getdigits(&end); /* positive offset */
5742 }
5743 else if (*end == '-')
5744 {
5745 ++end;
5746 *p = -getdigits(&end); /* negative offset */
5747 }
5748 }
5749 if (*end != ',')
5750 break;
5751 ++end;
5752 }
5753 }
5754 } while (idx >= 0);
5755
5756 if (!ends_excmd(*end) && !vim_iswhite(*end))
5757 {
5758 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5759 return NULL;
5760 }
5761 return skipwhite(end);
5762}
5763
5764/*
5765 * Handle ":syntax sync .." command.
5766 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005767 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005768syn_cmd_sync(exarg_T *eap, int syncing UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005769{
5770 char_u *arg_start = eap->arg;
5771 char_u *arg_end;
5772 char_u *key = NULL;
5773 char_u *next_arg;
5774 int illegal = FALSE;
5775 int finished = FALSE;
5776 long n;
5777 char_u *cpo_save;
5778
5779 if (ends_excmd(*arg_start))
5780 {
5781 syn_cmd_list(eap, TRUE);
5782 return;
5783 }
5784
5785 while (!ends_excmd(*arg_start))
5786 {
5787 arg_end = skiptowhite(arg_start);
5788 next_arg = skipwhite(arg_end);
5789 vim_free(key);
5790 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5791 if (STRCMP(key, "CCOMMENT") == 0)
5792 {
5793 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005794 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795 if (!ends_excmd(*next_arg))
5796 {
5797 arg_end = skiptowhite(next_arg);
5798 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005799 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005800 (int)(arg_end - next_arg));
5801 next_arg = skipwhite(arg_end);
5802 }
5803 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005804 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005805 }
5806 else if ( STRNCMP(key, "LINES", 5) == 0
5807 || STRNCMP(key, "MINLINES", 8) == 0
5808 || STRNCMP(key, "MAXLINES", 8) == 0
5809 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5810 {
5811 if (key[4] == 'S')
5812 arg_end = key + 6;
5813 else if (key[0] == 'L')
5814 arg_end = key + 11;
5815 else
5816 arg_end = key + 9;
5817 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5818 {
5819 illegal = TRUE;
5820 break;
5821 }
5822 n = getdigits(&arg_end);
5823 if (!eap->skip)
5824 {
5825 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005826 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005827 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005828 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005830 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 }
5832 }
5833 else if (STRCMP(key, "FROMSTART") == 0)
5834 {
5835 if (!eap->skip)
5836 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005837 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5838 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005839 }
5840 }
5841 else if (STRCMP(key, "LINECONT") == 0)
5842 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005843 if (*next_arg == NUL) /* missing pattern */
5844 {
5845 illegal = TRUE;
5846 break;
5847 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005848 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005849 {
5850 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5851 finished = TRUE;
5852 break;
5853 }
5854 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5855 if (*arg_end != *next_arg) /* end delimiter not found */
5856 {
5857 illegal = TRUE;
5858 break;
5859 }
5860
5861 if (!eap->skip)
5862 {
5863 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005864 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005865 (int)(arg_end - next_arg - 1))) == NULL)
5866 {
5867 finished = TRUE;
5868 break;
5869 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005870 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005871
5872 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5873 cpo_save = p_cpo;
5874 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005875 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005876 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005878#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005879 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5880#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881
Bram Moolenaar860cae12010-06-05 23:22:07 +02005882 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005883 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005884 vim_free(curwin->w_s->b_syn_linecont_pat);
5885 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 finished = TRUE;
5887 break;
5888 }
5889 }
5890 next_arg = skipwhite(arg_end + 1);
5891 }
5892 else
5893 {
5894 eap->arg = next_arg;
5895 if (STRCMP(key, "MATCH") == 0)
5896 syn_cmd_match(eap, TRUE);
5897 else if (STRCMP(key, "REGION") == 0)
5898 syn_cmd_region(eap, TRUE);
5899 else if (STRCMP(key, "CLEAR") == 0)
5900 syn_cmd_clear(eap, TRUE);
5901 else
5902 illegal = TRUE;
5903 finished = TRUE;
5904 break;
5905 }
5906 arg_start = next_arg;
5907 }
5908 vim_free(key);
5909 if (illegal)
5910 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5911 else if (!finished)
5912 {
5913 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005914 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005915 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005916 }
5917}
5918
5919/*
5920 * Convert a line of highlight group names into a list of group ID numbers.
5921 * "arg" should point to the "contains" or "nextgroup" keyword.
5922 * "arg" is advanced to after the last group name.
5923 * Careful: the argument is modified (NULs added).
5924 * returns FAIL for some error, OK for success.
5925 */
5926 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01005927get_id_list(
5928 char_u **arg,
5929 int keylen, /* length of keyword */
5930 short **list) /* where to store the resulting list, if not
Bram Moolenaar071d4272004-06-13 20:20:40 +00005931 NULL, the list is silently skipped! */
5932{
5933 char_u *p = NULL;
5934 char_u *end;
5935 int round;
5936 int count;
5937 int total_count = 0;
5938 short *retval = NULL;
5939 char_u *name;
5940 regmatch_T regmatch;
5941 int id;
5942 int i;
5943 int failed = FALSE;
5944
5945 /*
5946 * We parse the list twice:
5947 * round == 1: count the number of items, allocate the array.
5948 * round == 2: fill the array with the items.
5949 * In round 1 new groups may be added, causing the number of items to
5950 * grow when a regexp is used. In that case round 1 is done once again.
5951 */
5952 for (round = 1; round <= 2; ++round)
5953 {
5954 /*
5955 * skip "contains"
5956 */
5957 p = skipwhite(*arg + keylen);
5958 if (*p != '=')
5959 {
5960 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5961 break;
5962 }
5963 p = skipwhite(p + 1);
5964 if (ends_excmd(*p))
5965 {
5966 EMSG2(_("E406: Empty argument: %s"), *arg);
5967 break;
5968 }
5969
5970 /*
5971 * parse the arguments after "contains"
5972 */
5973 count = 0;
5974 while (!ends_excmd(*p))
5975 {
5976 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5977 ;
5978 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5979 if (name == NULL)
5980 {
5981 failed = TRUE;
5982 break;
5983 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005984 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005985 if ( STRCMP(name + 1, "ALLBUT") == 0
5986 || STRCMP(name + 1, "ALL") == 0
5987 || STRCMP(name + 1, "TOP") == 0
5988 || STRCMP(name + 1, "CONTAINED") == 0)
5989 {
5990 if (TOUPPER_ASC(**arg) != 'C')
5991 {
5992 EMSG2(_("E407: %s not allowed here"), name + 1);
5993 failed = TRUE;
5994 vim_free(name);
5995 break;
5996 }
5997 if (count != 0)
5998 {
5999 EMSG2(_("E408: %s must be first in contains list"), name + 1);
6000 failed = TRUE;
6001 vim_free(name);
6002 break;
6003 }
6004 if (name[1] == 'A')
6005 id = SYNID_ALLBUT;
6006 else if (name[1] == 'T')
6007 id = SYNID_TOP;
6008 else
6009 id = SYNID_CONTAINED;
6010 id += current_syn_inc_tag;
6011 }
6012 else if (name[1] == '@')
6013 {
6014 id = syn_check_cluster(name + 2, (int)(end - p - 1));
6015 }
6016 else
6017 {
6018 /*
6019 * Handle full group name.
6020 */
6021 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6022 id = syn_check_group(name + 1, (int)(end - p));
6023 else
6024 {
6025 /*
6026 * Handle match of regexp with group names.
6027 */
6028 *name = '^';
6029 STRCAT(name, "$");
6030 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6031 if (regmatch.regprog == NULL)
6032 {
6033 failed = TRUE;
6034 vim_free(name);
6035 break;
6036 }
6037
6038 regmatch.rm_ic = TRUE;
6039 id = 0;
6040 for (i = highlight_ga.ga_len; --i >= 0; )
6041 {
6042 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6043 (colnr_T)0))
6044 {
6045 if (round == 2)
6046 {
6047 /* Got more items than expected; can happen
6048 * when adding items that match:
6049 * "contains=a.*b,axb".
6050 * Go back to first round */
6051 if (count >= total_count)
6052 {
6053 vim_free(retval);
6054 round = 1;
6055 }
6056 else
6057 retval[count] = i + 1;
6058 }
6059 ++count;
6060 id = -1; /* remember that we found one */
6061 }
6062 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006063 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006064 }
6065 }
6066 vim_free(name);
6067 if (id == 0)
6068 {
6069 EMSG2(_("E409: Unknown group name: %s"), p);
6070 failed = TRUE;
6071 break;
6072 }
6073 if (id > 0)
6074 {
6075 if (round == 2)
6076 {
6077 /* Got more items than expected, go back to first round */
6078 if (count >= total_count)
6079 {
6080 vim_free(retval);
6081 round = 1;
6082 }
6083 else
6084 retval[count] = id;
6085 }
6086 ++count;
6087 }
6088 p = skipwhite(end);
6089 if (*p != ',')
6090 break;
6091 p = skipwhite(p + 1); /* skip comma in between arguments */
6092 }
6093 if (failed)
6094 break;
6095 if (round == 1)
6096 {
6097 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6098 if (retval == NULL)
6099 break;
6100 retval[count] = 0; /* zero means end of the list */
6101 total_count = count;
6102 }
6103 }
6104
6105 *arg = p;
6106 if (failed || retval == NULL)
6107 {
6108 vim_free(retval);
6109 return FAIL;
6110 }
6111
6112 if (*list == NULL)
6113 *list = retval;
6114 else
6115 vim_free(retval); /* list already found, don't overwrite it */
6116
6117 return OK;
6118}
6119
6120/*
6121 * Make a copy of an ID list.
6122 */
6123 static short *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006124copy_id_list(short *list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006125{
6126 int len;
6127 int count;
6128 short *retval;
6129
6130 if (list == NULL)
6131 return NULL;
6132
6133 for (count = 0; list[count]; ++count)
6134 ;
6135 len = (count + 1) * sizeof(short);
6136 retval = (short *)alloc((unsigned)len);
6137 if (retval != NULL)
6138 mch_memmove(retval, list, (size_t)len);
6139
6140 return retval;
6141}
6142
6143/*
6144 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6145 * "cur_si" can be NULL if not checking the "containedin" list.
6146 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6147 * the current item.
6148 * This function is called very often, keep it fast!!
6149 */
6150 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006151in_id_list(
6152 stateitem_T *cur_si, /* current item or NULL */
6153 short *list, /* id list */
6154 struct sp_syn *ssp, /* group id and ":syn include" tag of group */
6155 int contained) /* group id is contained */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006156{
6157 int retval;
6158 short *scl_list;
6159 short item;
6160 short id = ssp->id;
6161 static int depth = 0;
6162 int r;
6163
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006164 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006165 if (cur_si != NULL && ssp->cont_in_list != NULL
6166 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006167 {
6168 /* Ignore transparent items without a contains argument. Double check
6169 * that we don't go back past the first one. */
6170 while ((cur_si->si_flags & HL_TRANS_CONT)
6171 && cur_si > (stateitem_T *)(current_state.ga_data))
6172 --cur_si;
6173 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6174 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006175 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6176 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006177 return TRUE;
6178 }
6179
6180 if (list == NULL)
6181 return FALSE;
6182
6183 /*
6184 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6185 * inside anything. Only allow not-contained groups.
6186 */
6187 if (list == ID_LIST_ALL)
6188 return !contained;
6189
6190 /*
6191 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6192 * contains list. We also require that "id" is at the same ":syn include"
6193 * level as the list.
6194 */
6195 item = *list;
6196 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6197 {
6198 if (item < SYNID_TOP)
6199 {
6200 /* ALL or ALLBUT: accept all groups in the same file */
6201 if (item - SYNID_ALLBUT != ssp->inc_tag)
6202 return FALSE;
6203 }
6204 else if (item < SYNID_CONTAINED)
6205 {
6206 /* TOP: accept all not-contained groups in the same file */
6207 if (item - SYNID_TOP != ssp->inc_tag || contained)
6208 return FALSE;
6209 }
6210 else
6211 {
6212 /* CONTAINED: accept all contained groups in the same file */
6213 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6214 return FALSE;
6215 }
6216 item = *++list;
6217 retval = FALSE;
6218 }
6219 else
6220 retval = TRUE;
6221
6222 /*
6223 * Return "retval" if id is in the contains list.
6224 */
6225 while (item != 0)
6226 {
6227 if (item == id)
6228 return retval;
6229 if (item >= SYNID_CLUSTER)
6230 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006231 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006232 /* restrict recursiveness to 30 to avoid an endless loop for a
6233 * cluster that includes itself (indirectly) */
6234 if (scl_list != NULL && depth < 30)
6235 {
6236 ++depth;
6237 r = in_id_list(NULL, scl_list, ssp, contained);
6238 --depth;
6239 if (r)
6240 return retval;
6241 }
6242 }
6243 item = *++list;
6244 }
6245 return !retval;
6246}
6247
6248struct subcommand
6249{
Bram Moolenaard99df422016-01-29 23:20:40 +01006250 char *name; /* subcommand name */
6251 void (*func)(exarg_T *, int); /* function to call */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006252};
6253
6254static struct subcommand subcommands[] =
6255{
6256 {"case", syn_cmd_case},
6257 {"clear", syn_cmd_clear},
6258 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006259 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006260 {"enable", syn_cmd_enable},
6261 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006262 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006263 {"keyword", syn_cmd_keyword},
6264 {"list", syn_cmd_list},
6265 {"manual", syn_cmd_manual},
6266 {"match", syn_cmd_match},
6267 {"on", syn_cmd_on},
6268 {"off", syn_cmd_off},
6269 {"region", syn_cmd_region},
6270 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006271 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006272 {"sync", syn_cmd_sync},
6273 {"", syn_cmd_list},
6274 {NULL, NULL}
6275};
6276
6277/*
6278 * ":syntax".
6279 * This searches the subcommands[] table for the subcommand name, and calls a
6280 * syntax_subcommand() function to do the rest.
6281 */
6282 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006283ex_syntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006284{
6285 char_u *arg = eap->arg;
6286 char_u *subcmd_end;
6287 char_u *subcmd_name;
6288 int i;
6289
6290 syn_cmdlinep = eap->cmdlinep;
6291
6292 /* isolate subcommand name */
6293 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6294 ;
6295 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6296 if (subcmd_name != NULL)
6297 {
6298 if (eap->skip) /* skip error messages for all subcommands */
6299 ++emsg_skip;
6300 for (i = 0; ; ++i)
6301 {
6302 if (subcommands[i].name == NULL)
6303 {
6304 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6305 break;
6306 }
6307 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6308 {
6309 eap->arg = skipwhite(subcmd_end);
6310 (subcommands[i].func)(eap, FALSE);
6311 break;
6312 }
6313 }
6314 vim_free(subcmd_name);
6315 if (eap->skip)
6316 --emsg_skip;
6317 }
6318}
6319
Bram Moolenaar860cae12010-06-05 23:22:07 +02006320 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006321ex_ownsyntax(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006322{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006323 char_u *old_value;
6324 char_u *new_value;
6325
Bram Moolenaar860cae12010-06-05 23:22:07 +02006326 if (curwin->w_s == &curwin->w_buffer->b_s)
6327 {
6328 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6329 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006330 hash_init(&curwin->w_s->b_keywtab);
6331 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006333 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006334 curwin->w_p_spell = FALSE; /* No spell checking */
6335 clear_string_option(&curwin->w_s->b_p_spc);
6336 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006337 clear_string_option(&curwin->w_s->b_p_spl);
6338#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006339 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006340 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006341
6342 /* save value of b:current_syntax */
6343 old_value = get_var_value((char_u *)"b:current_syntax");
6344 if (old_value != NULL)
6345 old_value = vim_strsave(old_value);
6346
Bram Moolenaard1413d92016-03-02 21:51:56 +01006347#ifdef FEAT_AUTOCMD
Bram Moolenaar1950c352010-06-06 15:21:10 +02006348 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6349 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006350 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaard1413d92016-03-02 21:51:56 +01006351#endif
Bram Moolenaar1950c352010-06-06 15:21:10 +02006352
6353 /* move value of b:current_syntax to w:current_syntax */
6354 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006355 if (new_value != NULL)
6356 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006357
6358 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006359 if (old_value == NULL)
6360 do_unlet((char_u *)"b:current_syntax", TRUE);
6361 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006362 {
6363 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6364 vim_free(old_value);
6365 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006366}
6367
6368 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006369syntax_present(win_T *win)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006370{
6371 return (win->w_s->b_syn_patterns.ga_len != 0
6372 || win->w_s->b_syn_clusters.ga_len != 0
6373 || win->w_s->b_keywtab.ht_used > 0
6374 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006375}
6376
6377#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6378
6379static enum
6380{
6381 EXP_SUBCMD, /* expand ":syn" sub-commands */
6382 EXP_CASE /* expand ":syn case" arguments */
6383} expand_what;
6384
Bram Moolenaar4f688582007-07-24 12:34:30 +00006385/*
6386 * Reset include_link, include_default, include_none to 0.
6387 * Called when we are done expanding.
6388 */
6389 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006390reset_expand_highlight(void)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006391{
6392 include_link = include_default = include_none = 0;
6393}
6394
6395/*
6396 * Handle command line completion for :match and :echohl command: Add "None"
6397 * as highlight group.
6398 */
6399 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006400set_context_in_echohl_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar4f688582007-07-24 12:34:30 +00006401{
6402 xp->xp_context = EXPAND_HIGHLIGHT;
6403 xp->xp_pattern = arg;
6404 include_none = 1;
6405}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006406
6407/*
6408 * Handle command line completion for :syntax command.
6409 */
6410 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006411set_context_in_syntax_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006412{
6413 char_u *p;
6414
6415 /* Default: expand subcommands */
6416 xp->xp_context = EXPAND_SYNTAX;
6417 expand_what = EXP_SUBCMD;
6418 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006419 include_link = 0;
6420 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006421
6422 /* (part of) subcommand already typed */
6423 if (*arg != NUL)
6424 {
6425 p = skiptowhite(arg);
6426 if (*p != NUL) /* past first word */
6427 {
6428 xp->xp_pattern = skipwhite(p);
6429 if (*skiptowhite(xp->xp_pattern) != NUL)
6430 xp->xp_context = EXPAND_NOTHING;
6431 else if (STRNICMP(arg, "case", p - arg) == 0)
6432 expand_what = EXP_CASE;
6433 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6434 || STRNICMP(arg, "region", p - arg) == 0
6435 || STRNICMP(arg, "match", p - arg) == 0
6436 || STRNICMP(arg, "list", p - arg) == 0)
6437 xp->xp_context = EXPAND_HIGHLIGHT;
6438 else
6439 xp->xp_context = EXPAND_NOTHING;
6440 }
6441 }
6442}
6443
6444static char *(case_args[]) = {"match", "ignore", NULL};
6445
6446/*
6447 * Function given to ExpandGeneric() to obtain the list syntax names for
6448 * expansion.
6449 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006451get_syntax_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006452{
6453 if (expand_what == EXP_SUBCMD)
6454 return (char_u *)subcommands[idx].name;
6455 return (char_u *)case_args[idx];
6456}
6457
6458#endif /* FEAT_CMDL_COMPL */
6459
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460/*
6461 * Function called for expression evaluation: get syntax ID at file position.
6462 */
6463 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006464syn_get_id(
6465 win_T *wp,
6466 long lnum,
6467 colnr_T col,
6468 int trans, /* remove transparency */
6469 int *spellp, /* return: can do spell checking */
6470 int keep_state) /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006471{
6472 /* When the position is not after the current position and in the same
6473 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006474 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006475 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006476 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006477 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006478 else if (wp->w_buffer == syn_buf
6479 && lnum == current_lnum
6480 && col > current_col)
6481 /* next_match may not be correct when moving around, e.g. with the
6482 * "skip" expression in searchpair() */
6483 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006484
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006485 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486
6487 return (trans ? current_trans_id : current_id);
6488}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006489
Bram Moolenaar860cae12010-06-05 23:22:07 +02006490#if defined(FEAT_CONCEAL) || defined(PROTO)
6491/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006492 * Get extra information about the syntax item. Must be called right after
6493 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006494 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006495 * Returns the current flags.
6496 */
6497 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006498get_syntax_info(int *seqnrp)
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006499{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006500 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006501 return current_flags;
6502}
6503
6504/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006505 * Return conceal substitution character
6506 */
6507 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006508syn_get_sub_char(void)
Bram Moolenaar860cae12010-06-05 23:22:07 +02006509{
6510 return current_sub_char;
6511}
6512#endif
6513
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006514#if defined(FEAT_EVAL) || defined(PROTO)
6515/*
6516 * Return the syntax ID at position "i" in the current stack.
6517 * The caller must have called syn_get_id() before to fill the stack.
6518 * Returns -1 when "i" is out of range.
6519 */
6520 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006521syn_get_stack_item(int i)
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006522{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006523 if (i >= current_state.ga_len)
6524 {
6525 /* Need to invalidate the state, because we didn't properly finish it
6526 * for the last character, "keep_state" was TRUE. */
6527 invalidate_current_state();
6528 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006529 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006530 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006531 return CUR_STATE(i).si_id;
6532}
6533#endif
6534
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535#if defined(FEAT_FOLDING) || defined(PROTO)
6536/*
6537 * Function called to get folding level for line "lnum" in window "wp".
6538 */
6539 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006540syn_get_foldlevel(win_T *wp, long lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006541{
6542 int level = 0;
6543 int i;
6544
6545 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006546 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006547 {
6548 syntax_start(wp, lnum);
6549
6550 for (i = 0; i < current_state.ga_len; ++i)
6551 if (CUR_STATE(i).si_flags & HL_FOLD)
6552 ++level;
6553 }
6554 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006555 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006556 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006557 if (level < 0)
6558 level = 0;
6559 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560 return level;
6561}
6562#endif
6563
Bram Moolenaar01615492015-02-03 13:00:38 +01006564#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006565/*
6566 * ":syntime".
6567 */
6568 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006569ex_syntime(exarg_T *eap)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006570{
6571 if (STRCMP(eap->arg, "on") == 0)
6572 syn_time_on = TRUE;
6573 else if (STRCMP(eap->arg, "off") == 0)
6574 syn_time_on = FALSE;
6575 else if (STRCMP(eap->arg, "clear") == 0)
6576 syntime_clear();
6577 else if (STRCMP(eap->arg, "report") == 0)
6578 syntime_report();
6579 else
6580 EMSG2(_(e_invarg2), eap->arg);
6581}
6582
6583 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006584syn_clear_time(syn_time_T *st)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006585{
6586 profile_zero(&st->total);
6587 profile_zero(&st->slowest);
6588 st->count = 0;
6589 st->match = 0;
6590}
6591
6592/*
6593 * Clear the syntax timing for the current buffer.
6594 */
6595 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006596syntime_clear(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006597{
6598 int idx;
6599 synpat_T *spp;
6600
6601 if (!syntax_present(curwin))
6602 {
6603 MSG(_(msg_no_items));
6604 return;
6605 }
6606 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6607 {
6608 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6609 syn_clear_time(&spp->sp_time);
6610 }
6611}
6612
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006613#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6614/*
6615 * Function given to ExpandGeneric() to obtain the possible arguments of the
6616 * ":syntime {on,off,clear,report}" command.
6617 */
6618 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006619get_syntime_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006620{
6621 switch (idx)
6622 {
6623 case 0: return (char_u *)"on";
6624 case 1: return (char_u *)"off";
6625 case 2: return (char_u *)"clear";
6626 case 3: return (char_u *)"report";
6627 }
6628 return NULL;
6629}
6630#endif
6631
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006632typedef struct
6633{
6634 proftime_T total;
6635 int count;
6636 int match;
6637 proftime_T slowest;
6638 proftime_T average;
6639 int id;
6640 char_u *pattern;
6641} time_entry_T;
6642
6643 static int
6644#ifdef __BORLANDC__
6645_RTLENTRYF
6646#endif
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006647syn_compare_syntime(const void *v1, const void *v2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006648{
6649 const time_entry_T *s1 = v1;
6650 const time_entry_T *s2 = v2;
6651
6652 return profile_cmp(&s1->total, &s2->total);
6653}
6654
6655/*
6656 * Clear the syntax timing for the current buffer.
6657 */
6658 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006659syntime_report(void)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006660{
6661 int idx;
6662 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006663# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006664 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006665# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006666 int len;
6667 proftime_T total_total;
6668 int total_count = 0;
6669 garray_T ga;
6670 time_entry_T *p;
6671
6672 if (!syntax_present(curwin))
6673 {
6674 MSG(_(msg_no_items));
6675 return;
6676 }
6677
6678 ga_init2(&ga, sizeof(time_entry_T), 50);
6679 profile_zero(&total_total);
6680 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6681 {
6682 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6683 if (spp->sp_time.count > 0)
6684 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006685 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006686 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6687 p->total = spp->sp_time.total;
6688 profile_add(&total_total, &spp->sp_time.total);
6689 p->count = spp->sp_time.count;
6690 p->match = spp->sp_time.match;
6691 total_count += spp->sp_time.count;
6692 p->slowest = spp->sp_time.slowest;
6693# ifdef FEAT_FLOAT
6694 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6695 p->average = tm;
6696# endif
6697 p->id = spp->sp_syn.id;
6698 p->pattern = spp->sp_pattern;
6699 ++ga.ga_len;
6700 }
6701 }
6702
6703 /* sort on total time */
Bram Moolenaar4e312962013-06-06 21:19:51 +02006704 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6705 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006706
6707 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6708 MSG_PUTS("\n");
6709 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6710 {
6711 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6712 p = ((time_entry_T *)ga.ga_data) + idx;
6713
6714 MSG_PUTS(profile_msg(&p->total));
6715 MSG_PUTS(" "); /* make sure there is always a separating space */
6716 msg_advance(13);
6717 msg_outnum(p->count);
6718 MSG_PUTS(" ");
6719 msg_advance(20);
6720 msg_outnum(p->match);
6721 MSG_PUTS(" ");
6722 msg_advance(26);
6723 MSG_PUTS(profile_msg(&p->slowest));
6724 MSG_PUTS(" ");
6725 msg_advance(38);
6726# ifdef FEAT_FLOAT
6727 MSG_PUTS(profile_msg(&p->average));
6728 MSG_PUTS(" ");
6729# endif
6730 msg_advance(50);
6731 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6732 MSG_PUTS(" ");
6733
6734 msg_advance(69);
6735 if (Columns < 80)
6736 len = 20; /* will wrap anyway */
6737 else
6738 len = Columns - 70;
6739 if (len > (int)STRLEN(p->pattern))
6740 len = (int)STRLEN(p->pattern);
6741 msg_outtrans_len(p->pattern, len);
6742 MSG_PUTS("\n");
6743 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006744 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006745 if (!got_int)
6746 {
6747 MSG_PUTS("\n");
6748 MSG_PUTS(profile_msg(&total_total));
6749 msg_advance(13);
6750 msg_outnum(total_count);
6751 MSG_PUTS("\n");
6752 }
6753}
6754#endif
6755
Bram Moolenaar071d4272004-06-13 20:20:40 +00006756#endif /* FEAT_SYN_HL */
6757
Bram Moolenaar071d4272004-06-13 20:20:40 +00006758/**************************************
6759 * Highlighting stuff *
6760 **************************************/
6761
6762/*
6763 * The default highlight groups. These are compiled-in for fast startup and
6764 * they still work when the runtime files can't be found.
6765 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006766 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6767 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006768 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006769#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006770# define CENT(a, b) b
6771#else
6772# define CENT(a, b) a
6773#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006774static char *(highlight_init_both[]) =
6775 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006776 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6777 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6778 CENT("IncSearch term=reverse cterm=reverse",
6779 "IncSearch term=reverse cterm=reverse gui=reverse"),
6780 CENT("ModeMsg term=bold cterm=bold",
6781 "ModeMsg term=bold cterm=bold gui=bold"),
6782 CENT("NonText term=bold ctermfg=Blue",
6783 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6784 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6785 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6786 CENT("StatusLineNC term=reverse cterm=reverse",
6787 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar44a2f922016-03-19 22:11:51 +01006788#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006789 CENT("VertSplit term=reverse cterm=reverse",
6790 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006791#endif
6792#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006793 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6794 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006795#endif
6796#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006797 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6798 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006799#endif
6800#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006801 CENT("PmenuSbar ctermbg=Grey",
6802 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006803#endif
6804#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006805 CENT("TabLineSel term=bold cterm=bold",
6806 "TabLineSel term=bold cterm=bold gui=bold"),
6807 CENT("TabLineFill term=reverse cterm=reverse",
6808 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006809#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006810#ifdef FEAT_GUI
6811 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006812 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006813#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006814 NULL
6815 };
6816
6817static char *(highlight_init_light[]) =
6818 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006819 CENT("Directory term=bold ctermfg=DarkBlue",
6820 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6821 CENT("LineNr term=underline ctermfg=Brown",
6822 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006823 CENT("CursorLineNr term=bold ctermfg=Brown",
6824 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006825 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6826 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6827 CENT("Question term=standout ctermfg=DarkGreen",
6828 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6829 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6830 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006831#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006832 CENT("SpellBad term=reverse ctermbg=LightRed",
6833 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6834 CENT("SpellCap term=reverse ctermbg=LightBlue",
6835 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6836 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6837 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6838 CENT("SpellLocal term=underline ctermbg=Cyan",
6839 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006840#endif
6841#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006842 CENT("PmenuThumb ctermbg=Black",
6843 "PmenuThumb ctermbg=Black guibg=Black"),
6844 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6845 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6846 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6847 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006848#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006849 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6850 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6851 CENT("Title term=bold ctermfg=DarkMagenta",
6852 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6853 CENT("WarningMsg term=standout ctermfg=DarkRed",
6854 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006855#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006856 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6857 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006858#endif
6859#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006860 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6861 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6862 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6863 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006864#endif
6865#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006866 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6867 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006868#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006869 CENT("Visual term=reverse",
6870 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006871#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006872 CENT("DiffAdd term=bold ctermbg=LightBlue",
6873 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6874 CENT("DiffChange term=bold ctermbg=LightMagenta",
6875 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6876 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6877 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006878#endif
6879#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006880 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6881 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006882#endif
6883#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006884 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006885 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006886 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006887 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006888 CENT("ColorColumn term=reverse ctermbg=LightRed",
6889 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006890#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006891#ifdef FEAT_CONCEAL
6892 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6893 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6894#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006895#ifdef FEAT_AUTOCMD
6896 CENT("MatchParen term=reverse ctermbg=Cyan",
6897 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6898#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006899#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006900 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006902 NULL
6903 };
6904
6905static char *(highlight_init_dark[]) =
6906 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006907 CENT("Directory term=bold ctermfg=LightCyan",
6908 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6909 CENT("LineNr term=underline ctermfg=Yellow",
6910 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006911 CENT("CursorLineNr term=bold ctermfg=Yellow",
6912 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006913 CENT("MoreMsg term=bold ctermfg=LightGreen",
6914 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6915 CENT("Question term=standout ctermfg=LightGreen",
6916 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6917 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6918 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6919 CENT("SpecialKey term=bold ctermfg=LightBlue",
6920 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006921#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006922 CENT("SpellBad term=reverse ctermbg=Red",
6923 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6924 CENT("SpellCap term=reverse ctermbg=Blue",
6925 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6926 CENT("SpellRare term=reverse ctermbg=Magenta",
6927 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6928 CENT("SpellLocal term=underline ctermbg=Cyan",
6929 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006930#endif
6931#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006932 CENT("PmenuThumb ctermbg=White",
6933 "PmenuThumb ctermbg=White guibg=White"),
6934 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6935 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006936 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6937 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006938#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006939 CENT("Title term=bold ctermfg=LightMagenta",
6940 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6941 CENT("WarningMsg term=standout ctermfg=LightRed",
6942 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006943#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006944 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6945 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006946#endif
6947#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006948 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6949 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6950 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6951 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006952#endif
6953#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006954 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6955 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006956#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006957 CENT("Visual term=reverse",
6958 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006959#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006960 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6961 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6962 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6963 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6964 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6965 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006966#endif
6967#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006968 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6969 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006970#endif
6971#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006972 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006973 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006974 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006975 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006976 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6977 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006978#endif
6979#ifdef FEAT_AUTOCMD
6980 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6981 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006982#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006983#ifdef FEAT_CONCEAL
6984 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6985 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6986#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006987#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006988 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006989#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006990 NULL
6991 };
6992
6993 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01006994init_highlight(
6995 int both, /* include groups where 'bg' doesn't matter */
6996 int reset) /* clear group first */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006997{
6998 int i;
6999 char **pp;
7000 static int had_both = FALSE;
7001#ifdef FEAT_EVAL
7002 char_u *p;
7003
7004 /*
7005 * Try finding the color scheme file. Used when a color file was loaded
7006 * and 'background' or 't_Co' is changed.
7007 */
7008 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007009 if (p != NULL)
7010 {
7011 /* The value of g:colors_name could be freed when sourcing the script,
7012 * making "p" invalid, so copy it. */
7013 char_u *copy_p = vim_strsave(p);
7014 int r;
7015
7016 if (copy_p != NULL)
7017 {
7018 r = load_colors(copy_p);
7019 vim_free(copy_p);
7020 if (r == OK)
7021 return;
7022 }
7023 }
7024
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025#endif
7026
7027 /*
7028 * Didn't use a color file, use the compiled-in colors.
7029 */
7030 if (both)
7031 {
7032 had_both = TRUE;
7033 pp = highlight_init_both;
7034 for (i = 0; pp[i] != NULL; ++i)
7035 do_highlight((char_u *)pp[i], reset, TRUE);
7036 }
7037 else if (!had_both)
7038 /* Don't do anything before the call with both == TRUE from main().
7039 * Not everything has been setup then, and that call will overrule
7040 * everything anyway. */
7041 return;
7042
7043 if (*p_bg == 'l')
7044 pp = highlight_init_light;
7045 else
7046 pp = highlight_init_dark;
7047 for (i = 0; pp[i] != NULL; ++i)
7048 do_highlight((char_u *)pp[i], reset, TRUE);
7049
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007050 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007051 * depend on the number of colors available.
7052 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007053 * to avoid Statement highlighted text disappears.
7054 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007055 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007056 do_highlight((char_u *)(*p_bg == 'l'
7057 ? "Visual cterm=NONE ctermbg=LightGrey"
7058 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007059 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007060 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007061 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7062 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007063 if (*p_bg == 'l')
7064 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7065 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007066
Bram Moolenaar071d4272004-06-13 20:20:40 +00007067#ifdef FEAT_SYN_HL
7068 /*
7069 * If syntax highlighting is enabled load the highlighting for it.
7070 */
7071 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007072 {
7073 static int recursive = 0;
7074
7075 if (recursive >= 5)
7076 EMSG(_("E679: recursive loop loading syncolor.vim"));
7077 else
7078 {
7079 ++recursive;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007080 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007081 --recursive;
7082 }
7083 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007084#endif
7085}
7086
7087/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007088 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007089 * Return OK for success, FAIL for failure.
7090 */
7091 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007092load_colors(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007093{
7094 char_u *buf;
7095 int retval = FAIL;
7096 static int recursive = FALSE;
7097
7098 /* When being called recursively, this is probably because setting
7099 * 'background' caused the highlighting to be reloaded. This means it is
7100 * working, thus we should return OK. */
7101 if (recursive)
7102 return OK;
7103
7104 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007105 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106 if (buf != NULL)
7107 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007108 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01007109 retval = source_runtime(buf, DIP_START + DIP_OPT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007111#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007112 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007113#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114 }
7115 recursive = FALSE;
7116
7117 return retval;
7118}
7119
7120/*
7121 * Handle the ":highlight .." command.
7122 * When using ":hi clear" this is called recursively for each group with
7123 * "forceit" and "init" both TRUE.
7124 */
7125 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01007126do_highlight(
7127 char_u *line,
7128 int forceit,
7129 int init) /* TRUE when called for initializing */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007130{
7131 char_u *name_end;
7132 char_u *p;
7133 char_u *linep;
7134 char_u *key_start;
7135 char_u *arg_start;
7136 char_u *key = NULL, *arg = NULL;
7137 long i;
7138 int off;
7139 int len;
7140 int attr;
7141 int id;
7142 int idx;
7143 int dodefault = FALSE;
7144 int doclear = FALSE;
7145 int dolink = FALSE;
7146 int error = FALSE;
7147 int color;
7148 int is_normal_group = FALSE; /* "Normal" group */
7149#ifdef FEAT_GUI_X11
7150 int is_menu_group = FALSE; /* "Menu" group */
7151 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7152 int is_tooltip_group = FALSE; /* "Tooltip" group */
7153 int do_colors = FALSE; /* need to update colors? */
7154#else
7155# define is_menu_group 0
7156# define is_tooltip_group 0
7157#endif
7158
7159 /*
7160 * If no argument, list current highlighting.
7161 */
7162 if (ends_excmd(*line))
7163 {
7164 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7165 /* TODO: only call when the group has attributes set */
7166 highlight_list_one((int)i);
7167 return;
7168 }
7169
7170 /*
7171 * Isolate the name.
7172 */
7173 name_end = skiptowhite(line);
7174 linep = skipwhite(name_end);
7175
7176 /*
7177 * Check for "default" argument.
7178 */
7179 if (STRNCMP(line, "default", name_end - line) == 0)
7180 {
7181 dodefault = TRUE;
7182 line = linep;
7183 name_end = skiptowhite(line);
7184 linep = skipwhite(name_end);
7185 }
7186
7187 /*
7188 * Check for "clear" or "link" argument.
7189 */
7190 if (STRNCMP(line, "clear", name_end - line) == 0)
7191 doclear = TRUE;
7192 if (STRNCMP(line, "link", name_end - line) == 0)
7193 dolink = TRUE;
7194
7195 /*
7196 * ":highlight {group-name}": list highlighting for one group.
7197 */
7198 if (!doclear && !dolink && ends_excmd(*linep))
7199 {
7200 id = syn_namen2id(line, (int)(name_end - line));
7201 if (id == 0)
7202 EMSG2(_("E411: highlight group not found: %s"), line);
7203 else
7204 highlight_list_one(id);
7205 return;
7206 }
7207
7208 /*
7209 * Handle ":highlight link {from} {to}" command.
7210 */
7211 if (dolink)
7212 {
7213 char_u *from_start = linep;
7214 char_u *from_end;
7215 char_u *to_start;
7216 char_u *to_end;
7217 int from_id;
7218 int to_id;
7219
7220 from_end = skiptowhite(from_start);
7221 to_start = skipwhite(from_end);
7222 to_end = skiptowhite(to_start);
7223
7224 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7225 {
7226 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7227 from_start);
7228 return;
7229 }
7230
7231 if (!ends_excmd(*skipwhite(to_end)))
7232 {
7233 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7234 return;
7235 }
7236
7237 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7238 if (STRNCMP(to_start, "NONE", 4) == 0)
7239 to_id = 0;
7240 else
7241 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7242
7243 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7244 {
7245 /*
7246 * Don't allow a link when there already is some highlighting
7247 * for the group, unless '!' is used
7248 */
7249 if (to_id > 0 && !forceit && !init
7250 && hl_has_settings(from_id - 1, dodefault))
7251 {
7252 if (sourcing_name == NULL && !dodefault)
7253 EMSG(_("E414: group has settings, highlight link ignored"));
7254 }
7255 else
7256 {
7257 if (!init)
7258 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7259 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007260#ifdef FEAT_EVAL
7261 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7262#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007263 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007264 }
7265 }
7266
7267 /* Only call highlight_changed() once, after sourcing a syntax file */
7268 need_highlight_changed = TRUE;
7269
7270 return;
7271 }
7272
7273 if (doclear)
7274 {
7275 /*
7276 * ":highlight clear [group]" command.
7277 */
7278 line = linep;
7279 if (ends_excmd(*line))
7280 {
7281#ifdef FEAT_GUI
7282 /* First, we do not destroy the old values, but allocate the new
7283 * ones and update the display. THEN we destroy the old values.
7284 * If we destroy the old values first, then the old values
7285 * (such as GuiFont's or GuiFontset's) will still be displayed but
7286 * invalid because they were free'd.
7287 */
7288 if (gui.in_use)
7289 {
7290# ifdef FEAT_BEVAL_TIP
7291 gui_init_tooltip_font();
7292# endif
7293# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7294 gui_init_menu_font();
7295# endif
7296 }
7297# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7298 gui_mch_def_colors();
7299# endif
7300# ifdef FEAT_GUI_X11
7301# ifdef FEAT_MENU
7302
7303 /* This only needs to be done when there is no Menu highlight
7304 * group defined by default, which IS currently the case.
7305 */
7306 gui_mch_new_menu_colors();
7307# endif
7308 if (gui.in_use)
7309 {
7310 gui_new_scrollbar_colors();
7311# ifdef FEAT_BEVAL
7312 gui_mch_new_tooltip_colors();
7313# endif
7314# ifdef FEAT_MENU
7315 gui_mch_new_menu_font();
7316# endif
7317 }
7318# endif
7319
7320 /* Ok, we're done allocating the new default graphics items.
7321 * The screen should already be refreshed at this point.
7322 * It is now Ok to clear out the old data.
7323 */
7324#endif
7325#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007326 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007327#endif
7328 restore_cterm_colors();
7329
7330 /*
7331 * Clear all default highlight groups and load the defaults.
7332 */
7333 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7334 highlight_clear(idx);
7335 init_highlight(TRUE, TRUE);
7336#ifdef FEAT_GUI
7337 if (gui.in_use)
7338 highlight_gui_started();
7339#endif
7340 highlight_changed();
7341 redraw_later_clear();
7342 return;
7343 }
7344 name_end = skiptowhite(line);
7345 linep = skipwhite(name_end);
7346 }
7347
7348 /*
7349 * Find the group name in the table. If it does not exist yet, add it.
7350 */
7351 id = syn_check_group(line, (int)(name_end - line));
7352 if (id == 0) /* failed (out of memory) */
7353 return;
7354 idx = id - 1; /* index is ID minus one */
7355
7356 /* Return if "default" was used and the group already has settings. */
7357 if (dodefault && hl_has_settings(idx, TRUE))
7358 return;
7359
7360 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7361 is_normal_group = TRUE;
7362#ifdef FEAT_GUI_X11
7363 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7364 is_menu_group = TRUE;
7365 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7366 is_scrollbar_group = TRUE;
7367 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7368 is_tooltip_group = TRUE;
7369#endif
7370
7371 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7372 if (doclear || (forceit && init))
7373 {
7374 highlight_clear(idx);
7375 if (!doclear)
7376 HL_TABLE()[idx].sg_set = 0;
7377 }
7378
7379 if (!doclear)
7380 while (!ends_excmd(*linep))
7381 {
7382 key_start = linep;
7383 if (*linep == '=')
7384 {
7385 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7386 error = TRUE;
7387 break;
7388 }
7389
7390 /*
7391 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7392 * "guibg").
7393 */
7394 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7395 ++linep;
7396 vim_free(key);
7397 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7398 if (key == NULL)
7399 {
7400 error = TRUE;
7401 break;
7402 }
7403 linep = skipwhite(linep);
7404
7405 if (STRCMP(key, "NONE") == 0)
7406 {
7407 if (!init || HL_TABLE()[idx].sg_set == 0)
7408 {
7409 if (!init)
7410 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7411 highlight_clear(idx);
7412 }
7413 continue;
7414 }
7415
7416 /*
7417 * Check for the equal sign.
7418 */
7419 if (*linep != '=')
7420 {
7421 EMSG2(_("E416: missing equal sign: %s"), key_start);
7422 error = TRUE;
7423 break;
7424 }
7425 ++linep;
7426
7427 /*
7428 * Isolate the argument.
7429 */
7430 linep = skipwhite(linep);
7431 if (*linep == '\'') /* guifg='color name' */
7432 {
7433 arg_start = ++linep;
7434 linep = vim_strchr(linep, '\'');
7435 if (linep == NULL)
7436 {
7437 EMSG2(_(e_invarg2), key_start);
7438 error = TRUE;
7439 break;
7440 }
7441 }
7442 else
7443 {
7444 arg_start = linep;
7445 linep = skiptowhite(linep);
7446 }
7447 if (linep == arg_start)
7448 {
7449 EMSG2(_("E417: missing argument: %s"), key_start);
7450 error = TRUE;
7451 break;
7452 }
7453 vim_free(arg);
7454 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7455 if (arg == NULL)
7456 {
7457 error = TRUE;
7458 break;
7459 }
7460 if (*linep == '\'')
7461 ++linep;
7462
7463 /*
7464 * Store the argument.
7465 */
7466 if ( STRCMP(key, "TERM") == 0
7467 || STRCMP(key, "CTERM") == 0
7468 || STRCMP(key, "GUI") == 0)
7469 {
7470 attr = 0;
7471 off = 0;
7472 while (arg[off] != NUL)
7473 {
7474 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7475 {
7476 len = (int)STRLEN(hl_name_table[i]);
7477 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7478 {
7479 attr |= hl_attr_table[i];
7480 off += len;
7481 break;
7482 }
7483 }
7484 if (i < 0)
7485 {
7486 EMSG2(_("E418: Illegal value: %s"), arg);
7487 error = TRUE;
7488 break;
7489 }
7490 if (arg[off] == ',') /* another one follows */
7491 ++off;
7492 }
7493 if (error)
7494 break;
7495 if (*key == 'T')
7496 {
7497 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7498 {
7499 if (!init)
7500 HL_TABLE()[idx].sg_set |= SG_TERM;
7501 HL_TABLE()[idx].sg_term = attr;
7502 }
7503 }
7504 else if (*key == 'C')
7505 {
7506 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7507 {
7508 if (!init)
7509 HL_TABLE()[idx].sg_set |= SG_CTERM;
7510 HL_TABLE()[idx].sg_cterm = attr;
7511 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7512 }
7513 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007514#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007515 else
7516 {
7517 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7518 {
7519 if (!init)
7520 HL_TABLE()[idx].sg_set |= SG_GUI;
7521 HL_TABLE()[idx].sg_gui = attr;
7522 }
7523 }
7524#endif
7525 }
7526 else if (STRCMP(key, "FONT") == 0)
7527 {
7528 /* in non-GUI fonts are simply ignored */
7529#ifdef FEAT_GUI
7530 if (!gui.shell_created)
7531 {
7532 /* GUI not started yet, always accept the name. */
7533 vim_free(HL_TABLE()[idx].sg_font_name);
7534 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7535 }
7536 else
7537 {
7538 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7539# ifdef FEAT_XFONTSET
7540 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7541# endif
7542 /* First, save the current font/fontset.
7543 * Then try to allocate the font/fontset.
7544 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7545 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7546 */
7547
7548 HL_TABLE()[idx].sg_font = NOFONT;
7549# ifdef FEAT_XFONTSET
7550 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7551# endif
7552 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007553 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007554
7555# ifdef FEAT_XFONTSET
7556 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7557 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007558 /* New fontset was accepted. Free the old one, if there
7559 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560 gui_mch_free_fontset(temp_sg_fontset);
7561 vim_free(HL_TABLE()[idx].sg_font_name);
7562 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7563 }
7564 else
7565 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7566# endif
7567 if (HL_TABLE()[idx].sg_font != NOFONT)
7568 {
7569 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007570 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007571 gui_mch_free_font(temp_sg_font);
7572 vim_free(HL_TABLE()[idx].sg_font_name);
7573 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7574 }
7575 else
7576 HL_TABLE()[idx].sg_font = temp_sg_font;
7577 }
7578#endif
7579 }
7580 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7581 {
7582 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7583 {
7584 if (!init)
7585 HL_TABLE()[idx].sg_set |= SG_CTERM;
7586
7587 /* When setting the foreground color, and previously the "bold"
7588 * flag was set for a light color, reset it now */
7589 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7590 {
7591 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7592 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7593 }
7594
7595 if (VIM_ISDIGIT(*arg))
7596 color = atoi((char *)arg);
7597 else if (STRICMP(arg, "fg") == 0)
7598 {
7599 if (cterm_normal_fg_color)
7600 color = cterm_normal_fg_color - 1;
7601 else
7602 {
7603 EMSG(_("E419: FG color unknown"));
7604 error = TRUE;
7605 break;
7606 }
7607 }
7608 else if (STRICMP(arg, "bg") == 0)
7609 {
7610 if (cterm_normal_bg_color > 0)
7611 color = cterm_normal_bg_color - 1;
7612 else
7613 {
7614 EMSG(_("E420: BG color unknown"));
7615 error = TRUE;
7616 break;
7617 }
7618 }
7619 else
7620 {
7621 static char *(color_names[28]) = {
7622 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7623 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7624 "Gray", "Grey",
7625 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7626 "Blue", "LightBlue", "Green", "LightGreen",
7627 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7628 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7629 static int color_numbers_16[28] = {0, 1, 2, 3,
7630 4, 5, 6, 6,
7631 7, 7,
7632 7, 7, 8, 8,
7633 9, 9, 10, 10,
7634 11, 11, 12, 12, 13,
7635 13, 14, 14, 15, -1};
7636 /* for xterm with 88 colors... */
7637 static int color_numbers_88[28] = {0, 4, 2, 6,
7638 1, 5, 32, 72,
7639 84, 84,
7640 7, 7, 82, 82,
7641 12, 43, 10, 61,
7642 14, 63, 9, 74, 13,
7643 75, 11, 78, 15, -1};
7644 /* for xterm with 256 colors... */
7645 static int color_numbers_256[28] = {0, 4, 2, 6,
7646 1, 5, 130, 130,
7647 248, 248,
7648 7, 7, 242, 242,
7649 12, 81, 10, 121,
7650 14, 159, 9, 224, 13,
7651 225, 11, 229, 15, -1};
7652 /* for terminals with less than 16 colors... */
7653 static int color_numbers_8[28] = {0, 4, 2, 6,
7654 1, 5, 3, 3,
7655 7, 7,
7656 7, 7, 0+8, 0+8,
7657 4+8, 4+8, 2+8, 2+8,
7658 6+8, 6+8, 1+8, 1+8, 5+8,
7659 5+8, 3+8, 3+8, 7+8, -1};
7660#if defined(__QNXNTO__)
7661 static int *color_numbers_8_qansi = color_numbers_8;
7662 /* On qnx, the 8 & 16 color arrays are the same */
7663 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7664 color_numbers_8_qansi = color_numbers_16;
7665#endif
7666
7667 /* reduce calls to STRICMP a bit, it can be slow */
7668 off = TOUPPER_ASC(*arg);
7669 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7670 if (off == color_names[i][0]
7671 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7672 break;
7673 if (i < 0)
7674 {
7675 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7676 error = TRUE;
7677 break;
7678 }
7679
7680 /* Use the _16 table to check if its a valid color name. */
7681 color = color_numbers_16[i];
7682 if (color >= 0)
7683 {
7684 if (t_colors == 8)
7685 {
7686 /* t_Co is 8: use the 8 colors table */
7687#if defined(__QNXNTO__)
7688 color = color_numbers_8_qansi[i];
7689#else
7690 color = color_numbers_8[i];
7691#endif
7692 if (key[5] == 'F')
7693 {
7694 /* set/reset bold attribute to get light foreground
7695 * colors (on some terminals, e.g. "linux") */
7696 if (color & 8)
7697 {
7698 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7699 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7700 }
7701 else
7702 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7703 }
7704 color &= 7; /* truncate to 8 colors */
7705 }
7706 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007707 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007708 {
7709 /*
7710 * Guess: if the termcap entry ends in 'm', it is
7711 * probably an xterm-like terminal. Use the changed
7712 * order for colors.
7713 */
7714 if (*T_CAF != NUL)
7715 p = T_CAF;
7716 else
7717 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007718 if (*p != NUL && (t_colors > 256
7719 || *(p + STRLEN(p) - 1) == 'm'))
7720 {
7721 if (t_colors == 88)
7722 color = color_numbers_88[i];
7723 else if (t_colors >= 256)
7724 color = color_numbers_256[i];
7725 else
7726 color = color_numbers_8[i];
7727 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007728 }
7729 }
7730 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007731 /* Add one to the argument, to avoid zero. Zero is used for
7732 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007733 if (key[5] == 'F')
7734 {
7735 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7736 if (is_normal_group)
7737 {
7738 cterm_normal_fg_color = color + 1;
7739 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7740#ifdef FEAT_GUI
7741 /* Don't do this if the GUI is used. */
7742 if (!gui.in_use && !gui.starting)
7743#endif
7744 {
7745 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007746 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007747 term_fg_color(color);
7748 }
7749 }
7750 }
7751 else
7752 {
7753 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7754 if (is_normal_group)
7755 {
7756 cterm_normal_bg_color = color + 1;
7757#ifdef FEAT_GUI
7758 /* Don't mess with 'background' if the GUI is used. */
7759 if (!gui.in_use && !gui.starting)
7760#endif
7761 {
7762 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007763 if (color >= 0)
7764 {
7765 if (termcap_active)
7766 term_bg_color(color);
7767 if (t_colors < 16)
7768 i = (color == 0 || color == 4);
7769 else
7770 i = (color < 7 || color == 8);
7771 /* Set the 'background' option if the value is
7772 * wrong. */
7773 if (i != (*p_bg == 'd'))
7774 set_option_value((char_u *)"bg", 0L,
7775 i ? (char_u *)"dark"
7776 : (char_u *)"light", 0);
7777 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778 }
7779 }
7780 }
7781 }
7782 }
7783 else if (STRCMP(key, "GUIFG") == 0)
7784 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007785#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007786 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007787 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007788 if (!init)
7789 HL_TABLE()[idx].sg_set |= SG_GUI;
7790
Bram Moolenaar61623362010-07-14 22:04:22 +02007791# ifdef FEAT_GUI
7792 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007793 i = color_name2handle(arg);
7794 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7795 {
7796 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007797# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007798 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7799 if (STRCMP(arg, "NONE"))
7800 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7801 else
7802 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007803# ifdef FEAT_GUI
7804# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007805 if (is_menu_group)
7806 gui.menu_fg_pixel = i;
7807 if (is_scrollbar_group)
7808 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007809# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007810 if (is_tooltip_group)
7811 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007812# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007813 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007814# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007815 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007816# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007817 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818#endif
7819 }
7820 else if (STRCMP(key, "GUIBG") == 0)
7821 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007822#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007823 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007824 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007825 if (!init)
7826 HL_TABLE()[idx].sg_set |= SG_GUI;
7827
Bram Moolenaar61623362010-07-14 22:04:22 +02007828# ifdef FEAT_GUI
7829 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007830 i = color_name2handle(arg);
7831 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7832 {
7833 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007834# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007835 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7836 if (STRCMP(arg, "NONE") != 0)
7837 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7838 else
7839 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007840# ifdef FEAT_GUI
7841# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007842 if (is_menu_group)
7843 gui.menu_bg_pixel = i;
7844 if (is_scrollbar_group)
7845 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007846# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007847 if (is_tooltip_group)
7848 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007849# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007850 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007851# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007852 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007853# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007855#endif
7856 }
7857 else if (STRCMP(key, "GUISP") == 0)
7858 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007859#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007860 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7861 {
7862 if (!init)
7863 HL_TABLE()[idx].sg_set |= SG_GUI;
7864
Bram Moolenaar61623362010-07-14 22:04:22 +02007865# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007866 i = color_name2handle(arg);
7867 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7868 {
7869 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007870# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007871 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7872 if (STRCMP(arg, "NONE") != 0)
7873 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7874 else
7875 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007876# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007877 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007878# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007879 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007880#endif
7881 }
7882 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7883 {
7884 char_u buf[100];
7885 char_u *tname;
7886
7887 if (!init)
7888 HL_TABLE()[idx].sg_set |= SG_TERM;
7889
7890 /*
7891 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007892 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007893 */
7894 if (STRNCMP(arg, "t_", 2) == 0)
7895 {
7896 off = 0;
7897 buf[0] = 0;
7898 while (arg[off] != NUL)
7899 {
7900 /* Isolate one termcap name */
7901 for (len = 0; arg[off + len] &&
7902 arg[off + len] != ','; ++len)
7903 ;
7904 tname = vim_strnsave(arg + off, len);
7905 if (tname == NULL) /* out of memory */
7906 {
7907 error = TRUE;
7908 break;
7909 }
7910 /* lookup the escape sequence for the item */
7911 p = get_term_code(tname);
7912 vim_free(tname);
7913 if (p == NULL) /* ignore non-existing things */
7914 p = (char_u *)"";
7915
7916 /* Append it to the already found stuff */
7917 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7918 {
7919 EMSG2(_("E422: terminal code too long: %s"), arg);
7920 error = TRUE;
7921 break;
7922 }
7923 STRCAT(buf, p);
7924
7925 /* Advance to the next item */
7926 off += len;
7927 if (arg[off] == ',') /* another one follows */
7928 ++off;
7929 }
7930 }
7931 else
7932 {
7933 /*
7934 * Copy characters from arg[] to buf[], translating <> codes.
7935 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007936 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007937 {
7938 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007939 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007940 off += len;
7941 else /* copy as normal char */
7942 buf[off++] = *p++;
7943 }
7944 buf[off] = NUL;
7945 }
7946 if (error)
7947 break;
7948
7949 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7950 p = NULL;
7951 else
7952 p = vim_strsave(buf);
7953 if (key[2] == 'A')
7954 {
7955 vim_free(HL_TABLE()[idx].sg_start);
7956 HL_TABLE()[idx].sg_start = p;
7957 }
7958 else
7959 {
7960 vim_free(HL_TABLE()[idx].sg_stop);
7961 HL_TABLE()[idx].sg_stop = p;
7962 }
7963 }
7964 else
7965 {
7966 EMSG2(_("E423: Illegal argument: %s"), key_start);
7967 error = TRUE;
7968 break;
7969 }
7970
7971 /*
7972 * When highlighting has been given for a group, don't link it.
7973 */
7974 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7975 HL_TABLE()[idx].sg_link = 0;
7976
7977 /*
7978 * Continue with next argument.
7979 */
7980 linep = skipwhite(linep);
7981 }
7982
7983 /*
7984 * If there is an error, and it's a new entry, remove it from the table.
7985 */
7986 if (error && idx == highlight_ga.ga_len)
7987 syn_unadd_group();
7988 else
7989 {
7990 if (is_normal_group)
7991 {
7992 HL_TABLE()[idx].sg_term_attr = 0;
7993 HL_TABLE()[idx].sg_cterm_attr = 0;
7994#ifdef FEAT_GUI
7995 HL_TABLE()[idx].sg_gui_attr = 0;
7996 /*
7997 * Need to update all groups, because they might be using "bg"
7998 * and/or "fg", which have been changed now.
7999 */
8000 if (gui.in_use)
8001 highlight_gui_started();
8002#endif
8003 }
8004#ifdef FEAT_GUI_X11
8005# ifdef FEAT_MENU
8006 else if (is_menu_group)
8007 {
8008 if (gui.in_use && do_colors)
8009 gui_mch_new_menu_colors();
8010 }
8011# endif
8012 else if (is_scrollbar_group)
8013 {
8014 if (gui.in_use && do_colors)
8015 gui_new_scrollbar_colors();
8016 }
8017# ifdef FEAT_BEVAL
8018 else if (is_tooltip_group)
8019 {
8020 if (gui.in_use && do_colors)
8021 gui_mch_new_tooltip_colors();
8022 }
8023# endif
8024#endif
8025 else
8026 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008027#ifdef FEAT_EVAL
8028 HL_TABLE()[idx].sg_scriptID = current_SID;
8029#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008030 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008031 }
8032 vim_free(key);
8033 vim_free(arg);
8034
8035 /* Only call highlight_changed() once, after sourcing a syntax file */
8036 need_highlight_changed = TRUE;
8037}
8038
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008039#if defined(EXITFREE) || defined(PROTO)
8040 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008041free_highlight(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008042{
8043 int i;
8044
8045 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008046 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008047 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008048 vim_free(HL_TABLE()[i].sg_name);
8049 vim_free(HL_TABLE()[i].sg_name_u);
8050 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008051 ga_clear(&highlight_ga);
8052}
8053#endif
8054
Bram Moolenaar071d4272004-06-13 20:20:40 +00008055/*
8056 * Reset the cterm colors to what they were before Vim was started, if
8057 * possible. Otherwise reset them to zero.
8058 */
8059 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008060restore_cterm_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008061{
Bram Moolenaar48e330a2016-02-23 14:53:34 +01008062#if defined(WIN3264) && !defined(FEAT_GUI_W32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008063 /* Since t_me has been set, this probably means that the user
8064 * wants to use this as default colors. Need to reset default
8065 * background/foreground colors. */
8066 mch_set_normal_colors();
8067#else
8068 cterm_normal_fg_color = 0;
8069 cterm_normal_fg_bold = 0;
8070 cterm_normal_bg_color = 0;
8071#endif
8072}
8073
8074/*
8075 * Return TRUE if highlight group "idx" has any settings.
8076 * When "check_link" is TRUE also check for an existing link.
8077 */
8078 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008079hl_has_settings(int idx, int check_link)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008080{
8081 return ( HL_TABLE()[idx].sg_term_attr != 0
8082 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008083 || HL_TABLE()[idx].sg_cterm_fg != 0
8084 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008085#ifdef FEAT_GUI
8086 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008087 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8088 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8089 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8090 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008091#endif
8092 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8093}
8094
8095/*
8096 * Clear highlighting for one group.
8097 */
8098 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008099highlight_clear(int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008100{
8101 HL_TABLE()[idx].sg_term = 0;
8102 vim_free(HL_TABLE()[idx].sg_start);
8103 HL_TABLE()[idx].sg_start = NULL;
8104 vim_free(HL_TABLE()[idx].sg_stop);
8105 HL_TABLE()[idx].sg_stop = NULL;
8106 HL_TABLE()[idx].sg_term_attr = 0;
8107 HL_TABLE()[idx].sg_cterm = 0;
8108 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8109 HL_TABLE()[idx].sg_cterm_fg = 0;
8110 HL_TABLE()[idx].sg_cterm_bg = 0;
8111 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008112#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8115 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008116 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8117 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008118 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8119 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008120#endif
8121#ifdef FEAT_GUI
8122 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8123 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8124 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008125 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8126 HL_TABLE()[idx].sg_font = NOFONT;
8127# ifdef FEAT_XFONTSET
8128 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8129 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8130# endif
8131 vim_free(HL_TABLE()[idx].sg_font_name);
8132 HL_TABLE()[idx].sg_font_name = NULL;
8133 HL_TABLE()[idx].sg_gui_attr = 0;
8134#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008135#ifdef FEAT_EVAL
8136 /* Clear the script ID only when there is no link, since that is not
8137 * cleared. */
8138 if (HL_TABLE()[idx].sg_link == 0)
8139 HL_TABLE()[idx].sg_scriptID = 0;
8140#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141}
8142
8143#if defined(FEAT_GUI) || defined(PROTO)
8144/*
8145 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008146 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008147 * "Tooltip" colors.
8148 */
8149 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008150set_normal_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008151{
8152 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008153 &gui.norm_pixel, &gui.back_pixel,
8154 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008155 {
8156 gui_mch_new_colors();
8157 must_redraw = CLEAR;
8158 }
8159#ifdef FEAT_GUI_X11
8160 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008161 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8162 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008163 {
8164# ifdef FEAT_MENU
8165 gui_mch_new_menu_colors();
8166# endif
8167 must_redraw = CLEAR;
8168 }
8169# ifdef FEAT_BEVAL
8170 if (set_group_colors((char_u *)"Tooltip",
8171 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8172 FALSE, FALSE, TRUE))
8173 {
8174# ifdef FEAT_TOOLBAR
8175 gui_mch_new_tooltip_colors();
8176# endif
8177 must_redraw = CLEAR;
8178 }
8179#endif
8180 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008181 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8182 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008183 {
8184 gui_new_scrollbar_colors();
8185 must_redraw = CLEAR;
8186 }
8187#endif
8188}
8189
8190/*
8191 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8192 */
8193 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008194set_group_colors(
8195 char_u *name,
8196 guicolor_T *fgp,
8197 guicolor_T *bgp,
8198 int do_menu,
8199 int use_norm,
8200 int do_tooltip)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008201{
8202 int idx;
8203
8204 idx = syn_name2id(name) - 1;
8205 if (idx >= 0)
8206 {
8207 gui_do_one_color(idx, do_menu, do_tooltip);
8208
8209 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8210 *fgp = HL_TABLE()[idx].sg_gui_fg;
8211 else if (use_norm)
8212 *fgp = gui.def_norm_pixel;
8213 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8214 *bgp = HL_TABLE()[idx].sg_gui_bg;
8215 else if (use_norm)
8216 *bgp = gui.def_back_pixel;
8217 return TRUE;
8218 }
8219 return FALSE;
8220}
8221
8222/*
8223 * Get the font of the "Normal" group.
8224 * Returns "" when it's not found or not set.
8225 */
8226 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008227hl_get_font_name(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008228{
8229 int id;
8230 char_u *s;
8231
8232 id = syn_name2id((char_u *)"Normal");
8233 if (id > 0)
8234 {
8235 s = HL_TABLE()[id - 1].sg_font_name;
8236 if (s != NULL)
8237 return s;
8238 }
8239 return (char_u *)"";
8240}
8241
8242/*
8243 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8244 * actually chosen to be used.
8245 */
8246 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008247hl_set_font_name(char_u *font_name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008248{
8249 int id;
8250
8251 id = syn_name2id((char_u *)"Normal");
8252 if (id > 0)
8253 {
8254 vim_free(HL_TABLE()[id - 1].sg_font_name);
8255 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8256 }
8257}
8258
8259/*
8260 * Set background color for "Normal" group. Called by gui_set_bg_color()
8261 * when the color is known.
8262 */
8263 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008264hl_set_bg_color_name(
8265 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008266{
8267 int id;
8268
8269 if (name != NULL)
8270 {
8271 id = syn_name2id((char_u *)"Normal");
8272 if (id > 0)
8273 {
8274 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8275 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8276 }
8277 }
8278}
8279
8280/*
8281 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8282 * when the color is known.
8283 */
8284 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008285hl_set_fg_color_name(
8286 char_u *name) /* must have been allocated */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008287{
8288 int id;
8289
8290 if (name != NULL)
8291 {
8292 id = syn_name2id((char_u *)"Normal");
8293 if (id > 0)
8294 {
8295 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8296 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8297 }
8298 }
8299}
8300
8301/*
8302 * Return the handle for a color name.
8303 * Returns INVALCOLOR when failed.
8304 */
8305 static guicolor_T
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008306color_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008307{
8308 if (STRCMP(name, "NONE") == 0)
8309 return INVALCOLOR;
8310
8311 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8312 return gui.norm_pixel;
8313 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8314 return gui.back_pixel;
8315
8316 return gui_get_color(name);
8317}
8318
8319/*
8320 * Return the handle for a font name.
8321 * Returns NOFONT when failed.
8322 */
8323 static GuiFont
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008324font_name2handle(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008325{
8326 if (STRCMP(name, "NONE") == 0)
8327 return NOFONT;
8328
8329 return gui_mch_get_font(name, TRUE);
8330}
8331
8332# ifdef FEAT_XFONTSET
8333/*
8334 * Return the handle for a fontset name.
8335 * Returns NOFONTSET when failed.
8336 */
8337 static GuiFontset
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008338fontset_name2handle(char_u *name, int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339{
8340 if (STRCMP(name, "NONE") == 0)
8341 return NOFONTSET;
8342
8343 return gui_mch_get_fontset(name, TRUE, fixed_width);
8344}
8345# endif
8346
8347/*
8348 * Get the font or fontset for one highlight group.
8349 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008350 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008351hl_do_font(
8352 int idx,
8353 char_u *arg,
8354 int do_normal, /* set normal font */
8355 int do_menu UNUSED, /* set menu font */
8356 int do_tooltip UNUSED, /* set tooltip font */
8357 int free_font) /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008358{
8359# ifdef FEAT_XFONTSET
8360 /* If 'guifontset' is not empty, first try using the name as a
8361 * fontset. If that doesn't work, use it as a font name. */
8362 if (*p_guifontset != NUL
8363# ifdef FONTSET_ALWAYS
8364 || do_menu
8365# endif
8366# ifdef FEAT_BEVAL_TIP
8367 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8368 || do_tooltip
8369# endif
8370 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008371 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008372 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008373 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008374 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8375# ifdef FONTSET_ALWAYS
8376 || do_menu
8377# endif
8378# ifdef FEAT_BEVAL_TIP
8379 || do_tooltip
8380# endif
8381 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008382 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8384 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008385 /* If it worked and it's the Normal group, use it as the normal
8386 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008387 if (do_normal)
8388 gui_init_font(arg, TRUE);
8389# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8390 if (do_menu)
8391 {
8392# ifdef FONTSET_ALWAYS
8393 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8394# else
8395 /* YIKES! This is a bug waiting to crash the program */
8396 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8397# endif
8398 gui_mch_new_menu_font();
8399 }
8400# ifdef FEAT_BEVAL
8401 if (do_tooltip)
8402 {
8403 /* The Athena widget set cannot currently handle switching between
8404 * displaying a single font and a fontset.
8405 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008406 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008407 * XFontStruct is used.
8408 */
8409 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8410 gui_mch_new_tooltip_font();
8411 }
8412# endif
8413# endif
8414 }
8415 else
8416# endif
8417 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008418 if (free_font)
8419 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008420 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8421 /* If it worked and it's the Normal group, use it as the
8422 * normal font. Same for the Menu group. */
8423 if (HL_TABLE()[idx].sg_font != NOFONT)
8424 {
8425 if (do_normal)
8426 gui_init_font(arg, FALSE);
8427#ifndef FONTSET_ALWAYS
8428# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8429 if (do_menu)
8430 {
8431 gui.menu_font = HL_TABLE()[idx].sg_font;
8432 gui_mch_new_menu_font();
8433 }
8434# endif
8435#endif
8436 }
8437 }
8438}
8439
8440#endif /* FEAT_GUI */
8441
8442/*
8443 * Table with the specifications for an attribute number.
8444 * Note that this table is used by ALL buffers. This is required because the
8445 * GUI can redraw at any time for any buffer.
8446 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008447static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008448
8449#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8450
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008451static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452
8453#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8454
8455#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008456static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008457
8458#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8459#endif
8460
8461/*
8462 * Return the attr number for a set of colors and font.
8463 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8464 * if the combination is new.
8465 * Return 0 for error (no more room).
8466 */
8467 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008468get_attr_entry(garray_T *table, attrentry_T *aep)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008469{
8470 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008471 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008472 static int recursive = FALSE;
8473
8474 /*
8475 * Init the table, in case it wasn't done yet.
8476 */
8477 table->ga_itemsize = sizeof(attrentry_T);
8478 table->ga_growsize = 7;
8479
8480 /*
8481 * Try to find an entry with the same specifications.
8482 */
8483 for (i = 0; i < table->ga_len; ++i)
8484 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008485 taep = &(((attrentry_T *)table->ga_data)[i]);
8486 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008487 && (
8488#ifdef FEAT_GUI
8489 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008490 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8491 && aep->ae_u.gui.bg_color
8492 == taep->ae_u.gui.bg_color
8493 && aep->ae_u.gui.sp_color
8494 == taep->ae_u.gui.sp_color
8495 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008496# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008497 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008498# endif
8499 ))
8500 ||
8501#endif
8502 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008503 && (aep->ae_u.term.start == NULL)
8504 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505 && (aep->ae_u.term.start == NULL
8506 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008507 taep->ae_u.term.start) == 0)
8508 && (aep->ae_u.term.stop == NULL)
8509 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008510 && (aep->ae_u.term.stop == NULL
8511 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008512 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008513 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008514 && aep->ae_u.cterm.fg_color
8515 == taep->ae_u.cterm.fg_color
8516 && aep->ae_u.cterm.bg_color
8517 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 ))
8519
8520 return i + ATTR_OFF;
8521 }
8522
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008523 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524 {
8525 /*
8526 * Running out of attribute entries! remove all attributes, and
8527 * compute new ones for all groups.
8528 * When called recursively, we are really out of numbers.
8529 */
8530 if (recursive)
8531 {
8532 EMSG(_("E424: Too many different highlighting attributes in use"));
8533 return 0;
8534 }
8535 recursive = TRUE;
8536
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008537 clear_hl_tables();
8538
Bram Moolenaar071d4272004-06-13 20:20:40 +00008539 must_redraw = CLEAR;
8540
8541 for (i = 0; i < highlight_ga.ga_len; ++i)
8542 set_hl_attr(i);
8543
8544 recursive = FALSE;
8545 }
8546
8547 /*
8548 * This is a new combination of colors and font, add an entry.
8549 */
8550 if (ga_grow(table, 1) == FAIL)
8551 return 0;
8552
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008553 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8554 vim_memset(taep, 0, sizeof(attrentry_T));
8555 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008556#ifdef FEAT_GUI
8557 if (table == &gui_attr_table)
8558 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008559 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8560 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8561 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8562 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008563# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008564 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008565# endif
8566 }
8567#endif
8568 if (table == &term_attr_table)
8569 {
8570 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008571 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008572 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008573 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008574 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008575 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008576 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008577 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008578 }
8579 else if (table == &cterm_attr_table)
8580 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008581 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8582 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583 }
8584 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585 return (table->ga_len - 1 + ATTR_OFF);
8586}
8587
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008588/*
8589 * Clear all highlight tables.
8590 */
8591 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008592clear_hl_tables(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008593{
8594 int i;
8595 attrentry_T *taep;
8596
8597#ifdef FEAT_GUI
8598 ga_clear(&gui_attr_table);
8599#endif
8600 for (i = 0; i < term_attr_table.ga_len; ++i)
8601 {
8602 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8603 vim_free(taep->ae_u.term.start);
8604 vim_free(taep->ae_u.term.stop);
8605 }
8606 ga_clear(&term_attr_table);
8607 ga_clear(&cterm_attr_table);
8608}
8609
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008610#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008611/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008612 * Combine special attributes (e.g., for spelling) with other attributes
8613 * (e.g., for syntax highlighting).
8614 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008615 * This creates a new group when required.
8616 * Since we expect there to be few spelling mistakes we don't cache the
8617 * result.
8618 * Return the resulting attributes.
8619 */
8620 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008621hl_combine_attr(int char_attr, int prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008622{
8623 attrentry_T *char_aep = NULL;
8624 attrentry_T *spell_aep;
8625 attrentry_T new_en;
8626
8627 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008628 return prim_attr;
8629 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8630 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008631#ifdef FEAT_GUI
8632 if (gui.in_use)
8633 {
8634 if (char_attr > HL_ALL)
8635 char_aep = syn_gui_attr2entry(char_attr);
8636 if (char_aep != NULL)
8637 new_en = *char_aep;
8638 else
8639 {
8640 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008641 new_en.ae_u.gui.fg_color = INVALCOLOR;
8642 new_en.ae_u.gui.bg_color = INVALCOLOR;
8643 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008644 if (char_attr <= HL_ALL)
8645 new_en.ae_attr = char_attr;
8646 }
8647
Bram Moolenaar30abd282005-06-22 22:35:10 +00008648 if (prim_attr <= HL_ALL)
8649 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008650 else
8651 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008652 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008653 if (spell_aep != NULL)
8654 {
8655 new_en.ae_attr |= spell_aep->ae_attr;
8656 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8657 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8658 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8659 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8660 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8661 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8662 if (spell_aep->ae_u.gui.font != NOFONT)
8663 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8664# ifdef FEAT_XFONTSET
8665 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8666 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8667# endif
8668 }
8669 }
8670 return get_attr_entry(&gui_attr_table, &new_en);
8671 }
8672#endif
8673
8674 if (t_colors > 1)
8675 {
8676 if (char_attr > HL_ALL)
8677 char_aep = syn_cterm_attr2entry(char_attr);
8678 if (char_aep != NULL)
8679 new_en = *char_aep;
8680 else
8681 {
8682 vim_memset(&new_en, 0, sizeof(new_en));
8683 if (char_attr <= HL_ALL)
8684 new_en.ae_attr = char_attr;
8685 }
8686
Bram Moolenaar30abd282005-06-22 22:35:10 +00008687 if (prim_attr <= HL_ALL)
8688 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008689 else
8690 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008691 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008692 if (spell_aep != NULL)
8693 {
8694 new_en.ae_attr |= spell_aep->ae_attr;
8695 if (spell_aep->ae_u.cterm.fg_color > 0)
8696 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8697 if (spell_aep->ae_u.cterm.bg_color > 0)
8698 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8699 }
8700 }
8701 return get_attr_entry(&cterm_attr_table, &new_en);
8702 }
8703
8704 if (char_attr > HL_ALL)
8705 char_aep = syn_term_attr2entry(char_attr);
8706 if (char_aep != NULL)
8707 new_en = *char_aep;
8708 else
8709 {
8710 vim_memset(&new_en, 0, sizeof(new_en));
8711 if (char_attr <= HL_ALL)
8712 new_en.ae_attr = char_attr;
8713 }
8714
Bram Moolenaar30abd282005-06-22 22:35:10 +00008715 if (prim_attr <= HL_ALL)
8716 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008717 else
8718 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008719 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008720 if (spell_aep != NULL)
8721 {
8722 new_en.ae_attr |= spell_aep->ae_attr;
8723 if (spell_aep->ae_u.term.start != NULL)
8724 {
8725 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8726 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8727 }
8728 }
8729 }
8730 return get_attr_entry(&term_attr_table, &new_en);
8731}
8732#endif
8733
Bram Moolenaar071d4272004-06-13 20:20:40 +00008734#ifdef FEAT_GUI
8735
8736 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008737syn_gui_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008738{
8739 attr -= ATTR_OFF;
8740 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8741 return NULL;
8742 return &(GUI_ATTR_ENTRY(attr));
8743}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008744#endif /* FEAT_GUI */
8745
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008746/*
8747 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8748 * Only to be used when "attr" > HL_ALL.
8749 */
8750 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008751syn_attr2attr(int attr)
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008752{
8753 attrentry_T *aep;
8754
8755#ifdef FEAT_GUI
8756 if (gui.in_use)
8757 aep = syn_gui_attr2entry(attr);
8758 else
8759#endif
8760 if (t_colors > 1)
8761 aep = syn_cterm_attr2entry(attr);
8762 else
8763 aep = syn_term_attr2entry(attr);
8764
8765 if (aep == NULL) /* highlighting not set */
8766 return 0;
8767 return aep->ae_attr;
8768}
8769
8770
Bram Moolenaar071d4272004-06-13 20:20:40 +00008771 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008772syn_term_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008773{
8774 attr -= ATTR_OFF;
8775 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8776 return NULL;
8777 return &(TERM_ATTR_ENTRY(attr));
8778}
8779
8780 attrentry_T *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008781syn_cterm_attr2entry(int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008782{
8783 attr -= ATTR_OFF;
8784 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8785 return NULL;
8786 return &(CTERM_ATTR_ENTRY(attr));
8787}
8788
8789#define LIST_ATTR 1
8790#define LIST_STRING 2
8791#define LIST_INT 3
8792
8793 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008794highlight_list_one(int id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008795{
8796 struct hl_group *sgp;
8797 int didh = FALSE;
8798
8799 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8800
8801 didh = highlight_list_arg(id, didh, LIST_ATTR,
8802 sgp->sg_term, NULL, "term");
8803 didh = highlight_list_arg(id, didh, LIST_STRING,
8804 0, sgp->sg_start, "start");
8805 didh = highlight_list_arg(id, didh, LIST_STRING,
8806 0, sgp->sg_stop, "stop");
8807
8808 didh = highlight_list_arg(id, didh, LIST_ATTR,
8809 sgp->sg_cterm, NULL, "cterm");
8810 didh = highlight_list_arg(id, didh, LIST_INT,
8811 sgp->sg_cterm_fg, NULL, "ctermfg");
8812 didh = highlight_list_arg(id, didh, LIST_INT,
8813 sgp->sg_cterm_bg, NULL, "ctermbg");
8814
Bram Moolenaar61623362010-07-14 22:04:22 +02008815#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008816 didh = highlight_list_arg(id, didh, LIST_ATTR,
8817 sgp->sg_gui, NULL, "gui");
8818 didh = highlight_list_arg(id, didh, LIST_STRING,
8819 0, sgp->sg_gui_fg_name, "guifg");
8820 didh = highlight_list_arg(id, didh, LIST_STRING,
8821 0, sgp->sg_gui_bg_name, "guibg");
8822 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008823 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008824#endif
8825#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008826 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008827 0, sgp->sg_font_name, "font");
8828#endif
8829
Bram Moolenaar661b1822005-07-28 22:36:45 +00008830 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008831 {
8832 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008833 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008834 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8835 msg_putchar(' ');
8836 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8837 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008838
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008839 if (!didh)
8840 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008841#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008842 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008843 last_set_msg(sgp->sg_scriptID);
8844#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008845}
8846
8847 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008848highlight_list_arg(
8849 int id,
8850 int didh,
8851 int type,
8852 int iarg,
8853 char_u *sarg,
8854 char *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008855{
8856 char_u buf[100];
8857 char_u *ts;
8858 int i;
8859
Bram Moolenaar661b1822005-07-28 22:36:45 +00008860 if (got_int)
8861 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008862 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8863 {
8864 ts = buf;
8865 if (type == LIST_INT)
8866 sprintf((char *)buf, "%d", iarg - 1);
8867 else if (type == LIST_STRING)
8868 ts = sarg;
8869 else /* type == LIST_ATTR */
8870 {
8871 buf[0] = NUL;
8872 for (i = 0; hl_attr_table[i] != 0; ++i)
8873 {
8874 if (iarg & hl_attr_table[i])
8875 {
8876 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008877 vim_strcat(buf, (char_u *)",", 100);
8878 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008879 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8880 }
8881 }
8882 }
8883
8884 (void)syn_list_header(didh,
8885 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8886 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008887 if (!got_int)
8888 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008889 if (*name != NUL)
8890 {
8891 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8892 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8893 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008894 msg_outtrans(ts);
8895 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008896 }
8897 return didh;
8898}
8899
8900#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8901/*
8902 * Return "1" if highlight group "id" has attribute "flag".
8903 * Return NULL otherwise.
8904 */
8905 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008906highlight_has_attr(
8907 int id,
8908 int flag,
8909 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008910{
8911 int attr;
8912
8913 if (id <= 0 || id > highlight_ga.ga_len)
8914 return NULL;
8915
Bram Moolenaar61623362010-07-14 22:04:22 +02008916#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008917 if (modec == 'g')
8918 attr = HL_TABLE()[id - 1].sg_gui;
8919 else
8920#endif
8921 if (modec == 'c')
8922 attr = HL_TABLE()[id - 1].sg_cterm;
8923 else
8924 attr = HL_TABLE()[id - 1].sg_term;
8925
8926 if (attr & flag)
8927 return (char_u *)"1";
8928 return NULL;
8929}
8930#endif
8931
8932#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8933/*
8934 * Return color name of highlight group "id".
8935 */
8936 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01008937highlight_color(
8938 int id,
8939 char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
8940 int modec) /* 'g' for GUI, 'c' for cterm, 't' for term */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008941{
8942 static char_u name[20];
8943 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008944 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008945 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008946 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008947
8948 if (id <= 0 || id > highlight_ga.ga_len)
8949 return NULL;
8950
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008951 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008952 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008953 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008954 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008955 font = TRUE;
8956 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008957 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008958 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8959 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008960 if (modec == 'g')
8961 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008962# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008963 /* return font name */
8964 if (font)
8965 return HL_TABLE()[id - 1].sg_font_name;
8966
Bram Moolenaar071d4272004-06-13 20:20:40 +00008967 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008968 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008969 {
8970 guicolor_T color;
8971 long_u rgb;
8972 static char_u buf[10];
8973
8974 if (fg)
8975 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008976 else if (sp)
8977 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008978 else
8979 color = HL_TABLE()[id - 1].sg_gui_bg;
8980 if (color == INVALCOLOR)
8981 return NULL;
8982 rgb = gui_mch_get_rgb(color);
8983 sprintf((char *)buf, "#%02x%02x%02x",
8984 (unsigned)(rgb >> 16),
8985 (unsigned)(rgb >> 8) & 255,
8986 (unsigned)rgb & 255);
8987 return buf;
8988 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008989#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008990 if (fg)
8991 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008992 if (sp)
8993 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008994 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8995 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008996 if (font || sp)
8997 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008998 if (modec == 'c')
8999 {
9000 if (fg)
9001 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9002 else
9003 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
Bram Moolenaar385111b2016-03-12 19:23:00 +01009004 if (n < 0)
9005 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009006 sprintf((char *)name, "%d", n);
9007 return name;
9008 }
9009 /* term doesn't have color */
9010 return NULL;
9011}
9012#endif
9013
9014#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
9015 || defined(PROTO)
9016/*
9017 * Return color name of highlight group "id" as RGB value.
9018 */
9019 long_u
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009020highlight_gui_color_rgb(
9021 int id,
9022 int fg) /* TRUE = fg, FALSE = bg */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009023{
9024 guicolor_T color;
9025
9026 if (id <= 0 || id > highlight_ga.ga_len)
9027 return 0L;
9028
9029 if (fg)
9030 color = HL_TABLE()[id - 1].sg_gui_fg;
9031 else
9032 color = HL_TABLE()[id - 1].sg_gui_bg;
9033
9034 if (color == INVALCOLOR)
9035 return 0L;
9036
9037 return gui_mch_get_rgb(color);
9038}
9039#endif
9040
9041/*
9042 * Output the syntax list header.
9043 * Return TRUE when started a new line.
9044 */
9045 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009046syn_list_header(
9047 int did_header, /* did header already */
9048 int outlen, /* length of string that comes */
9049 int id) /* highlight group id */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009050{
9051 int endcol = 19;
9052 int newline = TRUE;
9053
9054 if (!did_header)
9055 {
9056 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009057 if (got_int)
9058 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009059 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9060 endcol = 15;
9061 }
9062 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009063 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009064 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009065 if (got_int)
9066 return TRUE;
9067 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009068 else
9069 {
9070 if (msg_col >= endcol) /* wrap around is like starting a new line */
9071 newline = FALSE;
9072 }
9073
9074 if (msg_col >= endcol) /* output at least one space */
9075 endcol = msg_col + 1;
9076 if (Columns <= endcol) /* avoid hang for tiny window */
9077 endcol = Columns - 1;
9078
9079 msg_advance(endcol);
9080
9081 /* Show "xxx" with the attributes. */
9082 if (!did_header)
9083 {
9084 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9085 msg_putchar(' ');
9086 }
9087
9088 return newline;
9089}
9090
9091/*
9092 * Set the attribute numbers for a highlight group.
9093 * Called after one of the attributes has changed.
9094 */
9095 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009096set_hl_attr(
9097 int idx) /* index in array */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009098{
9099 attrentry_T at_en;
9100 struct hl_group *sgp = HL_TABLE() + idx;
9101
9102 /* The "Normal" group doesn't need an attribute number */
9103 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9104 return;
9105
9106#ifdef FEAT_GUI
9107 /*
9108 * For the GUI mode: If there are other than "normal" highlighting
9109 * attributes, need to allocate an attr number.
9110 */
9111 if (sgp->sg_gui_fg == INVALCOLOR
9112 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009113 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009114 && sgp->sg_font == NOFONT
9115# ifdef FEAT_XFONTSET
9116 && sgp->sg_fontset == NOFONTSET
9117# endif
9118 )
9119 {
9120 sgp->sg_gui_attr = sgp->sg_gui;
9121 }
9122 else
9123 {
9124 at_en.ae_attr = sgp->sg_gui;
9125 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9126 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009127 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009128 at_en.ae_u.gui.font = sgp->sg_font;
9129# ifdef FEAT_XFONTSET
9130 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9131# endif
9132 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9133 }
9134#endif
9135 /*
9136 * For the term mode: If there are other than "normal" highlighting
9137 * attributes, need to allocate an attr number.
9138 */
9139 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9140 sgp->sg_term_attr = sgp->sg_term;
9141 else
9142 {
9143 at_en.ae_attr = sgp->sg_term;
9144 at_en.ae_u.term.start = sgp->sg_start;
9145 at_en.ae_u.term.stop = sgp->sg_stop;
9146 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9147 }
9148
9149 /*
9150 * For the color term mode: If there are other than "normal"
9151 * highlighting attributes, need to allocate an attr number.
9152 */
9153 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9154 sgp->sg_cterm_attr = sgp->sg_cterm;
9155 else
9156 {
9157 at_en.ae_attr = sgp->sg_cterm;
9158 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9159 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9160 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9161 }
9162}
9163
9164/*
9165 * Lookup a highlight group name and return it's ID.
9166 * If it is not found, 0 is returned.
9167 */
9168 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009169syn_name2id(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009170{
9171 int i;
9172 char_u name_u[200];
9173
9174 /* Avoid using stricmp() too much, it's slow on some systems */
9175 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9176 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009177 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009178 vim_strup(name_u);
9179 for (i = highlight_ga.ga_len; --i >= 0; )
9180 if (HL_TABLE()[i].sg_name_u != NULL
9181 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9182 break;
9183 return i + 1;
9184}
9185
9186#if defined(FEAT_EVAL) || defined(PROTO)
9187/*
9188 * Return TRUE if highlight group "name" exists.
9189 */
9190 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009191highlight_exists(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009192{
9193 return (syn_name2id(name) > 0);
9194}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009195
9196# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9197/*
9198 * Return the name of highlight group "id".
9199 * When not a valid ID return an empty string.
9200 */
9201 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009202syn_id2name(int id)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009203{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009204 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009205 return (char_u *)"";
9206 return HL_TABLE()[id - 1].sg_name;
9207}
9208# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009209#endif
9210
9211/*
9212 * Like syn_name2id(), but take a pointer + length argument.
9213 */
9214 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009215syn_namen2id(char_u *linep, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009216{
9217 char_u *name;
9218 int id = 0;
9219
9220 name = vim_strnsave(linep, len);
9221 if (name != NULL)
9222 {
9223 id = syn_name2id(name);
9224 vim_free(name);
9225 }
9226 return id;
9227}
9228
9229/*
9230 * Find highlight group name in the table and return it's ID.
9231 * The argument is a pointer to the name and the length of the name.
9232 * If it doesn't exist yet, a new entry is created.
9233 * Return 0 for failure.
9234 */
9235 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009236syn_check_group(char_u *pp, int len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009237{
9238 int id;
9239 char_u *name;
9240
9241 name = vim_strnsave(pp, len);
9242 if (name == NULL)
9243 return 0;
9244
9245 id = syn_name2id(name);
9246 if (id == 0) /* doesn't exist yet */
9247 id = syn_add_group(name);
9248 else
9249 vim_free(name);
9250 return id;
9251}
9252
9253/*
9254 * Add new highlight group and return it's ID.
9255 * "name" must be an allocated string, it will be consumed.
9256 * Return 0 for failure.
9257 */
9258 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009259syn_add_group(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009260{
9261 char_u *p;
9262
9263 /* Check that the name is ASCII letters, digits and underscore. */
9264 for (p = name; *p != NUL; ++p)
9265 {
9266 if (!vim_isprintc(*p))
9267 {
9268 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009269 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009270 return 0;
9271 }
9272 else if (!ASCII_ISALNUM(*p) && *p != '_')
9273 {
9274 /* This is an error, but since there previously was no check only
9275 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009276 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009277 MSG(_("W18: Invalid character in group name"));
9278 break;
9279 }
9280 }
9281
9282 /*
9283 * First call for this growarray: init growing array.
9284 */
9285 if (highlight_ga.ga_data == NULL)
9286 {
9287 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9288 highlight_ga.ga_growsize = 10;
9289 }
9290
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009291 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009292 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009293 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009294 vim_free(name);
9295 return 0;
9296 }
9297
Bram Moolenaar071d4272004-06-13 20:20:40 +00009298 /*
9299 * Make room for at least one other syntax_highlight entry.
9300 */
9301 if (ga_grow(&highlight_ga, 1) == FAIL)
9302 {
9303 vim_free(name);
9304 return 0;
9305 }
9306
9307 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9308 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9309 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9310#ifdef FEAT_GUI
9311 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9312 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009313 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009314#endif
9315 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009316
9317 return highlight_ga.ga_len; /* ID is index plus one */
9318}
9319
9320/*
9321 * When, just after calling syn_add_group(), an error is discovered, this
9322 * function deletes the new name.
9323 */
9324 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009325syn_unadd_group(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009326{
9327 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009328 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9329 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9330}
9331
9332/*
9333 * Translate a group ID to highlight attributes.
9334 */
9335 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009336syn_id2attr(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009337{
9338 int attr;
9339 struct hl_group *sgp;
9340
9341 hl_id = syn_get_final_id(hl_id);
9342 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9343
9344#ifdef FEAT_GUI
9345 /*
9346 * Only use GUI attr when the GUI is being used.
9347 */
9348 if (gui.in_use)
9349 attr = sgp->sg_gui_attr;
9350 else
9351#endif
9352 if (t_colors > 1)
9353 attr = sgp->sg_cterm_attr;
9354 else
9355 attr = sgp->sg_term_attr;
9356
9357 return attr;
9358}
9359
9360#ifdef FEAT_GUI
9361/*
9362 * Get the GUI colors and attributes for a group ID.
9363 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9364 */
9365 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009366syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009367{
9368 struct hl_group *sgp;
9369
9370 hl_id = syn_get_final_id(hl_id);
9371 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9372
9373 *fgp = sgp->sg_gui_fg;
9374 *bgp = sgp->sg_gui_bg;
9375 return sgp->sg_gui;
9376}
9377#endif
9378
9379/*
9380 * Translate a group ID to the final group ID (following links).
9381 */
9382 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009383syn_get_final_id(int hl_id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009384{
9385 int count;
9386 struct hl_group *sgp;
9387
9388 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9389 return 0; /* Can be called from eval!! */
9390
9391 /*
9392 * Follow links until there is no more.
9393 * Look out for loops! Break after 100 links.
9394 */
9395 for (count = 100; --count >= 0; )
9396 {
9397 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9398 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9399 break;
9400 hl_id = sgp->sg_link;
9401 }
9402
9403 return hl_id;
9404}
9405
9406#ifdef FEAT_GUI
9407/*
9408 * Call this function just after the GUI has started.
9409 * It finds the font and color handles for the highlighting groups.
9410 */
9411 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009412highlight_gui_started(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009413{
9414 int idx;
9415
9416 /* First get the colors from the "Normal" and "Menu" group, if set */
9417 set_normal_colors();
9418
9419 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9420 gui_do_one_color(idx, FALSE, FALSE);
9421
9422 highlight_changed();
9423}
9424
9425 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009426gui_do_one_color(
9427 int idx,
9428 int do_menu, /* TRUE: might set the menu font */
9429 int do_tooltip) /* TRUE: might set the tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009430{
9431 int didit = FALSE;
9432
9433 if (HL_TABLE()[idx].sg_font_name != NULL)
9434 {
9435 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009436 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009437 didit = TRUE;
9438 }
9439 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9440 {
9441 HL_TABLE()[idx].sg_gui_fg =
9442 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9443 didit = TRUE;
9444 }
9445 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9446 {
9447 HL_TABLE()[idx].sg_gui_bg =
9448 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9449 didit = TRUE;
9450 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009451 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9452 {
9453 HL_TABLE()[idx].sg_gui_sp =
9454 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9455 didit = TRUE;
9456 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009457 if (didit) /* need to get a new attr number */
9458 set_hl_attr(idx);
9459}
9460
9461#endif
9462
9463/*
9464 * Translate the 'highlight' option into attributes in highlight_attr[] and
9465 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9466 * corresponding highlights to use on top of HLF_SNC is computed.
9467 * Called only when the 'highlight' option has been changed and upon first
9468 * screen redraw after any :highlight command.
9469 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9470 */
9471 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009472highlight_changed(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009473{
9474 int hlf;
9475 int i;
9476 char_u *p;
9477 int attr;
9478 char_u *end;
9479 int id;
9480#ifdef USER_HIGHLIGHT
9481 char_u userhl[10];
9482# ifdef FEAT_STL_OPT
9483 int id_SNC = -1;
9484 int id_S = -1;
9485 int hlcnt;
9486# endif
9487#endif
9488 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9489
9490 need_highlight_changed = FALSE;
9491
9492 /*
9493 * Clear all attributes.
9494 */
9495 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9496 highlight_attr[hlf] = 0;
9497
9498 /*
9499 * First set all attributes to their default value.
9500 * Then use the attributes from the 'highlight' option.
9501 */
9502 for (i = 0; i < 2; ++i)
9503 {
9504 if (i)
9505 p = p_hl;
9506 else
9507 p = get_highlight_default();
9508 if (p == NULL) /* just in case */
9509 continue;
9510
9511 while (*p)
9512 {
9513 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9514 if (hl_flags[hlf] == *p)
9515 break;
9516 ++p;
9517 if (hlf == (int)HLF_COUNT || *p == NUL)
9518 return FAIL;
9519
9520 /*
9521 * Allow several hl_flags to be combined, like "bu" for
9522 * bold-underlined.
9523 */
9524 attr = 0;
9525 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9526 {
9527 if (vim_iswhite(*p)) /* ignore white space */
9528 continue;
9529
9530 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9531 return FAIL;
9532
9533 switch (*p)
9534 {
9535 case 'b': attr |= HL_BOLD;
9536 break;
9537 case 'i': attr |= HL_ITALIC;
9538 break;
9539 case '-':
9540 case 'n': /* no highlighting */
9541 break;
9542 case 'r': attr |= HL_INVERSE;
9543 break;
9544 case 's': attr |= HL_STANDOUT;
9545 break;
9546 case 'u': attr |= HL_UNDERLINE;
9547 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009548 case 'c': attr |= HL_UNDERCURL;
9549 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009550 case ':': ++p; /* highlight group name */
9551 if (attr || *p == NUL) /* no combinations */
9552 return FAIL;
9553 end = vim_strchr(p, ',');
9554 if (end == NULL)
9555 end = p + STRLEN(p);
9556 id = syn_check_group(p, (int)(end - p));
9557 if (id == 0)
9558 return FAIL;
9559 attr = syn_id2attr(id);
9560 p = end - 1;
9561#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9562 if (hlf == (int)HLF_SNC)
9563 id_SNC = syn_get_final_id(id);
9564 else if (hlf == (int)HLF_S)
9565 id_S = syn_get_final_id(id);
9566#endif
9567 break;
9568 default: return FAIL;
9569 }
9570 }
9571 highlight_attr[hlf] = attr;
9572
9573 p = skip_to_option_part(p); /* skip comma and spaces */
9574 }
9575 }
9576
9577#ifdef USER_HIGHLIGHT
9578 /* Setup the user highlights
9579 *
9580 * Temporarily utilize 10 more hl entries. Have to be in there
9581 * simultaneously in case of table overflows in get_attr_entry()
9582 */
9583# ifdef FEAT_STL_OPT
9584 if (ga_grow(&highlight_ga, 10) == FAIL)
9585 return FAIL;
9586 hlcnt = highlight_ga.ga_len;
9587 if (id_S == 0)
9588 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009589 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009590 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9591 id_S = hlcnt + 10;
9592 }
9593# endif
9594 for (i = 0; i < 9; i++)
9595 {
9596 sprintf((char *)userhl, "User%d", i + 1);
9597 id = syn_name2id(userhl);
9598 if (id == 0)
9599 {
9600 highlight_user[i] = 0;
9601# ifdef FEAT_STL_OPT
9602 highlight_stlnc[i] = 0;
9603# endif
9604 }
9605 else
9606 {
9607# ifdef FEAT_STL_OPT
9608 struct hl_group *hlt = HL_TABLE();
9609# endif
9610
9611 highlight_user[i] = syn_id2attr(id);
9612# ifdef FEAT_STL_OPT
9613 if (id_SNC == 0)
9614 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009615 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009616 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9617 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009618# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009619 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9620# endif
9621 }
9622 else
9623 mch_memmove(&hlt[hlcnt + i],
9624 &hlt[id_SNC - 1],
9625 sizeof(struct hl_group));
9626 hlt[hlcnt + i].sg_link = 0;
9627
9628 /* Apply difference between UserX and HLF_S to HLF_SNC */
9629 hlt[hlcnt + i].sg_term ^=
9630 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9631 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9632 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9633 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9634 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9635 hlt[hlcnt + i].sg_cterm ^=
9636 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9637 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9638 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9639 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9640 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009641# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009642 hlt[hlcnt + i].sg_gui ^=
9643 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009644# endif
9645# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009646 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9647 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9648 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9649 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009650 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9651 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009652 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9653 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9654# ifdef FEAT_XFONTSET
9655 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9656 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9657# endif
9658# endif
9659 highlight_ga.ga_len = hlcnt + i + 1;
9660 set_hl_attr(hlcnt + i); /* At long last we can apply */
9661 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9662# endif
9663 }
9664 }
9665# ifdef FEAT_STL_OPT
9666 highlight_ga.ga_len = hlcnt;
9667# endif
9668
9669#endif /* USER_HIGHLIGHT */
9670
9671 return OK;
9672}
9673
Bram Moolenaar4f688582007-07-24 12:34:30 +00009674#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009675
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +01009676static void highlight_list(void);
9677static void highlight_list_two(int cnt, int attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009678
9679/*
9680 * Handle command line completion for :highlight command.
9681 */
9682 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009683set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009684{
9685 char_u *p;
9686
9687 /* Default: expand group names */
9688 xp->xp_context = EXPAND_HIGHLIGHT;
9689 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009690 include_link = 2;
9691 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009692
9693 /* (part of) subcommand already typed */
9694 if (*arg != NUL)
9695 {
9696 p = skiptowhite(arg);
9697 if (*p != NUL) /* past "default" or group name */
9698 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009699 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009700 if (STRNCMP("default", arg, p - arg) == 0)
9701 {
9702 arg = skipwhite(p);
9703 xp->xp_pattern = arg;
9704 p = skiptowhite(arg);
9705 }
9706 if (*p != NUL) /* past group name */
9707 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009708 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009709 if (arg[1] == 'i' && arg[0] == 'N')
9710 highlight_list();
9711 if (STRNCMP("link", arg, p - arg) == 0
9712 || STRNCMP("clear", arg, p - arg) == 0)
9713 {
9714 xp->xp_pattern = skipwhite(p);
9715 p = skiptowhite(xp->xp_pattern);
9716 if (*p != NUL) /* past first group name */
9717 {
9718 xp->xp_pattern = skipwhite(p);
9719 p = skiptowhite(xp->xp_pattern);
9720 }
9721 }
9722 if (*p != NUL) /* past group name(s) */
9723 xp->xp_context = EXPAND_NOTHING;
9724 }
9725 }
9726 }
9727}
9728
9729/*
9730 * List highlighting matches in a nice way.
9731 */
9732 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009733highlight_list(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009734{
9735 int i;
9736
9737 for (i = 10; --i >= 0; )
9738 highlight_list_two(i, hl_attr(HLF_D));
9739 for (i = 40; --i >= 0; )
9740 highlight_list_two(99, 0);
9741}
9742
9743 static void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009744highlight_list_two(int cnt, int attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009745{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009746 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009747 msg_clr_eos();
9748 out_flush();
9749 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9750}
9751
9752#endif /* FEAT_CMDL_COMPL */
9753
9754#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9755 || defined(FEAT_SIGNS) || defined(PROTO)
9756/*
9757 * Function given to ExpandGeneric() to obtain the list of group names.
9758 * Also used for synIDattr() function.
9759 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009760 char_u *
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009761get_highlight_name(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009762{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009763#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009764 if (idx == highlight_ga.ga_len && include_none != 0)
9765 return (char_u *)"none";
9766 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009767 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009768 if (idx == highlight_ga.ga_len + include_none + include_default
9769 && include_link != 0)
9770 return (char_u *)"link";
9771 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9772 && include_link != 0)
9773 return (char_u *)"clear";
9774#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009775 if (idx < 0 || idx >= highlight_ga.ga_len)
9776 return NULL;
9777 return HL_TABLE()[idx].sg_name;
9778}
9779#endif
9780
Bram Moolenaar4f688582007-07-24 12:34:30 +00009781#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009782/*
9783 * Free all the highlight group fonts.
9784 * Used when quitting for systems which need it.
9785 */
9786 void
Bram Moolenaar764b23c2016-01-30 21:10:09 +01009787free_highlight_fonts(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009788{
9789 int idx;
9790
9791 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9792 {
9793 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9794 HL_TABLE()[idx].sg_font = NOFONT;
9795# ifdef FEAT_XFONTSET
9796 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9797 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9798# endif
9799 }
9800
9801 gui_mch_free_font(gui.norm_font);
9802# ifdef FEAT_XFONTSET
9803 gui_mch_free_fontset(gui.fontset);
9804# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009805# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009806 gui_mch_free_font(gui.bold_font);
9807 gui_mch_free_font(gui.ital_font);
9808 gui_mch_free_font(gui.boldital_font);
9809# endif
9810}
9811#endif
9812
9813/**************************************
9814 * End of Highlighting stuff *
9815 **************************************/