blob: d9f0ffc9f1880af603d1e05a2e2eb2d776376fd4 [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
90static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
91static void syn_unadd_group __ARGS((void));
92static void set_hl_attr __ARGS((int idx));
93static void highlight_list_one __ARGS((int id));
94static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
95static int syn_add_group __ARGS((char_u *name));
96static int syn_list_header __ARGS((int did_header, int outlen, int id));
97static int hl_has_settings __ARGS((int idx, int check_link));
98static void highlight_clear __ARGS((int idx));
99
100#ifdef FEAT_GUI
101static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
102static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
103static guicolor_T color_name2handle __ARGS((char_u *name));
104static GuiFont font_name2handle __ARGS((char_u *name));
105# ifdef FEAT_XFONTSET
106static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
107# endif
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +0200108static void hl_do_font __ARGS((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
378static void syn_sync __ARGS((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 Moolenaar071d4272004-06-13 20:20:40 +0000381static int syn_match_linecont __ARGS((linenr_T lnum));
382static void syn_start_line __ARGS((void));
383static void syn_update_ends __ARGS((int startofline));
384static void syn_stack_alloc __ARGS((void));
385static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200386static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000388static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389static void load_current_state __ARGS((synstate_T *from));
390static void invalidate_current_state __ARGS((void));
391static int syn_stack_equal __ARGS((synstate_T *sp));
392static void validate_current_state __ARGS((void));
393static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000394static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395static int did_match_already __ARGS((int idx, garray_T *gap));
396static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
397static void check_state_ends __ARGS((void));
398static void update_si_attr __ARGS((int idx));
399static void check_keepend __ARGS((void));
400static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
401static short *copy_id_list __ARGS((short *list));
402static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
403static int push_current_state __ARGS((int idx));
404static void pop_current_state __ARGS((void));
Bram Moolenaarf7512552013-06-06 14:55:19 +0200405#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200406static void syn_clear_time __ARGS((syn_time_T *tt));
407static void syntime_clear __ARGS((void));
408#ifdef __BORLANDC__
409static int _RTLENTRYF syn_compare_syntime __ARGS((const void *v1, const void *v2));
410#else
411static int syn_compare_syntime __ARGS((const void *v1, const void *v2));
412#endif
413static void syntime_report __ARGS((void));
414static 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 Moolenaar860cae12010-06-05 23:22:07 +0200421static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422static void find_endpos __ARGS((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 __ARGS((synstate_T *p));
424static void clear_current_state __ARGS((void));
425
426static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
427static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
428static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
429static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
430static char_u *syn_getcurline __ARGS((void));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200431static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200432static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000433static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000434static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200436static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
437static void syn_clear_pattern __ARGS((synblock_T *block, int i));
438static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000439static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200440static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441static void syn_clear_one __ARGS((int id, int syncing));
442static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
443static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
444static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
445static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
446static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
447static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
448static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
449static void syn_lines_msg __ARGS((void));
450static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200451static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452static void syn_list_one __ARGS((int id, int syncing, int link_only));
453static void syn_list_cluster __ARGS((int id));
454static void put_id_list __ARGS((char_u *name, short *list, int attr));
455static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000456static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
457static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
458static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200459static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000460static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200461static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000462static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100463static void syn_cmd_iskeyword __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000464static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
465static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
466static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
467#ifdef __BORLANDC__
468static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
469#else
470static int syn_compare_stub __ARGS((const void *v1, const void *v2));
471#endif
472static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
473static int syn_scl_name2id __ARGS((char_u *name));
474static int syn_scl_namen2id __ARGS((char_u *linep, int len));
475static int syn_check_cluster __ARGS((char_u *pp, int len));
476static int syn_add_cluster __ARGS((char_u *name));
477static void init_syn_patterns __ARGS((void));
478static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
479static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
480static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
481static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
482static void syn_incl_toplevel __ARGS((int id, int *flagsp));
483
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
492syntax_start(wp, lnum)
493 win_T *wp;
494 linenr_T lnum;
495{
496 synstate_T *p;
497 synstate_T *last_valid = NULL;
498 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000499 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000500 linenr_T parsed_lnum;
501 linenr_T first_stored;
502 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000503 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200505#ifdef FEAT_CONCEAL
506 current_sub_char = NUL;
507#endif
508
Bram Moolenaar071d4272004-06-13 20:20:40 +0000509 /*
510 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000511 * Also do this when a change was made, the current state may be invalid
512 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000513 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200514 if (syn_block != wp->w_s || 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
671clear_syn_state(p)
672 synstate_T *p;
673{
674 int i;
675 garray_T *gap;
676
677 if (p->sst_stacksize > SST_FIX_STATES)
678 {
679 gap = &(p->sst_union.sst_ga);
680 for (i = 0; i < gap->ga_len; i++)
681 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
682 ga_clear(gap);
683 }
684 else
685 {
686 for (i = 0; i < p->sst_stacksize; i++)
687 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
688 }
689}
690
691/*
692 * Cleanup the current_state stack.
693 */
694 static void
695clear_current_state()
696{
697 int i;
698 stateitem_T *sip;
699
700 sip = (stateitem_T *)(current_state.ga_data);
701 for (i = 0; i < current_state.ga_len; i++)
702 unref_extmatch(sip[i].si_extmatch);
703 ga_clear(&current_state);
704}
705
706/*
707 * Try to find a synchronisation point for line "lnum".
708 *
709 * This sets current_lnum and the current state. One of three methods is
710 * used:
711 * 1. Search backwards for the end of a C-comment.
712 * 2. Search backwards for given sync patterns.
713 * 3. Simply start on a given number of lines above "lnum".
714 */
715 static void
716syn_sync(wp, start_lnum, last_valid)
717 win_T *wp;
718 linenr_T start_lnum;
719 synstate_T *last_valid;
720{
721 buf_T *curbuf_save;
722 win_T *curwin_save;
723 pos_T cursor_save;
724 int idx;
725 linenr_T lnum;
726 linenr_T end_lnum;
727 linenr_T break_lnum;
728 int had_sync_point;
729 stateitem_T *cur_si;
730 synpat_T *spp;
731 char_u *line;
732 int found_flags = 0;
733 int found_match_idx = 0;
734 linenr_T found_current_lnum = 0;
735 int found_current_col= 0;
736 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000737 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000738
739 /*
740 * Clear any current state that might be hanging around.
741 */
742 invalidate_current_state();
743
744 /*
745 * Start at least "minlines" back. Default starting point for parsing is
746 * there.
747 * Start further back, to avoid that scrolling backwards will result in
748 * resyncing for every line. Now it resyncs only one out of N lines,
749 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
750 * Watch out for overflow when minlines is MAXLNUM.
751 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200752 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000753 start_lnum = 1;
754 else
755 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200756 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200758 else if (syn_block->b_syn_sync_minlines < 10)
759 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
762 if (syn_block->b_syn_sync_maxlines != 0
763 && lnum > syn_block->b_syn_sync_maxlines)
764 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 if (lnum >= start_lnum)
766 start_lnum = 1;
767 else
768 start_lnum -= lnum;
769 }
770 current_lnum = start_lnum;
771
772 /*
773 * 1. Search backwards for the end of a C-style comment.
774 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200775 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 {
777 /* Need to make syn_buf the current buffer for a moment, to be able to
778 * use find_start_comment(). */
779 curwin_save = curwin;
780 curwin = wp;
781 curbuf_save = curbuf;
782 curbuf = syn_buf;
783
784 /*
785 * Skip lines that end in a backslash.
786 */
787 for ( ; start_lnum > 1; --start_lnum)
788 {
789 line = ml_get(start_lnum - 1);
790 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
791 break;
792 }
793 current_lnum = start_lnum;
794
795 /* set cursor to start of search */
796 cursor_save = wp->w_cursor;
797 wp->w_cursor.lnum = start_lnum;
798 wp->w_cursor.col = 0;
799
800 /*
801 * If the line is inside a comment, need to find the syntax item that
802 * defines the comment.
803 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
804 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200805 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200807 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
808 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
809 == syn_block->b_syn_sync_id
810 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000811 {
812 validate_current_state();
813 if (push_current_state(idx) == OK)
814 update_si_attr(current_state.ga_len - 1);
815 break;
816 }
817 }
818
819 /* restore cursor and buffer */
820 wp->w_cursor = cursor_save;
821 curwin = curwin_save;
822 curbuf = curbuf_save;
823 }
824
825 /*
826 * 2. Search backwards for given sync patterns.
827 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200828 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000829 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200830 if (syn_block->b_syn_sync_maxlines != 0
831 && start_lnum > syn_block->b_syn_sync_maxlines)
832 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000833 else
834 break_lnum = 0;
835
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000836 found_m_endpos.lnum = 0;
837 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000838 end_lnum = start_lnum;
839 lnum = start_lnum;
840 while (--lnum > break_lnum)
841 {
842 /* This can take a long time: break when CTRL-C pressed. */
843 line_breakcheck();
844 if (got_int)
845 {
846 invalidate_current_state();
847 current_lnum = start_lnum;
848 break;
849 }
850
851 /* Check if we have run into a valid saved state stack now. */
852 if (last_valid != NULL && lnum == last_valid->sst_lnum)
853 {
854 load_current_state(last_valid);
855 break;
856 }
857
858 /*
859 * Check if the previous line has the line-continuation pattern.
860 */
861 if (lnum > 1 && syn_match_linecont(lnum - 1))
862 continue;
863
864 /*
865 * Start with nothing on the state stack
866 */
867 validate_current_state();
868
869 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
870 {
871 syn_start_line();
872 for (;;)
873 {
874 had_sync_point = syn_finish_line(TRUE);
875 /*
876 * When a sync point has been found, remember where, and
877 * continue to look for another one, further on in the line.
878 */
879 if (had_sync_point && current_state.ga_len)
880 {
881 cur_si = &CUR_STATE(current_state.ga_len - 1);
882 if (cur_si->si_m_endpos.lnum > start_lnum)
883 {
884 /* ignore match that goes to after where started */
885 current_lnum = end_lnum;
886 break;
887 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000888 if (cur_si->si_idx < 0)
889 {
890 /* Cannot happen? */
891 found_flags = 0;
892 found_match_idx = KEYWORD_IDX;
893 }
894 else
895 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200896 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000897 found_flags = spp->sp_flags;
898 found_match_idx = spp->sp_sync_idx;
899 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000900 found_current_lnum = current_lnum;
901 found_current_col = current_col;
902 found_m_endpos = cur_si->si_m_endpos;
903 /*
904 * Continue after the match (be aware of a zero-length
905 * match).
906 */
907 if (found_m_endpos.lnum > current_lnum)
908 {
909 current_lnum = found_m_endpos.lnum;
910 current_col = found_m_endpos.col;
911 if (current_lnum >= end_lnum)
912 break;
913 }
914 else if (found_m_endpos.col > current_col)
915 current_col = found_m_endpos.col;
916 else
917 ++current_col;
918
919 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000920 * an item that ends here, need to do that now. Be
921 * careful not to go past the NUL. */
922 prev_current_col = current_col;
923 if (syn_getcurline()[current_col] != NUL)
924 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000925 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000926 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000927 }
928 else
929 break;
930 }
931 }
932
933 /*
934 * If a sync point was encountered, break here.
935 */
936 if (found_flags)
937 {
938 /*
939 * Put the item that was specified by the sync point on the
940 * state stack. If there was no item specified, make the
941 * state stack empty.
942 */
943 clear_current_state();
944 if (found_match_idx >= 0
945 && push_current_state(found_match_idx) == OK)
946 update_si_attr(current_state.ga_len - 1);
947
948 /*
949 * When using "grouphere", continue from the sync point
950 * match, until the end of the line. Parsing starts at
951 * the next line.
952 * For "groupthere" the parsing starts at start_lnum.
953 */
954 if (found_flags & HL_SYNC_HERE)
955 {
956 if (current_state.ga_len)
957 {
958 cur_si = &CUR_STATE(current_state.ga_len - 1);
959 cur_si->si_h_startpos.lnum = found_current_lnum;
960 cur_si->si_h_startpos.col = found_current_col;
961 update_si_end(cur_si, (int)current_col, TRUE);
962 check_keepend();
963 }
964 current_col = found_m_endpos.col;
965 current_lnum = found_m_endpos.lnum;
966 (void)syn_finish_line(FALSE);
967 ++current_lnum;
968 }
969 else
970 current_lnum = start_lnum;
971
972 break;
973 }
974
975 end_lnum = lnum;
976 invalidate_current_state();
977 }
978
979 /* Ran into start of the file or exceeded maximum number of lines */
980 if (lnum <= break_lnum)
981 {
982 invalidate_current_state();
983 current_lnum = break_lnum + 1;
984 }
985 }
986
987 validate_current_state();
988}
989
Bram Moolenaarb8060fe2016-01-19 22:29:28 +0100990 static void
991save_chartab(char_u *chartab)
992{
993 if (syn_block->b_syn_isk != empty_option)
994 {
995 mch_memmove(chartab, syn_buf->b_chartab, (size_t)32);
996 mch_memmove(syn_buf->b_chartab, syn_win->w_s->b_syn_chartab,
997 (size_t)32);
998 }
999}
1000
1001 static void
1002restore_chartab(char_u *chartab)
1003{
1004 if (syn_win->w_s->b_syn_isk != empty_option)
1005 mch_memmove(syn_buf->b_chartab, chartab, (size_t)32);
1006}
1007
Bram Moolenaar071d4272004-06-13 20:20:40 +00001008/*
1009 * Return TRUE if the line-continuation pattern matches in line "lnum".
1010 */
1011 static int
1012syn_match_linecont(lnum)
1013 linenr_T lnum;
1014{
1015 regmmatch_T regmatch;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001016 int r;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001017 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018
Bram Moolenaar860cae12010-06-05 23:22:07 +02001019 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001021 /* use syntax iskeyword option */
1022 save_chartab(buf_chartab);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001023 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1024 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001025 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001026 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001027 syn_block->b_syn_linecont_prog = regmatch.regprog;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001028 restore_chartab(buf_chartab);
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001029 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001030 }
1031 return FALSE;
1032}
1033
1034/*
1035 * Prepare the current state for the start of a line.
1036 */
1037 static void
1038syn_start_line()
1039{
1040 current_finished = FALSE;
1041 current_col = 0;
1042
1043 /*
1044 * Need to update the end of a start/skip/end that continues from the
1045 * previous line and regions that have "keepend".
1046 */
1047 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001048 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001050 check_state_ends();
1051 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052
1053 next_match_idx = -1;
1054 ++current_line_id;
1055}
1056
1057/*
1058 * Check for items in the stack that need their end updated.
1059 * When "startofline" is TRUE the last item is always updated.
1060 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1061 */
1062 static void
1063syn_update_ends(startofline)
1064 int startofline;
1065{
1066 stateitem_T *cur_si;
1067 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001068 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001069
1070 if (startofline)
1071 {
1072 /* Check for a match carried over from a previous line with a
1073 * contained region. The match ends as soon as the region ends. */
1074 for (i = 0; i < current_state.ga_len; ++i)
1075 {
1076 cur_si = &CUR_STATE(i);
1077 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001078 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001079 == SPTYPE_MATCH
1080 && cur_si->si_m_endpos.lnum < current_lnum)
1081 {
1082 cur_si->si_flags |= HL_MATCHCONT;
1083 cur_si->si_m_endpos.lnum = 0;
1084 cur_si->si_m_endpos.col = 0;
1085 cur_si->si_h_endpos = cur_si->si_m_endpos;
1086 cur_si->si_ends = TRUE;
1087 }
1088 }
1089 }
1090
1091 /*
1092 * Need to update the end of a start/skip/end that continues from the
1093 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001094 * influence contained items. If we've just removed "extend"
1095 * (startofline == 0) then we should update ends of normal regions
1096 * contained inside "keepend" because "extend" could have extended
1097 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098 * Then check for items ending in column 0.
1099 */
1100 i = current_state.ga_len - 1;
1101 if (keepend_level >= 0)
1102 for ( ; i > keepend_level; --i)
1103 if (CUR_STATE(i).si_flags & HL_EXTEND)
1104 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001105
1106 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001107 for ( ; i < current_state.ga_len; ++i)
1108 {
1109 cur_si = &CUR_STATE(i);
1110 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001111 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001112 || (i == current_state.ga_len - 1 && startofline))
1113 {
1114 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1115 cur_si->si_h_startpos.lnum = current_lnum;
1116
1117 if (!(cur_si->si_flags & HL_MATCHCONT))
1118 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001119
1120 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1121 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001122 }
1123 }
1124 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125}
1126
1127/****************************************
1128 * Handling of the state stack cache.
1129 */
1130
1131/*
1132 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1133 *
1134 * To speed up syntax highlighting, the state stack for the start of some
1135 * lines is cached. These entries can be used to start parsing at that point.
1136 *
1137 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1138 * valid entries. b_sst_first points to the first one, then follow sst_next.
1139 * The entries are sorted on line number. The first entry is often for line 2
1140 * (line 1 always starts with an empty stack).
1141 * There is also a list for free entries. This construction is used to avoid
1142 * having to allocate and free memory blocks too often.
1143 *
1144 * When making changes to the buffer, this is logged in b_mod_*. When calling
1145 * update_screen() to update the display, it will call
1146 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1147 * entries. The entries which are inside the changed area are removed,
1148 * because they must be recomputed. Entries below the changed have their line
1149 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1150 * set to indicate that a check must be made if the changed lines would change
1151 * the cached entry.
1152 *
1153 * When later displaying lines, an entry is stored for each line. Displayed
1154 * lines are likely to be displayed again, in which case the state at the
1155 * start of the line is needed.
1156 * For not displayed lines, an entry is stored for every so many lines. These
1157 * entries will be used e.g., when scrolling backwards. The distance between
1158 * entries depends on the number of lines in the buffer. For small buffers
1159 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1160 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1161 */
1162
Bram Moolenaar860cae12010-06-05 23:22:07 +02001163 static void
1164syn_stack_free_block(block)
1165 synblock_T *block;
1166{
1167 synstate_T *p;
1168
1169 if (block->b_sst_array != NULL)
1170 {
1171 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1172 clear_syn_state(p);
1173 vim_free(block->b_sst_array);
1174 block->b_sst_array = NULL;
1175 block->b_sst_len = 0;
1176 }
1177}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001178/*
1179 * Free b_sst_array[] for buffer "buf".
1180 * Used when syntax items changed to force resyncing everywhere.
1181 */
1182 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001183syn_stack_free_all(block)
1184 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186 win_T *wp;
1187
Bram Moolenaar860cae12010-06-05 23:22:07 +02001188 syn_stack_free_block(block);
1189
1190
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191#ifdef FEAT_FOLDING
1192 /* When using "syntax" fold method, must update all folds. */
1193 FOR_ALL_WINDOWS(wp)
1194 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001195 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 foldUpdateAll(wp);
1197 }
1198#endif
1199}
1200
1201/*
1202 * Allocate the syntax state stack for syn_buf when needed.
1203 * If the number of entries in b_sst_array[] is much too big or a bit too
1204 * small, reallocate it.
1205 * Also used to allocate b_sst_array[] for the first time.
1206 */
1207 static void
1208syn_stack_alloc()
1209{
1210 long len;
1211 synstate_T *to, *from;
1212 synstate_T *sstp;
1213
1214 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1215 if (len < SST_MIN_ENTRIES)
1216 len = SST_MIN_ENTRIES;
1217 else if (len > SST_MAX_ENTRIES)
1218 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001219 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 {
1221 /* Allocate 50% too much, to avoid reallocating too often. */
1222 len = syn_buf->b_ml.ml_line_count;
1223 len = (len + len / 2) / SST_DIST + Rows * 2;
1224 if (len < SST_MIN_ENTRIES)
1225 len = SST_MIN_ENTRIES;
1226 else if (len > SST_MAX_ENTRIES)
1227 len = SST_MAX_ENTRIES;
1228
Bram Moolenaar860cae12010-06-05 23:22:07 +02001229 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001230 {
1231 /* When shrinking the array, cleanup the existing stack.
1232 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001233 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 && syn_stack_cleanup())
1235 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1237 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 }
1239
1240 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1241 if (sstp == NULL) /* out of memory! */
1242 return;
1243
1244 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001245 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001246 {
1247 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001248 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 from = from->sst_next)
1250 {
1251 ++to;
1252 *to = *from;
1253 to->sst_next = to + 1;
1254 }
1255 }
1256 if (to != sstp - 1)
1257 {
1258 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001259 syn_block->b_sst_first = sstp;
1260 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 }
1262 else
1263 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 syn_block->b_sst_first = NULL;
1265 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 }
1267
1268 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 while (++to < sstp + len)
1271 to->sst_next = to + 1;
1272 (sstp + len - 1)->sst_next = NULL;
1273
Bram Moolenaar860cae12010-06-05 23:22:07 +02001274 vim_free(syn_block->b_sst_array);
1275 syn_block->b_sst_array = sstp;
1276 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001277 }
1278}
1279
1280/*
1281 * Check for changes in a buffer to affect stored syntax states. Uses the
1282 * b_mod_* fields.
1283 * Called from update_screen(), before screen is being updated, once for each
1284 * displayed buffer.
1285 */
1286 void
1287syn_stack_apply_changes(buf)
1288 buf_T *buf;
1289{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001290 win_T *wp;
1291
1292 syn_stack_apply_changes_block(&buf->b_s, buf);
1293
1294 FOR_ALL_WINDOWS(wp)
1295 {
1296 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1297 syn_stack_apply_changes_block(wp->w_s, buf);
1298 }
1299}
1300
1301 static void
1302syn_stack_apply_changes_block(block, buf)
1303 synblock_T *block;
1304 buf_T *buf;
1305{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 synstate_T *p, *prev, *np;
1307 linenr_T n;
1308
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 return;
1311
1312 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001315 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316 {
1317 n = p->sst_lnum + buf->b_mod_xlines;
1318 if (n <= buf->b_mod_bot)
1319 {
1320 /* this state is inside the changed area, remove it */
1321 np = p->sst_next;
1322 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 else
1325 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001326 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 p = np;
1328 continue;
1329 }
1330 /* This state is below the changed area. Remember the line
1331 * that needs to be parsed before this entry can be made valid
1332 * again. */
1333 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1334 {
1335 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1336 p->sst_change_lnum += buf->b_mod_xlines;
1337 else
1338 p->sst_change_lnum = buf->b_mod_top;
1339 }
1340 if (p->sst_change_lnum == 0
1341 || p->sst_change_lnum < buf->b_mod_bot)
1342 p->sst_change_lnum = buf->b_mod_bot;
1343
1344 p->sst_lnum = n;
1345 }
1346 prev = p;
1347 p = p->sst_next;
1348 }
1349}
1350
1351/*
1352 * Reduce the number of entries in the state stack for syn_buf.
1353 * Returns TRUE if at least one entry was freed.
1354 */
1355 static int
1356syn_stack_cleanup()
1357{
1358 synstate_T *p, *prev;
1359 disptick_T tick;
1360 int above;
1361 int dist;
1362 int retval = FALSE;
1363
Bram Moolenaar860cae12010-06-05 23:22:07 +02001364 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365 return retval;
1366
1367 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001368 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001369 dist = 999999;
1370 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001371 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001372
1373 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001374 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375 * be removed. Set "above" when the "tick" for the oldest entry is above
1376 * "b_sst_lasttick" (the display tick wraps around).
1377 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001378 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001380 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1382 {
1383 if (prev->sst_lnum + dist > p->sst_lnum)
1384 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001385 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 {
1387 if (!above || p->sst_tick < tick)
1388 tick = p->sst_tick;
1389 above = TRUE;
1390 }
1391 else if (!above && p->sst_tick < tick)
1392 tick = p->sst_tick;
1393 }
1394 }
1395
1396 /*
1397 * Go through the list to make the entries for the oldest tick at an
1398 * interval of several lines.
1399 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001400 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1402 {
1403 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1404 {
1405 /* Move this entry from used list to free list */
1406 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001407 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408 p = prev;
1409 retval = TRUE;
1410 }
1411 }
1412 return retval;
1413}
1414
1415/*
1416 * Free the allocated memory for a syn_state item.
1417 * Move the entry into the free list.
1418 */
1419 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001420syn_stack_free_entry(block, p)
1421 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 synstate_T *p;
1423{
1424 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001425 p->sst_next = block->b_sst_firstfree;
1426 block->b_sst_firstfree = p;
1427 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428}
1429
1430/*
1431 * Find an entry in the list of state stacks at or before "lnum".
1432 * Returns NULL when there is no entry or the first entry is after "lnum".
1433 */
1434 static synstate_T *
1435syn_stack_find_entry(lnum)
1436 linenr_T lnum;
1437{
1438 synstate_T *p, *prev;
1439
1440 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001441 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442 {
1443 if (p->sst_lnum == lnum)
1444 return p;
1445 if (p->sst_lnum > lnum)
1446 break;
1447 }
1448 return prev;
1449}
1450
1451/*
1452 * Try saving the current state in b_sst_array[].
1453 * The current state must be valid for the start of the current_lnum line!
1454 */
1455 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001456store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457{
1458 int i;
1459 synstate_T *p;
1460 bufstate_T *bp;
1461 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001462 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001463
1464 /*
1465 * If the current state contains a start or end pattern that continues
1466 * from the previous line, we can't use it. Don't store it then.
1467 */
1468 for (i = current_state.ga_len - 1; i >= 0; --i)
1469 {
1470 cur_si = &CUR_STATE(i);
1471 if (cur_si->si_h_startpos.lnum >= current_lnum
1472 || cur_si->si_m_endpos.lnum >= current_lnum
1473 || cur_si->si_h_endpos.lnum >= current_lnum
1474 || (cur_si->si_end_idx
1475 && cur_si->si_eoe_pos.lnum >= current_lnum))
1476 break;
1477 }
1478 if (i >= 0)
1479 {
1480 if (sp != NULL)
1481 {
1482 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001483 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001484 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001485 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001486 else
1487 {
1488 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001489 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 if (p->sst_next == sp)
1491 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001492 if (p != NULL) /* just in case */
1493 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001495 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 sp = NULL;
1497 }
1498 }
1499 else if (sp == NULL || sp->sst_lnum != current_lnum)
1500 {
1501 /*
1502 * Add a new entry
1503 */
1504 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001505 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 {
1507 (void)syn_stack_cleanup();
1508 /* "sp" may have been moved to the freelist now */
1509 sp = syn_stack_find_entry(current_lnum);
1510 }
1511 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001512 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001513 sp = NULL;
1514 else
1515 {
1516 /* Take the first item from the free list and put it in the used
1517 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001518 p = syn_block->b_sst_firstfree;
1519 syn_block->b_sst_firstfree = p->sst_next;
1520 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521 if (sp == NULL)
1522 {
1523 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001524 p->sst_next = syn_block->b_sst_first;
1525 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 }
1527 else
1528 {
1529 /* insert in list after *sp */
1530 p->sst_next = sp->sst_next;
1531 sp->sst_next = p;
1532 }
1533 sp = p;
1534 sp->sst_stacksize = 0;
1535 sp->sst_lnum = current_lnum;
1536 }
1537 }
1538 if (sp != NULL)
1539 {
1540 /* When overwriting an existing state stack, clear it first */
1541 clear_syn_state(sp);
1542 sp->sst_stacksize = current_state.ga_len;
1543 if (current_state.ga_len > SST_FIX_STATES)
1544 {
1545 /* Need to clear it, might be something remaining from when the
1546 * length was less than SST_FIX_STATES. */
1547 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1548 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1549 sp->sst_stacksize = 0;
1550 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001551 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001552 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1553 }
1554 else
1555 bp = sp->sst_union.sst_stack;
1556 for (i = 0; i < sp->sst_stacksize; ++i)
1557 {
1558 bp[i].bs_idx = CUR_STATE(i).si_idx;
1559 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001560#ifdef FEAT_CONCEAL
1561 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1562 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1563#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1565 }
1566 sp->sst_next_flags = current_next_flags;
1567 sp->sst_next_list = current_next_list;
1568 sp->sst_tick = display_tick;
1569 sp->sst_change_lnum = 0;
1570 }
1571 current_state_stored = TRUE;
1572 return sp;
1573}
1574
1575/*
1576 * Copy a state stack from "from" in b_sst_array[] to current_state;
1577 */
1578 static void
1579load_current_state(from)
1580 synstate_T *from;
1581{
1582 int i;
1583 bufstate_T *bp;
1584
1585 clear_current_state();
1586 validate_current_state();
1587 keepend_level = -1;
1588 if (from->sst_stacksize
1589 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1590 {
1591 if (from->sst_stacksize > SST_FIX_STATES)
1592 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1593 else
1594 bp = from->sst_union.sst_stack;
1595 for (i = 0; i < from->sst_stacksize; ++i)
1596 {
1597 CUR_STATE(i).si_idx = bp[i].bs_idx;
1598 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001599#ifdef FEAT_CONCEAL
1600 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1601 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1602#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1604 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1605 keepend_level = i;
1606 CUR_STATE(i).si_ends = FALSE;
1607 CUR_STATE(i).si_m_lnum = 0;
1608 if (CUR_STATE(i).si_idx >= 0)
1609 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001610 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001611 else
1612 CUR_STATE(i).si_next_list = NULL;
1613 update_si_attr(i);
1614 }
1615 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 }
1617 current_next_list = from->sst_next_list;
1618 current_next_flags = from->sst_next_flags;
1619 current_lnum = from->sst_lnum;
1620}
1621
1622/*
1623 * Compare saved state stack "*sp" with the current state.
1624 * Return TRUE when they are equal.
1625 */
1626 static int
1627syn_stack_equal(sp)
1628 synstate_T *sp;
1629{
1630 int i, j;
1631 bufstate_T *bp;
1632 reg_extmatch_T *six, *bsx;
1633
1634 /* First a quick check if the stacks have the same size end nextlist. */
1635 if (sp->sst_stacksize == current_state.ga_len
1636 && sp->sst_next_list == current_next_list)
1637 {
1638 /* Need to compare all states on both stacks. */
1639 if (sp->sst_stacksize > SST_FIX_STATES)
1640 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1641 else
1642 bp = sp->sst_union.sst_stack;
1643
1644 for (i = current_state.ga_len; --i >= 0; )
1645 {
1646 /* If the item has another index the state is different. */
1647 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1648 break;
1649 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1650 {
1651 /* When the extmatch pointers are different, the strings in
1652 * them can still be the same. Check if the extmatch
1653 * references are equal. */
1654 bsx = bp[i].bs_extmatch;
1655 six = CUR_STATE(i).si_extmatch;
1656 /* If one of the extmatch pointers is NULL the states are
1657 * different. */
1658 if (bsx == NULL || six == NULL)
1659 break;
1660 for (j = 0; j < NSUBEXP; ++j)
1661 {
1662 /* Check each referenced match string. They must all be
1663 * equal. */
1664 if (bsx->matches[j] != six->matches[j])
1665 {
1666 /* If the pointer is different it can still be the
1667 * same text. Compare the strings, ignore case when
1668 * the start item has the sp_ic flag set. */
1669 if (bsx->matches[j] == NULL
1670 || six->matches[j] == NULL)
1671 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001672 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673 ? MB_STRICMP(bsx->matches[j],
1674 six->matches[j]) != 0
1675 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1676 break;
1677 }
1678 }
1679 if (j != NSUBEXP)
1680 break;
1681 }
1682 }
1683 if (i < 0)
1684 return TRUE;
1685 }
1686 return FALSE;
1687}
1688
1689/*
1690 * We stop parsing syntax above line "lnum". If the stored state at or below
1691 * this line depended on a change before it, it now depends on the line below
1692 * the last parsed line.
1693 * The window looks like this:
1694 * line which changed
1695 * displayed line
1696 * displayed line
1697 * lnum -> line below window
1698 */
1699 void
1700syntax_end_parsing(lnum)
1701 linenr_T lnum;
1702{
1703 synstate_T *sp;
1704
1705 sp = syn_stack_find_entry(lnum);
1706 if (sp != NULL && sp->sst_lnum < lnum)
1707 sp = sp->sst_next;
1708
1709 if (sp != NULL && sp->sst_change_lnum != 0)
1710 sp->sst_change_lnum = lnum;
1711}
1712
1713/*
1714 * End of handling of the state stack.
1715 ****************************************/
1716
1717 static void
1718invalidate_current_state()
1719{
1720 clear_current_state();
1721 current_state.ga_itemsize = 0; /* mark current_state invalid */
1722 current_next_list = NULL;
1723 keepend_level = -1;
1724}
1725
1726 static void
1727validate_current_state()
1728{
1729 current_state.ga_itemsize = sizeof(stateitem_T);
1730 current_state.ga_growsize = 3;
1731}
1732
1733/*
1734 * Return TRUE if the syntax at start of lnum changed since last time.
1735 * This will only be called just after get_syntax_attr() for the previous
1736 * line, to check if the next line needs to be redrawn too.
1737 */
1738 int
1739syntax_check_changed(lnum)
1740 linenr_T lnum;
1741{
1742 int retval = TRUE;
1743 synstate_T *sp;
1744
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 /*
1746 * Check the state stack when:
1747 * - lnum is just below the previously syntaxed line.
1748 * - lnum is not before the lines with saved states.
1749 * - lnum is not past the lines with saved states.
1750 * - lnum is at or before the last changed line.
1751 */
1752 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1753 {
1754 sp = syn_stack_find_entry(lnum);
1755 if (sp != NULL && sp->sst_lnum == lnum)
1756 {
1757 /*
1758 * finish the previous line (needed when not all of the line was
1759 * drawn)
1760 */
1761 (void)syn_finish_line(FALSE);
1762
1763 /*
1764 * Compare the current state with the previously saved state of
1765 * the line.
1766 */
1767 if (syn_stack_equal(sp))
1768 retval = FALSE;
1769
1770 /*
1771 * Store the current state in b_sst_array[] for later use.
1772 */
1773 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001774 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001775 }
1776 }
1777
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 return retval;
1779}
1780
1781/*
1782 * Finish the current line.
1783 * This doesn't return any attributes, it only gets the state at the end of
1784 * the line. It can start anywhere in the line, as long as the current state
1785 * is valid.
1786 */
1787 static int
1788syn_finish_line(syncing)
1789 int syncing; /* called for syncing */
1790{
1791 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001792 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793
1794 if (!current_finished)
1795 {
1796 while (!current_finished)
1797 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001798 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 /*
1800 * When syncing, and found some item, need to check the item.
1801 */
1802 if (syncing && current_state.ga_len)
1803 {
1804 /*
1805 * Check for match with sync item.
1806 */
1807 cur_si = &CUR_STATE(current_state.ga_len - 1);
1808 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001809 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1811 return TRUE;
1812
1813 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001814 * that ends here, need to do that now. Be careful not to go
1815 * past the NUL. */
1816 prev_current_col = current_col;
1817 if (syn_getcurline()[current_col] != NUL)
1818 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001820 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821 }
1822 ++current_col;
1823 }
1824 }
1825 return FALSE;
1826}
1827
1828/*
1829 * Return highlight attributes for next character.
1830 * Must first call syntax_start() once for the line.
1831 * "col" is normally 0 for the first use in a line, and increments by one each
1832 * time. It's allowed to skip characters and to stop before the end of the
1833 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001834 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1835 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001836 */
1837 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001838get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001840 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001841 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842{
1843 int attr = 0;
1844
Bram Moolenaar349955a2007-08-14 21:07:36 +00001845 if (can_spell != NULL)
1846 /* Default: Only do spelling when there is no @Spell cluster or when
1847 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001848 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1849 ? (syn_block->b_spell_cluster_id == 0)
1850 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001851
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001853 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 return 0;
1855
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001856 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001857 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001858 {
1859 clear_current_state();
1860#ifdef FEAT_EVAL
1861 current_id = 0;
1862 current_trans_id = 0;
1863#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001864#ifdef FEAT_CONCEAL
1865 current_flags = 0;
1866#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001867 return 0;
1868 }
1869
Bram Moolenaar071d4272004-06-13 20:20:40 +00001870 /* Make sure current_state is valid */
1871 if (INVALID_STATE(&current_state))
1872 validate_current_state();
1873
1874 /*
1875 * Skip from the current column to "col", get the attributes for "col".
1876 */
1877 while (current_col <= col)
1878 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001879 attr = syn_current_attr(FALSE, TRUE, can_spell,
1880 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001881 ++current_col;
1882 }
1883
Bram Moolenaar071d4272004-06-13 20:20:40 +00001884 return attr;
1885}
1886
1887/*
1888 * Get syntax attributes for current_lnum, current_col.
1889 */
1890 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001891syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 int syncing; /* When 1: called for syncing */
1893 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001894 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001895 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896{
1897 int syn_id;
1898 lpos_T endpos; /* was: char_u *endp; */
1899 lpos_T hl_startpos; /* was: int hl_startcol; */
1900 lpos_T hl_endpos;
1901 lpos_T eos_pos; /* end-of-start match (start region) */
1902 lpos_T eoe_pos; /* end-of-end pattern */
1903 int end_idx; /* group ID for end pattern */
1904 int idx;
1905 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001906 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001907 int startcol;
1908 int endcol;
1909 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001910 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911 short *next_list;
1912 int found_match; /* found usable match */
1913 static int try_next_column = FALSE; /* must try in next col */
1914 int do_keywords;
1915 regmmatch_T regmatch;
1916 lpos_T pos;
1917 int lc_col;
1918 reg_extmatch_T *cur_extmatch = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001919 char_u buf_chartab[32]; /* chartab array for syn iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001920 char_u *line; /* current line. NOTE: becomes invalid after
1921 looking for a pattern match! */
1922
1923 /* variables for zero-width matches that have a "nextgroup" argument */
1924 int keep_next_list;
1925 int zero_width_next_list = FALSE;
1926 garray_T zero_width_next_ga;
1927
1928 /*
1929 * No character, no attributes! Past end of line?
1930 * Do try matching with an empty line (could be the start of a region).
1931 */
1932 line = syn_getcurline();
1933 if (line[current_col] == NUL && current_col != 0)
1934 {
1935 /*
1936 * If we found a match after the last column, use it.
1937 */
1938 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1939 && next_match_col != MAXCOL)
1940 (void)push_next_match(NULL);
1941
1942 current_finished = TRUE;
1943 current_state_stored = FALSE;
1944 return 0;
1945 }
1946
1947 /* if the current or next character is NUL, we will finish the line now */
1948 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1949 {
1950 current_finished = TRUE;
1951 current_state_stored = FALSE;
1952 }
1953
1954 /*
1955 * When in the previous column there was a match but it could not be used
1956 * (empty match or already matched in this column) need to try again in
1957 * the next column.
1958 */
1959 if (try_next_column)
1960 {
1961 next_match_idx = -1;
1962 try_next_column = FALSE;
1963 }
1964
1965 /* Only check for keywords when not syncing and there are some. */
1966 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001967 && (syn_block->b_keywtab.ht_used > 0
1968 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969
1970 /* Init the list of zero-width matches with a nextlist. This is used to
1971 * avoid matching the same item in the same position twice. */
1972 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1973
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001974 /* use syntax iskeyword option */
1975 save_chartab(buf_chartab);
1976
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977 /*
1978 * Repeat matching keywords and patterns, to find contained items at the
1979 * same column. This stops when there are no extra matches at the current
1980 * column.
1981 */
1982 do
1983 {
1984 found_match = FALSE;
1985 keep_next_list = FALSE;
1986 syn_id = 0;
1987
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01001988
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989 /*
1990 * 1. Check for a current state.
1991 * Only when there is no current state, or if the current state may
1992 * contain other things, we need to check for keywords and patterns.
1993 * Always need to check for contained items if some item has the
1994 * "containedin" argument (takes extra time!).
1995 */
1996 if (current_state.ga_len)
1997 cur_si = &CUR_STATE(current_state.ga_len - 1);
1998 else
1999 cur_si = NULL;
2000
Bram Moolenaar860cae12010-06-05 23:22:07 +02002001 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002002 || cur_si->si_cont_list != NULL)
2003 {
2004 /*
2005 * 2. Check for keywords, if on a keyword char after a non-keyword
2006 * char. Don't do this when syncing.
2007 */
2008 if (do_keywords)
2009 {
2010 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002011 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01002013 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014#ifdef FEAT_MBYTE
2015 - (has_mbyte
2016 ? (*mb_head_off)(line, line + current_col - 1)
2017 : 0)
2018#endif
2019 , syn_buf)))
2020 {
2021 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02002022 &endcol, &flags, &next_list, cur_si,
2023 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002024 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025 {
2026 if (push_current_state(KEYWORD_IDX) == OK)
2027 {
2028 cur_si = &CUR_STATE(current_state.ga_len - 1);
2029 cur_si->si_m_startcol = current_col;
2030 cur_si->si_h_startpos.lnum = current_lnum;
2031 cur_si->si_h_startpos.col = 0; /* starts right away */
2032 cur_si->si_m_endpos.lnum = current_lnum;
2033 cur_si->si_m_endpos.col = endcol;
2034 cur_si->si_h_endpos.lnum = current_lnum;
2035 cur_si->si_h_endpos.col = endcol;
2036 cur_si->si_ends = TRUE;
2037 cur_si->si_end_idx = 0;
2038 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002039#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002040 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002041 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002042 if (current_state.ga_len > 1)
2043 cur_si->si_flags |=
2044 CUR_STATE(current_state.ga_len - 2).si_flags
2045 & HL_CONCEAL;
2046#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047 cur_si->si_id = syn_id;
2048 cur_si->si_trans_id = syn_id;
2049 if (flags & HL_TRANSP)
2050 {
2051 if (current_state.ga_len < 2)
2052 {
2053 cur_si->si_attr = 0;
2054 cur_si->si_trans_id = 0;
2055 }
2056 else
2057 {
2058 cur_si->si_attr = CUR_STATE(
2059 current_state.ga_len - 2).si_attr;
2060 cur_si->si_trans_id = CUR_STATE(
2061 current_state.ga_len - 2).si_trans_id;
2062 }
2063 }
2064 else
2065 cur_si->si_attr = syn_id2attr(syn_id);
2066 cur_si->si_cont_list = NULL;
2067 cur_si->si_next_list = next_list;
2068 check_keepend();
2069 }
2070 else
2071 vim_free(next_list);
2072 }
2073 }
2074 }
2075
2076 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002077 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002078 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002079 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 {
2081 /*
2082 * If we didn't check for a match yet, or we are past it, check
2083 * for any match with a pattern.
2084 */
2085 if (next_match_idx < 0 || next_match_col < (int)current_col)
2086 {
2087 /*
2088 * Check all relevant patterns for a match at this
2089 * position. This is complicated, because matching with a
2090 * pattern takes quite a bit of time, thus we want to
2091 * avoid doing it when it's not needed.
2092 */
2093 next_match_idx = 0; /* no match in this line yet */
2094 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002095 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002097 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 if ( spp->sp_syncing == syncing
2099 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2100 && (spp->sp_type == SPTYPE_MATCH
2101 || spp->sp_type == SPTYPE_START)
2102 && (current_next_list != NULL
2103 ? in_id_list(NULL, current_next_list,
2104 &spp->sp_syn, 0)
2105 : (cur_si == NULL
2106 ? !(spp->sp_flags & HL_CONTAINED)
2107 : in_id_list(cur_si,
2108 cur_si->si_cont_list, &spp->sp_syn,
2109 spp->sp_flags & HL_CONTAINED))))
2110 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002111 int r;
2112
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113 /* If we already tried matching in this line, and
2114 * there isn't a match before next_match_col, skip
2115 * this item. */
2116 if (spp->sp_line_id == current_line_id
2117 && spp->sp_startcol >= next_match_col)
2118 continue;
2119 spp->sp_line_id = current_line_id;
2120
2121 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2122 if (lc_col < 0)
2123 lc_col = 0;
2124
2125 regmatch.rmm_ic = spp->sp_ic;
2126 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002127 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002128 current_lnum,
2129 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002130 IF_SYN_TIME(&spp->sp_time));
2131 spp->sp_prog = regmatch.regprog;
2132 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133 {
2134 /* no match in this line, try another one */
2135 spp->sp_startcol = MAXCOL;
2136 continue;
2137 }
2138
2139 /*
2140 * Compute the first column of the match.
2141 */
2142 syn_add_start_off(&pos, &regmatch,
2143 spp, SPO_MS_OFF, -1);
2144 if (pos.lnum > current_lnum)
2145 {
2146 /* must have used end of match in a next line,
2147 * we can't handle that */
2148 spp->sp_startcol = MAXCOL;
2149 continue;
2150 }
2151 startcol = pos.col;
2152
2153 /* remember the next column where this pattern
2154 * matches in the current line */
2155 spp->sp_startcol = startcol;
2156
2157 /*
2158 * If a previously found match starts at a lower
2159 * column number, don't use this one.
2160 */
2161 if (startcol >= next_match_col)
2162 continue;
2163
2164 /*
2165 * If we matched this pattern at this position
2166 * before, skip it. Must retry in the next
2167 * column, because it may match from there.
2168 */
2169 if (did_match_already(idx, &zero_width_next_ga))
2170 {
2171 try_next_column = TRUE;
2172 continue;
2173 }
2174
2175 endpos.lnum = regmatch.endpos[0].lnum;
2176 endpos.col = regmatch.endpos[0].col;
2177
2178 /* Compute the highlight start. */
2179 syn_add_start_off(&hl_startpos, &regmatch,
2180 spp, SPO_HS_OFF, -1);
2181
2182 /* Compute the region start. */
2183 /* Default is to use the end of the match. */
2184 syn_add_end_off(&eos_pos, &regmatch,
2185 spp, SPO_RS_OFF, 0);
2186
2187 /*
2188 * Grab the external submatches before they get
2189 * overwritten. Reference count doesn't change.
2190 */
2191 unref_extmatch(cur_extmatch);
2192 cur_extmatch = re_extmatch_out;
2193 re_extmatch_out = NULL;
2194
2195 flags = 0;
2196 eoe_pos.lnum = 0; /* avoid warning */
2197 eoe_pos.col = 0;
2198 end_idx = 0;
2199 hl_endpos.lnum = 0;
2200
2201 /*
2202 * For a "oneline" the end must be found in the
2203 * same line too. Search for it after the end of
2204 * the match with the start pattern. Set the
2205 * resulting end positions at the same time.
2206 */
2207 if (spp->sp_type == SPTYPE_START
2208 && (spp->sp_flags & HL_ONELINE))
2209 {
2210 lpos_T startpos;
2211
2212 startpos = endpos;
2213 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2214 &flags, &eoe_pos, &end_idx, cur_extmatch);
2215 if (endpos.lnum == 0)
2216 continue; /* not found */
2217 }
2218
2219 /*
2220 * For a "match" the size must be > 0 after the
2221 * end offset needs has been added. Except when
2222 * syncing.
2223 */
2224 else if (spp->sp_type == SPTYPE_MATCH)
2225 {
2226 syn_add_end_off(&hl_endpos, &regmatch, spp,
2227 SPO_HE_OFF, 0);
2228 syn_add_end_off(&endpos, &regmatch, spp,
2229 SPO_ME_OFF, 0);
2230 if (endpos.lnum == current_lnum
2231 && (int)endpos.col + syncing < startcol)
2232 {
2233 /*
2234 * If an empty string is matched, may need
2235 * to try matching again at next column.
2236 */
2237 if (regmatch.startpos[0].col
2238 == regmatch.endpos[0].col)
2239 try_next_column = TRUE;
2240 continue;
2241 }
2242 }
2243
2244 /*
2245 * keep the best match so far in next_match_*
2246 */
2247 /* Highlighting must start after startpos and end
2248 * before endpos. */
2249 if (hl_startpos.lnum == current_lnum
2250 && (int)hl_startpos.col < startcol)
2251 hl_startpos.col = startcol;
2252 limit_pos_zero(&hl_endpos, &endpos);
2253
2254 next_match_idx = idx;
2255 next_match_col = startcol;
2256 next_match_m_endpos = endpos;
2257 next_match_h_endpos = hl_endpos;
2258 next_match_h_startpos = hl_startpos;
2259 next_match_flags = flags;
2260 next_match_eos_pos = eos_pos;
2261 next_match_eoe_pos = eoe_pos;
2262 next_match_end_idx = end_idx;
2263 unref_extmatch(next_match_extmatch);
2264 next_match_extmatch = cur_extmatch;
2265 cur_extmatch = NULL;
2266 }
2267 }
2268 }
2269
2270 /*
2271 * If we found a match at the current column, use it.
2272 */
2273 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2274 {
2275 synpat_T *lspp;
2276
2277 /* When a zero-width item matched which has a nextgroup,
2278 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002279 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280 if (next_match_m_endpos.lnum == current_lnum
2281 && next_match_m_endpos.col == current_col
2282 && lspp->sp_next_list != NULL)
2283 {
2284 current_next_list = lspp->sp_next_list;
2285 current_next_flags = lspp->sp_flags;
2286 keep_next_list = TRUE;
2287 zero_width_next_list = TRUE;
2288
2289 /* Add the index to a list, so that we can check
2290 * later that we don't match it again (and cause an
2291 * endless loop). */
2292 if (ga_grow(&zero_width_next_ga, 1) == OK)
2293 {
2294 ((int *)(zero_width_next_ga.ga_data))
2295 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002296 }
2297 next_match_idx = -1;
2298 }
2299 else
2300 cur_si = push_next_match(cur_si);
2301 found_match = TRUE;
2302 }
2303 }
2304 }
2305
2306 /*
2307 * Handle searching for nextgroup match.
2308 */
2309 if (current_next_list != NULL && !keep_next_list)
2310 {
2311 /*
2312 * If a nextgroup was not found, continue looking for one if:
2313 * - this is an empty line and the "skipempty" option was given
2314 * - we are on white space and the "skipwhite" option was given
2315 */
2316 if (!found_match)
2317 {
2318 line = syn_getcurline();
2319 if (((current_next_flags & HL_SKIPWHITE)
2320 && vim_iswhite(line[current_col]))
2321 || ((current_next_flags & HL_SKIPEMPTY)
2322 && *line == NUL))
2323 break;
2324 }
2325
2326 /*
2327 * If a nextgroup was found: Use it, and continue looking for
2328 * contained matches.
2329 * If a nextgroup was not found: Continue looking for a normal
2330 * match.
2331 * When did set current_next_list for a zero-width item and no
2332 * match was found don't loop (would get stuck).
2333 */
2334 current_next_list = NULL;
2335 next_match_idx = -1;
2336 if (!zero_width_next_list)
2337 found_match = TRUE;
2338 }
2339
2340 } while (found_match);
2341
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002342 restore_chartab(buf_chartab);
2343
Bram Moolenaar071d4272004-06-13 20:20:40 +00002344 /*
2345 * Use attributes from the current state, if within its highlighting.
2346 * If not, use attributes from the current-but-one state, etc.
2347 */
2348 current_attr = 0;
2349#ifdef FEAT_EVAL
2350 current_id = 0;
2351 current_trans_id = 0;
2352#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002353#ifdef FEAT_CONCEAL
2354 current_flags = 0;
2355#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002356 if (cur_si != NULL)
2357 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002358#ifndef FEAT_EVAL
2359 int current_trans_id = 0;
2360#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002361 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2362 {
2363 sip = &CUR_STATE(idx);
2364 if ((current_lnum > sip->si_h_startpos.lnum
2365 || (current_lnum == sip->si_h_startpos.lnum
2366 && current_col >= sip->si_h_startpos.col))
2367 && (sip->si_h_endpos.lnum == 0
2368 || current_lnum < sip->si_h_endpos.lnum
2369 || (current_lnum == sip->si_h_endpos.lnum
2370 && current_col < sip->si_h_endpos.col)))
2371 {
2372 current_attr = sip->si_attr;
2373#ifdef FEAT_EVAL
2374 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002375#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002376 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002377#ifdef FEAT_CONCEAL
2378 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002379 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002380 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002381#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 break;
2383 }
2384 }
2385
Bram Moolenaar217ad922005-03-20 22:37:15 +00002386 if (can_spell != NULL)
2387 {
2388 struct sp_syn sps;
2389
2390 /*
2391 * set "can_spell" to TRUE if spell checking is supposed to be
2392 * done in the current item.
2393 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002394 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002395 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002396 /* There is no @Spell cluster: Do spelling for items without
2397 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002398 if (syn_block->b_nospell_cluster_id == 0
2399 || current_trans_id == 0)
2400 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002401 else
2402 {
2403 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002404 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002405 sps.cont_in_list = NULL;
2406 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2407 }
2408 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002409 else
2410 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002411 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002412 * the @Spell cluster. But not when @NoSpell is also there.
2413 * At the toplevel only spell check when ":syn spell toplevel"
2414 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002415 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002416 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002417 else
2418 {
2419 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002420 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002421 sps.cont_in_list = NULL;
2422 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2423
Bram Moolenaar860cae12010-06-05 23:22:07 +02002424 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002425 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002426 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002427 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2428 *can_spell = FALSE;
2429 }
2430 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002431 }
2432 }
2433
2434
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435 /*
2436 * Check for end of current state (and the states before it) at the
2437 * next column. Don't do this for syncing, because we would miss a
2438 * single character match.
2439 * First check if the current state ends at the current column. It
2440 * may be for an empty match and a containing item might end in the
2441 * current column.
2442 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002443 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002444 {
2445 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002446 if (current_state.ga_len > 0
2447 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 {
2449 ++current_col;
2450 check_state_ends();
2451 --current_col;
2452 }
2453 }
2454 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002455 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002456 /* Default: Only do spelling when there is no @Spell cluster or when
2457 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002458 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2459 ? (syn_block->b_spell_cluster_id == 0)
2460 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002461
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002462 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002463 if (current_next_list != NULL
2464 && syn_getcurline()[current_col + 1] == NUL
2465 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2466 current_next_list = NULL;
2467
2468 if (zero_width_next_ga.ga_len > 0)
2469 ga_clear(&zero_width_next_ga);
2470
2471 /* No longer need external matches. But keep next_match_extmatch. */
2472 unref_extmatch(re_extmatch_out);
2473 re_extmatch_out = NULL;
2474 unref_extmatch(cur_extmatch);
2475
2476 return current_attr;
2477}
2478
2479
2480/*
2481 * Check if we already matched pattern "idx" at the current column.
2482 */
2483 static int
2484did_match_already(idx, gap)
2485 int idx;
2486 garray_T *gap;
2487{
2488 int i;
2489
2490 for (i = current_state.ga_len; --i >= 0; )
2491 if (CUR_STATE(i).si_m_startcol == (int)current_col
2492 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2493 && CUR_STATE(i).si_idx == idx)
2494 return TRUE;
2495
2496 /* Zero-width matches with a nextgroup argument are not put on the syntax
2497 * stack, and can only be matched once anyway. */
2498 for (i = gap->ga_len; --i >= 0; )
2499 if (((int *)(gap->ga_data))[i] == idx)
2500 return TRUE;
2501
2502 return FALSE;
2503}
2504
2505/*
2506 * Push the next match onto the stack.
2507 */
2508 static stateitem_T *
2509push_next_match(cur_si)
2510 stateitem_T *cur_si;
2511{
2512 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002513#ifdef FEAT_CONCEAL
2514 int save_flags;
2515#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002516
Bram Moolenaar860cae12010-06-05 23:22:07 +02002517 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518
2519 /*
2520 * Push the item in current_state stack;
2521 */
2522 if (push_current_state(next_match_idx) == OK)
2523 {
2524 /*
2525 * If it's a start-skip-end type that crosses lines, figure out how
2526 * much it continues in this line. Otherwise just fill in the length.
2527 */
2528 cur_si = &CUR_STATE(current_state.ga_len - 1);
2529 cur_si->si_h_startpos = next_match_h_startpos;
2530 cur_si->si_m_startcol = current_col;
2531 cur_si->si_m_lnum = current_lnum;
2532 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002533#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002534 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002535 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002536 if (current_state.ga_len > 1)
2537 cur_si->si_flags |=
2538 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2539#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002540 cur_si->si_next_list = spp->sp_next_list;
2541 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2542 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2543 {
2544 /* Try to find the end pattern in the current line */
2545 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2546 check_keepend();
2547 }
2548 else
2549 {
2550 cur_si->si_m_endpos = next_match_m_endpos;
2551 cur_si->si_h_endpos = next_match_h_endpos;
2552 cur_si->si_ends = TRUE;
2553 cur_si->si_flags |= next_match_flags;
2554 cur_si->si_eoe_pos = next_match_eoe_pos;
2555 cur_si->si_end_idx = next_match_end_idx;
2556 }
2557 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2558 keepend_level = current_state.ga_len - 1;
2559 check_keepend();
2560 update_si_attr(current_state.ga_len - 1);
2561
Bram Moolenaar860cae12010-06-05 23:22:07 +02002562#ifdef FEAT_CONCEAL
2563 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2564#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002565 /*
2566 * If the start pattern has another highlight group, push another item
2567 * on the stack for the start pattern.
2568 */
2569 if ( spp->sp_type == SPTYPE_START
2570 && spp->sp_syn_match_id != 0
2571 && push_current_state(next_match_idx) == OK)
2572 {
2573 cur_si = &CUR_STATE(current_state.ga_len - 1);
2574 cur_si->si_h_startpos = next_match_h_startpos;
2575 cur_si->si_m_startcol = current_col;
2576 cur_si->si_m_lnum = current_lnum;
2577 cur_si->si_m_endpos = next_match_eos_pos;
2578 cur_si->si_h_endpos = next_match_eos_pos;
2579 cur_si->si_ends = TRUE;
2580 cur_si->si_end_idx = 0;
2581 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002582#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002583 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002584 cur_si->si_flags |= save_flags;
2585 if (cur_si->si_flags & HL_CONCEALENDS)
2586 cur_si->si_flags |= HL_CONCEAL;
2587#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002588 cur_si->si_next_list = NULL;
2589 check_keepend();
2590 update_si_attr(current_state.ga_len - 1);
2591 }
2592 }
2593
2594 next_match_idx = -1; /* try other match next time */
2595
2596 return cur_si;
2597}
2598
2599/*
2600 * Check for end of current state (and the states before it).
2601 */
2602 static void
2603check_state_ends()
2604{
2605 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002606 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002607
2608 cur_si = &CUR_STATE(current_state.ga_len - 1);
2609 for (;;)
2610 {
2611 if (cur_si->si_ends
2612 && (cur_si->si_m_endpos.lnum < current_lnum
2613 || (cur_si->si_m_endpos.lnum == current_lnum
2614 && cur_si->si_m_endpos.col <= current_col)))
2615 {
2616 /*
2617 * If there is an end pattern group ID, highlight the end pattern
2618 * now. No need to pop the current item from the stack.
2619 * Only do this if the end pattern continues beyond the current
2620 * position.
2621 */
2622 if (cur_si->si_end_idx
2623 && (cur_si->si_eoe_pos.lnum > current_lnum
2624 || (cur_si->si_eoe_pos.lnum == current_lnum
2625 && cur_si->si_eoe_pos.col > current_col)))
2626 {
2627 cur_si->si_idx = cur_si->si_end_idx;
2628 cur_si->si_end_idx = 0;
2629 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2630 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2631 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002632#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002633 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002634 if (cur_si->si_flags & HL_CONCEALENDS)
2635 cur_si->si_flags |= HL_CONCEAL;
2636#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002637 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002638
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002639 /* nextgroup= should not match in the end pattern */
2640 current_next_list = NULL;
2641
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002642 /* what matches next may be different now, clear it */
2643 next_match_idx = 0;
2644 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002645 break;
2646 }
2647 else
2648 {
2649 /* handle next_list, unless at end of line and no "skipnl" or
2650 * "skipempty" */
2651 current_next_list = cur_si->si_next_list;
2652 current_next_flags = cur_si->si_flags;
2653 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2654 && syn_getcurline()[current_col] == NUL)
2655 current_next_list = NULL;
2656
2657 /* When the ended item has "extend", another item with
2658 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002659 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660
2661 pop_current_state();
2662
2663 if (current_state.ga_len == 0)
2664 break;
2665
Bram Moolenaar81993f42008-01-11 20:27:45 +00002666 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002667 {
2668 syn_update_ends(FALSE);
2669 if (current_state.ga_len == 0)
2670 break;
2671 }
2672
2673 cur_si = &CUR_STATE(current_state.ga_len - 1);
2674
2675 /*
2676 * Only for a region the search for the end continues after
2677 * the end of the contained item. If the contained match
2678 * included the end-of-line, break here, the region continues.
2679 * Don't do this when:
2680 * - "keepend" is used for the contained item
2681 * - not at the end of the line (could be end="x$"me=e-1).
2682 * - "excludenl" is used (HL_HAS_EOL won't be set)
2683 */
2684 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002685 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002686 == SPTYPE_START
2687 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2688 {
2689 update_si_end(cur_si, (int)current_col, TRUE);
2690 check_keepend();
2691 if ((current_next_flags & HL_HAS_EOL)
2692 && keepend_level < 0
2693 && syn_getcurline()[current_col] == NUL)
2694 break;
2695 }
2696 }
2697 }
2698 else
2699 break;
2700 }
2701}
2702
2703/*
2704 * Update an entry in the current_state stack for a match or region. This
2705 * fills in si_attr, si_next_list and si_cont_list.
2706 */
2707 static void
2708update_si_attr(idx)
2709 int idx;
2710{
2711 stateitem_T *sip = &CUR_STATE(idx);
2712 synpat_T *spp;
2713
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002714 /* This should not happen... */
2715 if (sip->si_idx < 0)
2716 return;
2717
Bram Moolenaar860cae12010-06-05 23:22:07 +02002718 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719 if (sip->si_flags & HL_MATCH)
2720 sip->si_id = spp->sp_syn_match_id;
2721 else
2722 sip->si_id = spp->sp_syn.id;
2723 sip->si_attr = syn_id2attr(sip->si_id);
2724 sip->si_trans_id = sip->si_id;
2725 if (sip->si_flags & HL_MATCH)
2726 sip->si_cont_list = NULL;
2727 else
2728 sip->si_cont_list = spp->sp_cont_list;
2729
2730 /*
2731 * For transparent items, take attr from outer item.
2732 * Also take cont_list, if there is none.
2733 * Don't do this for the matchgroup of a start or end pattern.
2734 */
2735 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2736 {
2737 if (idx == 0)
2738 {
2739 sip->si_attr = 0;
2740 sip->si_trans_id = 0;
2741 if (sip->si_cont_list == NULL)
2742 sip->si_cont_list = ID_LIST_ALL;
2743 }
2744 else
2745 {
2746 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2747 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002748 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2749 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750 if (sip->si_cont_list == NULL)
2751 {
2752 sip->si_flags |= HL_TRANS_CONT;
2753 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2754 }
2755 }
2756 }
2757}
2758
2759/*
2760 * Check the current stack for patterns with "keepend" flag.
2761 * Propagate the match-end to contained items, until a "skipend" item is found.
2762 */
2763 static void
2764check_keepend()
2765{
2766 int i;
2767 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002768 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769 stateitem_T *sip;
2770
2771 /*
2772 * This check can consume a lot of time; only do it from the level where
2773 * there really is a keepend.
2774 */
2775 if (keepend_level < 0)
2776 return;
2777
2778 /*
2779 * Find the last index of an "extend" item. "keepend" items before that
2780 * won't do anything. If there is no "extend" item "i" will be
2781 * "keepend_level" and all "keepend" items will work normally.
2782 */
2783 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2784 if (CUR_STATE(i).si_flags & HL_EXTEND)
2785 break;
2786
2787 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002788 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002789 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002790 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 for ( ; i < current_state.ga_len; ++i)
2792 {
2793 sip = &CUR_STATE(i);
2794 if (maxpos.lnum != 0)
2795 {
2796 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002797 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002798 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2799 sip->si_ends = TRUE;
2800 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002801 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2802 {
2803 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 || maxpos.lnum > sip->si_m_endpos.lnum
2805 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002806 && maxpos.col > sip->si_m_endpos.col))
2807 maxpos = sip->si_m_endpos;
2808 if (maxpos_h.lnum == 0
2809 || maxpos_h.lnum > sip->si_h_endpos.lnum
2810 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2811 && maxpos_h.col > sip->si_h_endpos.col))
2812 maxpos_h = sip->si_h_endpos;
2813 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002814 }
2815}
2816
2817/*
2818 * Update an entry in the current_state stack for a start-skip-end pattern.
2819 * This finds the end of the current item, if it's in the current line.
2820 *
2821 * Return the flags for the matched END.
2822 */
2823 static void
2824update_si_end(sip, startcol, force)
2825 stateitem_T *sip;
2826 int startcol; /* where to start searching for the end */
2827 int force; /* when TRUE overrule a previous end */
2828{
2829 lpos_T startpos;
2830 lpos_T endpos;
2831 lpos_T hl_endpos;
2832 lpos_T end_endpos;
2833 int end_idx;
2834
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002835 /* return quickly for a keyword */
2836 if (sip->si_idx < 0)
2837 return;
2838
Bram Moolenaar071d4272004-06-13 20:20:40 +00002839 /* Don't update when it's already done. Can be a match of an end pattern
2840 * that started in a previous line. Watch out: can also be a "keepend"
2841 * from a containing item. */
2842 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2843 return;
2844
2845 /*
2846 * We need to find the end of the region. It may continue in the next
2847 * line.
2848 */
2849 end_idx = 0;
2850 startpos.lnum = current_lnum;
2851 startpos.col = startcol;
2852 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2853 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2854
2855 if (endpos.lnum == 0)
2856 {
2857 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002858 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002859 {
2860 /* a "oneline" never continues in the next line */
2861 sip->si_ends = TRUE;
2862 sip->si_m_endpos.lnum = current_lnum;
2863 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2864 }
2865 else
2866 {
2867 /* continues in the next line */
2868 sip->si_ends = FALSE;
2869 sip->si_m_endpos.lnum = 0;
2870 }
2871 sip->si_h_endpos = sip->si_m_endpos;
2872 }
2873 else
2874 {
2875 /* match within this line */
2876 sip->si_m_endpos = endpos;
2877 sip->si_h_endpos = hl_endpos;
2878 sip->si_eoe_pos = end_endpos;
2879 sip->si_ends = TRUE;
2880 sip->si_end_idx = end_idx;
2881 }
2882}
2883
2884/*
2885 * Add a new state to the current state stack.
2886 * It is cleared and the index set to "idx".
2887 * Return FAIL if it's not possible (out of memory).
2888 */
2889 static int
2890push_current_state(idx)
2891 int idx;
2892{
2893 if (ga_grow(&current_state, 1) == FAIL)
2894 return FAIL;
2895 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2896 CUR_STATE(current_state.ga_len).si_idx = idx;
2897 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002898 return OK;
2899}
2900
2901/*
2902 * Remove a state from the current_state stack.
2903 */
2904 static void
2905pop_current_state()
2906{
2907 if (current_state.ga_len)
2908 {
2909 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2910 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911 }
2912 /* after the end of a pattern, try matching a keyword or pattern */
2913 next_match_idx = -1;
2914
2915 /* if first state with "keepend" is popped, reset keepend_level */
2916 if (keepend_level >= current_state.ga_len)
2917 keepend_level = -1;
2918}
2919
2920/*
2921 * Find the end of a start/skip/end syntax region after "startpos".
2922 * Only checks one line.
2923 * Also handles a match item that continued from a previous line.
2924 * If not found, the syntax item continues in the next line. m_endpos->lnum
2925 * will be 0.
2926 * If found, the end of the region and the end of the highlighting is
2927 * computed.
2928 */
2929 static void
2930find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2931 end_idx, start_ext)
2932 int idx; /* index of the pattern */
2933 lpos_T *startpos; /* where to start looking for an END match */
2934 lpos_T *m_endpos; /* return: end of match */
2935 lpos_T *hl_endpos; /* return: end of highlighting */
2936 long *flagsp; /* return: flags of matching END */
2937 lpos_T *end_endpos; /* return: end of end pattern match */
2938 int *end_idx; /* return: group ID for end pat. match, or 0 */
2939 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2940{
2941 colnr_T matchcol;
2942 synpat_T *spp, *spp_skip;
2943 int start_idx;
2944 int best_idx;
2945 regmmatch_T regmatch;
2946 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2947 lpos_T pos;
2948 char_u *line;
2949 int had_match = FALSE;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002950 char_u buf_chartab[32]; /* chartab array for syn option iskyeyword */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002951
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002952 /* just in case we are invoked for a keyword */
2953 if (idx < 0)
2954 return;
2955
Bram Moolenaar071d4272004-06-13 20:20:40 +00002956 /*
2957 * Check for being called with a START pattern.
2958 * Can happen with a match that continues to the next line, because it
2959 * contained a region.
2960 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002961 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002962 if (spp->sp_type != SPTYPE_START)
2963 {
2964 *hl_endpos = *startpos;
2965 return;
2966 }
2967
2968 /*
2969 * Find the SKIP or first END pattern after the last START pattern.
2970 */
2971 for (;;)
2972 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002973 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974 if (spp->sp_type != SPTYPE_START)
2975 break;
2976 ++idx;
2977 }
2978
2979 /*
2980 * Lookup the SKIP pattern (if present)
2981 */
2982 if (spp->sp_type == SPTYPE_SKIP)
2983 {
2984 spp_skip = spp;
2985 ++idx;
2986 }
2987 else
2988 spp_skip = NULL;
2989
2990 /* Setup external matches for syn_regexec(). */
2991 unref_extmatch(re_extmatch_in);
2992 re_extmatch_in = ref_extmatch(start_ext);
2993
2994 matchcol = startpos->col; /* start looking for a match at sstart */
2995 start_idx = idx; /* remember the first END pattern. */
2996 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01002997
2998 /* use syntax iskeyword option */
2999 save_chartab(buf_chartab);
3000
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 for (;;)
3002 {
3003 /*
3004 * Find end pattern that matches first after "matchcol".
3005 */
3006 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003007 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 {
3009 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003010 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003011
Bram Moolenaar860cae12010-06-05 23:22:07 +02003012 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
3014 break;
3015 lc_col -= spp->sp_offsets[SPO_LC_OFF];
3016 if (lc_col < 0)
3017 lc_col = 0;
3018
3019 regmatch.rmm_ic = spp->sp_ic;
3020 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003021 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3022 IF_SYN_TIME(&spp->sp_time));
3023 spp->sp_prog = regmatch.regprog;
3024 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003025 {
3026 if (best_idx == -1 || regmatch.startpos[0].col
3027 < best_regmatch.startpos[0].col)
3028 {
3029 best_idx = idx;
3030 best_regmatch.startpos[0] = regmatch.startpos[0];
3031 best_regmatch.endpos[0] = regmatch.endpos[0];
3032 }
3033 }
3034 }
3035
3036 /*
3037 * If all end patterns have been tried, and there is no match, the
3038 * item continues until end-of-line.
3039 */
3040 if (best_idx == -1)
3041 break;
3042
3043 /*
3044 * If the skip pattern matches before the end pattern,
3045 * continue searching after the skip pattern.
3046 */
3047 if (spp_skip != NULL)
3048 {
3049 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003050 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051
3052 if (lc_col < 0)
3053 lc_col = 0;
3054 regmatch.rmm_ic = spp_skip->sp_ic;
3055 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003056 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3057 IF_SYN_TIME(&spp_skip->sp_time));
3058 spp_skip->sp_prog = regmatch.regprog;
3059 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 <= best_regmatch.startpos[0].col)
3061 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003062 int line_len;
3063
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 /* Add offset to skip pattern match */
3065 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3066
3067 /* If the skip pattern goes on to the next line, there is no
3068 * match with an end pattern in this line. */
3069 if (pos.lnum > startpos->lnum)
3070 break;
3071
3072 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003073 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074
3075 /* take care of an empty match or negative offset */
3076 if (pos.col <= matchcol)
3077 ++matchcol;
3078 else if (pos.col <= regmatch.endpos[0].col)
3079 matchcol = pos.col;
3080 else
3081 /* Be careful not to jump over the NUL at the end-of-line */
3082 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003083 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 ++matchcol)
3085 ;
3086
3087 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003088 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003089 break;
3090
3091 continue; /* start with first end pattern again */
3092 }
3093 }
3094
3095 /*
3096 * Match from start pattern to end pattern.
3097 * Correct for match and highlight offset of end pattern.
3098 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003099 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003100 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3101 /* can't end before the start */
3102 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3103 m_endpos->col = startpos->col;
3104
3105 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3106 /* can't end before the start */
3107 if (end_endpos->lnum == startpos->lnum
3108 && end_endpos->col < startpos->col)
3109 end_endpos->col = startpos->col;
3110 /* can't end after the match */
3111 limit_pos(end_endpos, m_endpos);
3112
3113 /*
3114 * If the end group is highlighted differently, adjust the pointers.
3115 */
3116 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3117 {
3118 *end_idx = best_idx;
3119 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3120 {
3121 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3122 hl_endpos->col = best_regmatch.endpos[0].col;
3123 }
3124 else
3125 {
3126 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3127 hl_endpos->col = best_regmatch.startpos[0].col;
3128 }
3129 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3130
3131 /* can't end before the start */
3132 if (hl_endpos->lnum == startpos->lnum
3133 && hl_endpos->col < startpos->col)
3134 hl_endpos->col = startpos->col;
3135 limit_pos(hl_endpos, m_endpos);
3136
3137 /* now the match ends where the highlighting ends, it is turned
3138 * into the matchgroup for the end */
3139 *m_endpos = *hl_endpos;
3140 }
3141 else
3142 {
3143 *end_idx = 0;
3144 *hl_endpos = *end_endpos;
3145 }
3146
3147 *flagsp = spp->sp_flags;
3148
3149 had_match = TRUE;
3150 break;
3151 }
3152
3153 /* no match for an END pattern in this line */
3154 if (!had_match)
3155 m_endpos->lnum = 0;
3156
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003157 restore_chartab(buf_chartab);
3158
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159 /* Remove external matches. */
3160 unref_extmatch(re_extmatch_in);
3161 re_extmatch_in = NULL;
3162}
3163
3164/*
3165 * Limit "pos" not to be after "limit".
3166 */
3167 static void
3168limit_pos(pos, limit)
3169 lpos_T *pos;
3170 lpos_T *limit;
3171{
3172 if (pos->lnum > limit->lnum)
3173 *pos = *limit;
3174 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3175 pos->col = limit->col;
3176}
3177
3178/*
3179 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3180 */
3181 static void
3182limit_pos_zero(pos, limit)
3183 lpos_T *pos;
3184 lpos_T *limit;
3185{
3186 if (pos->lnum == 0)
3187 *pos = *limit;
3188 else
3189 limit_pos(pos, limit);
3190}
3191
3192/*
3193 * Add offset to matched text for end of match or highlight.
3194 */
3195 static void
3196syn_add_end_off(result, regmatch, spp, idx, extra)
3197 lpos_T *result; /* returned position */
3198 regmmatch_T *regmatch; /* start/end of match */
3199 synpat_T *spp; /* matched pattern */
3200 int idx; /* index of offset */
3201 int extra; /* extra chars for offset to start */
3202{
3203 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003204 int off;
3205 char_u *base;
3206 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207
3208 if (spp->sp_off_flags & (1 << idx))
3209 {
3210 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003211 col = regmatch->startpos[0].col;
3212 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213 }
3214 else
3215 {
3216 result->lnum = regmatch->endpos[0].lnum;
3217 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003218 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003220 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3221 * is a matchgroup. Watch out for match with last NL in the buffer. */
3222 if (result->lnum > syn_buf->b_ml.ml_line_count)
3223 col = 0;
3224 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003225 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003226 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3227 p = base + col;
3228 if (off > 0)
3229 {
3230 while (off-- > 0 && *p != NUL)
3231 mb_ptr_adv(p);
3232 }
3233 else if (off < 0)
3234 {
3235 while (off++ < 0 && base < p)
3236 mb_ptr_back(base, p);
3237 }
3238 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003239 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003240 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241}
3242
3243/*
3244 * Add offset to matched text for start of match or highlight.
3245 * Avoid resulting column to become negative.
3246 */
3247 static void
3248syn_add_start_off(result, regmatch, spp, idx, extra)
3249 lpos_T *result; /* returned position */
3250 regmmatch_T *regmatch; /* start/end of match */
3251 synpat_T *spp;
3252 int idx;
3253 int extra; /* extra chars for offset to end */
3254{
3255 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003256 int off;
3257 char_u *base;
3258 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259
3260 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3261 {
3262 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003263 col = regmatch->endpos[0].col;
3264 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265 }
3266 else
3267 {
3268 result->lnum = regmatch->startpos[0].lnum;
3269 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003270 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003272 if (result->lnum > syn_buf->b_ml.ml_line_count)
3273 {
3274 /* a "\n" at the end of the pattern may take us below the last line */
3275 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003276 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003277 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003278 if (off != 0)
3279 {
3280 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3281 p = base + col;
3282 if (off > 0)
3283 {
3284 while (off-- && *p != NUL)
3285 mb_ptr_adv(p);
3286 }
3287 else if (off < 0)
3288 {
3289 while (off++ && base < p)
3290 mb_ptr_back(base, p);
3291 }
3292 col = (int)(p - base);
3293 }
3294 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295}
3296
3297/*
3298 * Get current line in syntax buffer.
3299 */
3300 static char_u *
3301syn_getcurline()
3302{
3303 return ml_get_buf(syn_buf, current_lnum, FALSE);
3304}
3305
3306/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003307 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308 * Returns TRUE when there is a match.
3309 */
3310 static int
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003311syn_regexec(rmp, lnum, col, st)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003312 regmmatch_T *rmp;
3313 linenr_T lnum;
3314 colnr_T col;
Bram Moolenaar4e312962013-06-06 21:19:51 +02003315 syn_time_T *st UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003317 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003318#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003319 proftime_T pt;
3320
3321 if (syn_time_on)
3322 profile_start(&pt);
3323#endif
3324
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003325 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003326 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3327
Bram Moolenaarf7512552013-06-06 14:55:19 +02003328#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003329 if (syn_time_on)
3330 {
3331 profile_end(&pt);
3332 profile_add(&st->total, &pt);
3333 if (profile_cmp(&pt, &st->slowest) < 0)
3334 st->slowest = pt;
3335 ++st->count;
3336 if (r > 0)
3337 ++st->match;
3338 }
3339#endif
3340
3341 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003342 {
3343 rmp->startpos[0].lnum += lnum;
3344 rmp->endpos[0].lnum += lnum;
3345 return TRUE;
3346 }
3347 return FALSE;
3348}
3349
3350/*
3351 * Check one position in a line for a matching keyword.
3352 * The caller must check if a keyword can start at startcol.
3353 * Return it's ID if found, 0 otherwise.
3354 */
3355 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003356check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 char_u *line;
3358 int startcol; /* position in line to check for keyword */
3359 int *endcolp; /* return: character after found keyword */
3360 long *flagsp; /* return: flags of matching keyword */
3361 short **next_listp; /* return: next_list of matching keyword */
3362 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003363 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003365 keyentry_T *kp;
3366 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003368 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003370 hashtab_T *ht;
3371 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372
3373 /* Find first character after the keyword. First character was already
3374 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003375 kwp = line + startcol;
3376 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003377 do
3378 {
3379#ifdef FEAT_MBYTE
3380 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003381 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 else
3383#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003384 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003386 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387
Bram Moolenaardad6b692005-01-25 22:14:34 +00003388 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003389 return 0;
3390
3391 /*
3392 * Must make a copy of the keyword, so we can add a NUL and make it
3393 * lowercase.
3394 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003395 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396
3397 /*
3398 * Try twice:
3399 * 1. matching case
3400 * 2. ignoring case
3401 */
3402 for (round = 1; round <= 2; ++round)
3403 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003404 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003405 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003406 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003407 if (round == 2) /* ignore case */
3408 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409
3410 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003411 * Find keywords that match. There can be several with different
3412 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413 * When current_next_list is non-zero accept only that group, otherwise:
3414 * Accept a not-contained keyword at toplevel.
3415 * Accept a keyword at other levels only if it is in the contains list.
3416 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003417 hi = hash_find(ht, keyword);
3418 if (!HASHITEM_EMPTY(hi))
3419 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003421 if (current_next_list != 0
3422 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3423 : (cur_si == NULL
3424 ? !(kp->flags & HL_CONTAINED)
3425 : in_id_list(cur_si, cur_si->si_cont_list,
3426 &kp->k_syn, kp->flags & HL_CONTAINED)))
3427 {
3428 *endcolp = startcol + kwlen;
3429 *flagsp = kp->flags;
3430 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003431#ifdef FEAT_CONCEAL
3432 *ccharp = kp->k_char;
3433#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003434 return kp->k_syn.id;
3435 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436 }
3437 }
3438 return 0;
3439}
3440
3441/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003442 * Handle ":syntax conceal" command.
3443 */
3444 static void
3445syn_cmd_conceal(eap, syncing)
3446 exarg_T *eap UNUSED;
3447 int syncing UNUSED;
3448{
3449#ifdef FEAT_CONCEAL
3450 char_u *arg = eap->arg;
3451 char_u *next;
3452
3453 eap->nextcmd = find_nextcmd(arg);
3454 if (eap->skip)
3455 return;
3456
3457 next = skiptowhite(arg);
3458 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3459 curwin->w_s->b_syn_conceal = TRUE;
3460 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3461 curwin->w_s->b_syn_conceal = FALSE;
3462 else
3463 EMSG2(_("E390: Illegal argument: %s"), arg);
3464#endif
3465}
3466
3467/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468 * Handle ":syntax case" command.
3469 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470 static void
3471syn_cmd_case(eap, syncing)
3472 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003473 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474{
3475 char_u *arg = eap->arg;
3476 char_u *next;
3477
3478 eap->nextcmd = find_nextcmd(arg);
3479 if (eap->skip)
3480 return;
3481
3482 next = skiptowhite(arg);
3483 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003484 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003486 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 else
3488 EMSG2(_("E390: Illegal argument: %s"), arg);
3489}
3490
3491/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003492 * Handle ":syntax spell" command.
3493 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003494 static void
3495syn_cmd_spell(eap, syncing)
3496 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003497 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003498{
3499 char_u *arg = eap->arg;
3500 char_u *next;
3501
3502 eap->nextcmd = find_nextcmd(arg);
3503 if (eap->skip)
3504 return;
3505
3506 next = skiptowhite(arg);
3507 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003509 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003511 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003512 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003513 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003514 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003515 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003516 return;
3517 }
3518
3519 /* assume spell checking changed, force a redraw */
3520 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003521}
3522
3523/*
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003524 * Handle ":syntax iskeyword" command.
3525 */
3526 static void
3527syn_cmd_iskeyword(eap, syncing)
3528 exarg_T *eap;
3529 int syncing UNUSED;
3530{
3531 char_u *arg = eap->arg;
3532 char_u save_chartab[32];
3533 char_u *save_isk;
3534
3535 if (eap->skip)
3536 return;
3537
3538 arg = skipwhite(arg);
3539 if (*arg == NUL)
3540 {
3541 MSG_PUTS("\n");
3542 MSG_PUTS(_("syntax iskeyword "));
3543 if (curwin->w_s->b_syn_isk != empty_option)
3544 msg_outtrans(curwin->w_s->b_syn_isk);
3545 else
3546 msg_outtrans((char_u *)"not set");
3547 }
3548 else
3549 {
3550 if (STRNICMP(arg, "clear", 5) == 0)
3551 {
3552 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3553 (size_t)32);
3554 clear_string_option(&curwin->w_s->b_syn_isk);
3555 }
3556 else
3557 {
3558 mch_memmove(save_chartab, curbuf->b_chartab, (size_t)32);
3559 save_isk = curbuf->b_p_isk;
3560 curbuf->b_p_isk = vim_strsave(arg);
3561
3562 buf_init_chartab(curbuf, FALSE);
3563 mch_memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab,
3564 (size_t)32);
3565 mch_memmove(curbuf->b_chartab, save_chartab, (size_t)32);
3566 clear_string_option(&curwin->w_s->b_syn_isk);
3567 curwin->w_s->b_syn_isk = curbuf->b_p_isk;
3568 curbuf->b_p_isk = save_isk;
3569 }
3570 }
3571 redraw_win_later(curwin, NOT_VALID);
3572}
3573
3574/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575 * Clear all syntax info for one buffer.
3576 */
3577 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003578syntax_clear(block)
3579 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580{
3581 int i;
3582
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583 block->b_syn_error = FALSE; /* clear previous error */
3584 block->b_syn_ic = FALSE; /* Use case, by default */
3585 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3586 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587
3588 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 clear_keywtab(&block->b_keywtab);
3590 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591
3592 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003593 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3594 syn_clear_pattern(block, i);
3595 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003596
3597 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003598 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3599 syn_clear_cluster(block, i);
3600 ga_clear(&block->b_syn_clusters);
3601 block->b_spell_cluster_id = 0;
3602 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603
Bram Moolenaar860cae12010-06-05 23:22:07 +02003604 block->b_syn_sync_flags = 0;
3605 block->b_syn_sync_minlines = 0;
3606 block->b_syn_sync_maxlines = 0;
3607 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608
Bram Moolenaar473de612013-06-08 18:19:48 +02003609 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003610 block->b_syn_linecont_prog = NULL;
3611 vim_free(block->b_syn_linecont_pat);
3612 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003613#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003615#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003616 clear_string_option(&block->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617
3618 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003619 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003621
3622 /* Reset the counter for ":syn include" */
3623 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003624}
3625
3626/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003627 * Get rid of ownsyntax for window "wp".
3628 */
3629 void
3630reset_synblock(wp)
3631 win_T *wp;
3632{
3633 if (wp->w_s != &wp->w_buffer->b_s)
3634 {
3635 syntax_clear(wp->w_s);
3636 vim_free(wp->w_s);
3637 wp->w_s = &wp->w_buffer->b_s;
3638 }
3639}
3640
3641/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642 * Clear syncing info for one buffer.
3643 */
3644 static void
3645syntax_sync_clear()
3646{
3647 int i;
3648
3649 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3651 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3652 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653
Bram Moolenaar860cae12010-06-05 23:22:07 +02003654 curwin->w_s->b_syn_sync_flags = 0;
3655 curwin->w_s->b_syn_sync_minlines = 0;
3656 curwin->w_s->b_syn_sync_maxlines = 0;
3657 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658
Bram Moolenaar473de612013-06-08 18:19:48 +02003659 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003660 curwin->w_s->b_syn_linecont_prog = NULL;
3661 vim_free(curwin->w_s->b_syn_linecont_pat);
3662 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003663 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003665 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666}
3667
3668/*
3669 * Remove one pattern from the buffer's pattern list.
3670 */
3671 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003672syn_remove_pattern(block, idx)
3673 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 int idx;
3675{
3676 synpat_T *spp;
3677
Bram Moolenaar860cae12010-06-05 23:22:07 +02003678 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679#ifdef FEAT_FOLDING
3680 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003681 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003683 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003685 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3686 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687}
3688
3689/*
3690 * Clear and free one syntax pattern. When clearing all, must be called from
3691 * last to first!
3692 */
3693 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003694syn_clear_pattern(block, i)
3695 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 int i;
3697{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003698 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003699 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003701 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003703 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3704 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3705 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706 }
3707}
3708
3709/*
3710 * Clear and free one syntax cluster.
3711 */
3712 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003713syn_clear_cluster(block, i)
3714 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715 int i;
3716{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 vim_free(SYN_CLSTR(block)[i].scl_name);
3718 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3719 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720}
3721
3722/*
3723 * Handle ":syntax clear" command.
3724 */
3725 static void
3726syn_cmd_clear(eap, syncing)
3727 exarg_T *eap;
3728 int syncing;
3729{
3730 char_u *arg = eap->arg;
3731 char_u *arg_end;
3732 int id;
3733
3734 eap->nextcmd = find_nextcmd(arg);
3735 if (eap->skip)
3736 return;
3737
3738 /*
3739 * We have to disable this within ":syn include @group filename",
3740 * because otherwise @group would get deleted.
3741 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3742 * clear".
3743 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003744 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 return;
3746
3747 if (ends_excmd(*arg))
3748 {
3749 /*
3750 * No argument: Clear all syntax items.
3751 */
3752 if (syncing)
3753 syntax_sync_clear();
3754 else
3755 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003756 syntax_clear(curwin->w_s);
3757 if (curwin->w_s == &curwin->w_buffer->b_s)
3758 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003759 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 }
3761 }
3762 else
3763 {
3764 /*
3765 * Clear the group IDs that are in the argument.
3766 */
3767 while (!ends_excmd(*arg))
3768 {
3769 arg_end = skiptowhite(arg);
3770 if (*arg == '@')
3771 {
3772 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3773 if (id == 0)
3774 {
3775 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3776 break;
3777 }
3778 else
3779 {
3780 /*
3781 * We can't physically delete a cluster without changing
3782 * the IDs of other clusters, so we do the next best thing
3783 * and make it empty.
3784 */
3785 short scl_id = id - SYNID_CLUSTER;
3786
Bram Moolenaar860cae12010-06-05 23:22:07 +02003787 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3788 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003789 }
3790 }
3791 else
3792 {
3793 id = syn_namen2id(arg, (int)(arg_end - arg));
3794 if (id == 0)
3795 {
3796 EMSG2(_(e_nogroup), arg);
3797 break;
3798 }
3799 else
3800 syn_clear_one(id, syncing);
3801 }
3802 arg = skipwhite(arg_end);
3803 }
3804 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003805 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003806 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807}
3808
3809/*
3810 * Clear one syntax group for the current buffer.
3811 */
3812 static void
3813syn_clear_one(id, syncing)
3814 int id;
3815 int syncing;
3816{
3817 synpat_T *spp;
3818 int idx;
3819
3820 /* Clear keywords only when not ":syn sync clear group-name" */
3821 if (!syncing)
3822 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003823 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3824 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 }
3826
3827 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003828 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003829 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003830 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3832 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003833 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003834 }
3835}
3836
3837/*
3838 * Handle ":syntax on" command.
3839 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 static void
3841syn_cmd_on(eap, syncing)
3842 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003843 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844{
3845 syn_cmd_onoff(eap, "syntax");
3846}
3847
3848/*
3849 * Handle ":syntax enable" command.
3850 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 static void
3852syn_cmd_enable(eap, syncing)
3853 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003854 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855{
3856 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3857 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003858 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859}
3860
3861/*
3862 * Handle ":syntax reset" command.
3863 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 static void
3865syn_cmd_reset(eap, syncing)
3866 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003867 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868{
3869 eap->nextcmd = check_nextcmd(eap->arg);
3870 if (!eap->skip)
3871 {
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01003872 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3874 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003875 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 }
3877}
3878
3879/*
3880 * Handle ":syntax manual" command.
3881 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 static void
3883syn_cmd_manual(eap, syncing)
3884 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003885 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886{
3887 syn_cmd_onoff(eap, "manual");
3888}
3889
3890/*
3891 * Handle ":syntax off" command.
3892 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 static void
3894syn_cmd_off(eap, syncing)
3895 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003896 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003897{
3898 syn_cmd_onoff(eap, "nosyntax");
3899}
3900
3901 static void
3902syn_cmd_onoff(eap, name)
3903 exarg_T *eap;
3904 char *name;
3905{
3906 char_u buf[100];
3907
3908 eap->nextcmd = check_nextcmd(eap->arg);
3909 if (!eap->skip)
3910 {
3911 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003912 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913 do_cmdline_cmd(buf);
3914 }
3915}
3916
3917/*
3918 * Handle ":syntax [list]" command: list current syntax words.
3919 */
3920 static void
3921syn_cmd_list(eap, syncing)
3922 exarg_T *eap;
3923 int syncing; /* when TRUE: list syncing items */
3924{
3925 char_u *arg = eap->arg;
3926 int id;
3927 char_u *arg_end;
3928
3929 eap->nextcmd = find_nextcmd(arg);
3930 if (eap->skip)
3931 return;
3932
Bram Moolenaar860cae12010-06-05 23:22:07 +02003933 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003935 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936 return;
3937 }
3938
3939 if (syncing)
3940 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003941 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942 {
3943 MSG_PUTS(_("syncing on C-style comments"));
3944 syn_lines_msg();
3945 syn_match_msg();
3946 return;
3947 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003948 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003950 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003951 MSG_PUTS(_("no syncing"));
3952 else
3953 {
3954 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003955 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 MSG_PUTS(_(" lines before top line"));
3957 syn_match_msg();
3958 }
3959 return;
3960 }
3961 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003962 if (curwin->w_s->b_syn_sync_minlines > 0
3963 || curwin->w_s->b_syn_sync_maxlines > 0
3964 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965 {
3966 MSG_PUTS(_("\nsyncing on items"));
3967 syn_lines_msg();
3968 syn_match_msg();
3969 }
3970 }
3971 else
3972 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3973 if (ends_excmd(*arg))
3974 {
3975 /*
3976 * No argument: List all group IDs and all syntax clusters.
3977 */
3978 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3979 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003980 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 syn_list_cluster(id);
3982 }
3983 else
3984 {
3985 /*
3986 * List the group IDs and syntax clusters that are in the argument.
3987 */
3988 while (!ends_excmd(*arg) && !got_int)
3989 {
3990 arg_end = skiptowhite(arg);
3991 if (*arg == '@')
3992 {
3993 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3994 if (id == 0)
3995 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3996 else
3997 syn_list_cluster(id - SYNID_CLUSTER);
3998 }
3999 else
4000 {
4001 id = syn_namen2id(arg, (int)(arg_end - arg));
4002 if (id == 0)
4003 EMSG2(_(e_nogroup), arg);
4004 else
4005 syn_list_one(id, syncing, TRUE);
4006 }
4007 arg = skipwhite(arg_end);
4008 }
4009 }
4010 eap->nextcmd = check_nextcmd(arg);
4011}
4012
4013 static void
4014syn_lines_msg()
4015{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004016 if (curwin->w_s->b_syn_sync_maxlines > 0
4017 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 {
4019 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02004020 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004021 {
4022 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004023 msg_outnum(curwin->w_s->b_syn_sync_minlines);
4024 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004025 MSG_PUTS(", ");
4026 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004027 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 {
4029 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004030 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004031 }
4032 MSG_PUTS(_(" lines before top line"));
4033 }
4034}
4035
4036 static void
4037syn_match_msg()
4038{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004039 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 {
4041 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004042 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004043 MSG_PUTS(_(" line breaks"));
4044 }
4045}
4046
4047static int last_matchgroup;
4048
4049struct name_list
4050{
4051 int flag;
4052 char *name;
4053};
4054
4055static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
4056
4057/*
4058 * List one syntax item, for ":syntax" or "syntax list syntax_name".
4059 */
4060 static void
4061syn_list_one(id, syncing, link_only)
4062 int id;
4063 int syncing; /* when TRUE: list syncing items */
4064 int link_only; /* when TRUE; list link-only too */
4065{
4066 int attr;
4067 int idx;
4068 int did_header = FALSE;
4069 synpat_T *spp;
4070 static struct name_list namelist1[] =
4071 {
4072 {HL_DISPLAY, "display"},
4073 {HL_CONTAINED, "contained"},
4074 {HL_ONELINE, "oneline"},
4075 {HL_KEEPEND, "keepend"},
4076 {HL_EXTEND, "extend"},
4077 {HL_EXCLUDENL, "excludenl"},
4078 {HL_TRANSP, "transparent"},
4079 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004080#ifdef FEAT_CONCEAL
4081 {HL_CONCEAL, "conceal"},
4082 {HL_CONCEALENDS, "concealends"},
4083#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084 {0, NULL}
4085 };
4086 static struct name_list namelist2[] =
4087 {
4088 {HL_SKIPWHITE, "skipwhite"},
4089 {HL_SKIPNL, "skipnl"},
4090 {HL_SKIPEMPTY, "skipempty"},
4091 {0, NULL}
4092 };
4093
4094 attr = hl_attr(HLF_D); /* highlight like directories */
4095
4096 /* list the keywords for "id" */
4097 if (!syncing)
4098 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004099 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4100 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004101 did_header, attr);
4102 }
4103
4104 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004105 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004107 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004108 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4109 continue;
4110
4111 (void)syn_list_header(did_header, 999, id);
4112 did_header = TRUE;
4113 last_matchgroup = 0;
4114 if (spp->sp_type == SPTYPE_MATCH)
4115 {
4116 put_pattern("match", ' ', spp, attr);
4117 msg_putchar(' ');
4118 }
4119 else if (spp->sp_type == SPTYPE_START)
4120 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004121 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4122 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4123 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4124 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4125 while (idx < curwin->w_s->b_syn_patterns.ga_len
4126 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4127 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 --idx;
4129 msg_putchar(' ');
4130 }
4131 syn_list_flags(namelist1, spp->sp_flags, attr);
4132
4133 if (spp->sp_cont_list != NULL)
4134 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4135
4136 if (spp->sp_syn.cont_in_list != NULL)
4137 put_id_list((char_u *)"containedin",
4138 spp->sp_syn.cont_in_list, attr);
4139
4140 if (spp->sp_next_list != NULL)
4141 {
4142 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4143 syn_list_flags(namelist2, spp->sp_flags, attr);
4144 }
4145 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4146 {
4147 if (spp->sp_flags & HL_SYNC_HERE)
4148 msg_puts_attr((char_u *)"grouphere", attr);
4149 else
4150 msg_puts_attr((char_u *)"groupthere", attr);
4151 msg_putchar(' ');
4152 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004153 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4155 else
4156 MSG_PUTS("NONE");
4157 msg_putchar(' ');
4158 }
4159 }
4160
4161 /* list the link, if there is one */
4162 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4163 {
4164 (void)syn_list_header(did_header, 999, id);
4165 msg_puts_attr((char_u *)"links to", attr);
4166 msg_putchar(' ');
4167 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4168 }
4169}
4170
4171 static void
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004172syn_list_flags(nlist, flags, attr)
4173 struct name_list *nlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 int flags;
4175 int attr;
4176{
4177 int i;
4178
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004179 for (i = 0; nlist[i].flag != 0; ++i)
4180 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004182 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 msg_putchar(' ');
4184 }
4185}
4186
4187/*
4188 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4189 */
4190 static void
4191syn_list_cluster(id)
4192 int id;
4193{
4194 int endcol = 15;
4195
4196 /* slight hack: roughly duplicate the guts of syn_list_header() */
4197 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004198 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199
4200 if (msg_col >= endcol) /* output at least one space */
4201 endcol = msg_col + 1;
4202 if (Columns <= endcol) /* avoid hang for tiny window */
4203 endcol = Columns - 1;
4204
4205 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004206 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004208 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 hl_attr(HLF_D));
4210 }
4211 else
4212 {
4213 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4214 msg_puts((char_u *)"=NONE");
4215 }
4216}
4217
4218 static void
4219put_id_list(name, list, attr)
4220 char_u *name;
4221 short *list;
4222 int attr;
4223{
4224 short *p;
4225
4226 msg_puts_attr(name, attr);
4227 msg_putchar('=');
4228 for (p = list; *p; ++p)
4229 {
4230 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4231 {
4232 if (p[1])
4233 MSG_PUTS("ALLBUT");
4234 else
4235 MSG_PUTS("ALL");
4236 }
4237 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4238 {
4239 MSG_PUTS("TOP");
4240 }
4241 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4242 {
4243 MSG_PUTS("CONTAINED");
4244 }
4245 else if (*p >= SYNID_CLUSTER)
4246 {
4247 short scl_id = *p - SYNID_CLUSTER;
4248
4249 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004250 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 }
4252 else
4253 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4254 if (p[1])
4255 msg_putchar(',');
4256 }
4257 msg_putchar(' ');
4258}
4259
4260 static void
4261put_pattern(s, c, spp, attr)
4262 char *s;
4263 int c;
4264 synpat_T *spp;
4265 int attr;
4266{
4267 long n;
4268 int mask;
4269 int first;
4270 static char *sepchars = "/+=-#@\"|'^&";
4271 int i;
4272
4273 /* May have to write "matchgroup=group" */
4274 if (last_matchgroup != spp->sp_syn_match_id)
4275 {
4276 last_matchgroup = spp->sp_syn_match_id;
4277 msg_puts_attr((char_u *)"matchgroup", attr);
4278 msg_putchar('=');
4279 if (last_matchgroup == 0)
4280 msg_outtrans((char_u *)"NONE");
4281 else
4282 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4283 msg_putchar(' ');
4284 }
4285
4286 /* output the name of the pattern and an '=' or ' ' */
4287 msg_puts_attr((char_u *)s, attr);
4288 msg_putchar(c);
4289
4290 /* output the pattern, in between a char that is not in the pattern */
4291 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4292 if (sepchars[++i] == NUL)
4293 {
4294 i = 0; /* no good char found, just use the first one */
4295 break;
4296 }
4297 msg_putchar(sepchars[i]);
4298 msg_outtrans(spp->sp_pattern);
4299 msg_putchar(sepchars[i]);
4300
4301 /* output any pattern options */
4302 first = TRUE;
4303 for (i = 0; i < SPO_COUNT; ++i)
4304 {
4305 mask = (1 << i);
4306 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4307 {
4308 if (!first)
4309 msg_putchar(','); /* separate with commas */
4310 msg_puts((char_u *)spo_name_tab[i]);
4311 n = spp->sp_offsets[i];
4312 if (i != SPO_LC_OFF)
4313 {
4314 if (spp->sp_off_flags & mask)
4315 msg_putchar('s');
4316 else
4317 msg_putchar('e');
4318 if (n > 0)
4319 msg_putchar('+');
4320 }
4321 if (n || i == SPO_LC_OFF)
4322 msg_outnum(n);
4323 first = FALSE;
4324 }
4325 }
4326 msg_putchar(' ');
4327}
4328
4329/*
4330 * List or clear the keywords for one syntax group.
4331 * Return TRUE if the header has been printed.
4332 */
4333 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 int did_header; /* header has already been printed */
4338 int attr;
4339{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004341 hashitem_T *hi;
4342 keyentry_T *kp;
4343 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344 int prev_contained = 0;
4345 short *prev_next_list = NULL;
4346 short *prev_cont_in_list = NULL;
4347 int prev_skipnl = 0;
4348 int prev_skipwhite = 0;
4349 int prev_skipempty = 0;
4350
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 /*
4352 * Unfortunately, this list of keywords is not sorted on alphabet but on
4353 * hash value...
4354 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004355 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 --todo;
4361 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 if (prev_contained != (kp->flags & HL_CONTAINED)
4366 || prev_skipnl != (kp->flags & HL_SKIPNL)
4367 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4368 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4369 || prev_cont_in_list != kp->k_syn.cont_in_list
4370 || prev_next_list != kp->next_list)
4371 outlen = 9999;
4372 else
4373 outlen = (int)STRLEN(kp->keyword);
4374 /* output "contained" and "nextgroup" on each line */
4375 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004376 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004377 prev_contained = 0;
4378 prev_next_list = NULL;
4379 prev_cont_in_list = NULL;
4380 prev_skipnl = 0;
4381 prev_skipwhite = 0;
4382 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 did_header = TRUE;
4385 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004391 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004393 put_id_list((char_u *)"containedin",
4394 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004396 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004398 if (kp->next_list != prev_next_list)
4399 {
4400 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4401 msg_putchar(' ');
4402 prev_next_list = kp->next_list;
4403 if (kp->flags & HL_SKIPNL)
4404 {
4405 msg_puts_attr((char_u *)"skipnl", attr);
4406 msg_putchar(' ');
4407 prev_skipnl = (kp->flags & HL_SKIPNL);
4408 }
4409 if (kp->flags & HL_SKIPWHITE)
4410 {
4411 msg_puts_attr((char_u *)"skipwhite", attr);
4412 msg_putchar(' ');
4413 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4414 }
4415 if (kp->flags & HL_SKIPEMPTY)
4416 {
4417 msg_puts_attr((char_u *)"skipempty", attr);
4418 msg_putchar(' ');
4419 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4420 }
4421 }
4422 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004424 }
4425 }
4426 }
4427
4428 return did_header;
4429}
4430
4431 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004432syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004434 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004436 hashitem_T *hi;
4437 keyentry_T *kp;
4438 keyentry_T *kp_prev;
4439 keyentry_T *kp_next;
4440 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004443 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004444 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004448 --todo;
4449 kp_prev = NULL;
4450 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004452 if (kp->k_syn.id == id)
4453 {
4454 kp_next = kp->ke_next;
4455 if (kp_prev == NULL)
4456 {
4457 if (kp_next == NULL)
4458 hash_remove(ht, hi);
4459 else
4460 hi->hi_key = KE2HIKEY(kp_next);
4461 }
4462 else
4463 kp_prev->ke_next = kp_next;
4464 vim_free(kp->next_list);
4465 vim_free(kp->k_syn.cont_in_list);
4466 vim_free(kp);
4467 kp = kp_next;
4468 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004470 {
4471 kp_prev = kp;
4472 kp = kp->ke_next;
4473 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 }
4475 }
4476 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004477 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478}
4479
4480/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004481 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 */
4483 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004484clear_keywtab(ht)
4485 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004487 hashitem_T *hi;
4488 int todo;
4489 keyentry_T *kp;
4490 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004492 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004493 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004495 if (!HASHITEM_EMPTY(hi))
4496 {
4497 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004498 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004500 kp_next = kp->ke_next;
4501 vim_free(kp->next_list);
4502 vim_free(kp->k_syn.cont_in_list);
4503 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004505 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004507 hash_clear(ht);
4508 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509}
4510
4511/*
4512 * Add a keyword to the list of keywords.
4513 */
4514 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004515add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 char_u *name; /* name of keyword */
4517 int id; /* group ID for this keyword */
4518 int flags; /* flags for this keyword */
4519 short *cont_in_list; /* containedin for this keyword */
4520 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004521 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004523 keyentry_T *kp;
4524 hashtab_T *ht;
4525 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004526 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004527 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529
Bram Moolenaar860cae12010-06-05 23:22:07 +02004530 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004531 name_ic = str_foldcase(name, (int)STRLEN(name),
4532 name_folded, MAXKEYWLEN + 1);
4533 else
4534 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004535 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4536 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004537 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004538 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004539 kp->k_syn.id = id;
4540 kp->k_syn.inc_tag = current_syn_inc_tag;
4541 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004542 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004543 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004545 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004546 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547
Bram Moolenaar860cae12010-06-05 23:22:07 +02004548 if (curwin->w_s->b_syn_ic)
4549 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004551 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552
Bram Moolenaardad6b692005-01-25 22:14:34 +00004553 hash = hash_hash(kp->keyword);
4554 hi = hash_lookup(ht, kp->keyword, hash);
4555 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004557 /* new keyword, add to hashtable */
4558 kp->ke_next = NULL;
4559 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004561 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004563 /* keyword already exists, prepend to list */
4564 kp->ke_next = HI2KE(hi);
4565 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567}
4568
4569/*
4570 * Get the start and end of the group name argument.
4571 * Return a pointer to the first argument.
4572 * Return NULL if the end of the command was found instead of further args.
4573 */
4574 static char_u *
4575get_group_name(arg, name_end)
4576 char_u *arg; /* start of the argument */
4577 char_u **name_end; /* pointer to end of the name */
4578{
4579 char_u *rest;
4580
4581 *name_end = skiptowhite(arg);
4582 rest = skipwhite(*name_end);
4583
4584 /*
4585 * Check if there are enough arguments. The first argument may be a
4586 * pattern, where '|' is allowed, so only check for NUL.
4587 */
4588 if (ends_excmd(*arg) || *rest == NUL)
4589 return NULL;
4590 return rest;
4591}
4592
4593/*
4594 * Check for syntax command option arguments.
4595 * This can be called at any place in the list of arguments, and just picks
4596 * out the arguments that are known. Can be called several times in a row to
4597 * collect all options in between other arguments.
4598 * Return a pointer to the next argument (which isn't an option).
4599 * Return NULL for any error;
4600 */
4601 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004602get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004603 char_u *arg; /* next argument to be checked */
4604 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004605 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607 char_u *gname_start, *gname;
4608 int syn_id;
4609 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004610 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 int i;
4612 int fidx;
4613 static struct flag
4614 {
4615 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004616 int argtype;
4617 int flags;
4618 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4619 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4620 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4621 {"eExXtTeEnNdD", 0, HL_EXTEND},
4622 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4623 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4624 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4625 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4626 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4627 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4628 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4629 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4630 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004631 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4632 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4633 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004634 {"cCoOnNtTaAiInNsS", 1, 0},
4635 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4636 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004638 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639
4640 if (arg == NULL) /* already detected error */
4641 return NULL;
4642
Bram Moolenaar860cae12010-06-05 23:22:07 +02004643#ifdef FEAT_CONCEAL
4644 if (curwin->w_s->b_syn_conceal)
4645 opt->flags |= HL_CONCEAL;
4646#endif
4647
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 for (;;)
4649 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004650 /*
4651 * This is used very often when a large number of keywords is defined.
4652 * Need to skip quickly when no option name is found.
4653 * Also avoid tolower(), it's slow.
4654 */
4655 if (strchr(first_letters, *arg) == NULL)
4656 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657
4658 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4659 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004660 p = flagtab[fidx].name;
4661 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4662 if (arg[len] != p[i] && arg[len] != p[i + 1])
4663 break;
4664 if (p[i] == NUL && (vim_iswhite(arg[len])
4665 || (flagtab[fidx].argtype > 0
4666 ? arg[len] == '='
4667 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004669 if (opt->keyword
4670 && (flagtab[fidx].flags == HL_DISPLAY
4671 || flagtab[fidx].flags == HL_FOLD
4672 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004673 /* treat "display", "fold" and "extend" as a keyword */
4674 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675 break;
4676 }
4677 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004678 if (fidx < 0) /* no match found */
4679 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004680
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004681 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004683 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 {
4685 EMSG(_("E395: contains argument not accepted here"));
4686 return NULL;
4687 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004688 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689 return NULL;
4690 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004691 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004693 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694 return NULL;
4695 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004696 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004698 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 return NULL;
4700 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004701 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4702 {
4703#ifdef FEAT_MBYTE
4704 /* cchar=? */
4705 if (has_mbyte)
4706 {
4707# ifdef FEAT_CONCEAL
4708 *conceal_char = mb_ptr2char(arg + 6);
4709# endif
4710 arg += mb_ptr2len(arg + 6) - 1;
4711 }
4712 else
4713#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004714 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004715#ifdef FEAT_CONCEAL
4716 *conceal_char = arg[6];
4717#else
4718 ;
4719#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004720 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004721#ifdef FEAT_CONCEAL
4722 if (!vim_isprintc_strict(*conceal_char))
4723 {
4724 EMSG(_("E844: invalid cchar value"));
4725 return NULL;
4726 }
4727#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004728 arg = skipwhite(arg + 7);
4729 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004731 {
4732 opt->flags |= flagtab[fidx].flags;
4733 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004734
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004735 if (flagtab[fidx].flags == HL_SYNC_HERE
4736 || flagtab[fidx].flags == HL_SYNC_THERE)
4737 {
4738 if (opt->sync_idx == NULL)
4739 {
4740 EMSG(_("E393: group[t]here not accepted here"));
4741 return NULL;
4742 }
4743 gname_start = arg;
4744 arg = skiptowhite(arg);
4745 if (gname_start == arg)
4746 return NULL;
4747 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4748 if (gname == NULL)
4749 return NULL;
4750 if (STRCMP(gname, "NONE") == 0)
4751 *opt->sync_idx = NONE_IDX;
4752 else
4753 {
4754 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004755 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4756 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4757 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004758 {
4759 *opt->sync_idx = i;
4760 break;
4761 }
4762 if (i < 0)
4763 {
4764 EMSG2(_("E394: Didn't find region item for %s"), gname);
4765 vim_free(gname);
4766 return NULL;
4767 }
4768 }
4769
4770 vim_free(gname);
4771 arg = skipwhite(arg);
4772 }
4773#ifdef FEAT_FOLDING
4774 else if (flagtab[fidx].flags == HL_FOLD
4775 && foldmethodIsSyntax(curwin))
4776 /* Need to update folds later. */
4777 foldUpdateAll(curwin);
4778#endif
4779 }
4780 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004781
4782 return arg;
4783}
4784
4785/*
4786 * Adjustments to syntax item when declared in a ":syn include"'d file.
4787 * Set the contained flag, and if the item is not already contained, add it
4788 * to the specified top-level group, if any.
4789 */
4790 static void
4791syn_incl_toplevel(id, flagsp)
4792 int id;
4793 int *flagsp;
4794{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004795 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 return;
4797 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004798 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 {
4800 /* We have to alloc this, because syn_combine_list() will free it. */
4801 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004802 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803
4804 if (grp_list != NULL)
4805 {
4806 grp_list[0] = id;
4807 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004808 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004809 CLUSTER_ADD);
4810 }
4811 }
4812}
4813
4814/*
4815 * Handle ":syntax include [@{group-name}] filename" command.
4816 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 static void
4818syn_cmd_include(eap, syncing)
4819 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004820 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821{
4822 char_u *arg = eap->arg;
4823 int sgl_id = 1;
4824 char_u *group_name_end;
4825 char_u *rest;
4826 char_u *errormsg = NULL;
4827 int prev_toplvl_grp;
4828 int prev_syn_inc_tag;
4829 int source = FALSE;
4830
4831 eap->nextcmd = find_nextcmd(arg);
4832 if (eap->skip)
4833 return;
4834
4835 if (arg[0] == '@')
4836 {
4837 ++arg;
4838 rest = get_group_name(arg, &group_name_end);
4839 if (rest == NULL)
4840 {
4841 EMSG((char_u *)_("E397: Filename required"));
4842 return;
4843 }
4844 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004845 if (sgl_id == 0)
4846 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004847 /* separate_nextcmd() and expand_filename() depend on this */
4848 eap->arg = rest;
4849 }
4850
4851 /*
4852 * Everything that's left, up to the next command, should be the
4853 * filename to include.
4854 */
4855 eap->argt |= (XFILE | NOSPC);
4856 separate_nextcmd(eap);
4857 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4858 {
4859 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4860 * file. Need to expand the file name first. In other cases
4861 * ":runtime!" is used. */
4862 source = TRUE;
4863 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4864 {
4865 if (errormsg != NULL)
4866 EMSG(errormsg);
4867 return;
4868 }
4869 }
4870
4871 /*
4872 * Save and restore the existing top-level grouplist id and ":syn
4873 * include" tag around the actual inclusion.
4874 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004875 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4876 {
4877 EMSG((char_u *)_("E847: Too many syntax includes"));
4878 return;
4879 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880 prev_syn_inc_tag = current_syn_inc_tag;
4881 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004882 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4883 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004884 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4885 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004887 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004888 current_syn_inc_tag = prev_syn_inc_tag;
4889}
4890
4891/*
4892 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4893 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004894 static void
4895syn_cmd_keyword(eap, syncing)
4896 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004897 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898{
4899 char_u *arg = eap->arg;
4900 char_u *group_name_end;
4901 int syn_id;
4902 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004903 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004904 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004905 char_u *kw;
4906 syn_opt_arg_T syn_opt_arg;
4907 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004908 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909
4910 rest = get_group_name(arg, &group_name_end);
4911
4912 if (rest != NULL)
4913 {
4914 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004915 if (syn_id != 0)
4916 /* allocate a buffer, for removing backslashes in the keyword */
4917 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 if (keyword_copy != NULL)
4919 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004920 syn_opt_arg.flags = 0;
4921 syn_opt_arg.keyword = TRUE;
4922 syn_opt_arg.sync_idx = NULL;
4923 syn_opt_arg.has_cont_list = FALSE;
4924 syn_opt_arg.cont_in_list = NULL;
4925 syn_opt_arg.next_list = NULL;
4926
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 /*
4928 * The options given apply to ALL keywords, so all options must be
4929 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004930 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004932 cnt = 0;
4933 p = keyword_copy;
4934 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004935 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004936 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004937 if (rest == NULL || ends_excmd(*rest))
4938 break;
4939 /* Copy the keyword, removing backslashes, and add a NUL. */
4940 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004942 if (*rest == '\\' && rest[1] != NUL)
4943 ++rest;
4944 *p++ = *rest++;
4945 }
4946 *p++ = NUL;
4947 ++cnt;
4948 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004950 if (!eap->skip)
4951 {
4952 /* Adjust flags for use of ":syn include". */
4953 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4954
4955 /*
4956 * 2: Add an entry for each keyword.
4957 */
4958 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4959 {
4960 for (p = vim_strchr(kw, '['); ; )
4961 {
4962 if (p != NULL)
4963 *p = NUL;
4964 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004965 syn_opt_arg.cont_in_list,
4966 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004967 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004968 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004969 if (p[1] == NUL)
4970 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004971 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004972 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004973 }
4974 if (p[1] == ']')
4975 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004976 if (p[2] != NUL)
4977 {
4978 EMSG3(_("E890: trailing char after ']': %s]%s"),
4979 kw, &p[2]);
4980 goto error;
4981 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004982 kw = p + 1; /* skip over the "]" */
4983 break;
4984 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004985#ifdef FEAT_MBYTE
4986 if (has_mbyte)
4987 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004988 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004989
4990 mch_memmove(p, p + 1, l);
4991 p += l;
4992 }
4993 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004994#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004995 {
4996 p[0] = p[1];
4997 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004998 }
4999 }
5000 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02005002error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00005004 vim_free(syn_opt_arg.cont_in_list);
5005 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006 }
5007 }
5008
5009 if (rest != NULL)
5010 eap->nextcmd = check_nextcmd(rest);
5011 else
5012 EMSG2(_(e_invarg2), arg);
5013
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005014 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005015 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016}
5017
5018/*
5019 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
5020 *
5021 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
5022 */
5023 static void
5024syn_cmd_match(eap, syncing)
5025 exarg_T *eap;
5026 int syncing; /* TRUE for ":syntax sync match .. " */
5027{
5028 char_u *arg = eap->arg;
5029 char_u *group_name_end;
5030 char_u *rest;
5031 synpat_T item; /* the item found in the line */
5032 int syn_id;
5033 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005034 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005035 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005036 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005037
5038 /* Isolate the group name, check for validity */
5039 rest = get_group_name(arg, &group_name_end);
5040
5041 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005042 syn_opt_arg.flags = 0;
5043 syn_opt_arg.keyword = FALSE;
5044 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
5045 syn_opt_arg.has_cont_list = TRUE;
5046 syn_opt_arg.cont_list = NULL;
5047 syn_opt_arg.cont_in_list = NULL;
5048 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005049 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005050
5051 /* get the pattern. */
5052 init_syn_patterns();
5053 vim_memset(&item, 0, sizeof(item));
5054 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005055 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
5056 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057
5058 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005059 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060
5061 if (rest != NULL) /* all arguments are valid */
5062 {
5063 /*
5064 * Check for trailing command and illegal trailing arguments.
5065 */
5066 eap->nextcmd = check_nextcmd(rest);
5067 if (!ends_excmd(*rest) || eap->skip)
5068 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005069 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005070 && (syn_id = syn_check_group(arg,
5071 (int)(group_name_end - arg))) != 0)
5072 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005073 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 /*
5075 * Store the pattern in the syn_items list
5076 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005077 idx = curwin->w_s->b_syn_patterns.ga_len;
5078 SYN_ITEMS(curwin->w_s)[idx] = item;
5079 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5080 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
5081 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5082 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5083 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
5084 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
5085 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
5086 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005087 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005088#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005089 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005090#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005091 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005092 curwin->w_s->b_syn_containedin = TRUE;
5093 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5094 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005095
5096 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005097 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005098 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005100 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005101 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005102#endif
5103
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005104 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005105 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 return; /* don't free the progs and patterns now */
5107 }
5108 }
5109
5110 /*
5111 * Something failed, free the allocated memory.
5112 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005113 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005115 vim_free(syn_opt_arg.cont_list);
5116 vim_free(syn_opt_arg.cont_in_list);
5117 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118
5119 if (rest == NULL)
5120 EMSG2(_(e_invarg2), arg);
5121}
5122
5123/*
5124 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5125 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5126 */
5127 static void
5128syn_cmd_region(eap, syncing)
5129 exarg_T *eap;
5130 int syncing; /* TRUE for ":syntax sync region .." */
5131{
5132 char_u *arg = eap->arg;
5133 char_u *group_name_end;
5134 char_u *rest; /* next arg, NULL on error */
5135 char_u *key_end;
5136 char_u *key = NULL;
5137 char_u *p;
5138 int item;
5139#define ITEM_START 0
5140#define ITEM_SKIP 1
5141#define ITEM_END 2
5142#define ITEM_MATCHGROUP 3
5143 struct pat_ptr
5144 {
5145 synpat_T *pp_synp; /* pointer to syn_pattern */
5146 int pp_matchgroup_id; /* matchgroup ID */
5147 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5148 } *(pat_ptrs[3]);
5149 /* patterns found in the line */
5150 struct pat_ptr *ppp;
5151 struct pat_ptr *ppp_next;
5152 int pat_count = 0; /* nr of syn_patterns found */
5153 int syn_id;
5154 int matchgroup_id = 0;
5155 int not_enough = FALSE; /* not enough arguments */
5156 int illegal = FALSE; /* illegal arguments */
5157 int success = FALSE;
5158 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005159 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005160 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005161
5162 /* Isolate the group name, check for validity */
5163 rest = get_group_name(arg, &group_name_end);
5164
5165 pat_ptrs[0] = NULL;
5166 pat_ptrs[1] = NULL;
5167 pat_ptrs[2] = NULL;
5168
5169 init_syn_patterns();
5170
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005171 syn_opt_arg.flags = 0;
5172 syn_opt_arg.keyword = FALSE;
5173 syn_opt_arg.sync_idx = NULL;
5174 syn_opt_arg.has_cont_list = TRUE;
5175 syn_opt_arg.cont_list = NULL;
5176 syn_opt_arg.cont_in_list = NULL;
5177 syn_opt_arg.next_list = NULL;
5178
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 /*
5180 * get the options, patterns and matchgroup.
5181 */
5182 while (rest != NULL && !ends_excmd(*rest))
5183 {
5184 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005185 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005186 if (rest == NULL || ends_excmd(*rest))
5187 break;
5188
5189 /* must be a pattern or matchgroup then */
5190 key_end = rest;
5191 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5192 ++key_end;
5193 vim_free(key);
5194 key = vim_strnsave_up(rest, (int)(key_end - rest));
5195 if (key == NULL) /* out of memory */
5196 {
5197 rest = NULL;
5198 break;
5199 }
5200 if (STRCMP(key, "MATCHGROUP") == 0)
5201 item = ITEM_MATCHGROUP;
5202 else if (STRCMP(key, "START") == 0)
5203 item = ITEM_START;
5204 else if (STRCMP(key, "END") == 0)
5205 item = ITEM_END;
5206 else if (STRCMP(key, "SKIP") == 0)
5207 {
5208 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5209 {
5210 illegal = TRUE;
5211 break;
5212 }
5213 item = ITEM_SKIP;
5214 }
5215 else
5216 break;
5217 rest = skipwhite(key_end);
5218 if (*rest != '=')
5219 {
5220 rest = NULL;
5221 EMSG2(_("E398: Missing '=': %s"), arg);
5222 break;
5223 }
5224 rest = skipwhite(rest + 1);
5225 if (*rest == NUL)
5226 {
5227 not_enough = TRUE;
5228 break;
5229 }
5230
5231 if (item == ITEM_MATCHGROUP)
5232 {
5233 p = skiptowhite(rest);
5234 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5235 matchgroup_id = 0;
5236 else
5237 {
5238 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5239 if (matchgroup_id == 0)
5240 {
5241 illegal = TRUE;
5242 break;
5243 }
5244 }
5245 rest = skipwhite(p);
5246 }
5247 else
5248 {
5249 /*
5250 * Allocate room for a syn_pattern, and link it in the list of
5251 * syn_patterns for this item, at the start (because the list is
5252 * used from end to start).
5253 */
5254 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5255 if (ppp == NULL)
5256 {
5257 rest = NULL;
5258 break;
5259 }
5260 ppp->pp_next = pat_ptrs[item];
5261 pat_ptrs[item] = ppp;
5262 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5263 if (ppp->pp_synp == NULL)
5264 {
5265 rest = NULL;
5266 break;
5267 }
5268
5269 /*
5270 * Get the syntax pattern and the following offset(s).
5271 */
5272 /* Enable the appropriate \z specials. */
5273 if (item == ITEM_START)
5274 reg_do_extmatch = REX_SET;
5275 else if (item == ITEM_SKIP || item == ITEM_END)
5276 reg_do_extmatch = REX_USE;
5277 rest = get_syn_pattern(rest, ppp->pp_synp);
5278 reg_do_extmatch = 0;
5279 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005280 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005281 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5282 ppp->pp_matchgroup_id = matchgroup_id;
5283 ++pat_count;
5284 }
5285 }
5286 vim_free(key);
5287 if (illegal || not_enough)
5288 rest = NULL;
5289
5290 /*
5291 * Must have a "start" and "end" pattern.
5292 */
5293 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5294 pat_ptrs[ITEM_END] == NULL))
5295 {
5296 not_enough = TRUE;
5297 rest = NULL;
5298 }
5299
5300 if (rest != NULL)
5301 {
5302 /*
5303 * Check for trailing garbage or command.
5304 * If OK, add the item.
5305 */
5306 eap->nextcmd = check_nextcmd(rest);
5307 if (!ends_excmd(*rest) || eap->skip)
5308 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005309 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005310 && (syn_id = syn_check_group(arg,
5311 (int)(group_name_end - arg))) != 0)
5312 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005313 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 /*
5315 * Store the start/skip/end in the syn_items list
5316 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005317 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005318 for (item = ITEM_START; item <= ITEM_END; ++item)
5319 {
5320 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5321 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005322 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5323 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5324 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005325 (item == ITEM_START) ? SPTYPE_START :
5326 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005327 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5328 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005329 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5330 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005331 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005332 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005333#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005334 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005335#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005336 if (item == ITEM_START)
5337 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005338 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005339 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005340 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005341 syn_opt_arg.cont_in_list;
5342 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005343 curwin->w_s->b_syn_containedin = TRUE;
5344 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005345 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005346 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005347 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 ++idx;
5349#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005350 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005351 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005352#endif
5353 }
5354 }
5355
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005356 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005357 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005358 success = TRUE; /* don't free the progs and patterns now */
5359 }
5360 }
5361
5362 /*
5363 * Free the allocated memory.
5364 */
5365 for (item = ITEM_START; item <= ITEM_END; ++item)
5366 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5367 {
5368 if (!success)
5369 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005370 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005371 vim_free(ppp->pp_synp->sp_pattern);
5372 }
5373 vim_free(ppp->pp_synp);
5374 ppp_next = ppp->pp_next;
5375 vim_free(ppp);
5376 }
5377
5378 if (!success)
5379 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005380 vim_free(syn_opt_arg.cont_list);
5381 vim_free(syn_opt_arg.cont_in_list);
5382 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005383 if (not_enough)
5384 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5385 else if (illegal || rest == NULL)
5386 EMSG2(_(e_invarg2), arg);
5387 }
5388}
5389
5390/*
5391 * A simple syntax group ID comparison function suitable for use in qsort()
5392 */
5393 static int
5394#ifdef __BORLANDC__
5395_RTLENTRYF
5396#endif
5397syn_compare_stub(v1, v2)
5398 const void *v1;
5399 const void *v2;
5400{
5401 const short *s1 = v1;
5402 const short *s2 = v2;
5403
5404 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5405}
5406
5407/*
5408 * Combines lists of syntax clusters.
5409 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5410 */
5411 static void
5412syn_combine_list(clstr1, clstr2, list_op)
5413 short **clstr1;
5414 short **clstr2;
5415 int list_op;
5416{
5417 int count1 = 0;
5418 int count2 = 0;
5419 short *g1;
5420 short *g2;
5421 short *clstr = NULL;
5422 int count;
5423 int round;
5424
5425 /*
5426 * Handle degenerate cases.
5427 */
5428 if (*clstr2 == NULL)
5429 return;
5430 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5431 {
5432 if (list_op == CLUSTER_REPLACE)
5433 vim_free(*clstr1);
5434 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5435 *clstr1 = *clstr2;
5436 else
5437 vim_free(*clstr2);
5438 return;
5439 }
5440
5441 for (g1 = *clstr1; *g1; g1++)
5442 ++count1;
5443 for (g2 = *clstr2; *g2; g2++)
5444 ++count2;
5445
5446 /*
5447 * For speed purposes, sort both lists.
5448 */
5449 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5450 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5451
5452 /*
5453 * We proceed in two passes; in round 1, we count the elements to place
5454 * in the new list, and in round 2, we allocate and populate the new
5455 * list. For speed, we use a mergesort-like method, adding the smaller
5456 * of the current elements in each list to the new list.
5457 */
5458 for (round = 1; round <= 2; round++)
5459 {
5460 g1 = *clstr1;
5461 g2 = *clstr2;
5462 count = 0;
5463
5464 /*
5465 * First, loop through the lists until one of them is empty.
5466 */
5467 while (*g1 && *g2)
5468 {
5469 /*
5470 * We always want to add from the first list.
5471 */
5472 if (*g1 < *g2)
5473 {
5474 if (round == 2)
5475 clstr[count] = *g1;
5476 count++;
5477 g1++;
5478 continue;
5479 }
5480 /*
5481 * We only want to add from the second list if we're adding the
5482 * lists.
5483 */
5484 if (list_op == CLUSTER_ADD)
5485 {
5486 if (round == 2)
5487 clstr[count] = *g2;
5488 count++;
5489 }
5490 if (*g1 == *g2)
5491 g1++;
5492 g2++;
5493 }
5494
5495 /*
5496 * Now add the leftovers from whichever list didn't get finished
5497 * first. As before, we only want to add from the second list if
5498 * we're adding the lists.
5499 */
5500 for (; *g1; g1++, count++)
5501 if (round == 2)
5502 clstr[count] = *g1;
5503 if (list_op == CLUSTER_ADD)
5504 for (; *g2; g2++, count++)
5505 if (round == 2)
5506 clstr[count] = *g2;
5507
5508 if (round == 1)
5509 {
5510 /*
5511 * If the group ended up empty, we don't need to allocate any
5512 * space for it.
5513 */
5514 if (count == 0)
5515 {
5516 clstr = NULL;
5517 break;
5518 }
5519 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5520 if (clstr == NULL)
5521 break;
5522 clstr[count] = 0;
5523 }
5524 }
5525
5526 /*
5527 * Finally, put the new list in place.
5528 */
5529 vim_free(*clstr1);
5530 vim_free(*clstr2);
5531 *clstr1 = clstr;
5532}
5533
5534/*
5535 * Lookup a syntax cluster name and return it's ID.
5536 * If it is not found, 0 is returned.
5537 */
5538 static int
5539syn_scl_name2id(name)
5540 char_u *name;
5541{
5542 int i;
5543 char_u *name_u;
5544
5545 /* Avoid using stricmp() too much, it's slow on some systems */
5546 name_u = vim_strsave_up(name);
5547 if (name_u == NULL)
5548 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005549 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5550 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5551 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005552 break;
5553 vim_free(name_u);
5554 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5555}
5556
5557/*
5558 * Like syn_scl_name2id(), but take a pointer + length argument.
5559 */
5560 static int
5561syn_scl_namen2id(linep, len)
5562 char_u *linep;
5563 int len;
5564{
5565 char_u *name;
5566 int id = 0;
5567
5568 name = vim_strnsave(linep, len);
5569 if (name != NULL)
5570 {
5571 id = syn_scl_name2id(name);
5572 vim_free(name);
5573 }
5574 return id;
5575}
5576
5577/*
5578 * Find syntax cluster name in the table and return it's ID.
5579 * The argument is a pointer to the name and the length of the name.
5580 * If it doesn't exist yet, a new entry is created.
5581 * Return 0 for failure.
5582 */
5583 static int
5584syn_check_cluster(pp, len)
5585 char_u *pp;
5586 int len;
5587{
5588 int id;
5589 char_u *name;
5590
5591 name = vim_strnsave(pp, len);
5592 if (name == NULL)
5593 return 0;
5594
5595 id = syn_scl_name2id(name);
5596 if (id == 0) /* doesn't exist yet */
5597 id = syn_add_cluster(name);
5598 else
5599 vim_free(name);
5600 return id;
5601}
5602
5603/*
5604 * Add new syntax cluster and return it's ID.
5605 * "name" must be an allocated string, it will be consumed.
5606 * Return 0 for failure.
5607 */
5608 static int
5609syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005610 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005611{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005612 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005613
5614 /*
5615 * First call for this growarray: init growing array.
5616 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005617 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005619 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5620 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 }
5622
Bram Moolenaar42431a72011-04-01 14:44:59 +02005623 len = curwin->w_s->b_syn_clusters.ga_len;
5624 if (len >= MAX_CLUSTER_ID)
5625 {
5626 EMSG((char_u *)_("E848: Too many syntax clusters"));
5627 vim_free(name);
5628 return 0;
5629 }
5630
Bram Moolenaar071d4272004-06-13 20:20:40 +00005631 /*
5632 * Make room for at least one other cluster entry.
5633 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005634 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005635 {
5636 vim_free(name);
5637 return 0;
5638 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639
Bram Moolenaar860cae12010-06-05 23:22:07 +02005640 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5641 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5642 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5643 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5644 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005645
Bram Moolenaar217ad922005-03-20 22:37:15 +00005646 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005647 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005648 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005649 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005650
Bram Moolenaar071d4272004-06-13 20:20:40 +00005651 return len + SYNID_CLUSTER;
5652}
5653
5654/*
5655 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5656 * [add={groupname},..] [remove={groupname},..]".
5657 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658 static void
5659syn_cmd_cluster(eap, syncing)
5660 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005661 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005662{
5663 char_u *arg = eap->arg;
5664 char_u *group_name_end;
5665 char_u *rest;
5666 int scl_id;
5667 short *clstr_list;
5668 int got_clstr = FALSE;
5669 int opt_len;
5670 int list_op;
5671
5672 eap->nextcmd = find_nextcmd(arg);
5673 if (eap->skip)
5674 return;
5675
5676 rest = get_group_name(arg, &group_name_end);
5677
5678 if (rest != NULL)
5679 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005680 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5681 if (scl_id == 0)
5682 return;
5683 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684
5685 for (;;)
5686 {
5687 if (STRNICMP(rest, "add", 3) == 0
5688 && (vim_iswhite(rest[3]) || rest[3] == '='))
5689 {
5690 opt_len = 3;
5691 list_op = CLUSTER_ADD;
5692 }
5693 else if (STRNICMP(rest, "remove", 6) == 0
5694 && (vim_iswhite(rest[6]) || rest[6] == '='))
5695 {
5696 opt_len = 6;
5697 list_op = CLUSTER_SUBTRACT;
5698 }
5699 else if (STRNICMP(rest, "contains", 8) == 0
5700 && (vim_iswhite(rest[8]) || rest[8] == '='))
5701 {
5702 opt_len = 8;
5703 list_op = CLUSTER_REPLACE;
5704 }
5705 else
5706 break;
5707
5708 clstr_list = NULL;
5709 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5710 {
5711 EMSG2(_(e_invarg2), rest);
5712 break;
5713 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005714 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005715 &clstr_list, list_op);
5716 got_clstr = TRUE;
5717 }
5718
5719 if (got_clstr)
5720 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005721 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005722 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005723 }
5724 }
5725
5726 if (!got_clstr)
5727 EMSG(_("E400: No cluster specified"));
5728 if (rest == NULL || !ends_excmd(*rest))
5729 EMSG2(_(e_invarg2), arg);
5730}
5731
5732/*
5733 * On first call for current buffer: Init growing array.
5734 */
5735 static void
5736init_syn_patterns()
5737{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005738 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5739 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740}
5741
5742/*
5743 * Get one pattern for a ":syntax match" or ":syntax region" command.
5744 * Stores the pattern and program in a synpat_T.
5745 * Returns a pointer to the next argument, or NULL in case of an error.
5746 */
5747 static char_u *
5748get_syn_pattern(arg, ci)
5749 char_u *arg;
5750 synpat_T *ci;
5751{
5752 char_u *end;
5753 int *p;
5754 int idx;
5755 char_u *cpo_save;
5756
5757 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005758 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005759 return NULL;
5760
5761 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5762 if (*end != *arg) /* end delimiter not found */
5763 {
5764 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5765 return NULL;
5766 }
5767 /* store the pattern and compiled regexp program */
5768 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5769 return NULL;
5770
5771 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5772 cpo_save = p_cpo;
5773 p_cpo = (char_u *)"";
5774 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5775 p_cpo = cpo_save;
5776
5777 if (ci->sp_prog == NULL)
5778 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005780#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005781 syn_clear_time(&ci->sp_time);
5782#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005783
5784 /*
5785 * Check for a match, highlight or region offset.
5786 */
5787 ++end;
5788 do
5789 {
5790 for (idx = SPO_COUNT; --idx >= 0; )
5791 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5792 break;
5793 if (idx >= 0)
5794 {
5795 p = &(ci->sp_offsets[idx]);
5796 if (idx != SPO_LC_OFF)
5797 switch (end[3])
5798 {
5799 case 's': break;
5800 case 'b': break;
5801 case 'e': idx += SPO_COUNT; break;
5802 default: idx = -1; break;
5803 }
5804 if (idx >= 0)
5805 {
5806 ci->sp_off_flags |= (1 << idx);
5807 if (idx == SPO_LC_OFF) /* lc=99 */
5808 {
5809 end += 3;
5810 *p = getdigits(&end);
5811
5812 /* "lc=" offset automatically sets "ms=" offset */
5813 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5814 {
5815 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5816 ci->sp_offsets[SPO_MS_OFF] = *p;
5817 }
5818 }
5819 else /* yy=x+99 */
5820 {
5821 end += 4;
5822 if (*end == '+')
5823 {
5824 ++end;
5825 *p = getdigits(&end); /* positive offset */
5826 }
5827 else if (*end == '-')
5828 {
5829 ++end;
5830 *p = -getdigits(&end); /* negative offset */
5831 }
5832 }
5833 if (*end != ',')
5834 break;
5835 ++end;
5836 }
5837 }
5838 } while (idx >= 0);
5839
5840 if (!ends_excmd(*end) && !vim_iswhite(*end))
5841 {
5842 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5843 return NULL;
5844 }
5845 return skipwhite(end);
5846}
5847
5848/*
5849 * Handle ":syntax sync .." command.
5850 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005851 static void
5852syn_cmd_sync(eap, syncing)
5853 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005854 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005855{
5856 char_u *arg_start = eap->arg;
5857 char_u *arg_end;
5858 char_u *key = NULL;
5859 char_u *next_arg;
5860 int illegal = FALSE;
5861 int finished = FALSE;
5862 long n;
5863 char_u *cpo_save;
5864
5865 if (ends_excmd(*arg_start))
5866 {
5867 syn_cmd_list(eap, TRUE);
5868 return;
5869 }
5870
5871 while (!ends_excmd(*arg_start))
5872 {
5873 arg_end = skiptowhite(arg_start);
5874 next_arg = skipwhite(arg_end);
5875 vim_free(key);
5876 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5877 if (STRCMP(key, "CCOMMENT") == 0)
5878 {
5879 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005880 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881 if (!ends_excmd(*next_arg))
5882 {
5883 arg_end = skiptowhite(next_arg);
5884 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005885 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 (int)(arg_end - next_arg));
5887 next_arg = skipwhite(arg_end);
5888 }
5889 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005890 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005891 }
5892 else if ( STRNCMP(key, "LINES", 5) == 0
5893 || STRNCMP(key, "MINLINES", 8) == 0
5894 || STRNCMP(key, "MAXLINES", 8) == 0
5895 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5896 {
5897 if (key[4] == 'S')
5898 arg_end = key + 6;
5899 else if (key[0] == 'L')
5900 arg_end = key + 11;
5901 else
5902 arg_end = key + 9;
5903 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5904 {
5905 illegal = TRUE;
5906 break;
5907 }
5908 n = getdigits(&arg_end);
5909 if (!eap->skip)
5910 {
5911 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005912 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005913 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005914 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005915 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005916 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005917 }
5918 }
5919 else if (STRCMP(key, "FROMSTART") == 0)
5920 {
5921 if (!eap->skip)
5922 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005923 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5924 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005925 }
5926 }
5927 else if (STRCMP(key, "LINECONT") == 0)
5928 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005929 if (*next_arg == NUL) /* missing pattern */
5930 {
5931 illegal = TRUE;
5932 break;
5933 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005934 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005935 {
5936 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5937 finished = TRUE;
5938 break;
5939 }
5940 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5941 if (*arg_end != *next_arg) /* end delimiter not found */
5942 {
5943 illegal = TRUE;
5944 break;
5945 }
5946
5947 if (!eap->skip)
5948 {
5949 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005950 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005951 (int)(arg_end - next_arg - 1))) == NULL)
5952 {
5953 finished = TRUE;
5954 break;
5955 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005956 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005957
5958 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5959 cpo_save = p_cpo;
5960 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005961 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005962 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005963 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005964#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005965 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5966#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005967
Bram Moolenaar860cae12010-06-05 23:22:07 +02005968 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005969 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005970 vim_free(curwin->w_s->b_syn_linecont_pat);
5971 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005972 finished = TRUE;
5973 break;
5974 }
5975 }
5976 next_arg = skipwhite(arg_end + 1);
5977 }
5978 else
5979 {
5980 eap->arg = next_arg;
5981 if (STRCMP(key, "MATCH") == 0)
5982 syn_cmd_match(eap, TRUE);
5983 else if (STRCMP(key, "REGION") == 0)
5984 syn_cmd_region(eap, TRUE);
5985 else if (STRCMP(key, "CLEAR") == 0)
5986 syn_cmd_clear(eap, TRUE);
5987 else
5988 illegal = TRUE;
5989 finished = TRUE;
5990 break;
5991 }
5992 arg_start = next_arg;
5993 }
5994 vim_free(key);
5995 if (illegal)
5996 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5997 else if (!finished)
5998 {
5999 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006000 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006001 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006002 }
6003}
6004
6005/*
6006 * Convert a line of highlight group names into a list of group ID numbers.
6007 * "arg" should point to the "contains" or "nextgroup" keyword.
6008 * "arg" is advanced to after the last group name.
6009 * Careful: the argument is modified (NULs added).
6010 * returns FAIL for some error, OK for success.
6011 */
6012 static int
6013get_id_list(arg, keylen, list)
6014 char_u **arg;
6015 int keylen; /* length of keyword */
6016 short **list; /* where to store the resulting list, if not
6017 NULL, the list is silently skipped! */
6018{
6019 char_u *p = NULL;
6020 char_u *end;
6021 int round;
6022 int count;
6023 int total_count = 0;
6024 short *retval = NULL;
6025 char_u *name;
6026 regmatch_T regmatch;
6027 int id;
6028 int i;
6029 int failed = FALSE;
6030
6031 /*
6032 * We parse the list twice:
6033 * round == 1: count the number of items, allocate the array.
6034 * round == 2: fill the array with the items.
6035 * In round 1 new groups may be added, causing the number of items to
6036 * grow when a regexp is used. In that case round 1 is done once again.
6037 */
6038 for (round = 1; round <= 2; ++round)
6039 {
6040 /*
6041 * skip "contains"
6042 */
6043 p = skipwhite(*arg + keylen);
6044 if (*p != '=')
6045 {
6046 EMSG2(_("E405: Missing equal sign: %s"), *arg);
6047 break;
6048 }
6049 p = skipwhite(p + 1);
6050 if (ends_excmd(*p))
6051 {
6052 EMSG2(_("E406: Empty argument: %s"), *arg);
6053 break;
6054 }
6055
6056 /*
6057 * parse the arguments after "contains"
6058 */
6059 count = 0;
6060 while (!ends_excmd(*p))
6061 {
6062 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
6063 ;
6064 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
6065 if (name == NULL)
6066 {
6067 failed = TRUE;
6068 break;
6069 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006070 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006071 if ( STRCMP(name + 1, "ALLBUT") == 0
6072 || STRCMP(name + 1, "ALL") == 0
6073 || STRCMP(name + 1, "TOP") == 0
6074 || STRCMP(name + 1, "CONTAINED") == 0)
6075 {
6076 if (TOUPPER_ASC(**arg) != 'C')
6077 {
6078 EMSG2(_("E407: %s not allowed here"), name + 1);
6079 failed = TRUE;
6080 vim_free(name);
6081 break;
6082 }
6083 if (count != 0)
6084 {
6085 EMSG2(_("E408: %s must be first in contains list"), name + 1);
6086 failed = TRUE;
6087 vim_free(name);
6088 break;
6089 }
6090 if (name[1] == 'A')
6091 id = SYNID_ALLBUT;
6092 else if (name[1] == 'T')
6093 id = SYNID_TOP;
6094 else
6095 id = SYNID_CONTAINED;
6096 id += current_syn_inc_tag;
6097 }
6098 else if (name[1] == '@')
6099 {
6100 id = syn_check_cluster(name + 2, (int)(end - p - 1));
6101 }
6102 else
6103 {
6104 /*
6105 * Handle full group name.
6106 */
6107 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6108 id = syn_check_group(name + 1, (int)(end - p));
6109 else
6110 {
6111 /*
6112 * Handle match of regexp with group names.
6113 */
6114 *name = '^';
6115 STRCAT(name, "$");
6116 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6117 if (regmatch.regprog == NULL)
6118 {
6119 failed = TRUE;
6120 vim_free(name);
6121 break;
6122 }
6123
6124 regmatch.rm_ic = TRUE;
6125 id = 0;
6126 for (i = highlight_ga.ga_len; --i >= 0; )
6127 {
6128 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6129 (colnr_T)0))
6130 {
6131 if (round == 2)
6132 {
6133 /* Got more items than expected; can happen
6134 * when adding items that match:
6135 * "contains=a.*b,axb".
6136 * Go back to first round */
6137 if (count >= total_count)
6138 {
6139 vim_free(retval);
6140 round = 1;
6141 }
6142 else
6143 retval[count] = i + 1;
6144 }
6145 ++count;
6146 id = -1; /* remember that we found one */
6147 }
6148 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006149 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006150 }
6151 }
6152 vim_free(name);
6153 if (id == 0)
6154 {
6155 EMSG2(_("E409: Unknown group name: %s"), p);
6156 failed = TRUE;
6157 break;
6158 }
6159 if (id > 0)
6160 {
6161 if (round == 2)
6162 {
6163 /* Got more items than expected, go back to first round */
6164 if (count >= total_count)
6165 {
6166 vim_free(retval);
6167 round = 1;
6168 }
6169 else
6170 retval[count] = id;
6171 }
6172 ++count;
6173 }
6174 p = skipwhite(end);
6175 if (*p != ',')
6176 break;
6177 p = skipwhite(p + 1); /* skip comma in between arguments */
6178 }
6179 if (failed)
6180 break;
6181 if (round == 1)
6182 {
6183 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6184 if (retval == NULL)
6185 break;
6186 retval[count] = 0; /* zero means end of the list */
6187 total_count = count;
6188 }
6189 }
6190
6191 *arg = p;
6192 if (failed || retval == NULL)
6193 {
6194 vim_free(retval);
6195 return FAIL;
6196 }
6197
6198 if (*list == NULL)
6199 *list = retval;
6200 else
6201 vim_free(retval); /* list already found, don't overwrite it */
6202
6203 return OK;
6204}
6205
6206/*
6207 * Make a copy of an ID list.
6208 */
6209 static short *
6210copy_id_list(list)
6211 short *list;
6212{
6213 int len;
6214 int count;
6215 short *retval;
6216
6217 if (list == NULL)
6218 return NULL;
6219
6220 for (count = 0; list[count]; ++count)
6221 ;
6222 len = (count + 1) * sizeof(short);
6223 retval = (short *)alloc((unsigned)len);
6224 if (retval != NULL)
6225 mch_memmove(retval, list, (size_t)len);
6226
6227 return retval;
6228}
6229
6230/*
6231 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6232 * "cur_si" can be NULL if not checking the "containedin" list.
6233 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6234 * the current item.
6235 * This function is called very often, keep it fast!!
6236 */
6237 static int
6238in_id_list(cur_si, list, ssp, contained)
6239 stateitem_T *cur_si; /* current item or NULL */
6240 short *list; /* id list */
6241 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6242 int contained; /* group id is contained */
6243{
6244 int retval;
6245 short *scl_list;
6246 short item;
6247 short id = ssp->id;
6248 static int depth = 0;
6249 int r;
6250
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006251 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006252 if (cur_si != NULL && ssp->cont_in_list != NULL
6253 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006254 {
6255 /* Ignore transparent items without a contains argument. Double check
6256 * that we don't go back past the first one. */
6257 while ((cur_si->si_flags & HL_TRANS_CONT)
6258 && cur_si > (stateitem_T *)(current_state.ga_data))
6259 --cur_si;
6260 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6261 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006262 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6263 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 return TRUE;
6265 }
6266
6267 if (list == NULL)
6268 return FALSE;
6269
6270 /*
6271 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6272 * inside anything. Only allow not-contained groups.
6273 */
6274 if (list == ID_LIST_ALL)
6275 return !contained;
6276
6277 /*
6278 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6279 * contains list. We also require that "id" is at the same ":syn include"
6280 * level as the list.
6281 */
6282 item = *list;
6283 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6284 {
6285 if (item < SYNID_TOP)
6286 {
6287 /* ALL or ALLBUT: accept all groups in the same file */
6288 if (item - SYNID_ALLBUT != ssp->inc_tag)
6289 return FALSE;
6290 }
6291 else if (item < SYNID_CONTAINED)
6292 {
6293 /* TOP: accept all not-contained groups in the same file */
6294 if (item - SYNID_TOP != ssp->inc_tag || contained)
6295 return FALSE;
6296 }
6297 else
6298 {
6299 /* CONTAINED: accept all contained groups in the same file */
6300 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6301 return FALSE;
6302 }
6303 item = *++list;
6304 retval = FALSE;
6305 }
6306 else
6307 retval = TRUE;
6308
6309 /*
6310 * Return "retval" if id is in the contains list.
6311 */
6312 while (item != 0)
6313 {
6314 if (item == id)
6315 return retval;
6316 if (item >= SYNID_CLUSTER)
6317 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006318 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006319 /* restrict recursiveness to 30 to avoid an endless loop for a
6320 * cluster that includes itself (indirectly) */
6321 if (scl_list != NULL && depth < 30)
6322 {
6323 ++depth;
6324 r = in_id_list(NULL, scl_list, ssp, contained);
6325 --depth;
6326 if (r)
6327 return retval;
6328 }
6329 }
6330 item = *++list;
6331 }
6332 return !retval;
6333}
6334
6335struct subcommand
6336{
6337 char *name; /* subcommand name */
6338 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6339};
6340
6341static struct subcommand subcommands[] =
6342{
6343 {"case", syn_cmd_case},
6344 {"clear", syn_cmd_clear},
6345 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006346 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347 {"enable", syn_cmd_enable},
6348 {"include", syn_cmd_include},
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006349 {"iskeyword", syn_cmd_iskeyword},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350 {"keyword", syn_cmd_keyword},
6351 {"list", syn_cmd_list},
6352 {"manual", syn_cmd_manual},
6353 {"match", syn_cmd_match},
6354 {"on", syn_cmd_on},
6355 {"off", syn_cmd_off},
6356 {"region", syn_cmd_region},
6357 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006358 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006359 {"sync", syn_cmd_sync},
6360 {"", syn_cmd_list},
6361 {NULL, NULL}
6362};
6363
6364/*
6365 * ":syntax".
6366 * This searches the subcommands[] table for the subcommand name, and calls a
6367 * syntax_subcommand() function to do the rest.
6368 */
6369 void
6370ex_syntax(eap)
6371 exarg_T *eap;
6372{
6373 char_u *arg = eap->arg;
6374 char_u *subcmd_end;
6375 char_u *subcmd_name;
6376 int i;
6377
6378 syn_cmdlinep = eap->cmdlinep;
6379
6380 /* isolate subcommand name */
6381 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6382 ;
6383 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6384 if (subcmd_name != NULL)
6385 {
6386 if (eap->skip) /* skip error messages for all subcommands */
6387 ++emsg_skip;
6388 for (i = 0; ; ++i)
6389 {
6390 if (subcommands[i].name == NULL)
6391 {
6392 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6393 break;
6394 }
6395 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6396 {
6397 eap->arg = skipwhite(subcmd_end);
6398 (subcommands[i].func)(eap, FALSE);
6399 break;
6400 }
6401 }
6402 vim_free(subcmd_name);
6403 if (eap->skip)
6404 --emsg_skip;
6405 }
6406}
6407
Bram Moolenaar860cae12010-06-05 23:22:07 +02006408 void
6409ex_ownsyntax(eap)
6410 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006411{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006412 char_u *old_value;
6413 char_u *new_value;
6414
Bram Moolenaar860cae12010-06-05 23:22:07 +02006415 if (curwin->w_s == &curwin->w_buffer->b_s)
6416 {
6417 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6418 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006419 hash_init(&curwin->w_s->b_keywtab);
6420 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006421#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006422 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006423 curwin->w_p_spell = FALSE; /* No spell checking */
6424 clear_string_option(&curwin->w_s->b_p_spc);
6425 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006426 clear_string_option(&curwin->w_s->b_p_spl);
6427#endif
Bram Moolenaarb8060fe2016-01-19 22:29:28 +01006428 clear_string_option(&curwin->w_s->b_syn_isk);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006429 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006430
6431 /* save value of b:current_syntax */
6432 old_value = get_var_value((char_u *)"b:current_syntax");
6433 if (old_value != NULL)
6434 old_value = vim_strsave(old_value);
6435
6436 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6437 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006438 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006439
6440 /* move value of b:current_syntax to w:current_syntax */
6441 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006442 if (new_value != NULL)
6443 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006444
6445 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006446 if (old_value == NULL)
6447 do_unlet((char_u *)"b:current_syntax", TRUE);
6448 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006449 {
6450 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6451 vim_free(old_value);
6452 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006453}
6454
6455 int
6456syntax_present(win)
6457 win_T *win;
6458{
6459 return (win->w_s->b_syn_patterns.ga_len != 0
6460 || win->w_s->b_syn_clusters.ga_len != 0
6461 || win->w_s->b_keywtab.ht_used > 0
6462 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463}
6464
6465#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6466
6467static enum
6468{
6469 EXP_SUBCMD, /* expand ":syn" sub-commands */
6470 EXP_CASE /* expand ":syn case" arguments */
6471} expand_what;
6472
Bram Moolenaar4f688582007-07-24 12:34:30 +00006473/*
6474 * Reset include_link, include_default, include_none to 0.
6475 * Called when we are done expanding.
6476 */
6477 void
6478reset_expand_highlight()
6479{
6480 include_link = include_default = include_none = 0;
6481}
6482
6483/*
6484 * Handle command line completion for :match and :echohl command: Add "None"
6485 * as highlight group.
6486 */
6487 void
6488set_context_in_echohl_cmd(xp, arg)
6489 expand_T *xp;
6490 char_u *arg;
6491{
6492 xp->xp_context = EXPAND_HIGHLIGHT;
6493 xp->xp_pattern = arg;
6494 include_none = 1;
6495}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496
6497/*
6498 * Handle command line completion for :syntax command.
6499 */
6500 void
6501set_context_in_syntax_cmd(xp, arg)
6502 expand_T *xp;
6503 char_u *arg;
6504{
6505 char_u *p;
6506
6507 /* Default: expand subcommands */
6508 xp->xp_context = EXPAND_SYNTAX;
6509 expand_what = EXP_SUBCMD;
6510 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006511 include_link = 0;
6512 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006513
6514 /* (part of) subcommand already typed */
6515 if (*arg != NUL)
6516 {
6517 p = skiptowhite(arg);
6518 if (*p != NUL) /* past first word */
6519 {
6520 xp->xp_pattern = skipwhite(p);
6521 if (*skiptowhite(xp->xp_pattern) != NUL)
6522 xp->xp_context = EXPAND_NOTHING;
6523 else if (STRNICMP(arg, "case", p - arg) == 0)
6524 expand_what = EXP_CASE;
6525 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6526 || STRNICMP(arg, "region", p - arg) == 0
6527 || STRNICMP(arg, "match", p - arg) == 0
6528 || STRNICMP(arg, "list", p - arg) == 0)
6529 xp->xp_context = EXPAND_HIGHLIGHT;
6530 else
6531 xp->xp_context = EXPAND_NOTHING;
6532 }
6533 }
6534}
6535
6536static char *(case_args[]) = {"match", "ignore", NULL};
6537
6538/*
6539 * Function given to ExpandGeneric() to obtain the list syntax names for
6540 * expansion.
6541 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006542 char_u *
6543get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006544 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006545 int idx;
6546{
6547 if (expand_what == EXP_SUBCMD)
6548 return (char_u *)subcommands[idx].name;
6549 return (char_u *)case_args[idx];
6550}
6551
6552#endif /* FEAT_CMDL_COMPL */
6553
Bram Moolenaar071d4272004-06-13 20:20:40 +00006554/*
6555 * Function called for expression evaluation: get syntax ID at file position.
6556 */
6557 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006558syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006559 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006561 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006562 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006563 int *spellp; /* return: can do spell checking */
6564 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565{
6566 /* When the position is not after the current position and in the same
6567 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006568 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006569 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006570 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006571 syntax_start(wp, lnum);
Bram Moolenaar6773a342016-01-19 20:52:44 +01006572 else if (wp->w_buffer == syn_buf
6573 && lnum == current_lnum
6574 && col > current_col)
6575 /* next_match may not be correct when moving around, e.g. with the
6576 * "skip" expression in searchpair() */
6577 next_match_idx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006578
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006579 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006580
6581 return (trans ? current_trans_id : current_id);
6582}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006583
Bram Moolenaar860cae12010-06-05 23:22:07 +02006584#if defined(FEAT_CONCEAL) || defined(PROTO)
6585/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006586 * Get extra information about the syntax item. Must be called right after
6587 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006588 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006589 * Returns the current flags.
6590 */
6591 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006592get_syntax_info(seqnrp)
6593 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006594{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006595 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006596 return current_flags;
6597}
6598
6599/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006600 * Return conceal substitution character
6601 */
6602 int
6603syn_get_sub_char()
6604{
6605 return current_sub_char;
6606}
6607#endif
6608
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006609#if defined(FEAT_EVAL) || defined(PROTO)
6610/*
6611 * Return the syntax ID at position "i" in the current stack.
6612 * The caller must have called syn_get_id() before to fill the stack.
6613 * Returns -1 when "i" is out of range.
6614 */
6615 int
6616syn_get_stack_item(i)
6617 int i;
6618{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006619 if (i >= current_state.ga_len)
6620 {
6621 /* Need to invalidate the state, because we didn't properly finish it
6622 * for the last character, "keep_state" was TRUE. */
6623 invalidate_current_state();
6624 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006625 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006626 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006627 return CUR_STATE(i).si_id;
6628}
6629#endif
6630
Bram Moolenaar071d4272004-06-13 20:20:40 +00006631#if defined(FEAT_FOLDING) || defined(PROTO)
6632/*
6633 * Function called to get folding level for line "lnum" in window "wp".
6634 */
6635 int
6636syn_get_foldlevel(wp, lnum)
6637 win_T *wp;
6638 long lnum;
6639{
6640 int level = 0;
6641 int i;
6642
6643 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006644 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006645 {
6646 syntax_start(wp, lnum);
6647
6648 for (i = 0; i < current_state.ga_len; ++i)
6649 if (CUR_STATE(i).si_flags & HL_FOLD)
6650 ++level;
6651 }
6652 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006653 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006654 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006655 if (level < 0)
6656 level = 0;
6657 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006658 return level;
6659}
6660#endif
6661
Bram Moolenaar01615492015-02-03 13:00:38 +01006662#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006663/*
6664 * ":syntime".
6665 */
6666 void
6667ex_syntime(eap)
6668 exarg_T *eap;
6669{
6670 if (STRCMP(eap->arg, "on") == 0)
6671 syn_time_on = TRUE;
6672 else if (STRCMP(eap->arg, "off") == 0)
6673 syn_time_on = FALSE;
6674 else if (STRCMP(eap->arg, "clear") == 0)
6675 syntime_clear();
6676 else if (STRCMP(eap->arg, "report") == 0)
6677 syntime_report();
6678 else
6679 EMSG2(_(e_invarg2), eap->arg);
6680}
6681
6682 static void
6683syn_clear_time(st)
6684 syn_time_T *st;
6685{
6686 profile_zero(&st->total);
6687 profile_zero(&st->slowest);
6688 st->count = 0;
6689 st->match = 0;
6690}
6691
6692/*
6693 * Clear the syntax timing for the current buffer.
6694 */
6695 static void
6696syntime_clear()
6697{
6698 int idx;
6699 synpat_T *spp;
6700
6701 if (!syntax_present(curwin))
6702 {
6703 MSG(_(msg_no_items));
6704 return;
6705 }
6706 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6707 {
6708 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6709 syn_clear_time(&spp->sp_time);
6710 }
6711}
6712
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006713#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6714/*
6715 * Function given to ExpandGeneric() to obtain the possible arguments of the
6716 * ":syntime {on,off,clear,report}" command.
6717 */
6718 char_u *
6719get_syntime_arg(xp, idx)
6720 expand_T *xp UNUSED;
6721 int idx;
6722{
6723 switch (idx)
6724 {
6725 case 0: return (char_u *)"on";
6726 case 1: return (char_u *)"off";
6727 case 2: return (char_u *)"clear";
6728 case 3: return (char_u *)"report";
6729 }
6730 return NULL;
6731}
6732#endif
6733
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006734typedef struct
6735{
6736 proftime_T total;
6737 int count;
6738 int match;
6739 proftime_T slowest;
6740 proftime_T average;
6741 int id;
6742 char_u *pattern;
6743} time_entry_T;
6744
6745 static int
6746#ifdef __BORLANDC__
6747_RTLENTRYF
6748#endif
6749syn_compare_syntime(v1, v2)
6750 const void *v1;
6751 const void *v2;
6752{
6753 const time_entry_T *s1 = v1;
6754 const time_entry_T *s2 = v2;
6755
6756 return profile_cmp(&s1->total, &s2->total);
6757}
6758
6759/*
6760 * Clear the syntax timing for the current buffer.
6761 */
6762 static void
6763syntime_report()
6764{
6765 int idx;
6766 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006767# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006768 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006769# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006770 int len;
6771 proftime_T total_total;
6772 int total_count = 0;
6773 garray_T ga;
6774 time_entry_T *p;
6775
6776 if (!syntax_present(curwin))
6777 {
6778 MSG(_(msg_no_items));
6779 return;
6780 }
6781
6782 ga_init2(&ga, sizeof(time_entry_T), 50);
6783 profile_zero(&total_total);
6784 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6785 {
6786 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6787 if (spp->sp_time.count > 0)
6788 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006789 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006790 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6791 p->total = spp->sp_time.total;
6792 profile_add(&total_total, &spp->sp_time.total);
6793 p->count = spp->sp_time.count;
6794 p->match = spp->sp_time.match;
6795 total_count += spp->sp_time.count;
6796 p->slowest = spp->sp_time.slowest;
6797# ifdef FEAT_FLOAT
6798 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6799 p->average = tm;
6800# endif
6801 p->id = spp->sp_syn.id;
6802 p->pattern = spp->sp_pattern;
6803 ++ga.ga_len;
6804 }
6805 }
6806
6807 /* sort on total time */
Bram Moolenaar4e312962013-06-06 21:19:51 +02006808 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6809 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006810
6811 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6812 MSG_PUTS("\n");
6813 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6814 {
6815 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6816 p = ((time_entry_T *)ga.ga_data) + idx;
6817
6818 MSG_PUTS(profile_msg(&p->total));
6819 MSG_PUTS(" "); /* make sure there is always a separating space */
6820 msg_advance(13);
6821 msg_outnum(p->count);
6822 MSG_PUTS(" ");
6823 msg_advance(20);
6824 msg_outnum(p->match);
6825 MSG_PUTS(" ");
6826 msg_advance(26);
6827 MSG_PUTS(profile_msg(&p->slowest));
6828 MSG_PUTS(" ");
6829 msg_advance(38);
6830# ifdef FEAT_FLOAT
6831 MSG_PUTS(profile_msg(&p->average));
6832 MSG_PUTS(" ");
6833# endif
6834 msg_advance(50);
6835 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6836 MSG_PUTS(" ");
6837
6838 msg_advance(69);
6839 if (Columns < 80)
6840 len = 20; /* will wrap anyway */
6841 else
6842 len = Columns - 70;
6843 if (len > (int)STRLEN(p->pattern))
6844 len = (int)STRLEN(p->pattern);
6845 msg_outtrans_len(p->pattern, len);
6846 MSG_PUTS("\n");
6847 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006848 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006849 if (!got_int)
6850 {
6851 MSG_PUTS("\n");
6852 MSG_PUTS(profile_msg(&total_total));
6853 msg_advance(13);
6854 msg_outnum(total_count);
6855 MSG_PUTS("\n");
6856 }
6857}
6858#endif
6859
Bram Moolenaar071d4272004-06-13 20:20:40 +00006860#endif /* FEAT_SYN_HL */
6861
Bram Moolenaar071d4272004-06-13 20:20:40 +00006862/**************************************
6863 * Highlighting stuff *
6864 **************************************/
6865
6866/*
6867 * The default highlight groups. These are compiled-in for fast startup and
6868 * they still work when the runtime files can't be found.
6869 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006870 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6871 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006872 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006873#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006874# define CENT(a, b) b
6875#else
6876# define CENT(a, b) a
6877#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878static char *(highlight_init_both[]) =
6879 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006880 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6881 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6882 CENT("IncSearch term=reverse cterm=reverse",
6883 "IncSearch term=reverse cterm=reverse gui=reverse"),
6884 CENT("ModeMsg term=bold cterm=bold",
6885 "ModeMsg term=bold cterm=bold gui=bold"),
6886 CENT("NonText term=bold ctermfg=Blue",
6887 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6888 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6889 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6890 CENT("StatusLineNC term=reverse cterm=reverse",
6891 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006892#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006893 CENT("VertSplit term=reverse cterm=reverse",
6894 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006895#endif
6896#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006897 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6898 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006899#endif
6900#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006901 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6902 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006903#endif
6904#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006905 CENT("PmenuSbar ctermbg=Grey",
6906 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006907#endif
6908#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006909 CENT("TabLineSel term=bold cterm=bold",
6910 "TabLineSel term=bold cterm=bold gui=bold"),
6911 CENT("TabLineFill term=reverse cterm=reverse",
6912 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006913#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006914#ifdef FEAT_GUI
6915 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006916 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006917#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006918 NULL
6919 };
6920
6921static char *(highlight_init_light[]) =
6922 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006923 CENT("Directory term=bold ctermfg=DarkBlue",
6924 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6925 CENT("LineNr term=underline ctermfg=Brown",
6926 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006927 CENT("CursorLineNr term=bold ctermfg=Brown",
6928 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006929 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6930 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6931 CENT("Question term=standout ctermfg=DarkGreen",
6932 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6933 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6934 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006935#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006936 CENT("SpellBad term=reverse ctermbg=LightRed",
6937 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6938 CENT("SpellCap term=reverse ctermbg=LightBlue",
6939 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6940 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6941 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6942 CENT("SpellLocal term=underline ctermbg=Cyan",
6943 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006944#endif
6945#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006946 CENT("PmenuThumb ctermbg=Black",
6947 "PmenuThumb ctermbg=Black guibg=Black"),
6948 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6949 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6950 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6951 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006952#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006953 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6954 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6955 CENT("Title term=bold ctermfg=DarkMagenta",
6956 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6957 CENT("WarningMsg term=standout ctermfg=DarkRed",
6958 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006959#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006960 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6961 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006962#endif
6963#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006964 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6965 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6966 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6967 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006968#endif
6969#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006970 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6971 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006972#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006973 CENT("Visual term=reverse",
6974 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006975#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006976 CENT("DiffAdd term=bold ctermbg=LightBlue",
6977 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6978 CENT("DiffChange term=bold ctermbg=LightMagenta",
6979 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6980 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6981 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006982#endif
6983#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006984 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6985 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006986#endif
6987#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006988 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006989 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006990 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006991 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006992 CENT("ColorColumn term=reverse ctermbg=LightRed",
6993 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006994#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006995#ifdef FEAT_CONCEAL
6996 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6997 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6998#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006999#ifdef FEAT_AUTOCMD
7000 CENT("MatchParen term=reverse ctermbg=Cyan",
7001 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
7002#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007003#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007004 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007005#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007006 NULL
7007 };
7008
7009static char *(highlight_init_dark[]) =
7010 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007011 CENT("Directory term=bold ctermfg=LightCyan",
7012 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
7013 CENT("LineNr term=underline ctermfg=Yellow",
7014 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01007015 CENT("CursorLineNr term=bold ctermfg=Yellow",
7016 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007017 CENT("MoreMsg term=bold ctermfg=LightGreen",
7018 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
7019 CENT("Question term=standout ctermfg=LightGreen",
7020 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
7021 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
7022 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
7023 CENT("SpecialKey term=bold ctermfg=LightBlue",
7024 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007025#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007026 CENT("SpellBad term=reverse ctermbg=Red",
7027 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
7028 CENT("SpellCap term=reverse ctermbg=Blue",
7029 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
7030 CENT("SpellRare term=reverse ctermbg=Magenta",
7031 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
7032 CENT("SpellLocal term=underline ctermbg=Cyan",
7033 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007034#endif
7035#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01007036 CENT("PmenuThumb ctermbg=White",
7037 "PmenuThumb ctermbg=White guibg=White"),
7038 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
7039 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02007040 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
7041 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007042#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007043 CENT("Title term=bold ctermfg=LightMagenta",
7044 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
7045 CENT("WarningMsg term=standout ctermfg=LightRed",
7046 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007047#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007048 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
7049 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007050#endif
7051#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007052 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
7053 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
7054 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7055 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007056#endif
7057#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007058 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
7059 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007060#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007061 CENT("Visual term=reverse",
7062 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007063#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007064 CENT("DiffAdd term=bold ctermbg=DarkBlue",
7065 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
7066 CENT("DiffChange term=bold ctermbg=DarkMagenta",
7067 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
7068 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
7069 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007070#endif
7071#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007072 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
7073 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007074#endif
7075#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007076 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007077 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00007078 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007079 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02007080 CENT("ColorColumn term=reverse ctermbg=DarkRed",
7081 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00007082#endif
7083#ifdef FEAT_AUTOCMD
7084 CENT("MatchParen term=reverse ctermbg=DarkCyan",
7085 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007086#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02007087#ifdef FEAT_CONCEAL
7088 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
7089 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
7090#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007091#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00007092 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00007093#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007094 NULL
7095 };
7096
7097 void
7098init_highlight(both, reset)
7099 int both; /* include groups where 'bg' doesn't matter */
7100 int reset; /* clear group first */
7101{
7102 int i;
7103 char **pp;
7104 static int had_both = FALSE;
7105#ifdef FEAT_EVAL
7106 char_u *p;
7107
7108 /*
7109 * Try finding the color scheme file. Used when a color file was loaded
7110 * and 'background' or 't_Co' is changed.
7111 */
7112 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007113 if (p != NULL)
7114 {
7115 /* The value of g:colors_name could be freed when sourcing the script,
7116 * making "p" invalid, so copy it. */
7117 char_u *copy_p = vim_strsave(p);
7118 int r;
7119
7120 if (copy_p != NULL)
7121 {
7122 r = load_colors(copy_p);
7123 vim_free(copy_p);
7124 if (r == OK)
7125 return;
7126 }
7127 }
7128
Bram Moolenaar071d4272004-06-13 20:20:40 +00007129#endif
7130
7131 /*
7132 * Didn't use a color file, use the compiled-in colors.
7133 */
7134 if (both)
7135 {
7136 had_both = TRUE;
7137 pp = highlight_init_both;
7138 for (i = 0; pp[i] != NULL; ++i)
7139 do_highlight((char_u *)pp[i], reset, TRUE);
7140 }
7141 else if (!had_both)
7142 /* Don't do anything before the call with both == TRUE from main().
7143 * Not everything has been setup then, and that call will overrule
7144 * everything anyway. */
7145 return;
7146
7147 if (*p_bg == 'l')
7148 pp = highlight_init_light;
7149 else
7150 pp = highlight_init_dark;
7151 for (i = 0; pp[i] != NULL; ++i)
7152 do_highlight((char_u *)pp[i], reset, TRUE);
7153
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007154 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007155 * depend on the number of colors available.
7156 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007157 * to avoid Statement highlighted text disappears.
7158 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007159 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007160 do_highlight((char_u *)(*p_bg == 'l'
7161 ? "Visual cterm=NONE ctermbg=LightGrey"
7162 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007163 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007164 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007165 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7166 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007167 if (*p_bg == 'l')
7168 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7169 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007170
Bram Moolenaar071d4272004-06-13 20:20:40 +00007171#ifdef FEAT_SYN_HL
7172 /*
7173 * If syntax highlighting is enabled load the highlighting for it.
7174 */
7175 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007176 {
7177 static int recursive = 0;
7178
7179 if (recursive >= 5)
7180 EMSG(_("E679: recursive loop loading syncolor.vim"));
7181 else
7182 {
7183 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007184 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007185 --recursive;
7186 }
7187 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007188#endif
7189}
7190
7191/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007192 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007193 * Return OK for success, FAIL for failure.
7194 */
7195 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007196load_colors(name)
7197 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007198{
7199 char_u *buf;
7200 int retval = FAIL;
7201 static int recursive = FALSE;
7202
7203 /* When being called recursively, this is probably because setting
7204 * 'background' caused the highlighting to be reloaded. This means it is
7205 * working, thus we should return OK. */
7206 if (recursive)
7207 return OK;
7208
7209 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007210 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007211 if (buf != NULL)
7212 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007213 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007214 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007215 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007216#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007217 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007218#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007219 }
7220 recursive = FALSE;
7221
7222 return retval;
7223}
7224
7225/*
7226 * Handle the ":highlight .." command.
7227 * When using ":hi clear" this is called recursively for each group with
7228 * "forceit" and "init" both TRUE.
7229 */
7230 void
7231do_highlight(line, forceit, init)
7232 char_u *line;
7233 int forceit;
7234 int init; /* TRUE when called for initializing */
7235{
7236 char_u *name_end;
7237 char_u *p;
7238 char_u *linep;
7239 char_u *key_start;
7240 char_u *arg_start;
7241 char_u *key = NULL, *arg = NULL;
7242 long i;
7243 int off;
7244 int len;
7245 int attr;
7246 int id;
7247 int idx;
7248 int dodefault = FALSE;
7249 int doclear = FALSE;
7250 int dolink = FALSE;
7251 int error = FALSE;
7252 int color;
7253 int is_normal_group = FALSE; /* "Normal" group */
7254#ifdef FEAT_GUI_X11
7255 int is_menu_group = FALSE; /* "Menu" group */
7256 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7257 int is_tooltip_group = FALSE; /* "Tooltip" group */
7258 int do_colors = FALSE; /* need to update colors? */
7259#else
7260# define is_menu_group 0
7261# define is_tooltip_group 0
7262#endif
7263
7264 /*
7265 * If no argument, list current highlighting.
7266 */
7267 if (ends_excmd(*line))
7268 {
7269 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7270 /* TODO: only call when the group has attributes set */
7271 highlight_list_one((int)i);
7272 return;
7273 }
7274
7275 /*
7276 * Isolate the name.
7277 */
7278 name_end = skiptowhite(line);
7279 linep = skipwhite(name_end);
7280
7281 /*
7282 * Check for "default" argument.
7283 */
7284 if (STRNCMP(line, "default", name_end - line) == 0)
7285 {
7286 dodefault = TRUE;
7287 line = linep;
7288 name_end = skiptowhite(line);
7289 linep = skipwhite(name_end);
7290 }
7291
7292 /*
7293 * Check for "clear" or "link" argument.
7294 */
7295 if (STRNCMP(line, "clear", name_end - line) == 0)
7296 doclear = TRUE;
7297 if (STRNCMP(line, "link", name_end - line) == 0)
7298 dolink = TRUE;
7299
7300 /*
7301 * ":highlight {group-name}": list highlighting for one group.
7302 */
7303 if (!doclear && !dolink && ends_excmd(*linep))
7304 {
7305 id = syn_namen2id(line, (int)(name_end - line));
7306 if (id == 0)
7307 EMSG2(_("E411: highlight group not found: %s"), line);
7308 else
7309 highlight_list_one(id);
7310 return;
7311 }
7312
7313 /*
7314 * Handle ":highlight link {from} {to}" command.
7315 */
7316 if (dolink)
7317 {
7318 char_u *from_start = linep;
7319 char_u *from_end;
7320 char_u *to_start;
7321 char_u *to_end;
7322 int from_id;
7323 int to_id;
7324
7325 from_end = skiptowhite(from_start);
7326 to_start = skipwhite(from_end);
7327 to_end = skiptowhite(to_start);
7328
7329 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7330 {
7331 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7332 from_start);
7333 return;
7334 }
7335
7336 if (!ends_excmd(*skipwhite(to_end)))
7337 {
7338 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7339 return;
7340 }
7341
7342 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7343 if (STRNCMP(to_start, "NONE", 4) == 0)
7344 to_id = 0;
7345 else
7346 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7347
7348 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7349 {
7350 /*
7351 * Don't allow a link when there already is some highlighting
7352 * for the group, unless '!' is used
7353 */
7354 if (to_id > 0 && !forceit && !init
7355 && hl_has_settings(from_id - 1, dodefault))
7356 {
7357 if (sourcing_name == NULL && !dodefault)
7358 EMSG(_("E414: group has settings, highlight link ignored"));
7359 }
7360 else
7361 {
7362 if (!init)
7363 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7364 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007365#ifdef FEAT_EVAL
7366 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7367#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007368 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007369 }
7370 }
7371
7372 /* Only call highlight_changed() once, after sourcing a syntax file */
7373 need_highlight_changed = TRUE;
7374
7375 return;
7376 }
7377
7378 if (doclear)
7379 {
7380 /*
7381 * ":highlight clear [group]" command.
7382 */
7383 line = linep;
7384 if (ends_excmd(*line))
7385 {
7386#ifdef FEAT_GUI
7387 /* First, we do not destroy the old values, but allocate the new
7388 * ones and update the display. THEN we destroy the old values.
7389 * If we destroy the old values first, then the old values
7390 * (such as GuiFont's or GuiFontset's) will still be displayed but
7391 * invalid because they were free'd.
7392 */
7393 if (gui.in_use)
7394 {
7395# ifdef FEAT_BEVAL_TIP
7396 gui_init_tooltip_font();
7397# endif
7398# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7399 gui_init_menu_font();
7400# endif
7401 }
7402# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7403 gui_mch_def_colors();
7404# endif
7405# ifdef FEAT_GUI_X11
7406# ifdef FEAT_MENU
7407
7408 /* This only needs to be done when there is no Menu highlight
7409 * group defined by default, which IS currently the case.
7410 */
7411 gui_mch_new_menu_colors();
7412# endif
7413 if (gui.in_use)
7414 {
7415 gui_new_scrollbar_colors();
7416# ifdef FEAT_BEVAL
7417 gui_mch_new_tooltip_colors();
7418# endif
7419# ifdef FEAT_MENU
7420 gui_mch_new_menu_font();
7421# endif
7422 }
7423# endif
7424
7425 /* Ok, we're done allocating the new default graphics items.
7426 * The screen should already be refreshed at this point.
7427 * It is now Ok to clear out the old data.
7428 */
7429#endif
7430#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007431 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007432#endif
7433 restore_cterm_colors();
7434
7435 /*
7436 * Clear all default highlight groups and load the defaults.
7437 */
7438 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7439 highlight_clear(idx);
7440 init_highlight(TRUE, TRUE);
7441#ifdef FEAT_GUI
7442 if (gui.in_use)
7443 highlight_gui_started();
7444#endif
7445 highlight_changed();
7446 redraw_later_clear();
7447 return;
7448 }
7449 name_end = skiptowhite(line);
7450 linep = skipwhite(name_end);
7451 }
7452
7453 /*
7454 * Find the group name in the table. If it does not exist yet, add it.
7455 */
7456 id = syn_check_group(line, (int)(name_end - line));
7457 if (id == 0) /* failed (out of memory) */
7458 return;
7459 idx = id - 1; /* index is ID minus one */
7460
7461 /* Return if "default" was used and the group already has settings. */
7462 if (dodefault && hl_has_settings(idx, TRUE))
7463 return;
7464
7465 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7466 is_normal_group = TRUE;
7467#ifdef FEAT_GUI_X11
7468 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7469 is_menu_group = TRUE;
7470 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7471 is_scrollbar_group = TRUE;
7472 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7473 is_tooltip_group = TRUE;
7474#endif
7475
7476 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7477 if (doclear || (forceit && init))
7478 {
7479 highlight_clear(idx);
7480 if (!doclear)
7481 HL_TABLE()[idx].sg_set = 0;
7482 }
7483
7484 if (!doclear)
7485 while (!ends_excmd(*linep))
7486 {
7487 key_start = linep;
7488 if (*linep == '=')
7489 {
7490 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7491 error = TRUE;
7492 break;
7493 }
7494
7495 /*
7496 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7497 * "guibg").
7498 */
7499 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7500 ++linep;
7501 vim_free(key);
7502 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7503 if (key == NULL)
7504 {
7505 error = TRUE;
7506 break;
7507 }
7508 linep = skipwhite(linep);
7509
7510 if (STRCMP(key, "NONE") == 0)
7511 {
7512 if (!init || HL_TABLE()[idx].sg_set == 0)
7513 {
7514 if (!init)
7515 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7516 highlight_clear(idx);
7517 }
7518 continue;
7519 }
7520
7521 /*
7522 * Check for the equal sign.
7523 */
7524 if (*linep != '=')
7525 {
7526 EMSG2(_("E416: missing equal sign: %s"), key_start);
7527 error = TRUE;
7528 break;
7529 }
7530 ++linep;
7531
7532 /*
7533 * Isolate the argument.
7534 */
7535 linep = skipwhite(linep);
7536 if (*linep == '\'') /* guifg='color name' */
7537 {
7538 arg_start = ++linep;
7539 linep = vim_strchr(linep, '\'');
7540 if (linep == NULL)
7541 {
7542 EMSG2(_(e_invarg2), key_start);
7543 error = TRUE;
7544 break;
7545 }
7546 }
7547 else
7548 {
7549 arg_start = linep;
7550 linep = skiptowhite(linep);
7551 }
7552 if (linep == arg_start)
7553 {
7554 EMSG2(_("E417: missing argument: %s"), key_start);
7555 error = TRUE;
7556 break;
7557 }
7558 vim_free(arg);
7559 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7560 if (arg == NULL)
7561 {
7562 error = TRUE;
7563 break;
7564 }
7565 if (*linep == '\'')
7566 ++linep;
7567
7568 /*
7569 * Store the argument.
7570 */
7571 if ( STRCMP(key, "TERM") == 0
7572 || STRCMP(key, "CTERM") == 0
7573 || STRCMP(key, "GUI") == 0)
7574 {
7575 attr = 0;
7576 off = 0;
7577 while (arg[off] != NUL)
7578 {
7579 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7580 {
7581 len = (int)STRLEN(hl_name_table[i]);
7582 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7583 {
7584 attr |= hl_attr_table[i];
7585 off += len;
7586 break;
7587 }
7588 }
7589 if (i < 0)
7590 {
7591 EMSG2(_("E418: Illegal value: %s"), arg);
7592 error = TRUE;
7593 break;
7594 }
7595 if (arg[off] == ',') /* another one follows */
7596 ++off;
7597 }
7598 if (error)
7599 break;
7600 if (*key == 'T')
7601 {
7602 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7603 {
7604 if (!init)
7605 HL_TABLE()[idx].sg_set |= SG_TERM;
7606 HL_TABLE()[idx].sg_term = attr;
7607 }
7608 }
7609 else if (*key == 'C')
7610 {
7611 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7612 {
7613 if (!init)
7614 HL_TABLE()[idx].sg_set |= SG_CTERM;
7615 HL_TABLE()[idx].sg_cterm = attr;
7616 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7617 }
7618 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007619#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007620 else
7621 {
7622 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7623 {
7624 if (!init)
7625 HL_TABLE()[idx].sg_set |= SG_GUI;
7626 HL_TABLE()[idx].sg_gui = attr;
7627 }
7628 }
7629#endif
7630 }
7631 else if (STRCMP(key, "FONT") == 0)
7632 {
7633 /* in non-GUI fonts are simply ignored */
7634#ifdef FEAT_GUI
7635 if (!gui.shell_created)
7636 {
7637 /* GUI not started yet, always accept the name. */
7638 vim_free(HL_TABLE()[idx].sg_font_name);
7639 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7640 }
7641 else
7642 {
7643 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7644# ifdef FEAT_XFONTSET
7645 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7646# endif
7647 /* First, save the current font/fontset.
7648 * Then try to allocate the font/fontset.
7649 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7650 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7651 */
7652
7653 HL_TABLE()[idx].sg_font = NOFONT;
7654# ifdef FEAT_XFONTSET
7655 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7656# endif
7657 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007658 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007659
7660# ifdef FEAT_XFONTSET
7661 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7662 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007663 /* New fontset was accepted. Free the old one, if there
7664 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007665 gui_mch_free_fontset(temp_sg_fontset);
7666 vim_free(HL_TABLE()[idx].sg_font_name);
7667 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7668 }
7669 else
7670 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7671# endif
7672 if (HL_TABLE()[idx].sg_font != NOFONT)
7673 {
7674 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007675 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007676 gui_mch_free_font(temp_sg_font);
7677 vim_free(HL_TABLE()[idx].sg_font_name);
7678 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7679 }
7680 else
7681 HL_TABLE()[idx].sg_font = temp_sg_font;
7682 }
7683#endif
7684 }
7685 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7686 {
7687 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7688 {
7689 if (!init)
7690 HL_TABLE()[idx].sg_set |= SG_CTERM;
7691
7692 /* When setting the foreground color, and previously the "bold"
7693 * flag was set for a light color, reset it now */
7694 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7695 {
7696 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7697 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7698 }
7699
7700 if (VIM_ISDIGIT(*arg))
7701 color = atoi((char *)arg);
7702 else if (STRICMP(arg, "fg") == 0)
7703 {
7704 if (cterm_normal_fg_color)
7705 color = cterm_normal_fg_color - 1;
7706 else
7707 {
7708 EMSG(_("E419: FG color unknown"));
7709 error = TRUE;
7710 break;
7711 }
7712 }
7713 else if (STRICMP(arg, "bg") == 0)
7714 {
7715 if (cterm_normal_bg_color > 0)
7716 color = cterm_normal_bg_color - 1;
7717 else
7718 {
7719 EMSG(_("E420: BG color unknown"));
7720 error = TRUE;
7721 break;
7722 }
7723 }
7724 else
7725 {
7726 static char *(color_names[28]) = {
7727 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7728 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7729 "Gray", "Grey",
7730 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7731 "Blue", "LightBlue", "Green", "LightGreen",
7732 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7733 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7734 static int color_numbers_16[28] = {0, 1, 2, 3,
7735 4, 5, 6, 6,
7736 7, 7,
7737 7, 7, 8, 8,
7738 9, 9, 10, 10,
7739 11, 11, 12, 12, 13,
7740 13, 14, 14, 15, -1};
7741 /* for xterm with 88 colors... */
7742 static int color_numbers_88[28] = {0, 4, 2, 6,
7743 1, 5, 32, 72,
7744 84, 84,
7745 7, 7, 82, 82,
7746 12, 43, 10, 61,
7747 14, 63, 9, 74, 13,
7748 75, 11, 78, 15, -1};
7749 /* for xterm with 256 colors... */
7750 static int color_numbers_256[28] = {0, 4, 2, 6,
7751 1, 5, 130, 130,
7752 248, 248,
7753 7, 7, 242, 242,
7754 12, 81, 10, 121,
7755 14, 159, 9, 224, 13,
7756 225, 11, 229, 15, -1};
7757 /* for terminals with less than 16 colors... */
7758 static int color_numbers_8[28] = {0, 4, 2, 6,
7759 1, 5, 3, 3,
7760 7, 7,
7761 7, 7, 0+8, 0+8,
7762 4+8, 4+8, 2+8, 2+8,
7763 6+8, 6+8, 1+8, 1+8, 5+8,
7764 5+8, 3+8, 3+8, 7+8, -1};
7765#if defined(__QNXNTO__)
7766 static int *color_numbers_8_qansi = color_numbers_8;
7767 /* On qnx, the 8 & 16 color arrays are the same */
7768 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7769 color_numbers_8_qansi = color_numbers_16;
7770#endif
7771
7772 /* reduce calls to STRICMP a bit, it can be slow */
7773 off = TOUPPER_ASC(*arg);
7774 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7775 if (off == color_names[i][0]
7776 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7777 break;
7778 if (i < 0)
7779 {
7780 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7781 error = TRUE;
7782 break;
7783 }
7784
7785 /* Use the _16 table to check if its a valid color name. */
7786 color = color_numbers_16[i];
7787 if (color >= 0)
7788 {
7789 if (t_colors == 8)
7790 {
7791 /* t_Co is 8: use the 8 colors table */
7792#if defined(__QNXNTO__)
7793 color = color_numbers_8_qansi[i];
7794#else
7795 color = color_numbers_8[i];
7796#endif
7797 if (key[5] == 'F')
7798 {
7799 /* set/reset bold attribute to get light foreground
7800 * colors (on some terminals, e.g. "linux") */
7801 if (color & 8)
7802 {
7803 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7804 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7805 }
7806 else
7807 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7808 }
7809 color &= 7; /* truncate to 8 colors */
7810 }
7811 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007812 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813 {
7814 /*
7815 * Guess: if the termcap entry ends in 'm', it is
7816 * probably an xterm-like terminal. Use the changed
7817 * order for colors.
7818 */
7819 if (*T_CAF != NUL)
7820 p = T_CAF;
7821 else
7822 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007823 if (*p != NUL && (t_colors > 256
7824 || *(p + STRLEN(p) - 1) == 'm'))
7825 {
7826 if (t_colors == 88)
7827 color = color_numbers_88[i];
7828 else if (t_colors >= 256)
7829 color = color_numbers_256[i];
7830 else
7831 color = color_numbers_8[i];
7832 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833 }
7834 }
7835 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007836 /* Add one to the argument, to avoid zero. Zero is used for
7837 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007838 if (key[5] == 'F')
7839 {
7840 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7841 if (is_normal_group)
7842 {
7843 cterm_normal_fg_color = color + 1;
7844 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7845#ifdef FEAT_GUI
7846 /* Don't do this if the GUI is used. */
7847 if (!gui.in_use && !gui.starting)
7848#endif
7849 {
7850 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007851 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007852 term_fg_color(color);
7853 }
7854 }
7855 }
7856 else
7857 {
7858 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7859 if (is_normal_group)
7860 {
7861 cterm_normal_bg_color = color + 1;
7862#ifdef FEAT_GUI
7863 /* Don't mess with 'background' if the GUI is used. */
7864 if (!gui.in_use && !gui.starting)
7865#endif
7866 {
7867 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007868 if (color >= 0)
7869 {
7870 if (termcap_active)
7871 term_bg_color(color);
7872 if (t_colors < 16)
7873 i = (color == 0 || color == 4);
7874 else
7875 i = (color < 7 || color == 8);
7876 /* Set the 'background' option if the value is
7877 * wrong. */
7878 if (i != (*p_bg == 'd'))
7879 set_option_value((char_u *)"bg", 0L,
7880 i ? (char_u *)"dark"
7881 : (char_u *)"light", 0);
7882 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007883 }
7884 }
7885 }
7886 }
7887 }
7888 else if (STRCMP(key, "GUIFG") == 0)
7889 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007890#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007891 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007892 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007893 if (!init)
7894 HL_TABLE()[idx].sg_set |= SG_GUI;
7895
Bram Moolenaar61623362010-07-14 22:04:22 +02007896# ifdef FEAT_GUI
7897 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007898 i = color_name2handle(arg);
7899 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7900 {
7901 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007902# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007903 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7904 if (STRCMP(arg, "NONE"))
7905 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7906 else
7907 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007908# ifdef FEAT_GUI
7909# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007910 if (is_menu_group)
7911 gui.menu_fg_pixel = i;
7912 if (is_scrollbar_group)
7913 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007914# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007915 if (is_tooltip_group)
7916 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007917# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007918 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007919# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007920 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007921# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007922 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007923#endif
7924 }
7925 else if (STRCMP(key, "GUIBG") == 0)
7926 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007927#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007928 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007929 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007930 if (!init)
7931 HL_TABLE()[idx].sg_set |= SG_GUI;
7932
Bram Moolenaar61623362010-07-14 22:04:22 +02007933# ifdef FEAT_GUI
7934 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007935 i = color_name2handle(arg);
7936 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7937 {
7938 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007939# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007940 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7941 if (STRCMP(arg, "NONE") != 0)
7942 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7943 else
7944 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007945# ifdef FEAT_GUI
7946# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007947 if (is_menu_group)
7948 gui.menu_bg_pixel = i;
7949 if (is_scrollbar_group)
7950 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007951# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007952 if (is_tooltip_group)
7953 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007954# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007955 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007956# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007957 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007958# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007959 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007960#endif
7961 }
7962 else if (STRCMP(key, "GUISP") == 0)
7963 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007964#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007965 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7966 {
7967 if (!init)
7968 HL_TABLE()[idx].sg_set |= SG_GUI;
7969
Bram Moolenaar61623362010-07-14 22:04:22 +02007970# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007971 i = color_name2handle(arg);
7972 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7973 {
7974 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007975# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007976 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7977 if (STRCMP(arg, "NONE") != 0)
7978 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7979 else
7980 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007981# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007982 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007983# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007984 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007985#endif
7986 }
7987 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7988 {
7989 char_u buf[100];
7990 char_u *tname;
7991
7992 if (!init)
7993 HL_TABLE()[idx].sg_set |= SG_TERM;
7994
7995 /*
7996 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007997 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007998 */
7999 if (STRNCMP(arg, "t_", 2) == 0)
8000 {
8001 off = 0;
8002 buf[0] = 0;
8003 while (arg[off] != NUL)
8004 {
8005 /* Isolate one termcap name */
8006 for (len = 0; arg[off + len] &&
8007 arg[off + len] != ','; ++len)
8008 ;
8009 tname = vim_strnsave(arg + off, len);
8010 if (tname == NULL) /* out of memory */
8011 {
8012 error = TRUE;
8013 break;
8014 }
8015 /* lookup the escape sequence for the item */
8016 p = get_term_code(tname);
8017 vim_free(tname);
8018 if (p == NULL) /* ignore non-existing things */
8019 p = (char_u *)"";
8020
8021 /* Append it to the already found stuff */
8022 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
8023 {
8024 EMSG2(_("E422: terminal code too long: %s"), arg);
8025 error = TRUE;
8026 break;
8027 }
8028 STRCAT(buf, p);
8029
8030 /* Advance to the next item */
8031 off += len;
8032 if (arg[off] == ',') /* another one follows */
8033 ++off;
8034 }
8035 }
8036 else
8037 {
8038 /*
8039 * Copy characters from arg[] to buf[], translating <> codes.
8040 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008041 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00008042 {
8043 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02008044 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008045 off += len;
8046 else /* copy as normal char */
8047 buf[off++] = *p++;
8048 }
8049 buf[off] = NUL;
8050 }
8051 if (error)
8052 break;
8053
8054 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
8055 p = NULL;
8056 else
8057 p = vim_strsave(buf);
8058 if (key[2] == 'A')
8059 {
8060 vim_free(HL_TABLE()[idx].sg_start);
8061 HL_TABLE()[idx].sg_start = p;
8062 }
8063 else
8064 {
8065 vim_free(HL_TABLE()[idx].sg_stop);
8066 HL_TABLE()[idx].sg_stop = p;
8067 }
8068 }
8069 else
8070 {
8071 EMSG2(_("E423: Illegal argument: %s"), key_start);
8072 error = TRUE;
8073 break;
8074 }
8075
8076 /*
8077 * When highlighting has been given for a group, don't link it.
8078 */
8079 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
8080 HL_TABLE()[idx].sg_link = 0;
8081
8082 /*
8083 * Continue with next argument.
8084 */
8085 linep = skipwhite(linep);
8086 }
8087
8088 /*
8089 * If there is an error, and it's a new entry, remove it from the table.
8090 */
8091 if (error && idx == highlight_ga.ga_len)
8092 syn_unadd_group();
8093 else
8094 {
8095 if (is_normal_group)
8096 {
8097 HL_TABLE()[idx].sg_term_attr = 0;
8098 HL_TABLE()[idx].sg_cterm_attr = 0;
8099#ifdef FEAT_GUI
8100 HL_TABLE()[idx].sg_gui_attr = 0;
8101 /*
8102 * Need to update all groups, because they might be using "bg"
8103 * and/or "fg", which have been changed now.
8104 */
8105 if (gui.in_use)
8106 highlight_gui_started();
8107#endif
8108 }
8109#ifdef FEAT_GUI_X11
8110# ifdef FEAT_MENU
8111 else if (is_menu_group)
8112 {
8113 if (gui.in_use && do_colors)
8114 gui_mch_new_menu_colors();
8115 }
8116# endif
8117 else if (is_scrollbar_group)
8118 {
8119 if (gui.in_use && do_colors)
8120 gui_new_scrollbar_colors();
8121 }
8122# ifdef FEAT_BEVAL
8123 else if (is_tooltip_group)
8124 {
8125 if (gui.in_use && do_colors)
8126 gui_mch_new_tooltip_colors();
8127 }
8128# endif
8129#endif
8130 else
8131 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008132#ifdef FEAT_EVAL
8133 HL_TABLE()[idx].sg_scriptID = current_SID;
8134#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008135 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008136 }
8137 vim_free(key);
8138 vim_free(arg);
8139
8140 /* Only call highlight_changed() once, after sourcing a syntax file */
8141 need_highlight_changed = TRUE;
8142}
8143
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008144#if defined(EXITFREE) || defined(PROTO)
8145 void
8146free_highlight()
8147{
8148 int i;
8149
8150 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008151 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008152 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008153 vim_free(HL_TABLE()[i].sg_name);
8154 vim_free(HL_TABLE()[i].sg_name_u);
8155 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008156 ga_clear(&highlight_ga);
8157}
8158#endif
8159
Bram Moolenaar071d4272004-06-13 20:20:40 +00008160/*
8161 * Reset the cterm colors to what they were before Vim was started, if
8162 * possible. Otherwise reset them to zero.
8163 */
8164 void
8165restore_cterm_colors()
8166{
8167#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
8168 /* Since t_me has been set, this probably means that the user
8169 * wants to use this as default colors. Need to reset default
8170 * background/foreground colors. */
8171 mch_set_normal_colors();
8172#else
8173 cterm_normal_fg_color = 0;
8174 cterm_normal_fg_bold = 0;
8175 cterm_normal_bg_color = 0;
8176#endif
8177}
8178
8179/*
8180 * Return TRUE if highlight group "idx" has any settings.
8181 * When "check_link" is TRUE also check for an existing link.
8182 */
8183 static int
8184hl_has_settings(idx, check_link)
8185 int idx;
8186 int check_link;
8187{
8188 return ( HL_TABLE()[idx].sg_term_attr != 0
8189 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008190 || HL_TABLE()[idx].sg_cterm_fg != 0
8191 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008192#ifdef FEAT_GUI
8193 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008194 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8195 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8196 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8197 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008198#endif
8199 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8200}
8201
8202/*
8203 * Clear highlighting for one group.
8204 */
8205 static void
8206highlight_clear(idx)
8207 int idx;
8208{
8209 HL_TABLE()[idx].sg_term = 0;
8210 vim_free(HL_TABLE()[idx].sg_start);
8211 HL_TABLE()[idx].sg_start = NULL;
8212 vim_free(HL_TABLE()[idx].sg_stop);
8213 HL_TABLE()[idx].sg_stop = NULL;
8214 HL_TABLE()[idx].sg_term_attr = 0;
8215 HL_TABLE()[idx].sg_cterm = 0;
8216 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8217 HL_TABLE()[idx].sg_cterm_fg = 0;
8218 HL_TABLE()[idx].sg_cterm_bg = 0;
8219 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008220#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008221 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008222 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8223 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008224 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8225 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008226 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8227 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008228#endif
8229#ifdef FEAT_GUI
8230 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8231 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8232 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8234 HL_TABLE()[idx].sg_font = NOFONT;
8235# ifdef FEAT_XFONTSET
8236 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8237 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8238# endif
8239 vim_free(HL_TABLE()[idx].sg_font_name);
8240 HL_TABLE()[idx].sg_font_name = NULL;
8241 HL_TABLE()[idx].sg_gui_attr = 0;
8242#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008243#ifdef FEAT_EVAL
8244 /* Clear the script ID only when there is no link, since that is not
8245 * cleared. */
8246 if (HL_TABLE()[idx].sg_link == 0)
8247 HL_TABLE()[idx].sg_scriptID = 0;
8248#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249}
8250
8251#if defined(FEAT_GUI) || defined(PROTO)
8252/*
8253 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008254 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008255 * "Tooltip" colors.
8256 */
8257 void
8258set_normal_colors()
8259{
8260 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008261 &gui.norm_pixel, &gui.back_pixel,
8262 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008263 {
8264 gui_mch_new_colors();
8265 must_redraw = CLEAR;
8266 }
8267#ifdef FEAT_GUI_X11
8268 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008269 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8270 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008271 {
8272# ifdef FEAT_MENU
8273 gui_mch_new_menu_colors();
8274# endif
8275 must_redraw = CLEAR;
8276 }
8277# ifdef FEAT_BEVAL
8278 if (set_group_colors((char_u *)"Tooltip",
8279 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8280 FALSE, FALSE, TRUE))
8281 {
8282# ifdef FEAT_TOOLBAR
8283 gui_mch_new_tooltip_colors();
8284# endif
8285 must_redraw = CLEAR;
8286 }
8287#endif
8288 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008289 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8290 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008291 {
8292 gui_new_scrollbar_colors();
8293 must_redraw = CLEAR;
8294 }
8295#endif
8296}
8297
8298/*
8299 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8300 */
8301 static int
8302set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
8303 char_u *name;
8304 guicolor_T *fgp;
8305 guicolor_T *bgp;
8306 int do_menu;
8307 int use_norm;
8308 int do_tooltip;
8309{
8310 int idx;
8311
8312 idx = syn_name2id(name) - 1;
8313 if (idx >= 0)
8314 {
8315 gui_do_one_color(idx, do_menu, do_tooltip);
8316
8317 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8318 *fgp = HL_TABLE()[idx].sg_gui_fg;
8319 else if (use_norm)
8320 *fgp = gui.def_norm_pixel;
8321 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8322 *bgp = HL_TABLE()[idx].sg_gui_bg;
8323 else if (use_norm)
8324 *bgp = gui.def_back_pixel;
8325 return TRUE;
8326 }
8327 return FALSE;
8328}
8329
8330/*
8331 * Get the font of the "Normal" group.
8332 * Returns "" when it's not found or not set.
8333 */
8334 char_u *
8335hl_get_font_name()
8336{
8337 int id;
8338 char_u *s;
8339
8340 id = syn_name2id((char_u *)"Normal");
8341 if (id > 0)
8342 {
8343 s = HL_TABLE()[id - 1].sg_font_name;
8344 if (s != NULL)
8345 return s;
8346 }
8347 return (char_u *)"";
8348}
8349
8350/*
8351 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8352 * actually chosen to be used.
8353 */
8354 void
8355hl_set_font_name(font_name)
8356 char_u *font_name;
8357{
8358 int id;
8359
8360 id = syn_name2id((char_u *)"Normal");
8361 if (id > 0)
8362 {
8363 vim_free(HL_TABLE()[id - 1].sg_font_name);
8364 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8365 }
8366}
8367
8368/*
8369 * Set background color for "Normal" group. Called by gui_set_bg_color()
8370 * when the color is known.
8371 */
8372 void
8373hl_set_bg_color_name(name)
8374 char_u *name; /* must have been allocated */
8375{
8376 int id;
8377
8378 if (name != NULL)
8379 {
8380 id = syn_name2id((char_u *)"Normal");
8381 if (id > 0)
8382 {
8383 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8384 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8385 }
8386 }
8387}
8388
8389/*
8390 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8391 * when the color is known.
8392 */
8393 void
8394hl_set_fg_color_name(name)
8395 char_u *name; /* must have been allocated */
8396{
8397 int id;
8398
8399 if (name != NULL)
8400 {
8401 id = syn_name2id((char_u *)"Normal");
8402 if (id > 0)
8403 {
8404 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8405 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8406 }
8407 }
8408}
8409
8410/*
8411 * Return the handle for a color name.
8412 * Returns INVALCOLOR when failed.
8413 */
8414 static guicolor_T
8415color_name2handle(name)
8416 char_u *name;
8417{
8418 if (STRCMP(name, "NONE") == 0)
8419 return INVALCOLOR;
8420
8421 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8422 return gui.norm_pixel;
8423 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8424 return gui.back_pixel;
8425
8426 return gui_get_color(name);
8427}
8428
8429/*
8430 * Return the handle for a font name.
8431 * Returns NOFONT when failed.
8432 */
8433 static GuiFont
8434font_name2handle(name)
8435 char_u *name;
8436{
8437 if (STRCMP(name, "NONE") == 0)
8438 return NOFONT;
8439
8440 return gui_mch_get_font(name, TRUE);
8441}
8442
8443# ifdef FEAT_XFONTSET
8444/*
8445 * Return the handle for a fontset name.
8446 * Returns NOFONTSET when failed.
8447 */
8448 static GuiFontset
8449fontset_name2handle(name, fixed_width)
8450 char_u *name;
8451 int fixed_width;
8452{
8453 if (STRCMP(name, "NONE") == 0)
8454 return NOFONTSET;
8455
8456 return gui_mch_get_fontset(name, TRUE, fixed_width);
8457}
8458# endif
8459
8460/*
8461 * Get the font or fontset for one highlight group.
8462 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008463 static void
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008464hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008465 int idx;
8466 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008467 int do_normal; /* set normal font */
8468 int do_menu UNUSED; /* set menu font */
8469 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008470 int free_font; /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008471{
8472# ifdef FEAT_XFONTSET
8473 /* If 'guifontset' is not empty, first try using the name as a
8474 * fontset. If that doesn't work, use it as a font name. */
8475 if (*p_guifontset != NUL
8476# ifdef FONTSET_ALWAYS
8477 || do_menu
8478# endif
8479# ifdef FEAT_BEVAL_TIP
8480 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8481 || do_tooltip
8482# endif
8483 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008484 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008485 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008486 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008487 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8488# ifdef FONTSET_ALWAYS
8489 || do_menu
8490# endif
8491# ifdef FEAT_BEVAL_TIP
8492 || do_tooltip
8493# endif
8494 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008495 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008496 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8497 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008498 /* If it worked and it's the Normal group, use it as the normal
8499 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008500 if (do_normal)
8501 gui_init_font(arg, TRUE);
8502# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8503 if (do_menu)
8504 {
8505# ifdef FONTSET_ALWAYS
8506 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8507# else
8508 /* YIKES! This is a bug waiting to crash the program */
8509 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8510# endif
8511 gui_mch_new_menu_font();
8512 }
8513# ifdef FEAT_BEVAL
8514 if (do_tooltip)
8515 {
8516 /* The Athena widget set cannot currently handle switching between
8517 * displaying a single font and a fontset.
8518 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008519 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008520 * XFontStruct is used.
8521 */
8522 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8523 gui_mch_new_tooltip_font();
8524 }
8525# endif
8526# endif
8527 }
8528 else
8529# endif
8530 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008531 if (free_font)
8532 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008533 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8534 /* If it worked and it's the Normal group, use it as the
8535 * normal font. Same for the Menu group. */
8536 if (HL_TABLE()[idx].sg_font != NOFONT)
8537 {
8538 if (do_normal)
8539 gui_init_font(arg, FALSE);
8540#ifndef FONTSET_ALWAYS
8541# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8542 if (do_menu)
8543 {
8544 gui.menu_font = HL_TABLE()[idx].sg_font;
8545 gui_mch_new_menu_font();
8546 }
8547# endif
8548#endif
8549 }
8550 }
8551}
8552
8553#endif /* FEAT_GUI */
8554
8555/*
8556 * Table with the specifications for an attribute number.
8557 * Note that this table is used by ALL buffers. This is required because the
8558 * GUI can redraw at any time for any buffer.
8559 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008560static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008561
8562#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8563
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008564static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008565
8566#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8567
8568#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008569static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008570
8571#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8572#endif
8573
8574/*
8575 * Return the attr number for a set of colors and font.
8576 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8577 * if the combination is new.
8578 * Return 0 for error (no more room).
8579 */
8580 static int
8581get_attr_entry(table, aep)
8582 garray_T *table;
8583 attrentry_T *aep;
8584{
8585 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008586 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008587 static int recursive = FALSE;
8588
8589 /*
8590 * Init the table, in case it wasn't done yet.
8591 */
8592 table->ga_itemsize = sizeof(attrentry_T);
8593 table->ga_growsize = 7;
8594
8595 /*
8596 * Try to find an entry with the same specifications.
8597 */
8598 for (i = 0; i < table->ga_len; ++i)
8599 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008600 taep = &(((attrentry_T *)table->ga_data)[i]);
8601 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008602 && (
8603#ifdef FEAT_GUI
8604 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008605 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8606 && aep->ae_u.gui.bg_color
8607 == taep->ae_u.gui.bg_color
8608 && aep->ae_u.gui.sp_color
8609 == taep->ae_u.gui.sp_color
8610 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008611# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008612 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613# endif
8614 ))
8615 ||
8616#endif
8617 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008618 && (aep->ae_u.term.start == NULL)
8619 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008620 && (aep->ae_u.term.start == NULL
8621 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008622 taep->ae_u.term.start) == 0)
8623 && (aep->ae_u.term.stop == NULL)
8624 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008625 && (aep->ae_u.term.stop == NULL
8626 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008627 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008629 && aep->ae_u.cterm.fg_color
8630 == taep->ae_u.cterm.fg_color
8631 && aep->ae_u.cterm.bg_color
8632 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008633 ))
8634
8635 return i + ATTR_OFF;
8636 }
8637
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008638 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008639 {
8640 /*
8641 * Running out of attribute entries! remove all attributes, and
8642 * compute new ones for all groups.
8643 * When called recursively, we are really out of numbers.
8644 */
8645 if (recursive)
8646 {
8647 EMSG(_("E424: Too many different highlighting attributes in use"));
8648 return 0;
8649 }
8650 recursive = TRUE;
8651
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008652 clear_hl_tables();
8653
Bram Moolenaar071d4272004-06-13 20:20:40 +00008654 must_redraw = CLEAR;
8655
8656 for (i = 0; i < highlight_ga.ga_len; ++i)
8657 set_hl_attr(i);
8658
8659 recursive = FALSE;
8660 }
8661
8662 /*
8663 * This is a new combination of colors and font, add an entry.
8664 */
8665 if (ga_grow(table, 1) == FAIL)
8666 return 0;
8667
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008668 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8669 vim_memset(taep, 0, sizeof(attrentry_T));
8670 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008671#ifdef FEAT_GUI
8672 if (table == &gui_attr_table)
8673 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008674 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8675 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8676 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8677 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008678# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008679 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008680# endif
8681 }
8682#endif
8683 if (table == &term_attr_table)
8684 {
8685 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008686 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008687 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008688 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008689 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008690 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008691 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008692 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008693 }
8694 else if (table == &cterm_attr_table)
8695 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008696 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8697 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008698 }
8699 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008700 return (table->ga_len - 1 + ATTR_OFF);
8701}
8702
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008703/*
8704 * Clear all highlight tables.
8705 */
8706 void
8707clear_hl_tables()
8708{
8709 int i;
8710 attrentry_T *taep;
8711
8712#ifdef FEAT_GUI
8713 ga_clear(&gui_attr_table);
8714#endif
8715 for (i = 0; i < term_attr_table.ga_len; ++i)
8716 {
8717 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8718 vim_free(taep->ae_u.term.start);
8719 vim_free(taep->ae_u.term.stop);
8720 }
8721 ga_clear(&term_attr_table);
8722 ga_clear(&cterm_attr_table);
8723}
8724
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008725#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008726/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008727 * Combine special attributes (e.g., for spelling) with other attributes
8728 * (e.g., for syntax highlighting).
8729 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008730 * This creates a new group when required.
8731 * Since we expect there to be few spelling mistakes we don't cache the
8732 * result.
8733 * Return the resulting attributes.
8734 */
8735 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008736hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008737 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008738 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008739{
8740 attrentry_T *char_aep = NULL;
8741 attrentry_T *spell_aep;
8742 attrentry_T new_en;
8743
8744 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008745 return prim_attr;
8746 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8747 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008748#ifdef FEAT_GUI
8749 if (gui.in_use)
8750 {
8751 if (char_attr > HL_ALL)
8752 char_aep = syn_gui_attr2entry(char_attr);
8753 if (char_aep != NULL)
8754 new_en = *char_aep;
8755 else
8756 {
8757 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008758 new_en.ae_u.gui.fg_color = INVALCOLOR;
8759 new_en.ae_u.gui.bg_color = INVALCOLOR;
8760 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008761 if (char_attr <= HL_ALL)
8762 new_en.ae_attr = char_attr;
8763 }
8764
Bram Moolenaar30abd282005-06-22 22:35:10 +00008765 if (prim_attr <= HL_ALL)
8766 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008767 else
8768 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008769 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008770 if (spell_aep != NULL)
8771 {
8772 new_en.ae_attr |= spell_aep->ae_attr;
8773 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8774 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8775 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8776 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8777 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8778 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8779 if (spell_aep->ae_u.gui.font != NOFONT)
8780 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8781# ifdef FEAT_XFONTSET
8782 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8783 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8784# endif
8785 }
8786 }
8787 return get_attr_entry(&gui_attr_table, &new_en);
8788 }
8789#endif
8790
8791 if (t_colors > 1)
8792 {
8793 if (char_attr > HL_ALL)
8794 char_aep = syn_cterm_attr2entry(char_attr);
8795 if (char_aep != NULL)
8796 new_en = *char_aep;
8797 else
8798 {
8799 vim_memset(&new_en, 0, sizeof(new_en));
8800 if (char_attr <= HL_ALL)
8801 new_en.ae_attr = char_attr;
8802 }
8803
Bram Moolenaar30abd282005-06-22 22:35:10 +00008804 if (prim_attr <= HL_ALL)
8805 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008806 else
8807 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008808 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008809 if (spell_aep != NULL)
8810 {
8811 new_en.ae_attr |= spell_aep->ae_attr;
8812 if (spell_aep->ae_u.cterm.fg_color > 0)
8813 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8814 if (spell_aep->ae_u.cterm.bg_color > 0)
8815 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8816 }
8817 }
8818 return get_attr_entry(&cterm_attr_table, &new_en);
8819 }
8820
8821 if (char_attr > HL_ALL)
8822 char_aep = syn_term_attr2entry(char_attr);
8823 if (char_aep != NULL)
8824 new_en = *char_aep;
8825 else
8826 {
8827 vim_memset(&new_en, 0, sizeof(new_en));
8828 if (char_attr <= HL_ALL)
8829 new_en.ae_attr = char_attr;
8830 }
8831
Bram Moolenaar30abd282005-06-22 22:35:10 +00008832 if (prim_attr <= HL_ALL)
8833 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008834 else
8835 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008836 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008837 if (spell_aep != NULL)
8838 {
8839 new_en.ae_attr |= spell_aep->ae_attr;
8840 if (spell_aep->ae_u.term.start != NULL)
8841 {
8842 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8843 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8844 }
8845 }
8846 }
8847 return get_attr_entry(&term_attr_table, &new_en);
8848}
8849#endif
8850
Bram Moolenaar071d4272004-06-13 20:20:40 +00008851#ifdef FEAT_GUI
8852
8853 attrentry_T *
8854syn_gui_attr2entry(attr)
8855 int attr;
8856{
8857 attr -= ATTR_OFF;
8858 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8859 return NULL;
8860 return &(GUI_ATTR_ENTRY(attr));
8861}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008862#endif /* FEAT_GUI */
8863
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008864/*
8865 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8866 * Only to be used when "attr" > HL_ALL.
8867 */
8868 int
8869syn_attr2attr(attr)
8870 int attr;
8871{
8872 attrentry_T *aep;
8873
8874#ifdef FEAT_GUI
8875 if (gui.in_use)
8876 aep = syn_gui_attr2entry(attr);
8877 else
8878#endif
8879 if (t_colors > 1)
8880 aep = syn_cterm_attr2entry(attr);
8881 else
8882 aep = syn_term_attr2entry(attr);
8883
8884 if (aep == NULL) /* highlighting not set */
8885 return 0;
8886 return aep->ae_attr;
8887}
8888
8889
Bram Moolenaar071d4272004-06-13 20:20:40 +00008890 attrentry_T *
8891syn_term_attr2entry(attr)
8892 int attr;
8893{
8894 attr -= ATTR_OFF;
8895 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8896 return NULL;
8897 return &(TERM_ATTR_ENTRY(attr));
8898}
8899
8900 attrentry_T *
8901syn_cterm_attr2entry(attr)
8902 int attr;
8903{
8904 attr -= ATTR_OFF;
8905 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8906 return NULL;
8907 return &(CTERM_ATTR_ENTRY(attr));
8908}
8909
8910#define LIST_ATTR 1
8911#define LIST_STRING 2
8912#define LIST_INT 3
8913
8914 static void
8915highlight_list_one(id)
8916 int id;
8917{
8918 struct hl_group *sgp;
8919 int didh = FALSE;
8920
8921 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8922
8923 didh = highlight_list_arg(id, didh, LIST_ATTR,
8924 sgp->sg_term, NULL, "term");
8925 didh = highlight_list_arg(id, didh, LIST_STRING,
8926 0, sgp->sg_start, "start");
8927 didh = highlight_list_arg(id, didh, LIST_STRING,
8928 0, sgp->sg_stop, "stop");
8929
8930 didh = highlight_list_arg(id, didh, LIST_ATTR,
8931 sgp->sg_cterm, NULL, "cterm");
8932 didh = highlight_list_arg(id, didh, LIST_INT,
8933 sgp->sg_cterm_fg, NULL, "ctermfg");
8934 didh = highlight_list_arg(id, didh, LIST_INT,
8935 sgp->sg_cterm_bg, NULL, "ctermbg");
8936
Bram Moolenaar61623362010-07-14 22:04:22 +02008937#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008938 didh = highlight_list_arg(id, didh, LIST_ATTR,
8939 sgp->sg_gui, NULL, "gui");
8940 didh = highlight_list_arg(id, didh, LIST_STRING,
8941 0, sgp->sg_gui_fg_name, "guifg");
8942 didh = highlight_list_arg(id, didh, LIST_STRING,
8943 0, sgp->sg_gui_bg_name, "guibg");
8944 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008945 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008946#endif
8947#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008948 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008949 0, sgp->sg_font_name, "font");
8950#endif
8951
Bram Moolenaar661b1822005-07-28 22:36:45 +00008952 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008953 {
8954 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008955 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008956 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8957 msg_putchar(' ');
8958 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8959 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008960
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008961 if (!didh)
8962 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008963#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008964 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008965 last_set_msg(sgp->sg_scriptID);
8966#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008967}
8968
8969 static int
8970highlight_list_arg(id, didh, type, iarg, sarg, name)
8971 int id;
8972 int didh;
8973 int type;
8974 int iarg;
8975 char_u *sarg;
8976 char *name;
8977{
8978 char_u buf[100];
8979 char_u *ts;
8980 int i;
8981
Bram Moolenaar661b1822005-07-28 22:36:45 +00008982 if (got_int)
8983 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008984 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8985 {
8986 ts = buf;
8987 if (type == LIST_INT)
8988 sprintf((char *)buf, "%d", iarg - 1);
8989 else if (type == LIST_STRING)
8990 ts = sarg;
8991 else /* type == LIST_ATTR */
8992 {
8993 buf[0] = NUL;
8994 for (i = 0; hl_attr_table[i] != 0; ++i)
8995 {
8996 if (iarg & hl_attr_table[i])
8997 {
8998 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008999 vim_strcat(buf, (char_u *)",", 100);
9000 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009001 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
9002 }
9003 }
9004 }
9005
9006 (void)syn_list_header(didh,
9007 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
9008 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00009009 if (!got_int)
9010 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00009011 if (*name != NUL)
9012 {
9013 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
9014 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
9015 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00009016 msg_outtrans(ts);
9017 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009018 }
9019 return didh;
9020}
9021
9022#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
9023/*
9024 * Return "1" if highlight group "id" has attribute "flag".
9025 * Return NULL otherwise.
9026 */
9027 char_u *
9028highlight_has_attr(id, flag, modec)
9029 int id;
9030 int flag;
9031 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
9032{
9033 int attr;
9034
9035 if (id <= 0 || id > highlight_ga.ga_len)
9036 return NULL;
9037
Bram Moolenaar61623362010-07-14 22:04:22 +02009038#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009039 if (modec == 'g')
9040 attr = HL_TABLE()[id - 1].sg_gui;
9041 else
9042#endif
9043 if (modec == 'c')
9044 attr = HL_TABLE()[id - 1].sg_cterm;
9045 else
9046 attr = HL_TABLE()[id - 1].sg_term;
9047
9048 if (attr & flag)
9049 return (char_u *)"1";
9050 return NULL;
9051}
9052#endif
9053
9054#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
9055/*
9056 * Return color name of highlight group "id".
9057 */
9058 char_u *
9059highlight_color(id, what, modec)
9060 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009061 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009062 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
9063{
9064 static char_u name[20];
9065 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009066 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009067 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009068 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009069
9070 if (id <= 0 || id > highlight_ga.ga_len)
9071 return NULL;
9072
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009073 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009074 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009075 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02009076 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009077 font = TRUE;
9078 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009079 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009080 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
9081 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009082 if (modec == 'g')
9083 {
Bram Moolenaar61623362010-07-14 22:04:22 +02009084# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009085 /* return font name */
9086 if (font)
9087 return HL_TABLE()[id - 1].sg_font_name;
9088
Bram Moolenaar071d4272004-06-13 20:20:40 +00009089 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009090 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00009091 {
9092 guicolor_T color;
9093 long_u rgb;
9094 static char_u buf[10];
9095
9096 if (fg)
9097 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009098 else if (sp)
9099 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009100 else
9101 color = HL_TABLE()[id - 1].sg_gui_bg;
9102 if (color == INVALCOLOR)
9103 return NULL;
9104 rgb = gui_mch_get_rgb(color);
9105 sprintf((char *)buf, "#%02x%02x%02x",
9106 (unsigned)(rgb >> 16),
9107 (unsigned)(rgb >> 8) & 255,
9108 (unsigned)rgb & 255);
9109 return buf;
9110 }
Bram Moolenaar61623362010-07-14 22:04:22 +02009111#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009112 if (fg)
9113 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009114 if (sp)
9115 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009116 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9117 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009118 if (font || sp)
9119 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009120 if (modec == 'c')
9121 {
9122 if (fg)
9123 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9124 else
9125 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
9126 sprintf((char *)name, "%d", n);
9127 return name;
9128 }
9129 /* term doesn't have color */
9130 return NULL;
9131}
9132#endif
9133
9134#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
9135 || defined(PROTO)
9136/*
9137 * Return color name of highlight group "id" as RGB value.
9138 */
9139 long_u
9140highlight_gui_color_rgb(id, fg)
9141 int id;
9142 int fg; /* TRUE = fg, FALSE = bg */
9143{
9144 guicolor_T color;
9145
9146 if (id <= 0 || id > highlight_ga.ga_len)
9147 return 0L;
9148
9149 if (fg)
9150 color = HL_TABLE()[id - 1].sg_gui_fg;
9151 else
9152 color = HL_TABLE()[id - 1].sg_gui_bg;
9153
9154 if (color == INVALCOLOR)
9155 return 0L;
9156
9157 return gui_mch_get_rgb(color);
9158}
9159#endif
9160
9161/*
9162 * Output the syntax list header.
9163 * Return TRUE when started a new line.
9164 */
9165 static int
9166syn_list_header(did_header, outlen, id)
9167 int did_header; /* did header already */
9168 int outlen; /* length of string that comes */
9169 int id; /* highlight group id */
9170{
9171 int endcol = 19;
9172 int newline = TRUE;
9173
9174 if (!did_header)
9175 {
9176 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009177 if (got_int)
9178 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009179 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9180 endcol = 15;
9181 }
9182 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009183 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009184 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009185 if (got_int)
9186 return TRUE;
9187 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009188 else
9189 {
9190 if (msg_col >= endcol) /* wrap around is like starting a new line */
9191 newline = FALSE;
9192 }
9193
9194 if (msg_col >= endcol) /* output at least one space */
9195 endcol = msg_col + 1;
9196 if (Columns <= endcol) /* avoid hang for tiny window */
9197 endcol = Columns - 1;
9198
9199 msg_advance(endcol);
9200
9201 /* Show "xxx" with the attributes. */
9202 if (!did_header)
9203 {
9204 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9205 msg_putchar(' ');
9206 }
9207
9208 return newline;
9209}
9210
9211/*
9212 * Set the attribute numbers for a highlight group.
9213 * Called after one of the attributes has changed.
9214 */
9215 static void
9216set_hl_attr(idx)
9217 int idx; /* index in array */
9218{
9219 attrentry_T at_en;
9220 struct hl_group *sgp = HL_TABLE() + idx;
9221
9222 /* The "Normal" group doesn't need an attribute number */
9223 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9224 return;
9225
9226#ifdef FEAT_GUI
9227 /*
9228 * For the GUI mode: If there are other than "normal" highlighting
9229 * attributes, need to allocate an attr number.
9230 */
9231 if (sgp->sg_gui_fg == INVALCOLOR
9232 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009233 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009234 && sgp->sg_font == NOFONT
9235# ifdef FEAT_XFONTSET
9236 && sgp->sg_fontset == NOFONTSET
9237# endif
9238 )
9239 {
9240 sgp->sg_gui_attr = sgp->sg_gui;
9241 }
9242 else
9243 {
9244 at_en.ae_attr = sgp->sg_gui;
9245 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9246 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009247 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009248 at_en.ae_u.gui.font = sgp->sg_font;
9249# ifdef FEAT_XFONTSET
9250 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9251# endif
9252 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9253 }
9254#endif
9255 /*
9256 * For the term mode: If there are other than "normal" highlighting
9257 * attributes, need to allocate an attr number.
9258 */
9259 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9260 sgp->sg_term_attr = sgp->sg_term;
9261 else
9262 {
9263 at_en.ae_attr = sgp->sg_term;
9264 at_en.ae_u.term.start = sgp->sg_start;
9265 at_en.ae_u.term.stop = sgp->sg_stop;
9266 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9267 }
9268
9269 /*
9270 * For the color term mode: If there are other than "normal"
9271 * highlighting attributes, need to allocate an attr number.
9272 */
9273 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9274 sgp->sg_cterm_attr = sgp->sg_cterm;
9275 else
9276 {
9277 at_en.ae_attr = sgp->sg_cterm;
9278 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9279 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9280 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9281 }
9282}
9283
9284/*
9285 * Lookup a highlight group name and return it's ID.
9286 * If it is not found, 0 is returned.
9287 */
9288 int
9289syn_name2id(name)
9290 char_u *name;
9291{
9292 int i;
9293 char_u name_u[200];
9294
9295 /* Avoid using stricmp() too much, it's slow on some systems */
9296 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9297 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009298 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009299 vim_strup(name_u);
9300 for (i = highlight_ga.ga_len; --i >= 0; )
9301 if (HL_TABLE()[i].sg_name_u != NULL
9302 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9303 break;
9304 return i + 1;
9305}
9306
9307#if defined(FEAT_EVAL) || defined(PROTO)
9308/*
9309 * Return TRUE if highlight group "name" exists.
9310 */
9311 int
9312highlight_exists(name)
9313 char_u *name;
9314{
9315 return (syn_name2id(name) > 0);
9316}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009317
9318# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9319/*
9320 * Return the name of highlight group "id".
9321 * When not a valid ID return an empty string.
9322 */
9323 char_u *
9324syn_id2name(id)
9325 int id;
9326{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009327 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009328 return (char_u *)"";
9329 return HL_TABLE()[id - 1].sg_name;
9330}
9331# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009332#endif
9333
9334/*
9335 * Like syn_name2id(), but take a pointer + length argument.
9336 */
9337 int
9338syn_namen2id(linep, len)
9339 char_u *linep;
9340 int len;
9341{
9342 char_u *name;
9343 int id = 0;
9344
9345 name = vim_strnsave(linep, len);
9346 if (name != NULL)
9347 {
9348 id = syn_name2id(name);
9349 vim_free(name);
9350 }
9351 return id;
9352}
9353
9354/*
9355 * Find highlight group name in the table and return it's ID.
9356 * The argument is a pointer to the name and the length of the name.
9357 * If it doesn't exist yet, a new entry is created.
9358 * Return 0 for failure.
9359 */
9360 int
9361syn_check_group(pp, len)
9362 char_u *pp;
9363 int len;
9364{
9365 int id;
9366 char_u *name;
9367
9368 name = vim_strnsave(pp, len);
9369 if (name == NULL)
9370 return 0;
9371
9372 id = syn_name2id(name);
9373 if (id == 0) /* doesn't exist yet */
9374 id = syn_add_group(name);
9375 else
9376 vim_free(name);
9377 return id;
9378}
9379
9380/*
9381 * Add new highlight group and return it's ID.
9382 * "name" must be an allocated string, it will be consumed.
9383 * Return 0 for failure.
9384 */
9385 static int
9386syn_add_group(name)
9387 char_u *name;
9388{
9389 char_u *p;
9390
9391 /* Check that the name is ASCII letters, digits and underscore. */
9392 for (p = name; *p != NUL; ++p)
9393 {
9394 if (!vim_isprintc(*p))
9395 {
9396 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009397 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009398 return 0;
9399 }
9400 else if (!ASCII_ISALNUM(*p) && *p != '_')
9401 {
9402 /* This is an error, but since there previously was no check only
9403 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009404 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009405 MSG(_("W18: Invalid character in group name"));
9406 break;
9407 }
9408 }
9409
9410 /*
9411 * First call for this growarray: init growing array.
9412 */
9413 if (highlight_ga.ga_data == NULL)
9414 {
9415 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9416 highlight_ga.ga_growsize = 10;
9417 }
9418
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009419 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009420 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009421 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009422 vim_free(name);
9423 return 0;
9424 }
9425
Bram Moolenaar071d4272004-06-13 20:20:40 +00009426 /*
9427 * Make room for at least one other syntax_highlight entry.
9428 */
9429 if (ga_grow(&highlight_ga, 1) == FAIL)
9430 {
9431 vim_free(name);
9432 return 0;
9433 }
9434
9435 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9436 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9437 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9438#ifdef FEAT_GUI
9439 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9440 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009441 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009442#endif
9443 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009444
9445 return highlight_ga.ga_len; /* ID is index plus one */
9446}
9447
9448/*
9449 * When, just after calling syn_add_group(), an error is discovered, this
9450 * function deletes the new name.
9451 */
9452 static void
9453syn_unadd_group()
9454{
9455 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009456 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9457 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9458}
9459
9460/*
9461 * Translate a group ID to highlight attributes.
9462 */
9463 int
9464syn_id2attr(hl_id)
9465 int hl_id;
9466{
9467 int attr;
9468 struct hl_group *sgp;
9469
9470 hl_id = syn_get_final_id(hl_id);
9471 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9472
9473#ifdef FEAT_GUI
9474 /*
9475 * Only use GUI attr when the GUI is being used.
9476 */
9477 if (gui.in_use)
9478 attr = sgp->sg_gui_attr;
9479 else
9480#endif
9481 if (t_colors > 1)
9482 attr = sgp->sg_cterm_attr;
9483 else
9484 attr = sgp->sg_term_attr;
9485
9486 return attr;
9487}
9488
9489#ifdef FEAT_GUI
9490/*
9491 * Get the GUI colors and attributes for a group ID.
9492 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9493 */
9494 int
9495syn_id2colors(hl_id, fgp, bgp)
9496 int hl_id;
9497 guicolor_T *fgp;
9498 guicolor_T *bgp;
9499{
9500 struct hl_group *sgp;
9501
9502 hl_id = syn_get_final_id(hl_id);
9503 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9504
9505 *fgp = sgp->sg_gui_fg;
9506 *bgp = sgp->sg_gui_bg;
9507 return sgp->sg_gui;
9508}
9509#endif
9510
9511/*
9512 * Translate a group ID to the final group ID (following links).
9513 */
9514 int
9515syn_get_final_id(hl_id)
9516 int hl_id;
9517{
9518 int count;
9519 struct hl_group *sgp;
9520
9521 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9522 return 0; /* Can be called from eval!! */
9523
9524 /*
9525 * Follow links until there is no more.
9526 * Look out for loops! Break after 100 links.
9527 */
9528 for (count = 100; --count >= 0; )
9529 {
9530 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9531 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9532 break;
9533 hl_id = sgp->sg_link;
9534 }
9535
9536 return hl_id;
9537}
9538
9539#ifdef FEAT_GUI
9540/*
9541 * Call this function just after the GUI has started.
9542 * It finds the font and color handles for the highlighting groups.
9543 */
9544 void
9545highlight_gui_started()
9546{
9547 int idx;
9548
9549 /* First get the colors from the "Normal" and "Menu" group, if set */
9550 set_normal_colors();
9551
9552 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9553 gui_do_one_color(idx, FALSE, FALSE);
9554
9555 highlight_changed();
9556}
9557
9558 static void
9559gui_do_one_color(idx, do_menu, do_tooltip)
9560 int idx;
9561 int do_menu; /* TRUE: might set the menu font */
9562 int do_tooltip; /* TRUE: might set the tooltip font */
9563{
9564 int didit = FALSE;
9565
9566 if (HL_TABLE()[idx].sg_font_name != NULL)
9567 {
9568 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009569 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009570 didit = TRUE;
9571 }
9572 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9573 {
9574 HL_TABLE()[idx].sg_gui_fg =
9575 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9576 didit = TRUE;
9577 }
9578 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9579 {
9580 HL_TABLE()[idx].sg_gui_bg =
9581 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9582 didit = TRUE;
9583 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009584 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9585 {
9586 HL_TABLE()[idx].sg_gui_sp =
9587 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9588 didit = TRUE;
9589 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009590 if (didit) /* need to get a new attr number */
9591 set_hl_attr(idx);
9592}
9593
9594#endif
9595
9596/*
9597 * Translate the 'highlight' option into attributes in highlight_attr[] and
9598 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9599 * corresponding highlights to use on top of HLF_SNC is computed.
9600 * Called only when the 'highlight' option has been changed and upon first
9601 * screen redraw after any :highlight command.
9602 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9603 */
9604 int
9605highlight_changed()
9606{
9607 int hlf;
9608 int i;
9609 char_u *p;
9610 int attr;
9611 char_u *end;
9612 int id;
9613#ifdef USER_HIGHLIGHT
9614 char_u userhl[10];
9615# ifdef FEAT_STL_OPT
9616 int id_SNC = -1;
9617 int id_S = -1;
9618 int hlcnt;
9619# endif
9620#endif
9621 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9622
9623 need_highlight_changed = FALSE;
9624
9625 /*
9626 * Clear all attributes.
9627 */
9628 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9629 highlight_attr[hlf] = 0;
9630
9631 /*
9632 * First set all attributes to their default value.
9633 * Then use the attributes from the 'highlight' option.
9634 */
9635 for (i = 0; i < 2; ++i)
9636 {
9637 if (i)
9638 p = p_hl;
9639 else
9640 p = get_highlight_default();
9641 if (p == NULL) /* just in case */
9642 continue;
9643
9644 while (*p)
9645 {
9646 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9647 if (hl_flags[hlf] == *p)
9648 break;
9649 ++p;
9650 if (hlf == (int)HLF_COUNT || *p == NUL)
9651 return FAIL;
9652
9653 /*
9654 * Allow several hl_flags to be combined, like "bu" for
9655 * bold-underlined.
9656 */
9657 attr = 0;
9658 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9659 {
9660 if (vim_iswhite(*p)) /* ignore white space */
9661 continue;
9662
9663 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9664 return FAIL;
9665
9666 switch (*p)
9667 {
9668 case 'b': attr |= HL_BOLD;
9669 break;
9670 case 'i': attr |= HL_ITALIC;
9671 break;
9672 case '-':
9673 case 'n': /* no highlighting */
9674 break;
9675 case 'r': attr |= HL_INVERSE;
9676 break;
9677 case 's': attr |= HL_STANDOUT;
9678 break;
9679 case 'u': attr |= HL_UNDERLINE;
9680 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009681 case 'c': attr |= HL_UNDERCURL;
9682 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009683 case ':': ++p; /* highlight group name */
9684 if (attr || *p == NUL) /* no combinations */
9685 return FAIL;
9686 end = vim_strchr(p, ',');
9687 if (end == NULL)
9688 end = p + STRLEN(p);
9689 id = syn_check_group(p, (int)(end - p));
9690 if (id == 0)
9691 return FAIL;
9692 attr = syn_id2attr(id);
9693 p = end - 1;
9694#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9695 if (hlf == (int)HLF_SNC)
9696 id_SNC = syn_get_final_id(id);
9697 else if (hlf == (int)HLF_S)
9698 id_S = syn_get_final_id(id);
9699#endif
9700 break;
9701 default: return FAIL;
9702 }
9703 }
9704 highlight_attr[hlf] = attr;
9705
9706 p = skip_to_option_part(p); /* skip comma and spaces */
9707 }
9708 }
9709
9710#ifdef USER_HIGHLIGHT
9711 /* Setup the user highlights
9712 *
9713 * Temporarily utilize 10 more hl entries. Have to be in there
9714 * simultaneously in case of table overflows in get_attr_entry()
9715 */
9716# ifdef FEAT_STL_OPT
9717 if (ga_grow(&highlight_ga, 10) == FAIL)
9718 return FAIL;
9719 hlcnt = highlight_ga.ga_len;
9720 if (id_S == 0)
9721 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009722 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009723 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9724 id_S = hlcnt + 10;
9725 }
9726# endif
9727 for (i = 0; i < 9; i++)
9728 {
9729 sprintf((char *)userhl, "User%d", i + 1);
9730 id = syn_name2id(userhl);
9731 if (id == 0)
9732 {
9733 highlight_user[i] = 0;
9734# ifdef FEAT_STL_OPT
9735 highlight_stlnc[i] = 0;
9736# endif
9737 }
9738 else
9739 {
9740# ifdef FEAT_STL_OPT
9741 struct hl_group *hlt = HL_TABLE();
9742# endif
9743
9744 highlight_user[i] = syn_id2attr(id);
9745# ifdef FEAT_STL_OPT
9746 if (id_SNC == 0)
9747 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009748 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009749 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9750 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009751# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009752 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9753# endif
9754 }
9755 else
9756 mch_memmove(&hlt[hlcnt + i],
9757 &hlt[id_SNC - 1],
9758 sizeof(struct hl_group));
9759 hlt[hlcnt + i].sg_link = 0;
9760
9761 /* Apply difference between UserX and HLF_S to HLF_SNC */
9762 hlt[hlcnt + i].sg_term ^=
9763 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9764 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9765 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9766 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9767 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9768 hlt[hlcnt + i].sg_cterm ^=
9769 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9770 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9771 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9772 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9773 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009774# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009775 hlt[hlcnt + i].sg_gui ^=
9776 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009777# endif
9778# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009779 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9780 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9781 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9782 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009783 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9784 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009785 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9786 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9787# ifdef FEAT_XFONTSET
9788 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9789 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9790# endif
9791# endif
9792 highlight_ga.ga_len = hlcnt + i + 1;
9793 set_hl_attr(hlcnt + i); /* At long last we can apply */
9794 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9795# endif
9796 }
9797 }
9798# ifdef FEAT_STL_OPT
9799 highlight_ga.ga_len = hlcnt;
9800# endif
9801
9802#endif /* USER_HIGHLIGHT */
9803
9804 return OK;
9805}
9806
Bram Moolenaar4f688582007-07-24 12:34:30 +00009807#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009808
9809static void highlight_list __ARGS((void));
9810static void highlight_list_two __ARGS((int cnt, int attr));
9811
9812/*
9813 * Handle command line completion for :highlight command.
9814 */
9815 void
9816set_context_in_highlight_cmd(xp, arg)
9817 expand_T *xp;
9818 char_u *arg;
9819{
9820 char_u *p;
9821
9822 /* Default: expand group names */
9823 xp->xp_context = EXPAND_HIGHLIGHT;
9824 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009825 include_link = 2;
9826 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009827
9828 /* (part of) subcommand already typed */
9829 if (*arg != NUL)
9830 {
9831 p = skiptowhite(arg);
9832 if (*p != NUL) /* past "default" or group name */
9833 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009834 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009835 if (STRNCMP("default", arg, p - arg) == 0)
9836 {
9837 arg = skipwhite(p);
9838 xp->xp_pattern = arg;
9839 p = skiptowhite(arg);
9840 }
9841 if (*p != NUL) /* past group name */
9842 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009843 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009844 if (arg[1] == 'i' && arg[0] == 'N')
9845 highlight_list();
9846 if (STRNCMP("link", arg, p - arg) == 0
9847 || STRNCMP("clear", arg, p - arg) == 0)
9848 {
9849 xp->xp_pattern = skipwhite(p);
9850 p = skiptowhite(xp->xp_pattern);
9851 if (*p != NUL) /* past first group name */
9852 {
9853 xp->xp_pattern = skipwhite(p);
9854 p = skiptowhite(xp->xp_pattern);
9855 }
9856 }
9857 if (*p != NUL) /* past group name(s) */
9858 xp->xp_context = EXPAND_NOTHING;
9859 }
9860 }
9861 }
9862}
9863
9864/*
9865 * List highlighting matches in a nice way.
9866 */
9867 static void
9868highlight_list()
9869{
9870 int i;
9871
9872 for (i = 10; --i >= 0; )
9873 highlight_list_two(i, hl_attr(HLF_D));
9874 for (i = 40; --i >= 0; )
9875 highlight_list_two(99, 0);
9876}
9877
9878 static void
9879highlight_list_two(cnt, attr)
9880 int cnt;
9881 int attr;
9882{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009883 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009884 msg_clr_eos();
9885 out_flush();
9886 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9887}
9888
9889#endif /* FEAT_CMDL_COMPL */
9890
9891#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9892 || defined(FEAT_SIGNS) || defined(PROTO)
9893/*
9894 * Function given to ExpandGeneric() to obtain the list of group names.
9895 * Also used for synIDattr() function.
9896 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009897 char_u *
9898get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009899 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009900 int idx;
9901{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009902#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009903 if (idx == highlight_ga.ga_len && include_none != 0)
9904 return (char_u *)"none";
9905 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009906 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009907 if (idx == highlight_ga.ga_len + include_none + include_default
9908 && include_link != 0)
9909 return (char_u *)"link";
9910 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9911 && include_link != 0)
9912 return (char_u *)"clear";
9913#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009914 if (idx < 0 || idx >= highlight_ga.ga_len)
9915 return NULL;
9916 return HL_TABLE()[idx].sg_name;
9917}
9918#endif
9919
Bram Moolenaar4f688582007-07-24 12:34:30 +00009920#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009921/*
9922 * Free all the highlight group fonts.
9923 * Used when quitting for systems which need it.
9924 */
9925 void
9926free_highlight_fonts()
9927{
9928 int idx;
9929
9930 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9931 {
9932 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9933 HL_TABLE()[idx].sg_font = NOFONT;
9934# ifdef FEAT_XFONTSET
9935 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9936 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9937# endif
9938 }
9939
9940 gui_mch_free_font(gui.norm_font);
9941# ifdef FEAT_XFONTSET
9942 gui_mch_free_fontset(gui.fontset);
9943# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009944# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009945 gui_mch_free_font(gui.bold_font);
9946 gui_mch_free_font(gui.ital_font);
9947 gui_mch_free_font(gui.boldital_font);
9948# endif
9949}
9950#endif
9951
9952/**************************************
9953 * End of Highlighting stuff *
9954 **************************************/