blob: fa128017d727cdb5002e3f52848cf43d9c63c8bf [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
71#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000072/* Flags to indicate an additional string for highlight name completion. */
73static int include_none = 0; /* when 1 include "None" */
74static int include_default = 0; /* when 1 include "default" */
75static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#endif
77
78/*
79 * The "term", "cterm" and "gui" arguments can be any combination of the
80 * following names, separated by commas (but no spaces!).
81 */
82static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000083 {"bold", "standout", "underline", "undercurl",
84 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000085static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000086 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087
88static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
89static void syn_unadd_group __ARGS((void));
90static void set_hl_attr __ARGS((int idx));
91static void highlight_list_one __ARGS((int id));
92static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
93static int syn_add_group __ARGS((char_u *name));
94static int syn_list_header __ARGS((int did_header, int outlen, int id));
95static int hl_has_settings __ARGS((int idx, int check_link));
96static void highlight_clear __ARGS((int idx));
97
98#ifdef FEAT_GUI
99static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
100static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
101static guicolor_T color_name2handle __ARGS((char_u *name));
102static GuiFont font_name2handle __ARGS((char_u *name));
103# ifdef FEAT_XFONTSET
104static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
105# endif
106static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
107#endif
108
109/*
110 * An attribute number is the index in attr_table plus ATTR_OFF.
111 */
112#define ATTR_OFF (HL_ALL + 1)
113
114#if defined(FEAT_SYN_HL) || defined(PROTO)
115
116#define SYN_NAMELEN 50 /* maximum length of a syntax name */
117
118/* different types of offsets that are possible */
119#define SPO_MS_OFF 0 /* match start offset */
120#define SPO_ME_OFF 1 /* match end offset */
121#define SPO_HS_OFF 2 /* highl. start offset */
122#define SPO_HE_OFF 3 /* highl. end offset */
123#define SPO_RS_OFF 4 /* region start offset */
124#define SPO_RE_OFF 5 /* region end offset */
125#define SPO_LC_OFF 6 /* leading context offset */
126#define SPO_COUNT 7
127
128static char *(spo_name_tab[SPO_COUNT]) =
129 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
130
131/*
132 * The patterns that are being searched for are stored in a syn_pattern.
133 * A match item consists of one pattern.
134 * A start/end item consists of n start patterns and m end patterns.
135 * A start/skip/end item consists of n start patterns, one skip pattern and m
136 * end patterns.
137 * For the latter two, the patterns are always consecutive: start-skip-end.
138 *
139 * A character offset can be given for the matched text (_m_start and _m_end)
140 * and for the actually highlighted text (_h_start and _h_end).
141 */
142typedef struct syn_pattern
143{
144 char sp_type; /* see SPTYPE_ defines below */
145 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200146 int sp_flags; /* see HL_ defines below */
147#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200148 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200149#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 struct sp_syn sp_syn; /* struct passed to in_id_list() */
151 short sp_syn_match_id; /* highlight group ID of pattern */
152 char_u *sp_pattern; /* regexp to match, pattern */
153 regprog_T *sp_prog; /* regexp to match, program */
154 int sp_ic; /* ignore-case flag for sp_prog */
155 short sp_off_flags; /* see below */
156 int sp_offsets[SPO_COUNT]; /* offsets */
157 short *sp_cont_list; /* cont. group IDs, if non-zero */
158 short *sp_next_list; /* next group IDs, if non-zero */
159 int sp_sync_idx; /* sync item index (syncing only) */
160 int sp_line_id; /* ID of last line where tried */
161 int sp_startcol; /* next match in sp_line_id line */
162} synpat_T;
163
164/* The sp_off_flags are computed like this:
165 * offset from the start of the matched text: (1 << SPO_XX_OFF)
166 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
167 * When both are present, only one is used.
168 */
169
170#define SPTYPE_MATCH 1 /* match keyword with this group ID */
171#define SPTYPE_START 2 /* match a regexp, start of item */
172#define SPTYPE_END 3 /* match a regexp, end of item */
173#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
174
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175
176#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
177
178#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
179
180/*
181 * Flags for b_syn_sync_flags:
182 */
183#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
184#define SF_MATCH 0x02 /* sync by matching a pattern */
185
186#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
187
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188#define MAXKEYWLEN 80 /* maximum length of a keyword */
189
190/*
191 * The attributes of the syntax item that has been recognized.
192 */
193static int current_attr = 0; /* attr of current syntax word */
194#ifdef FEAT_EVAL
195static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000196static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200198#ifdef FEAT_CONCEAL
199static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200200static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200201static int current_sub_char = 0;
202#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203
Bram Moolenaar217ad922005-03-20 22:37:15 +0000204typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205{
206 char_u *scl_name; /* syntax cluster name */
207 char_u *scl_name_u; /* uppercase of scl_name */
208 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000209} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210
211/*
212 * Methods of combining two clusters
213 */
214#define CLUSTER_REPLACE 1 /* replace first list with second */
215#define CLUSTER_ADD 2 /* add second list to first */
216#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
217
Bram Moolenaar217ad922005-03-20 22:37:15 +0000218#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200222 * 0 - 19999 normal syntax groups
223 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
224 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
225 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
226 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000227 */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200228#define SYNID_ALLBUT 20000 /* syntax group ID for contains=ALLBUT */
229#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
230#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
231#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
232
233#define MAX_SYNID SYNID_ALLBUT
234#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
235#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236
237/*
238 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
239 * expand_filename(). Most of the other syntax commands don't need it, so
240 * instead of passing it to them, we stow it here.
241 */
242static char_u **syn_cmdlinep;
243
244/*
245 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200246 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 * rules in each ":syn include"'d file.
248 */
249static int current_syn_inc_tag = 0;
250static int running_syn_inc_tag = 0;
251
252/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000253 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
254 * This avoids adding a pointer to the hashtable item.
255 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
256 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
257 * HI2KE() converts a hashitem pointer to a var pointer.
258 */
259static keyentry_T dumkey;
260#define KE2HIKEY(kp) ((kp)->keyword)
261#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
262#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
263
264/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265 * To reduce the time spent in keepend(), remember at which level in the state
266 * stack the first item with "keepend" is present. When "-1", there is no
267 * "keepend" on the stack.
268 */
269static int keepend_level = -1;
270
271/*
272 * For the current state we need to remember more than just the idx.
273 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
274 * (The end positions have the column number of the next char)
275 */
276typedef struct state_item
277{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000278 int si_idx; /* index of syntax pattern or
279 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000281 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 int si_m_lnum; /* lnum of the match */
283 int si_m_startcol; /* starting column of the match */
284 lpos_T si_m_endpos; /* just after end posn of the match */
285 lpos_T si_h_startpos; /* start position of the highlighting */
286 lpos_T si_h_endpos; /* end position of the highlighting */
287 lpos_T si_eoe_pos; /* end position of end pattern */
288 int si_end_idx; /* group ID for end pattern or zero */
289 int si_ends; /* if match ends before si_m_endpos */
290 int si_attr; /* attributes in this state */
291 long si_flags; /* HL_HAS_EOL flag in this state, and
292 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200293#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200294 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200295 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200296#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000297 short *si_cont_list; /* list of contained groups */
298 short *si_next_list; /* nextgroup IDs after this item ends */
299 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
300 * pattern */
301} stateitem_T;
302
303#define KEYWORD_IDX -1 /* value of si_idx for keywords */
304#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
305 but contained groups */
306
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200307#ifdef FEAT_CONCEAL
308static int next_seqnr = 0; /* value to use for si_seqnr */
309#endif
310
Bram Moolenaar071d4272004-06-13 20:20:40 +0000311/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000312 * Struct to reduce the number of arguments to get_syn_options(), it's used
313 * very often.
314 */
315typedef struct
316{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000317 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000318 int keyword; /* TRUE for ":syn keyword" */
319 int *sync_idx; /* syntax item for "grouphere" argument, NULL
320 if not allowed */
321 char has_cont_list; /* TRUE if "cont_list" can be used */
322 short *cont_list; /* group IDs for "contains" argument */
323 short *cont_in_list; /* group IDs for "containedin" argument */
324 short *next_list; /* group IDs for "nextgroup" argument */
325} syn_opt_arg_T;
326
327/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 * The next possible match in the current line for any pattern is remembered,
329 * to avoid having to try for a match in each column.
330 * If next_match_idx == -1, not tried (in this line) yet.
331 * If next_match_col == MAXCOL, no match found in this line.
332 * (All end positions have the column of the char after the end)
333 */
334static int next_match_col; /* column for start of next match */
335static lpos_T next_match_m_endpos; /* position for end of next match */
336static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
337static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
338static int next_match_idx; /* index of matched item */
339static long next_match_flags; /* flags for next match */
340static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
341static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
342static int next_match_end_idx; /* ID of group for end pattn or zero */
343static reg_extmatch_T *next_match_extmatch = NULL;
344
345/*
346 * A state stack is an array of integers or stateitem_T, stored in a
347 * garray_T. A state stack is invalid if it's itemsize entry is zero.
348 */
349#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
350#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
351
352/*
353 * The current state (within the line) of the recognition engine.
354 * When current_state.ga_itemsize is 0 the current state is invalid.
355 */
356static win_T *syn_win; /* current window for highlighting */
357static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200358static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000359static linenr_T current_lnum = 0; /* lnum of current state */
360static colnr_T current_col = 0; /* column of current state */
361static int current_state_stored = 0; /* TRUE if stored current state
362 * after setting current_finished */
363static int current_finished = 0; /* current line has been finished */
364static garray_T current_state /* current stack of state_items */
365 = {0, 0, 0, 0, NULL};
366static short *current_next_list = NULL; /* when non-zero, nextgroup list */
367static int current_next_flags = 0; /* flags for current_next_list */
368static int current_line_id = 0; /* unique number for current line */
369
370#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
371
372static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
373static int syn_match_linecont __ARGS((linenr_T lnum));
374static void syn_start_line __ARGS((void));
375static void syn_update_ends __ARGS((int startofline));
376static void syn_stack_alloc __ARGS((void));
377static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200378static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000379static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000380static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000381static void load_current_state __ARGS((synstate_T *from));
382static void invalidate_current_state __ARGS((void));
383static int syn_stack_equal __ARGS((synstate_T *sp));
384static void validate_current_state __ARGS((void));
385static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000386static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387static int did_match_already __ARGS((int idx, garray_T *gap));
388static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
389static void check_state_ends __ARGS((void));
390static void update_si_attr __ARGS((int idx));
391static void check_keepend __ARGS((void));
392static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
393static short *copy_id_list __ARGS((short *list));
394static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
395static int push_current_state __ARGS((int idx));
396static void pop_current_state __ARGS((void));
397
Bram Moolenaar860cae12010-06-05 23:22:07 +0200398static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399static 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));
400static void clear_syn_state __ARGS((synstate_T *p));
401static void clear_current_state __ARGS((void));
402
403static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
404static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
405static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
406static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
407static char_u *syn_getcurline __ARGS((void));
408static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200409static 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 +0000410static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000411static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200413static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
414static void syn_clear_pattern __ARGS((synblock_T *block, int i));
415static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000416static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200417static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418static void syn_clear_one __ARGS((int id, int syncing));
419static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
420static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
421static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
422static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
423static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
424static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
425static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
426static void syn_lines_msg __ARGS((void));
427static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200428static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429static void syn_list_one __ARGS((int id, int syncing, int link_only));
430static void syn_list_cluster __ARGS((int id));
431static void put_id_list __ARGS((char_u *name, short *list, int attr));
432static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000433static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
434static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
435static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200436static 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 +0000437static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200438static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000439static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
440static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
441static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
442static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
443#ifdef __BORLANDC__
444static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
445#else
446static int syn_compare_stub __ARGS((const void *v1, const void *v2));
447#endif
448static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
449static int syn_scl_name2id __ARGS((char_u *name));
450static int syn_scl_namen2id __ARGS((char_u *linep, int len));
451static int syn_check_cluster __ARGS((char_u *pp, int len));
452static int syn_add_cluster __ARGS((char_u *name));
453static void init_syn_patterns __ARGS((void));
454static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
455static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
456static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
457static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
458static void syn_incl_toplevel __ARGS((int id, int *flagsp));
459
460/*
461 * Start the syntax recognition for a line. This function is normally called
462 * from the screen updating, once for each displayed line.
463 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
464 * it. Careful: curbuf and curwin are likely to point to another buffer and
465 * window.
466 */
467 void
468syntax_start(wp, lnum)
469 win_T *wp;
470 linenr_T lnum;
471{
472 synstate_T *p;
473 synstate_T *last_valid = NULL;
474 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000475 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476 linenr_T parsed_lnum;
477 linenr_T first_stored;
478 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000479 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200481#ifdef FEAT_CONCEAL
482 current_sub_char = NUL;
483#endif
484
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 /*
486 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000487 * Also do this when a change was made, the current state may be invalid
488 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200490 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 {
492 invalidate_current_state();
493 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200494 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000495 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000496 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 syn_win = wp;
498
499 /*
500 * Allocate syntax stack when needed.
501 */
502 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200503 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000504 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200505 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506
507 /*
508 * If the state of the end of the previous line is useful, store it.
509 */
510 if (VALID_STATE(&current_state)
511 && current_lnum < lnum
512 && current_lnum < syn_buf->b_ml.ml_line_count)
513 {
514 (void)syn_finish_line(FALSE);
515 if (!current_state_stored)
516 {
517 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000518 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 }
520
521 /*
522 * If the current_lnum is now the same as "lnum", keep the current
523 * state (this happens very often!). Otherwise invalidate
524 * current_state and figure it out below.
525 */
526 if (current_lnum != lnum)
527 invalidate_current_state();
528 }
529 else
530 invalidate_current_state();
531
532 /*
533 * Try to synchronize from a saved state in b_sst_array[].
534 * Only do this if lnum is not before and not to far beyond a saved state.
535 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200536 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537 {
538 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200539 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000540 {
541 if (p->sst_lnum > lnum)
542 break;
543 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
544 {
545 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200546 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547 last_min_valid = p;
548 }
549 }
550 if (last_min_valid != NULL)
551 load_current_state(last_min_valid);
552 }
553
554 /*
555 * If "lnum" is before or far beyond a line with a saved state, need to
556 * re-synchronize.
557 */
558 if (INVALID_STATE(&current_state))
559 {
560 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200561 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 }
563 else
564 first_stored = current_lnum;
565
566 /*
567 * Advance from the sync point or saved state until the current line.
568 * Save some entries for syncing with later on.
569 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200570 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000571 dist = 999999;
572 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200573 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574 while (current_lnum < lnum)
575 {
576 syn_start_line();
577 (void)syn_finish_line(FALSE);
578 ++current_lnum;
579
580 /* If we parsed at least "minlines" lines or started at a valid
581 * state, the current state is considered valid. */
582 if (current_lnum >= first_stored)
583 {
584 /* Check if the saved state entry is for the current line and is
585 * equal to the current state. If so, then validate all saved
586 * states that depended on a change before the parsed line. */
587 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000588 prev = syn_stack_find_entry(current_lnum - 1);
589 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200590 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000592 sp = prev;
593 while (sp != NULL && sp->sst_lnum < current_lnum)
594 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595 if (sp != NULL
596 && sp->sst_lnum == current_lnum
597 && syn_stack_equal(sp))
598 {
599 parsed_lnum = current_lnum;
600 prev = sp;
601 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
602 {
603 if (sp->sst_lnum <= lnum)
604 /* valid state before desired line, use this one */
605 prev = sp;
606 else if (sp->sst_change_lnum == 0)
607 /* past saved states depending on change, break here. */
608 break;
609 sp->sst_change_lnum = 0;
610 sp = sp->sst_next;
611 }
612 load_current_state(prev);
613 }
614 /* Store the state at this line when it's the first one, the line
615 * where we start parsing, or some distance from the previously
616 * saved state. But only when parsed at least 'minlines'. */
617 else if (prev == NULL
618 || current_lnum == lnum
619 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000620 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 }
622
623 /* This can take a long time: break when CTRL-C pressed. The current
624 * state will be wrong then. */
625 line_breakcheck();
626 if (got_int)
627 {
628 current_lnum = lnum;
629 break;
630 }
631 }
632
633 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634}
635
636/*
637 * We cannot simply discard growarrays full of state_items or buf_states; we
638 * have to manually release their extmatch pointers first.
639 */
640 static void
641clear_syn_state(p)
642 synstate_T *p;
643{
644 int i;
645 garray_T *gap;
646
647 if (p->sst_stacksize > SST_FIX_STATES)
648 {
649 gap = &(p->sst_union.sst_ga);
650 for (i = 0; i < gap->ga_len; i++)
651 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
652 ga_clear(gap);
653 }
654 else
655 {
656 for (i = 0; i < p->sst_stacksize; i++)
657 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
658 }
659}
660
661/*
662 * Cleanup the current_state stack.
663 */
664 static void
665clear_current_state()
666{
667 int i;
668 stateitem_T *sip;
669
670 sip = (stateitem_T *)(current_state.ga_data);
671 for (i = 0; i < current_state.ga_len; i++)
672 unref_extmatch(sip[i].si_extmatch);
673 ga_clear(&current_state);
674}
675
676/*
677 * Try to find a synchronisation point for line "lnum".
678 *
679 * This sets current_lnum and the current state. One of three methods is
680 * used:
681 * 1. Search backwards for the end of a C-comment.
682 * 2. Search backwards for given sync patterns.
683 * 3. Simply start on a given number of lines above "lnum".
684 */
685 static void
686syn_sync(wp, start_lnum, last_valid)
687 win_T *wp;
688 linenr_T start_lnum;
689 synstate_T *last_valid;
690{
691 buf_T *curbuf_save;
692 win_T *curwin_save;
693 pos_T cursor_save;
694 int idx;
695 linenr_T lnum;
696 linenr_T end_lnum;
697 linenr_T break_lnum;
698 int had_sync_point;
699 stateitem_T *cur_si;
700 synpat_T *spp;
701 char_u *line;
702 int found_flags = 0;
703 int found_match_idx = 0;
704 linenr_T found_current_lnum = 0;
705 int found_current_col= 0;
706 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000707 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708
709 /*
710 * Clear any current state that might be hanging around.
711 */
712 invalidate_current_state();
713
714 /*
715 * Start at least "minlines" back. Default starting point for parsing is
716 * there.
717 * Start further back, to avoid that scrolling backwards will result in
718 * resyncing for every line. Now it resyncs only one out of N lines,
719 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
720 * Watch out for overflow when minlines is MAXLNUM.
721 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200722 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723 start_lnum = 1;
724 else
725 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200726 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200728 else if (syn_block->b_syn_sync_minlines < 10)
729 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200731 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
732 if (syn_block->b_syn_sync_maxlines != 0
733 && lnum > syn_block->b_syn_sync_maxlines)
734 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735 if (lnum >= start_lnum)
736 start_lnum = 1;
737 else
738 start_lnum -= lnum;
739 }
740 current_lnum = start_lnum;
741
742 /*
743 * 1. Search backwards for the end of a C-style comment.
744 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200745 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000746 {
747 /* Need to make syn_buf the current buffer for a moment, to be able to
748 * use find_start_comment(). */
749 curwin_save = curwin;
750 curwin = wp;
751 curbuf_save = curbuf;
752 curbuf = syn_buf;
753
754 /*
755 * Skip lines that end in a backslash.
756 */
757 for ( ; start_lnum > 1; --start_lnum)
758 {
759 line = ml_get(start_lnum - 1);
760 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
761 break;
762 }
763 current_lnum = start_lnum;
764
765 /* set cursor to start of search */
766 cursor_save = wp->w_cursor;
767 wp->w_cursor.lnum = start_lnum;
768 wp->w_cursor.col = 0;
769
770 /*
771 * If the line is inside a comment, need to find the syntax item that
772 * defines the comment.
773 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
774 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200775 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200777 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
778 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
779 == syn_block->b_syn_sync_id
780 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 {
782 validate_current_state();
783 if (push_current_state(idx) == OK)
784 update_si_attr(current_state.ga_len - 1);
785 break;
786 }
787 }
788
789 /* restore cursor and buffer */
790 wp->w_cursor = cursor_save;
791 curwin = curwin_save;
792 curbuf = curbuf_save;
793 }
794
795 /*
796 * 2. Search backwards for given sync patterns.
797 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200798 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200800 if (syn_block->b_syn_sync_maxlines != 0
801 && start_lnum > syn_block->b_syn_sync_maxlines)
802 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 else
804 break_lnum = 0;
805
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000806 found_m_endpos.lnum = 0;
807 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808 end_lnum = start_lnum;
809 lnum = start_lnum;
810 while (--lnum > break_lnum)
811 {
812 /* This can take a long time: break when CTRL-C pressed. */
813 line_breakcheck();
814 if (got_int)
815 {
816 invalidate_current_state();
817 current_lnum = start_lnum;
818 break;
819 }
820
821 /* Check if we have run into a valid saved state stack now. */
822 if (last_valid != NULL && lnum == last_valid->sst_lnum)
823 {
824 load_current_state(last_valid);
825 break;
826 }
827
828 /*
829 * Check if the previous line has the line-continuation pattern.
830 */
831 if (lnum > 1 && syn_match_linecont(lnum - 1))
832 continue;
833
834 /*
835 * Start with nothing on the state stack
836 */
837 validate_current_state();
838
839 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
840 {
841 syn_start_line();
842 for (;;)
843 {
844 had_sync_point = syn_finish_line(TRUE);
845 /*
846 * When a sync point has been found, remember where, and
847 * continue to look for another one, further on in the line.
848 */
849 if (had_sync_point && current_state.ga_len)
850 {
851 cur_si = &CUR_STATE(current_state.ga_len - 1);
852 if (cur_si->si_m_endpos.lnum > start_lnum)
853 {
854 /* ignore match that goes to after where started */
855 current_lnum = end_lnum;
856 break;
857 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000858 if (cur_si->si_idx < 0)
859 {
860 /* Cannot happen? */
861 found_flags = 0;
862 found_match_idx = KEYWORD_IDX;
863 }
864 else
865 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200866 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000867 found_flags = spp->sp_flags;
868 found_match_idx = spp->sp_sync_idx;
869 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000870 found_current_lnum = current_lnum;
871 found_current_col = current_col;
872 found_m_endpos = cur_si->si_m_endpos;
873 /*
874 * Continue after the match (be aware of a zero-length
875 * match).
876 */
877 if (found_m_endpos.lnum > current_lnum)
878 {
879 current_lnum = found_m_endpos.lnum;
880 current_col = found_m_endpos.col;
881 if (current_lnum >= end_lnum)
882 break;
883 }
884 else if (found_m_endpos.col > current_col)
885 current_col = found_m_endpos.col;
886 else
887 ++current_col;
888
889 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000890 * an item that ends here, need to do that now. Be
891 * careful not to go past the NUL. */
892 prev_current_col = current_col;
893 if (syn_getcurline()[current_col] != NUL)
894 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000896 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 }
898 else
899 break;
900 }
901 }
902
903 /*
904 * If a sync point was encountered, break here.
905 */
906 if (found_flags)
907 {
908 /*
909 * Put the item that was specified by the sync point on the
910 * state stack. If there was no item specified, make the
911 * state stack empty.
912 */
913 clear_current_state();
914 if (found_match_idx >= 0
915 && push_current_state(found_match_idx) == OK)
916 update_si_attr(current_state.ga_len - 1);
917
918 /*
919 * When using "grouphere", continue from the sync point
920 * match, until the end of the line. Parsing starts at
921 * the next line.
922 * For "groupthere" the parsing starts at start_lnum.
923 */
924 if (found_flags & HL_SYNC_HERE)
925 {
926 if (current_state.ga_len)
927 {
928 cur_si = &CUR_STATE(current_state.ga_len - 1);
929 cur_si->si_h_startpos.lnum = found_current_lnum;
930 cur_si->si_h_startpos.col = found_current_col;
931 update_si_end(cur_si, (int)current_col, TRUE);
932 check_keepend();
933 }
934 current_col = found_m_endpos.col;
935 current_lnum = found_m_endpos.lnum;
936 (void)syn_finish_line(FALSE);
937 ++current_lnum;
938 }
939 else
940 current_lnum = start_lnum;
941
942 break;
943 }
944
945 end_lnum = lnum;
946 invalidate_current_state();
947 }
948
949 /* Ran into start of the file or exceeded maximum number of lines */
950 if (lnum <= break_lnum)
951 {
952 invalidate_current_state();
953 current_lnum = break_lnum + 1;
954 }
955 }
956
957 validate_current_state();
958}
959
960/*
961 * Return TRUE if the line-continuation pattern matches in line "lnum".
962 */
963 static int
964syn_match_linecont(lnum)
965 linenr_T lnum;
966{
967 regmmatch_T regmatch;
968
Bram Moolenaar860cae12010-06-05 23:22:07 +0200969 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000970 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200971 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
972 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973 return syn_regexec(&regmatch, lnum, (colnr_T)0);
974 }
975 return FALSE;
976}
977
978/*
979 * Prepare the current state for the start of a line.
980 */
981 static void
982syn_start_line()
983{
984 current_finished = FALSE;
985 current_col = 0;
986
987 /*
988 * Need to update the end of a start/skip/end that continues from the
989 * previous line and regions that have "keepend".
990 */
991 if (current_state.ga_len > 0)
992 syn_update_ends(TRUE);
993
994 next_match_idx = -1;
995 ++current_line_id;
996}
997
998/*
999 * Check for items in the stack that need their end updated.
1000 * When "startofline" is TRUE the last item is always updated.
1001 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1002 */
1003 static void
1004syn_update_ends(startofline)
1005 int startofline;
1006{
1007 stateitem_T *cur_si;
1008 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001009 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001010
1011 if (startofline)
1012 {
1013 /* Check for a match carried over from a previous line with a
1014 * contained region. The match ends as soon as the region ends. */
1015 for (i = 0; i < current_state.ga_len; ++i)
1016 {
1017 cur_si = &CUR_STATE(i);
1018 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001019 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020 == SPTYPE_MATCH
1021 && cur_si->si_m_endpos.lnum < current_lnum)
1022 {
1023 cur_si->si_flags |= HL_MATCHCONT;
1024 cur_si->si_m_endpos.lnum = 0;
1025 cur_si->si_m_endpos.col = 0;
1026 cur_si->si_h_endpos = cur_si->si_m_endpos;
1027 cur_si->si_ends = TRUE;
1028 }
1029 }
1030 }
1031
1032 /*
1033 * Need to update the end of a start/skip/end that continues from the
1034 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001035 * influence contained items. If we've just removed "extend"
1036 * (startofline == 0) then we should update ends of normal regions
1037 * contained inside "keepend" because "extend" could have extended
1038 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 * Then check for items ending in column 0.
1040 */
1041 i = current_state.ga_len - 1;
1042 if (keepend_level >= 0)
1043 for ( ; i > keepend_level; --i)
1044 if (CUR_STATE(i).si_flags & HL_EXTEND)
1045 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001046
1047 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048 for ( ; i < current_state.ga_len; ++i)
1049 {
1050 cur_si = &CUR_STATE(i);
1051 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001052 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001053 || (i == current_state.ga_len - 1 && startofline))
1054 {
1055 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1056 cur_si->si_h_startpos.lnum = current_lnum;
1057
1058 if (!(cur_si->si_flags & HL_MATCHCONT))
1059 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001060
1061 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1062 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063 }
1064 }
1065 check_keepend();
1066 check_state_ends();
1067}
1068
1069/****************************************
1070 * Handling of the state stack cache.
1071 */
1072
1073/*
1074 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1075 *
1076 * To speed up syntax highlighting, the state stack for the start of some
1077 * lines is cached. These entries can be used to start parsing at that point.
1078 *
1079 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1080 * valid entries. b_sst_first points to the first one, then follow sst_next.
1081 * The entries are sorted on line number. The first entry is often for line 2
1082 * (line 1 always starts with an empty stack).
1083 * There is also a list for free entries. This construction is used to avoid
1084 * having to allocate and free memory blocks too often.
1085 *
1086 * When making changes to the buffer, this is logged in b_mod_*. When calling
1087 * update_screen() to update the display, it will call
1088 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1089 * entries. The entries which are inside the changed area are removed,
1090 * because they must be recomputed. Entries below the changed have their line
1091 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1092 * set to indicate that a check must be made if the changed lines would change
1093 * the cached entry.
1094 *
1095 * When later displaying lines, an entry is stored for each line. Displayed
1096 * lines are likely to be displayed again, in which case the state at the
1097 * start of the line is needed.
1098 * For not displayed lines, an entry is stored for every so many lines. These
1099 * entries will be used e.g., when scrolling backwards. The distance between
1100 * entries depends on the number of lines in the buffer. For small buffers
1101 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1102 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1103 */
1104
Bram Moolenaar860cae12010-06-05 23:22:07 +02001105 static void
1106syn_stack_free_block(block)
1107 synblock_T *block;
1108{
1109 synstate_T *p;
1110
1111 if (block->b_sst_array != NULL)
1112 {
1113 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1114 clear_syn_state(p);
1115 vim_free(block->b_sst_array);
1116 block->b_sst_array = NULL;
1117 block->b_sst_len = 0;
1118 }
1119}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120/*
1121 * Free b_sst_array[] for buffer "buf".
1122 * Used when syntax items changed to force resyncing everywhere.
1123 */
1124 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001125syn_stack_free_all(block)
1126 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128 win_T *wp;
1129
Bram Moolenaar860cae12010-06-05 23:22:07 +02001130 syn_stack_free_block(block);
1131
1132
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133#ifdef FEAT_FOLDING
1134 /* When using "syntax" fold method, must update all folds. */
1135 FOR_ALL_WINDOWS(wp)
1136 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001137 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001138 foldUpdateAll(wp);
1139 }
1140#endif
1141}
1142
1143/*
1144 * Allocate the syntax state stack for syn_buf when needed.
1145 * If the number of entries in b_sst_array[] is much too big or a bit too
1146 * small, reallocate it.
1147 * Also used to allocate b_sst_array[] for the first time.
1148 */
1149 static void
1150syn_stack_alloc()
1151{
1152 long len;
1153 synstate_T *to, *from;
1154 synstate_T *sstp;
1155
1156 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1157 if (len < SST_MIN_ENTRIES)
1158 len = SST_MIN_ENTRIES;
1159 else if (len > SST_MAX_ENTRIES)
1160 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001161 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 {
1163 /* Allocate 50% too much, to avoid reallocating too often. */
1164 len = syn_buf->b_ml.ml_line_count;
1165 len = (len + len / 2) / SST_DIST + Rows * 2;
1166 if (len < SST_MIN_ENTRIES)
1167 len = SST_MIN_ENTRIES;
1168 else if (len > SST_MAX_ENTRIES)
1169 len = SST_MAX_ENTRIES;
1170
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 {
1173 /* When shrinking the array, cleanup the existing stack.
1174 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001175 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 && syn_stack_cleanup())
1177 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001178 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1179 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001180 }
1181
1182 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1183 if (sstp == NULL) /* out of memory! */
1184 return;
1185
1186 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188 {
1189 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001190 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 from = from->sst_next)
1192 {
1193 ++to;
1194 *to = *from;
1195 to->sst_next = to + 1;
1196 }
1197 }
1198 if (to != sstp - 1)
1199 {
1200 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001201 syn_block->b_sst_first = sstp;
1202 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001203 }
1204 else
1205 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001206 syn_block->b_sst_first = NULL;
1207 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 }
1209
1210 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001211 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 while (++to < sstp + len)
1213 to->sst_next = to + 1;
1214 (sstp + len - 1)->sst_next = NULL;
1215
Bram Moolenaar860cae12010-06-05 23:22:07 +02001216 vim_free(syn_block->b_sst_array);
1217 syn_block->b_sst_array = sstp;
1218 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001219 }
1220}
1221
1222/*
1223 * Check for changes in a buffer to affect stored syntax states. Uses the
1224 * b_mod_* fields.
1225 * Called from update_screen(), before screen is being updated, once for each
1226 * displayed buffer.
1227 */
1228 void
1229syn_stack_apply_changes(buf)
1230 buf_T *buf;
1231{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001232 win_T *wp;
1233
1234 syn_stack_apply_changes_block(&buf->b_s, buf);
1235
1236 FOR_ALL_WINDOWS(wp)
1237 {
1238 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1239 syn_stack_apply_changes_block(wp->w_s, buf);
1240 }
1241}
1242
1243 static void
1244syn_stack_apply_changes_block(block, buf)
1245 synblock_T *block;
1246 buf_T *buf;
1247{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 synstate_T *p, *prev, *np;
1249 linenr_T n;
1250
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 return;
1253
1254 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001255 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001257 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 {
1259 n = p->sst_lnum + buf->b_mod_xlines;
1260 if (n <= buf->b_mod_bot)
1261 {
1262 /* this state is inside the changed area, remove it */
1263 np = p->sst_next;
1264 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001266 else
1267 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 p = np;
1270 continue;
1271 }
1272 /* This state is below the changed area. Remember the line
1273 * that needs to be parsed before this entry can be made valid
1274 * again. */
1275 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1276 {
1277 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1278 p->sst_change_lnum += buf->b_mod_xlines;
1279 else
1280 p->sst_change_lnum = buf->b_mod_top;
1281 }
1282 if (p->sst_change_lnum == 0
1283 || p->sst_change_lnum < buf->b_mod_bot)
1284 p->sst_change_lnum = buf->b_mod_bot;
1285
1286 p->sst_lnum = n;
1287 }
1288 prev = p;
1289 p = p->sst_next;
1290 }
1291}
1292
1293/*
1294 * Reduce the number of entries in the state stack for syn_buf.
1295 * Returns TRUE if at least one entry was freed.
1296 */
1297 static int
1298syn_stack_cleanup()
1299{
1300 synstate_T *p, *prev;
1301 disptick_T tick;
1302 int above;
1303 int dist;
1304 int retval = FALSE;
1305
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 return retval;
1308
1309 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001310 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001311 dist = 999999;
1312 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314
1315 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001316 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 * be removed. Set "above" when the "tick" for the oldest entry is above
1318 * "b_sst_lasttick" (the display tick wraps around).
1319 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001320 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001321 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1324 {
1325 if (prev->sst_lnum + dist > p->sst_lnum)
1326 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001327 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 {
1329 if (!above || p->sst_tick < tick)
1330 tick = p->sst_tick;
1331 above = TRUE;
1332 }
1333 else if (!above && p->sst_tick < tick)
1334 tick = p->sst_tick;
1335 }
1336 }
1337
1338 /*
1339 * Go through the list to make the entries for the oldest tick at an
1340 * interval of several lines.
1341 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001342 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1344 {
1345 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1346 {
1347 /* Move this entry from used list to free list */
1348 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001349 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 p = prev;
1351 retval = TRUE;
1352 }
1353 }
1354 return retval;
1355}
1356
1357/*
1358 * Free the allocated memory for a syn_state item.
1359 * Move the entry into the free list.
1360 */
1361 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001362syn_stack_free_entry(block, p)
1363 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364 synstate_T *p;
1365{
1366 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001367 p->sst_next = block->b_sst_firstfree;
1368 block->b_sst_firstfree = p;
1369 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370}
1371
1372/*
1373 * Find an entry in the list of state stacks at or before "lnum".
1374 * Returns NULL when there is no entry or the first entry is after "lnum".
1375 */
1376 static synstate_T *
1377syn_stack_find_entry(lnum)
1378 linenr_T lnum;
1379{
1380 synstate_T *p, *prev;
1381
1382 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001383 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001384 {
1385 if (p->sst_lnum == lnum)
1386 return p;
1387 if (p->sst_lnum > lnum)
1388 break;
1389 }
1390 return prev;
1391}
1392
1393/*
1394 * Try saving the current state in b_sst_array[].
1395 * The current state must be valid for the start of the current_lnum line!
1396 */
1397 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001398store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001399{
1400 int i;
1401 synstate_T *p;
1402 bufstate_T *bp;
1403 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001404 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001405
1406 /*
1407 * If the current state contains a start or end pattern that continues
1408 * from the previous line, we can't use it. Don't store it then.
1409 */
1410 for (i = current_state.ga_len - 1; i >= 0; --i)
1411 {
1412 cur_si = &CUR_STATE(i);
1413 if (cur_si->si_h_startpos.lnum >= current_lnum
1414 || cur_si->si_m_endpos.lnum >= current_lnum
1415 || cur_si->si_h_endpos.lnum >= current_lnum
1416 || (cur_si->si_end_idx
1417 && cur_si->si_eoe_pos.lnum >= current_lnum))
1418 break;
1419 }
1420 if (i >= 0)
1421 {
1422 if (sp != NULL)
1423 {
1424 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001425 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001426 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001427 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 else
1429 {
1430 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001431 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 if (p->sst_next == sp)
1433 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001434 if (p != NULL) /* just in case */
1435 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001436 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001437 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438 sp = NULL;
1439 }
1440 }
1441 else if (sp == NULL || sp->sst_lnum != current_lnum)
1442 {
1443 /*
1444 * Add a new entry
1445 */
1446 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001447 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 {
1449 (void)syn_stack_cleanup();
1450 /* "sp" may have been moved to the freelist now */
1451 sp = syn_stack_find_entry(current_lnum);
1452 }
1453 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001454 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001455 sp = NULL;
1456 else
1457 {
1458 /* Take the first item from the free list and put it in the used
1459 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001460 p = syn_block->b_sst_firstfree;
1461 syn_block->b_sst_firstfree = p->sst_next;
1462 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001463 if (sp == NULL)
1464 {
1465 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001466 p->sst_next = syn_block->b_sst_first;
1467 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468 }
1469 else
1470 {
1471 /* insert in list after *sp */
1472 p->sst_next = sp->sst_next;
1473 sp->sst_next = p;
1474 }
1475 sp = p;
1476 sp->sst_stacksize = 0;
1477 sp->sst_lnum = current_lnum;
1478 }
1479 }
1480 if (sp != NULL)
1481 {
1482 /* When overwriting an existing state stack, clear it first */
1483 clear_syn_state(sp);
1484 sp->sst_stacksize = current_state.ga_len;
1485 if (current_state.ga_len > SST_FIX_STATES)
1486 {
1487 /* Need to clear it, might be something remaining from when the
1488 * length was less than SST_FIX_STATES. */
1489 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1490 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1491 sp->sst_stacksize = 0;
1492 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1495 }
1496 else
1497 bp = sp->sst_union.sst_stack;
1498 for (i = 0; i < sp->sst_stacksize; ++i)
1499 {
1500 bp[i].bs_idx = CUR_STATE(i).si_idx;
1501 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001502#ifdef FEAT_CONCEAL
1503 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1504 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1505#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1507 }
1508 sp->sst_next_flags = current_next_flags;
1509 sp->sst_next_list = current_next_list;
1510 sp->sst_tick = display_tick;
1511 sp->sst_change_lnum = 0;
1512 }
1513 current_state_stored = TRUE;
1514 return sp;
1515}
1516
1517/*
1518 * Copy a state stack from "from" in b_sst_array[] to current_state;
1519 */
1520 static void
1521load_current_state(from)
1522 synstate_T *from;
1523{
1524 int i;
1525 bufstate_T *bp;
1526
1527 clear_current_state();
1528 validate_current_state();
1529 keepend_level = -1;
1530 if (from->sst_stacksize
1531 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1532 {
1533 if (from->sst_stacksize > SST_FIX_STATES)
1534 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1535 else
1536 bp = from->sst_union.sst_stack;
1537 for (i = 0; i < from->sst_stacksize; ++i)
1538 {
1539 CUR_STATE(i).si_idx = bp[i].bs_idx;
1540 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001541#ifdef FEAT_CONCEAL
1542 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1543 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1544#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1546 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1547 keepend_level = i;
1548 CUR_STATE(i).si_ends = FALSE;
1549 CUR_STATE(i).si_m_lnum = 0;
1550 if (CUR_STATE(i).si_idx >= 0)
1551 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001552 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001553 else
1554 CUR_STATE(i).si_next_list = NULL;
1555 update_si_attr(i);
1556 }
1557 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001558 }
1559 current_next_list = from->sst_next_list;
1560 current_next_flags = from->sst_next_flags;
1561 current_lnum = from->sst_lnum;
1562}
1563
1564/*
1565 * Compare saved state stack "*sp" with the current state.
1566 * Return TRUE when they are equal.
1567 */
1568 static int
1569syn_stack_equal(sp)
1570 synstate_T *sp;
1571{
1572 int i, j;
1573 bufstate_T *bp;
1574 reg_extmatch_T *six, *bsx;
1575
1576 /* First a quick check if the stacks have the same size end nextlist. */
1577 if (sp->sst_stacksize == current_state.ga_len
1578 && sp->sst_next_list == current_next_list)
1579 {
1580 /* Need to compare all states on both stacks. */
1581 if (sp->sst_stacksize > SST_FIX_STATES)
1582 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1583 else
1584 bp = sp->sst_union.sst_stack;
1585
1586 for (i = current_state.ga_len; --i >= 0; )
1587 {
1588 /* If the item has another index the state is different. */
1589 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1590 break;
1591 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1592 {
1593 /* When the extmatch pointers are different, the strings in
1594 * them can still be the same. Check if the extmatch
1595 * references are equal. */
1596 bsx = bp[i].bs_extmatch;
1597 six = CUR_STATE(i).si_extmatch;
1598 /* If one of the extmatch pointers is NULL the states are
1599 * different. */
1600 if (bsx == NULL || six == NULL)
1601 break;
1602 for (j = 0; j < NSUBEXP; ++j)
1603 {
1604 /* Check each referenced match string. They must all be
1605 * equal. */
1606 if (bsx->matches[j] != six->matches[j])
1607 {
1608 /* If the pointer is different it can still be the
1609 * same text. Compare the strings, ignore case when
1610 * the start item has the sp_ic flag set. */
1611 if (bsx->matches[j] == NULL
1612 || six->matches[j] == NULL)
1613 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001614 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001615 ? MB_STRICMP(bsx->matches[j],
1616 six->matches[j]) != 0
1617 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1618 break;
1619 }
1620 }
1621 if (j != NSUBEXP)
1622 break;
1623 }
1624 }
1625 if (i < 0)
1626 return TRUE;
1627 }
1628 return FALSE;
1629}
1630
1631/*
1632 * We stop parsing syntax above line "lnum". If the stored state at or below
1633 * this line depended on a change before it, it now depends on the line below
1634 * the last parsed line.
1635 * The window looks like this:
1636 * line which changed
1637 * displayed line
1638 * displayed line
1639 * lnum -> line below window
1640 */
1641 void
1642syntax_end_parsing(lnum)
1643 linenr_T lnum;
1644{
1645 synstate_T *sp;
1646
1647 sp = syn_stack_find_entry(lnum);
1648 if (sp != NULL && sp->sst_lnum < lnum)
1649 sp = sp->sst_next;
1650
1651 if (sp != NULL && sp->sst_change_lnum != 0)
1652 sp->sst_change_lnum = lnum;
1653}
1654
1655/*
1656 * End of handling of the state stack.
1657 ****************************************/
1658
1659 static void
1660invalidate_current_state()
1661{
1662 clear_current_state();
1663 current_state.ga_itemsize = 0; /* mark current_state invalid */
1664 current_next_list = NULL;
1665 keepend_level = -1;
1666}
1667
1668 static void
1669validate_current_state()
1670{
1671 current_state.ga_itemsize = sizeof(stateitem_T);
1672 current_state.ga_growsize = 3;
1673}
1674
1675/*
1676 * Return TRUE if the syntax at start of lnum changed since last time.
1677 * This will only be called just after get_syntax_attr() for the previous
1678 * line, to check if the next line needs to be redrawn too.
1679 */
1680 int
1681syntax_check_changed(lnum)
1682 linenr_T lnum;
1683{
1684 int retval = TRUE;
1685 synstate_T *sp;
1686
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687 /*
1688 * Check the state stack when:
1689 * - lnum is just below the previously syntaxed line.
1690 * - lnum is not before the lines with saved states.
1691 * - lnum is not past the lines with saved states.
1692 * - lnum is at or before the last changed line.
1693 */
1694 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1695 {
1696 sp = syn_stack_find_entry(lnum);
1697 if (sp != NULL && sp->sst_lnum == lnum)
1698 {
1699 /*
1700 * finish the previous line (needed when not all of the line was
1701 * drawn)
1702 */
1703 (void)syn_finish_line(FALSE);
1704
1705 /*
1706 * Compare the current state with the previously saved state of
1707 * the line.
1708 */
1709 if (syn_stack_equal(sp))
1710 retval = FALSE;
1711
1712 /*
1713 * Store the current state in b_sst_array[] for later use.
1714 */
1715 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001716 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717 }
1718 }
1719
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 return retval;
1721}
1722
1723/*
1724 * Finish the current line.
1725 * This doesn't return any attributes, it only gets the state at the end of
1726 * the line. It can start anywhere in the line, as long as the current state
1727 * is valid.
1728 */
1729 static int
1730syn_finish_line(syncing)
1731 int syncing; /* called for syncing */
1732{
1733 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001734 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001735
1736 if (!current_finished)
1737 {
1738 while (!current_finished)
1739 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001740 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 /*
1742 * When syncing, and found some item, need to check the item.
1743 */
1744 if (syncing && current_state.ga_len)
1745 {
1746 /*
1747 * Check for match with sync item.
1748 */
1749 cur_si = &CUR_STATE(current_state.ga_len - 1);
1750 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001751 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1753 return TRUE;
1754
1755 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001756 * that ends here, need to do that now. Be careful not to go
1757 * past the NUL. */
1758 prev_current_col = current_col;
1759 if (syn_getcurline()[current_col] != NUL)
1760 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001762 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763 }
1764 ++current_col;
1765 }
1766 }
1767 return FALSE;
1768}
1769
1770/*
1771 * Return highlight attributes for next character.
1772 * Must first call syntax_start() once for the line.
1773 * "col" is normally 0 for the first use in a line, and increments by one each
1774 * time. It's allowed to skip characters and to stop before the end of the
1775 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001776 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1777 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 */
1779 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001780get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001782 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001783 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784{
1785 int attr = 0;
1786
Bram Moolenaar349955a2007-08-14 21:07:36 +00001787 if (can_spell != NULL)
1788 /* Default: Only do spelling when there is no @Spell cluster or when
1789 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001790 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1791 ? (syn_block->b_spell_cluster_id == 0)
1792 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001793
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001795 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 return 0;
1797
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001798 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001799 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001800 {
1801 clear_current_state();
1802#ifdef FEAT_EVAL
1803 current_id = 0;
1804 current_trans_id = 0;
1805#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001806#ifdef FEAT_CONCEAL
1807 current_flags = 0;
1808#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001809 return 0;
1810 }
1811
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812 /* Make sure current_state is valid */
1813 if (INVALID_STATE(&current_state))
1814 validate_current_state();
1815
1816 /*
1817 * Skip from the current column to "col", get the attributes for "col".
1818 */
1819 while (current_col <= col)
1820 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001821 attr = syn_current_attr(FALSE, TRUE, can_spell,
1822 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001823 ++current_col;
1824 }
1825
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826 return attr;
1827}
1828
1829/*
1830 * Get syntax attributes for current_lnum, current_col.
1831 */
1832 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001833syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834 int syncing; /* When 1: called for syncing */
1835 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001836 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001837 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838{
1839 int syn_id;
1840 lpos_T endpos; /* was: char_u *endp; */
1841 lpos_T hl_startpos; /* was: int hl_startcol; */
1842 lpos_T hl_endpos;
1843 lpos_T eos_pos; /* end-of-start match (start region) */
1844 lpos_T eoe_pos; /* end-of-end pattern */
1845 int end_idx; /* group ID for end pattern */
1846 int idx;
1847 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001848 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849 int startcol;
1850 int endcol;
1851 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001852 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001853 short *next_list;
1854 int found_match; /* found usable match */
1855 static int try_next_column = FALSE; /* must try in next col */
1856 int do_keywords;
1857 regmmatch_T regmatch;
1858 lpos_T pos;
1859 int lc_col;
1860 reg_extmatch_T *cur_extmatch = NULL;
1861 char_u *line; /* current line. NOTE: becomes invalid after
1862 looking for a pattern match! */
1863
1864 /* variables for zero-width matches that have a "nextgroup" argument */
1865 int keep_next_list;
1866 int zero_width_next_list = FALSE;
1867 garray_T zero_width_next_ga;
1868
1869 /*
1870 * No character, no attributes! Past end of line?
1871 * Do try matching with an empty line (could be the start of a region).
1872 */
1873 line = syn_getcurline();
1874 if (line[current_col] == NUL && current_col != 0)
1875 {
1876 /*
1877 * If we found a match after the last column, use it.
1878 */
1879 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1880 && next_match_col != MAXCOL)
1881 (void)push_next_match(NULL);
1882
1883 current_finished = TRUE;
1884 current_state_stored = FALSE;
1885 return 0;
1886 }
1887
1888 /* if the current or next character is NUL, we will finish the line now */
1889 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1890 {
1891 current_finished = TRUE;
1892 current_state_stored = FALSE;
1893 }
1894
1895 /*
1896 * When in the previous column there was a match but it could not be used
1897 * (empty match or already matched in this column) need to try again in
1898 * the next column.
1899 */
1900 if (try_next_column)
1901 {
1902 next_match_idx = -1;
1903 try_next_column = FALSE;
1904 }
1905
1906 /* Only check for keywords when not syncing and there are some. */
1907 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001908 && (syn_block->b_keywtab.ht_used > 0
1909 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001910
1911 /* Init the list of zero-width matches with a nextlist. This is used to
1912 * avoid matching the same item in the same position twice. */
1913 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1914
1915 /*
1916 * Repeat matching keywords and patterns, to find contained items at the
1917 * same column. This stops when there are no extra matches at the current
1918 * column.
1919 */
1920 do
1921 {
1922 found_match = FALSE;
1923 keep_next_list = FALSE;
1924 syn_id = 0;
1925
1926 /*
1927 * 1. Check for a current state.
1928 * Only when there is no current state, or if the current state may
1929 * contain other things, we need to check for keywords and patterns.
1930 * Always need to check for contained items if some item has the
1931 * "containedin" argument (takes extra time!).
1932 */
1933 if (current_state.ga_len)
1934 cur_si = &CUR_STATE(current_state.ga_len - 1);
1935 else
1936 cur_si = NULL;
1937
Bram Moolenaar860cae12010-06-05 23:22:07 +02001938 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 || cur_si->si_cont_list != NULL)
1940 {
1941 /*
1942 * 2. Check for keywords, if on a keyword char after a non-keyword
1943 * char. Don't do this when syncing.
1944 */
1945 if (do_keywords)
1946 {
1947 line = syn_getcurline();
1948 if (vim_iswordc_buf(line + current_col, syn_buf)
1949 && (current_col == 0
1950 || !vim_iswordc_buf(line + current_col - 1
1951#ifdef FEAT_MBYTE
1952 - (has_mbyte
1953 ? (*mb_head_off)(line, line + current_col - 1)
1954 : 0)
1955#endif
1956 , syn_buf)))
1957 {
1958 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001959 &endcol, &flags, &next_list, cur_si,
1960 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001961 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962 {
1963 if (push_current_state(KEYWORD_IDX) == OK)
1964 {
1965 cur_si = &CUR_STATE(current_state.ga_len - 1);
1966 cur_si->si_m_startcol = current_col;
1967 cur_si->si_h_startpos.lnum = current_lnum;
1968 cur_si->si_h_startpos.col = 0; /* starts right away */
1969 cur_si->si_m_endpos.lnum = current_lnum;
1970 cur_si->si_m_endpos.col = endcol;
1971 cur_si->si_h_endpos.lnum = current_lnum;
1972 cur_si->si_h_endpos.col = endcol;
1973 cur_si->si_ends = TRUE;
1974 cur_si->si_end_idx = 0;
1975 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001976#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001977 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001978 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001979 if (current_state.ga_len > 1)
1980 cur_si->si_flags |=
1981 CUR_STATE(current_state.ga_len - 2).si_flags
1982 & HL_CONCEAL;
1983#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001984 cur_si->si_id = syn_id;
1985 cur_si->si_trans_id = syn_id;
1986 if (flags & HL_TRANSP)
1987 {
1988 if (current_state.ga_len < 2)
1989 {
1990 cur_si->si_attr = 0;
1991 cur_si->si_trans_id = 0;
1992 }
1993 else
1994 {
1995 cur_si->si_attr = CUR_STATE(
1996 current_state.ga_len - 2).si_attr;
1997 cur_si->si_trans_id = CUR_STATE(
1998 current_state.ga_len - 2).si_trans_id;
1999 }
2000 }
2001 else
2002 cur_si->si_attr = syn_id2attr(syn_id);
2003 cur_si->si_cont_list = NULL;
2004 cur_si->si_next_list = next_list;
2005 check_keepend();
2006 }
2007 else
2008 vim_free(next_list);
2009 }
2010 }
2011 }
2012
2013 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002014 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002015 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002016 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 {
2018 /*
2019 * If we didn't check for a match yet, or we are past it, check
2020 * for any match with a pattern.
2021 */
2022 if (next_match_idx < 0 || next_match_col < (int)current_col)
2023 {
2024 /*
2025 * Check all relevant patterns for a match at this
2026 * position. This is complicated, because matching with a
2027 * pattern takes quite a bit of time, thus we want to
2028 * avoid doing it when it's not needed.
2029 */
2030 next_match_idx = 0; /* no match in this line yet */
2031 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002032 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002034 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002035 if ( spp->sp_syncing == syncing
2036 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2037 && (spp->sp_type == SPTYPE_MATCH
2038 || spp->sp_type == SPTYPE_START)
2039 && (current_next_list != NULL
2040 ? in_id_list(NULL, current_next_list,
2041 &spp->sp_syn, 0)
2042 : (cur_si == NULL
2043 ? !(spp->sp_flags & HL_CONTAINED)
2044 : in_id_list(cur_si,
2045 cur_si->si_cont_list, &spp->sp_syn,
2046 spp->sp_flags & HL_CONTAINED))))
2047 {
2048 /* If we already tried matching in this line, and
2049 * there isn't a match before next_match_col, skip
2050 * this item. */
2051 if (spp->sp_line_id == current_line_id
2052 && spp->sp_startcol >= next_match_col)
2053 continue;
2054 spp->sp_line_id = current_line_id;
2055
2056 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2057 if (lc_col < 0)
2058 lc_col = 0;
2059
2060 regmatch.rmm_ic = spp->sp_ic;
2061 regmatch.regprog = spp->sp_prog;
2062 if (!syn_regexec(&regmatch, current_lnum,
2063 (colnr_T)lc_col))
2064 {
2065 /* no match in this line, try another one */
2066 spp->sp_startcol = MAXCOL;
2067 continue;
2068 }
2069
2070 /*
2071 * Compute the first column of the match.
2072 */
2073 syn_add_start_off(&pos, &regmatch,
2074 spp, SPO_MS_OFF, -1);
2075 if (pos.lnum > current_lnum)
2076 {
2077 /* must have used end of match in a next line,
2078 * we can't handle that */
2079 spp->sp_startcol = MAXCOL;
2080 continue;
2081 }
2082 startcol = pos.col;
2083
2084 /* remember the next column where this pattern
2085 * matches in the current line */
2086 spp->sp_startcol = startcol;
2087
2088 /*
2089 * If a previously found match starts at a lower
2090 * column number, don't use this one.
2091 */
2092 if (startcol >= next_match_col)
2093 continue;
2094
2095 /*
2096 * If we matched this pattern at this position
2097 * before, skip it. Must retry in the next
2098 * column, because it may match from there.
2099 */
2100 if (did_match_already(idx, &zero_width_next_ga))
2101 {
2102 try_next_column = TRUE;
2103 continue;
2104 }
2105
2106 endpos.lnum = regmatch.endpos[0].lnum;
2107 endpos.col = regmatch.endpos[0].col;
2108
2109 /* Compute the highlight start. */
2110 syn_add_start_off(&hl_startpos, &regmatch,
2111 spp, SPO_HS_OFF, -1);
2112
2113 /* Compute the region start. */
2114 /* Default is to use the end of the match. */
2115 syn_add_end_off(&eos_pos, &regmatch,
2116 spp, SPO_RS_OFF, 0);
2117
2118 /*
2119 * Grab the external submatches before they get
2120 * overwritten. Reference count doesn't change.
2121 */
2122 unref_extmatch(cur_extmatch);
2123 cur_extmatch = re_extmatch_out;
2124 re_extmatch_out = NULL;
2125
2126 flags = 0;
2127 eoe_pos.lnum = 0; /* avoid warning */
2128 eoe_pos.col = 0;
2129 end_idx = 0;
2130 hl_endpos.lnum = 0;
2131
2132 /*
2133 * For a "oneline" the end must be found in the
2134 * same line too. Search for it after the end of
2135 * the match with the start pattern. Set the
2136 * resulting end positions at the same time.
2137 */
2138 if (spp->sp_type == SPTYPE_START
2139 && (spp->sp_flags & HL_ONELINE))
2140 {
2141 lpos_T startpos;
2142
2143 startpos = endpos;
2144 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2145 &flags, &eoe_pos, &end_idx, cur_extmatch);
2146 if (endpos.lnum == 0)
2147 continue; /* not found */
2148 }
2149
2150 /*
2151 * For a "match" the size must be > 0 after the
2152 * end offset needs has been added. Except when
2153 * syncing.
2154 */
2155 else if (spp->sp_type == SPTYPE_MATCH)
2156 {
2157 syn_add_end_off(&hl_endpos, &regmatch, spp,
2158 SPO_HE_OFF, 0);
2159 syn_add_end_off(&endpos, &regmatch, spp,
2160 SPO_ME_OFF, 0);
2161 if (endpos.lnum == current_lnum
2162 && (int)endpos.col + syncing < startcol)
2163 {
2164 /*
2165 * If an empty string is matched, may need
2166 * to try matching again at next column.
2167 */
2168 if (regmatch.startpos[0].col
2169 == regmatch.endpos[0].col)
2170 try_next_column = TRUE;
2171 continue;
2172 }
2173 }
2174
2175 /*
2176 * keep the best match so far in next_match_*
2177 */
2178 /* Highlighting must start after startpos and end
2179 * before endpos. */
2180 if (hl_startpos.lnum == current_lnum
2181 && (int)hl_startpos.col < startcol)
2182 hl_startpos.col = startcol;
2183 limit_pos_zero(&hl_endpos, &endpos);
2184
2185 next_match_idx = idx;
2186 next_match_col = startcol;
2187 next_match_m_endpos = endpos;
2188 next_match_h_endpos = hl_endpos;
2189 next_match_h_startpos = hl_startpos;
2190 next_match_flags = flags;
2191 next_match_eos_pos = eos_pos;
2192 next_match_eoe_pos = eoe_pos;
2193 next_match_end_idx = end_idx;
2194 unref_extmatch(next_match_extmatch);
2195 next_match_extmatch = cur_extmatch;
2196 cur_extmatch = NULL;
2197 }
2198 }
2199 }
2200
2201 /*
2202 * If we found a match at the current column, use it.
2203 */
2204 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2205 {
2206 synpat_T *lspp;
2207
2208 /* When a zero-width item matched which has a nextgroup,
2209 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002210 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211 if (next_match_m_endpos.lnum == current_lnum
2212 && next_match_m_endpos.col == current_col
2213 && lspp->sp_next_list != NULL)
2214 {
2215 current_next_list = lspp->sp_next_list;
2216 current_next_flags = lspp->sp_flags;
2217 keep_next_list = TRUE;
2218 zero_width_next_list = TRUE;
2219
2220 /* Add the index to a list, so that we can check
2221 * later that we don't match it again (and cause an
2222 * endless loop). */
2223 if (ga_grow(&zero_width_next_ga, 1) == OK)
2224 {
2225 ((int *)(zero_width_next_ga.ga_data))
2226 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002227 }
2228 next_match_idx = -1;
2229 }
2230 else
2231 cur_si = push_next_match(cur_si);
2232 found_match = TRUE;
2233 }
2234 }
2235 }
2236
2237 /*
2238 * Handle searching for nextgroup match.
2239 */
2240 if (current_next_list != NULL && !keep_next_list)
2241 {
2242 /*
2243 * If a nextgroup was not found, continue looking for one if:
2244 * - this is an empty line and the "skipempty" option was given
2245 * - we are on white space and the "skipwhite" option was given
2246 */
2247 if (!found_match)
2248 {
2249 line = syn_getcurline();
2250 if (((current_next_flags & HL_SKIPWHITE)
2251 && vim_iswhite(line[current_col]))
2252 || ((current_next_flags & HL_SKIPEMPTY)
2253 && *line == NUL))
2254 break;
2255 }
2256
2257 /*
2258 * If a nextgroup was found: Use it, and continue looking for
2259 * contained matches.
2260 * If a nextgroup was not found: Continue looking for a normal
2261 * match.
2262 * When did set current_next_list for a zero-width item and no
2263 * match was found don't loop (would get stuck).
2264 */
2265 current_next_list = NULL;
2266 next_match_idx = -1;
2267 if (!zero_width_next_list)
2268 found_match = TRUE;
2269 }
2270
2271 } while (found_match);
2272
2273 /*
2274 * Use attributes from the current state, if within its highlighting.
2275 * If not, use attributes from the current-but-one state, etc.
2276 */
2277 current_attr = 0;
2278#ifdef FEAT_EVAL
2279 current_id = 0;
2280 current_trans_id = 0;
2281#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002282#ifdef FEAT_CONCEAL
2283 current_flags = 0;
2284#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285 if (cur_si != NULL)
2286 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002287#ifndef FEAT_EVAL
2288 int current_trans_id = 0;
2289#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002290 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2291 {
2292 sip = &CUR_STATE(idx);
2293 if ((current_lnum > sip->si_h_startpos.lnum
2294 || (current_lnum == sip->si_h_startpos.lnum
2295 && current_col >= sip->si_h_startpos.col))
2296 && (sip->si_h_endpos.lnum == 0
2297 || current_lnum < sip->si_h_endpos.lnum
2298 || (current_lnum == sip->si_h_endpos.lnum
2299 && current_col < sip->si_h_endpos.col)))
2300 {
2301 current_attr = sip->si_attr;
2302#ifdef FEAT_EVAL
2303 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002305 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002306#ifdef FEAT_CONCEAL
2307 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002308 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002309 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002310#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002311 break;
2312 }
2313 }
2314
Bram Moolenaar217ad922005-03-20 22:37:15 +00002315 if (can_spell != NULL)
2316 {
2317 struct sp_syn sps;
2318
2319 /*
2320 * set "can_spell" to TRUE if spell checking is supposed to be
2321 * done in the current item.
2322 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002323 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002324 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002325 /* There is no @Spell cluster: Do spelling for items without
2326 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002327 if (syn_block->b_nospell_cluster_id == 0
2328 || current_trans_id == 0)
2329 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002330 else
2331 {
2332 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002333 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002334 sps.cont_in_list = NULL;
2335 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2336 }
2337 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002338 else
2339 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002340 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002341 * the @Spell cluster. But not when @NoSpell is also there.
2342 * At the toplevel only spell check when ":syn spell toplevel"
2343 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002344 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002345 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002346 else
2347 {
2348 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002349 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002350 sps.cont_in_list = NULL;
2351 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2352
Bram Moolenaar860cae12010-06-05 23:22:07 +02002353 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002354 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002355 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002356 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2357 *can_spell = FALSE;
2358 }
2359 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002360 }
2361 }
2362
2363
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364 /*
2365 * Check for end of current state (and the states before it) at the
2366 * next column. Don't do this for syncing, because we would miss a
2367 * single character match.
2368 * First check if the current state ends at the current column. It
2369 * may be for an empty match and a containing item might end in the
2370 * current column.
2371 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002372 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 {
2374 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002375 if (current_state.ga_len > 0
2376 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 {
2378 ++current_col;
2379 check_state_ends();
2380 --current_col;
2381 }
2382 }
2383 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002384 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002385 /* Default: Only do spelling when there is no @Spell cluster or when
2386 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002387 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2388 ? (syn_block->b_spell_cluster_id == 0)
2389 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002391 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002392 if (current_next_list != NULL
2393 && syn_getcurline()[current_col + 1] == NUL
2394 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2395 current_next_list = NULL;
2396
2397 if (zero_width_next_ga.ga_len > 0)
2398 ga_clear(&zero_width_next_ga);
2399
2400 /* No longer need external matches. But keep next_match_extmatch. */
2401 unref_extmatch(re_extmatch_out);
2402 re_extmatch_out = NULL;
2403 unref_extmatch(cur_extmatch);
2404
2405 return current_attr;
2406}
2407
2408
2409/*
2410 * Check if we already matched pattern "idx" at the current column.
2411 */
2412 static int
2413did_match_already(idx, gap)
2414 int idx;
2415 garray_T *gap;
2416{
2417 int i;
2418
2419 for (i = current_state.ga_len; --i >= 0; )
2420 if (CUR_STATE(i).si_m_startcol == (int)current_col
2421 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2422 && CUR_STATE(i).si_idx == idx)
2423 return TRUE;
2424
2425 /* Zero-width matches with a nextgroup argument are not put on the syntax
2426 * stack, and can only be matched once anyway. */
2427 for (i = gap->ga_len; --i >= 0; )
2428 if (((int *)(gap->ga_data))[i] == idx)
2429 return TRUE;
2430
2431 return FALSE;
2432}
2433
2434/*
2435 * Push the next match onto the stack.
2436 */
2437 static stateitem_T *
2438push_next_match(cur_si)
2439 stateitem_T *cur_si;
2440{
2441 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002442#ifdef FEAT_CONCEAL
2443 int save_flags;
2444#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002445
Bram Moolenaar860cae12010-06-05 23:22:07 +02002446 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002447
2448 /*
2449 * Push the item in current_state stack;
2450 */
2451 if (push_current_state(next_match_idx) == OK)
2452 {
2453 /*
2454 * If it's a start-skip-end type that crosses lines, figure out how
2455 * much it continues in this line. Otherwise just fill in the length.
2456 */
2457 cur_si = &CUR_STATE(current_state.ga_len - 1);
2458 cur_si->si_h_startpos = next_match_h_startpos;
2459 cur_si->si_m_startcol = current_col;
2460 cur_si->si_m_lnum = current_lnum;
2461 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002462#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002463 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002464 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002465 if (current_state.ga_len > 1)
2466 cur_si->si_flags |=
2467 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2468#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469 cur_si->si_next_list = spp->sp_next_list;
2470 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2471 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2472 {
2473 /* Try to find the end pattern in the current line */
2474 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2475 check_keepend();
2476 }
2477 else
2478 {
2479 cur_si->si_m_endpos = next_match_m_endpos;
2480 cur_si->si_h_endpos = next_match_h_endpos;
2481 cur_si->si_ends = TRUE;
2482 cur_si->si_flags |= next_match_flags;
2483 cur_si->si_eoe_pos = next_match_eoe_pos;
2484 cur_si->si_end_idx = next_match_end_idx;
2485 }
2486 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2487 keepend_level = current_state.ga_len - 1;
2488 check_keepend();
2489 update_si_attr(current_state.ga_len - 1);
2490
Bram Moolenaar860cae12010-06-05 23:22:07 +02002491#ifdef FEAT_CONCEAL
2492 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2493#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002494 /*
2495 * If the start pattern has another highlight group, push another item
2496 * on the stack for the start pattern.
2497 */
2498 if ( spp->sp_type == SPTYPE_START
2499 && spp->sp_syn_match_id != 0
2500 && push_current_state(next_match_idx) == OK)
2501 {
2502 cur_si = &CUR_STATE(current_state.ga_len - 1);
2503 cur_si->si_h_startpos = next_match_h_startpos;
2504 cur_si->si_m_startcol = current_col;
2505 cur_si->si_m_lnum = current_lnum;
2506 cur_si->si_m_endpos = next_match_eos_pos;
2507 cur_si->si_h_endpos = next_match_eos_pos;
2508 cur_si->si_ends = TRUE;
2509 cur_si->si_end_idx = 0;
2510 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002511#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002512 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002513 cur_si->si_flags |= save_flags;
2514 if (cur_si->si_flags & HL_CONCEALENDS)
2515 cur_si->si_flags |= HL_CONCEAL;
2516#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002517 cur_si->si_next_list = NULL;
2518 check_keepend();
2519 update_si_attr(current_state.ga_len - 1);
2520 }
2521 }
2522
2523 next_match_idx = -1; /* try other match next time */
2524
2525 return cur_si;
2526}
2527
2528/*
2529 * Check for end of current state (and the states before it).
2530 */
2531 static void
2532check_state_ends()
2533{
2534 stateitem_T *cur_si;
2535 int had_extend = FALSE;
2536
2537 cur_si = &CUR_STATE(current_state.ga_len - 1);
2538 for (;;)
2539 {
2540 if (cur_si->si_ends
2541 && (cur_si->si_m_endpos.lnum < current_lnum
2542 || (cur_si->si_m_endpos.lnum == current_lnum
2543 && cur_si->si_m_endpos.col <= current_col)))
2544 {
2545 /*
2546 * If there is an end pattern group ID, highlight the end pattern
2547 * now. No need to pop the current item from the stack.
2548 * Only do this if the end pattern continues beyond the current
2549 * position.
2550 */
2551 if (cur_si->si_end_idx
2552 && (cur_si->si_eoe_pos.lnum > current_lnum
2553 || (cur_si->si_eoe_pos.lnum == current_lnum
2554 && cur_si->si_eoe_pos.col > current_col)))
2555 {
2556 cur_si->si_idx = cur_si->si_end_idx;
2557 cur_si->si_end_idx = 0;
2558 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2559 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2560 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002561#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002562 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002563 if (cur_si->si_flags & HL_CONCEALENDS)
2564 cur_si->si_flags |= HL_CONCEAL;
2565#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002567
2568 /* what matches next may be different now, clear it */
2569 next_match_idx = 0;
2570 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002571 break;
2572 }
2573 else
2574 {
2575 /* handle next_list, unless at end of line and no "skipnl" or
2576 * "skipempty" */
2577 current_next_list = cur_si->si_next_list;
2578 current_next_flags = cur_si->si_flags;
2579 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2580 && syn_getcurline()[current_col] == NUL)
2581 current_next_list = NULL;
2582
2583 /* When the ended item has "extend", another item with
2584 * "keepend" now needs to check for its end. */
2585 if (cur_si->si_flags & HL_EXTEND)
2586 had_extend = TRUE;
2587
2588 pop_current_state();
2589
2590 if (current_state.ga_len == 0)
2591 break;
2592
Bram Moolenaar81993f42008-01-11 20:27:45 +00002593 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594 {
2595 syn_update_ends(FALSE);
2596 if (current_state.ga_len == 0)
2597 break;
2598 }
2599
2600 cur_si = &CUR_STATE(current_state.ga_len - 1);
2601
2602 /*
2603 * Only for a region the search for the end continues after
2604 * the end of the contained item. If the contained match
2605 * included the end-of-line, break here, the region continues.
2606 * Don't do this when:
2607 * - "keepend" is used for the contained item
2608 * - not at the end of the line (could be end="x$"me=e-1).
2609 * - "excludenl" is used (HL_HAS_EOL won't be set)
2610 */
2611 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002612 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613 == SPTYPE_START
2614 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2615 {
2616 update_si_end(cur_si, (int)current_col, TRUE);
2617 check_keepend();
2618 if ((current_next_flags & HL_HAS_EOL)
2619 && keepend_level < 0
2620 && syn_getcurline()[current_col] == NUL)
2621 break;
2622 }
2623 }
2624 }
2625 else
2626 break;
2627 }
2628}
2629
2630/*
2631 * Update an entry in the current_state stack for a match or region. This
2632 * fills in si_attr, si_next_list and si_cont_list.
2633 */
2634 static void
2635update_si_attr(idx)
2636 int idx;
2637{
2638 stateitem_T *sip = &CUR_STATE(idx);
2639 synpat_T *spp;
2640
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002641 /* This should not happen... */
2642 if (sip->si_idx < 0)
2643 return;
2644
Bram Moolenaar860cae12010-06-05 23:22:07 +02002645 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 if (sip->si_flags & HL_MATCH)
2647 sip->si_id = spp->sp_syn_match_id;
2648 else
2649 sip->si_id = spp->sp_syn.id;
2650 sip->si_attr = syn_id2attr(sip->si_id);
2651 sip->si_trans_id = sip->si_id;
2652 if (sip->si_flags & HL_MATCH)
2653 sip->si_cont_list = NULL;
2654 else
2655 sip->si_cont_list = spp->sp_cont_list;
2656
2657 /*
2658 * For transparent items, take attr from outer item.
2659 * Also take cont_list, if there is none.
2660 * Don't do this for the matchgroup of a start or end pattern.
2661 */
2662 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2663 {
2664 if (idx == 0)
2665 {
2666 sip->si_attr = 0;
2667 sip->si_trans_id = 0;
2668 if (sip->si_cont_list == NULL)
2669 sip->si_cont_list = ID_LIST_ALL;
2670 }
2671 else
2672 {
2673 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2674 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002675 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2676 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 if (sip->si_cont_list == NULL)
2678 {
2679 sip->si_flags |= HL_TRANS_CONT;
2680 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2681 }
2682 }
2683 }
2684}
2685
2686/*
2687 * Check the current stack for patterns with "keepend" flag.
2688 * Propagate the match-end to contained items, until a "skipend" item is found.
2689 */
2690 static void
2691check_keepend()
2692{
2693 int i;
2694 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002695 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002696 stateitem_T *sip;
2697
2698 /*
2699 * This check can consume a lot of time; only do it from the level where
2700 * there really is a keepend.
2701 */
2702 if (keepend_level < 0)
2703 return;
2704
2705 /*
2706 * Find the last index of an "extend" item. "keepend" items before that
2707 * won't do anything. If there is no "extend" item "i" will be
2708 * "keepend_level" and all "keepend" items will work normally.
2709 */
2710 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2711 if (CUR_STATE(i).si_flags & HL_EXTEND)
2712 break;
2713
2714 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002715 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002716 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002717 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002718 for ( ; i < current_state.ga_len; ++i)
2719 {
2720 sip = &CUR_STATE(i);
2721 if (maxpos.lnum != 0)
2722 {
2723 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002724 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002725 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2726 sip->si_ends = TRUE;
2727 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002728 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2729 {
2730 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002731 || maxpos.lnum > sip->si_m_endpos.lnum
2732 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002733 && maxpos.col > sip->si_m_endpos.col))
2734 maxpos = sip->si_m_endpos;
2735 if (maxpos_h.lnum == 0
2736 || maxpos_h.lnum > sip->si_h_endpos.lnum
2737 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2738 && maxpos_h.col > sip->si_h_endpos.col))
2739 maxpos_h = sip->si_h_endpos;
2740 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 }
2742}
2743
2744/*
2745 * Update an entry in the current_state stack for a start-skip-end pattern.
2746 * This finds the end of the current item, if it's in the current line.
2747 *
2748 * Return the flags for the matched END.
2749 */
2750 static void
2751update_si_end(sip, startcol, force)
2752 stateitem_T *sip;
2753 int startcol; /* where to start searching for the end */
2754 int force; /* when TRUE overrule a previous end */
2755{
2756 lpos_T startpos;
2757 lpos_T endpos;
2758 lpos_T hl_endpos;
2759 lpos_T end_endpos;
2760 int end_idx;
2761
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002762 /* return quickly for a keyword */
2763 if (sip->si_idx < 0)
2764 return;
2765
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766 /* Don't update when it's already done. Can be a match of an end pattern
2767 * that started in a previous line. Watch out: can also be a "keepend"
2768 * from a containing item. */
2769 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2770 return;
2771
2772 /*
2773 * We need to find the end of the region. It may continue in the next
2774 * line.
2775 */
2776 end_idx = 0;
2777 startpos.lnum = current_lnum;
2778 startpos.col = startcol;
2779 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2780 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2781
2782 if (endpos.lnum == 0)
2783 {
2784 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002785 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 {
2787 /* a "oneline" never continues in the next line */
2788 sip->si_ends = TRUE;
2789 sip->si_m_endpos.lnum = current_lnum;
2790 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2791 }
2792 else
2793 {
2794 /* continues in the next line */
2795 sip->si_ends = FALSE;
2796 sip->si_m_endpos.lnum = 0;
2797 }
2798 sip->si_h_endpos = sip->si_m_endpos;
2799 }
2800 else
2801 {
2802 /* match within this line */
2803 sip->si_m_endpos = endpos;
2804 sip->si_h_endpos = hl_endpos;
2805 sip->si_eoe_pos = end_endpos;
2806 sip->si_ends = TRUE;
2807 sip->si_end_idx = end_idx;
2808 }
2809}
2810
2811/*
2812 * Add a new state to the current state stack.
2813 * It is cleared and the index set to "idx".
2814 * Return FAIL if it's not possible (out of memory).
2815 */
2816 static int
2817push_current_state(idx)
2818 int idx;
2819{
2820 if (ga_grow(&current_state, 1) == FAIL)
2821 return FAIL;
2822 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2823 CUR_STATE(current_state.ga_len).si_idx = idx;
2824 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002825 return OK;
2826}
2827
2828/*
2829 * Remove a state from the current_state stack.
2830 */
2831 static void
2832pop_current_state()
2833{
2834 if (current_state.ga_len)
2835 {
2836 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2837 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002838 }
2839 /* after the end of a pattern, try matching a keyword or pattern */
2840 next_match_idx = -1;
2841
2842 /* if first state with "keepend" is popped, reset keepend_level */
2843 if (keepend_level >= current_state.ga_len)
2844 keepend_level = -1;
2845}
2846
2847/*
2848 * Find the end of a start/skip/end syntax region after "startpos".
2849 * Only checks one line.
2850 * Also handles a match item that continued from a previous line.
2851 * If not found, the syntax item continues in the next line. m_endpos->lnum
2852 * will be 0.
2853 * If found, the end of the region and the end of the highlighting is
2854 * computed.
2855 */
2856 static void
2857find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2858 end_idx, start_ext)
2859 int idx; /* index of the pattern */
2860 lpos_T *startpos; /* where to start looking for an END match */
2861 lpos_T *m_endpos; /* return: end of match */
2862 lpos_T *hl_endpos; /* return: end of highlighting */
2863 long *flagsp; /* return: flags of matching END */
2864 lpos_T *end_endpos; /* return: end of end pattern match */
2865 int *end_idx; /* return: group ID for end pat. match, or 0 */
2866 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2867{
2868 colnr_T matchcol;
2869 synpat_T *spp, *spp_skip;
2870 int start_idx;
2871 int best_idx;
2872 regmmatch_T regmatch;
2873 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2874 lpos_T pos;
2875 char_u *line;
2876 int had_match = FALSE;
2877
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002878 /* just in case we are invoked for a keyword */
2879 if (idx < 0)
2880 return;
2881
Bram Moolenaar071d4272004-06-13 20:20:40 +00002882 /*
2883 * Check for being called with a START pattern.
2884 * Can happen with a match that continues to the next line, because it
2885 * contained a region.
2886 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002887 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002888 if (spp->sp_type != SPTYPE_START)
2889 {
2890 *hl_endpos = *startpos;
2891 return;
2892 }
2893
2894 /*
2895 * Find the SKIP or first END pattern after the last START pattern.
2896 */
2897 for (;;)
2898 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002899 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 if (spp->sp_type != SPTYPE_START)
2901 break;
2902 ++idx;
2903 }
2904
2905 /*
2906 * Lookup the SKIP pattern (if present)
2907 */
2908 if (spp->sp_type == SPTYPE_SKIP)
2909 {
2910 spp_skip = spp;
2911 ++idx;
2912 }
2913 else
2914 spp_skip = NULL;
2915
2916 /* Setup external matches for syn_regexec(). */
2917 unref_extmatch(re_extmatch_in);
2918 re_extmatch_in = ref_extmatch(start_ext);
2919
2920 matchcol = startpos->col; /* start looking for a match at sstart */
2921 start_idx = idx; /* remember the first END pattern. */
2922 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2923 for (;;)
2924 {
2925 /*
2926 * Find end pattern that matches first after "matchcol".
2927 */
2928 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002929 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 {
2931 int lc_col = matchcol;
2932
Bram Moolenaar860cae12010-06-05 23:22:07 +02002933 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2935 break;
2936 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2937 if (lc_col < 0)
2938 lc_col = 0;
2939
2940 regmatch.rmm_ic = spp->sp_ic;
2941 regmatch.regprog = spp->sp_prog;
2942 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2943 {
2944 if (best_idx == -1 || regmatch.startpos[0].col
2945 < best_regmatch.startpos[0].col)
2946 {
2947 best_idx = idx;
2948 best_regmatch.startpos[0] = regmatch.startpos[0];
2949 best_regmatch.endpos[0] = regmatch.endpos[0];
2950 }
2951 }
2952 }
2953
2954 /*
2955 * If all end patterns have been tried, and there is no match, the
2956 * item continues until end-of-line.
2957 */
2958 if (best_idx == -1)
2959 break;
2960
2961 /*
2962 * If the skip pattern matches before the end pattern,
2963 * continue searching after the skip pattern.
2964 */
2965 if (spp_skip != NULL)
2966 {
2967 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2968
2969 if (lc_col < 0)
2970 lc_col = 0;
2971 regmatch.rmm_ic = spp_skip->sp_ic;
2972 regmatch.regprog = spp_skip->sp_prog;
2973 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2974 && regmatch.startpos[0].col
2975 <= best_regmatch.startpos[0].col)
2976 {
2977 /* Add offset to skip pattern match */
2978 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2979
2980 /* If the skip pattern goes on to the next line, there is no
2981 * match with an end pattern in this line. */
2982 if (pos.lnum > startpos->lnum)
2983 break;
2984
2985 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2986
2987 /* take care of an empty match or negative offset */
2988 if (pos.col <= matchcol)
2989 ++matchcol;
2990 else if (pos.col <= regmatch.endpos[0].col)
2991 matchcol = pos.col;
2992 else
2993 /* Be careful not to jump over the NUL at the end-of-line */
2994 for (matchcol = regmatch.endpos[0].col;
2995 line[matchcol] != NUL && matchcol < pos.col;
2996 ++matchcol)
2997 ;
2998
2999 /* if the skip pattern includes end-of-line, break here */
3000 if (line[matchcol] == NUL)
3001 break;
3002
3003 continue; /* start with first end pattern again */
3004 }
3005 }
3006
3007 /*
3008 * Match from start pattern to end pattern.
3009 * Correct for match and highlight offset of end pattern.
3010 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003011 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3013 /* can't end before the start */
3014 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3015 m_endpos->col = startpos->col;
3016
3017 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3018 /* can't end before the start */
3019 if (end_endpos->lnum == startpos->lnum
3020 && end_endpos->col < startpos->col)
3021 end_endpos->col = startpos->col;
3022 /* can't end after the match */
3023 limit_pos(end_endpos, m_endpos);
3024
3025 /*
3026 * If the end group is highlighted differently, adjust the pointers.
3027 */
3028 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3029 {
3030 *end_idx = best_idx;
3031 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3032 {
3033 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3034 hl_endpos->col = best_regmatch.endpos[0].col;
3035 }
3036 else
3037 {
3038 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3039 hl_endpos->col = best_regmatch.startpos[0].col;
3040 }
3041 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3042
3043 /* can't end before the start */
3044 if (hl_endpos->lnum == startpos->lnum
3045 && hl_endpos->col < startpos->col)
3046 hl_endpos->col = startpos->col;
3047 limit_pos(hl_endpos, m_endpos);
3048
3049 /* now the match ends where the highlighting ends, it is turned
3050 * into the matchgroup for the end */
3051 *m_endpos = *hl_endpos;
3052 }
3053 else
3054 {
3055 *end_idx = 0;
3056 *hl_endpos = *end_endpos;
3057 }
3058
3059 *flagsp = spp->sp_flags;
3060
3061 had_match = TRUE;
3062 break;
3063 }
3064
3065 /* no match for an END pattern in this line */
3066 if (!had_match)
3067 m_endpos->lnum = 0;
3068
3069 /* Remove external matches. */
3070 unref_extmatch(re_extmatch_in);
3071 re_extmatch_in = NULL;
3072}
3073
3074/*
3075 * Limit "pos" not to be after "limit".
3076 */
3077 static void
3078limit_pos(pos, limit)
3079 lpos_T *pos;
3080 lpos_T *limit;
3081{
3082 if (pos->lnum > limit->lnum)
3083 *pos = *limit;
3084 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3085 pos->col = limit->col;
3086}
3087
3088/*
3089 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3090 */
3091 static void
3092limit_pos_zero(pos, limit)
3093 lpos_T *pos;
3094 lpos_T *limit;
3095{
3096 if (pos->lnum == 0)
3097 *pos = *limit;
3098 else
3099 limit_pos(pos, limit);
3100}
3101
3102/*
3103 * Add offset to matched text for end of match or highlight.
3104 */
3105 static void
3106syn_add_end_off(result, regmatch, spp, idx, extra)
3107 lpos_T *result; /* returned position */
3108 regmmatch_T *regmatch; /* start/end of match */
3109 synpat_T *spp; /* matched pattern */
3110 int idx; /* index of offset */
3111 int extra; /* extra chars for offset to start */
3112{
3113 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003114 int off;
3115 char_u *base;
3116 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003117
3118 if (spp->sp_off_flags & (1 << idx))
3119 {
3120 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003121 col = regmatch->startpos[0].col;
3122 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003123 }
3124 else
3125 {
3126 result->lnum = regmatch->endpos[0].lnum;
3127 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003128 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003129 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003130 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3131 * is a matchgroup. Watch out for match with last NL in the buffer. */
3132 if (result->lnum > syn_buf->b_ml.ml_line_count)
3133 col = 0;
3134 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003135 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003136 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3137 p = base + col;
3138 if (off > 0)
3139 {
3140 while (off-- > 0 && *p != NUL)
3141 mb_ptr_adv(p);
3142 }
3143 else if (off < 0)
3144 {
3145 while (off++ < 0 && base < p)
3146 mb_ptr_back(base, p);
3147 }
3148 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003149 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003150 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151}
3152
3153/*
3154 * Add offset to matched text for start of match or highlight.
3155 * Avoid resulting column to become negative.
3156 */
3157 static void
3158syn_add_start_off(result, regmatch, spp, idx, extra)
3159 lpos_T *result; /* returned position */
3160 regmmatch_T *regmatch; /* start/end of match */
3161 synpat_T *spp;
3162 int idx;
3163 int extra; /* extra chars for offset to end */
3164{
3165 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003166 int off;
3167 char_u *base;
3168 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003169
3170 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3171 {
3172 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003173 col = regmatch->endpos[0].col;
3174 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003175 }
3176 else
3177 {
3178 result->lnum = regmatch->startpos[0].lnum;
3179 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003180 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003181 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003182 if (result->lnum > syn_buf->b_ml.ml_line_count)
3183 {
3184 /* a "\n" at the end of the pattern may take us below the last line */
3185 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003186 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003187 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003188 if (off != 0)
3189 {
3190 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3191 p = base + col;
3192 if (off > 0)
3193 {
3194 while (off-- && *p != NUL)
3195 mb_ptr_adv(p);
3196 }
3197 else if (off < 0)
3198 {
3199 while (off++ && base < p)
3200 mb_ptr_back(base, p);
3201 }
3202 col = (int)(p - base);
3203 }
3204 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205}
3206
3207/*
3208 * Get current line in syntax buffer.
3209 */
3210 static char_u *
3211syn_getcurline()
3212{
3213 return ml_get_buf(syn_buf, current_lnum, FALSE);
3214}
3215
3216/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003217 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003218 * Returns TRUE when there is a match.
3219 */
3220 static int
3221syn_regexec(rmp, lnum, col)
3222 regmmatch_T *rmp;
3223 linenr_T lnum;
3224 colnr_T col;
3225{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003226 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003227 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228 {
3229 rmp->startpos[0].lnum += lnum;
3230 rmp->endpos[0].lnum += lnum;
3231 return TRUE;
3232 }
3233 return FALSE;
3234}
3235
3236/*
3237 * Check one position in a line for a matching keyword.
3238 * The caller must check if a keyword can start at startcol.
3239 * Return it's ID if found, 0 otherwise.
3240 */
3241 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003242check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 char_u *line;
3244 int startcol; /* position in line to check for keyword */
3245 int *endcolp; /* return: character after found keyword */
3246 long *flagsp; /* return: flags of matching keyword */
3247 short **next_listp; /* return: next_list of matching keyword */
3248 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003249 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003251 keyentry_T *kp;
3252 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003254 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003256 hashtab_T *ht;
3257 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258
3259 /* Find first character after the keyword. First character was already
3260 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003261 kwp = line + startcol;
3262 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003263 do
3264 {
3265#ifdef FEAT_MBYTE
3266 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003267 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268 else
3269#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003270 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003272 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273
Bram Moolenaardad6b692005-01-25 22:14:34 +00003274 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275 return 0;
3276
3277 /*
3278 * Must make a copy of the keyword, so we can add a NUL and make it
3279 * lowercase.
3280 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003281 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282
3283 /*
3284 * Try twice:
3285 * 1. matching case
3286 * 2. ignoring case
3287 */
3288 for (round = 1; round <= 2; ++round)
3289 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003290 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003291 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003293 if (round == 2) /* ignore case */
3294 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295
3296 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003297 * Find keywords that match. There can be several with different
3298 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299 * When current_next_list is non-zero accept only that group, otherwise:
3300 * Accept a not-contained keyword at toplevel.
3301 * Accept a keyword at other levels only if it is in the contains list.
3302 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003303 hi = hash_find(ht, keyword);
3304 if (!HASHITEM_EMPTY(hi))
3305 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003306 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003307 if (current_next_list != 0
3308 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3309 : (cur_si == NULL
3310 ? !(kp->flags & HL_CONTAINED)
3311 : in_id_list(cur_si, cur_si->si_cont_list,
3312 &kp->k_syn, kp->flags & HL_CONTAINED)))
3313 {
3314 *endcolp = startcol + kwlen;
3315 *flagsp = kp->flags;
3316 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003317#ifdef FEAT_CONCEAL
3318 *ccharp = kp->k_char;
3319#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003320 return kp->k_syn.id;
3321 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322 }
3323 }
3324 return 0;
3325}
3326
3327/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003328 * Handle ":syntax conceal" command.
3329 */
3330 static void
3331syn_cmd_conceal(eap, syncing)
3332 exarg_T *eap UNUSED;
3333 int syncing UNUSED;
3334{
3335#ifdef FEAT_CONCEAL
3336 char_u *arg = eap->arg;
3337 char_u *next;
3338
3339 eap->nextcmd = find_nextcmd(arg);
3340 if (eap->skip)
3341 return;
3342
3343 next = skiptowhite(arg);
3344 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3345 curwin->w_s->b_syn_conceal = TRUE;
3346 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3347 curwin->w_s->b_syn_conceal = FALSE;
3348 else
3349 EMSG2(_("E390: Illegal argument: %s"), arg);
3350#endif
3351}
3352
3353/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354 * Handle ":syntax case" command.
3355 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 static void
3357syn_cmd_case(eap, syncing)
3358 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003359 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360{
3361 char_u *arg = eap->arg;
3362 char_u *next;
3363
3364 eap->nextcmd = find_nextcmd(arg);
3365 if (eap->skip)
3366 return;
3367
3368 next = skiptowhite(arg);
3369 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003370 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003372 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 else
3374 EMSG2(_("E390: Illegal argument: %s"), arg);
3375}
3376
3377/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003378 * Handle ":syntax spell" command.
3379 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003380 static void
3381syn_cmd_spell(eap, syncing)
3382 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003383 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003384{
3385 char_u *arg = eap->arg;
3386 char_u *next;
3387
3388 eap->nextcmd = find_nextcmd(arg);
3389 if (eap->skip)
3390 return;
3391
3392 next = skiptowhite(arg);
3393 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003394 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003395 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003396 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003397 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003398 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003399 else
3400 EMSG2(_("E390: Illegal argument: %s"), arg);
3401}
3402
3403/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404 * Clear all syntax info for one buffer.
3405 */
3406 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003407syntax_clear(block)
3408 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409{
3410 int i;
3411
Bram Moolenaar860cae12010-06-05 23:22:07 +02003412 block->b_syn_error = FALSE; /* clear previous error */
3413 block->b_syn_ic = FALSE; /* Use case, by default */
3414 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3415 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416
3417 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003418 clear_keywtab(&block->b_keywtab);
3419 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420
3421 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003422 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3423 syn_clear_pattern(block, i);
3424 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003425
3426 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003427 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3428 syn_clear_cluster(block, i);
3429 ga_clear(&block->b_syn_clusters);
3430 block->b_spell_cluster_id = 0;
3431 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432
Bram Moolenaar860cae12010-06-05 23:22:07 +02003433 block->b_syn_sync_flags = 0;
3434 block->b_syn_sync_minlines = 0;
3435 block->b_syn_sync_maxlines = 0;
3436 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003437
Bram Moolenaar860cae12010-06-05 23:22:07 +02003438 vim_free(block->b_syn_linecont_prog);
3439 block->b_syn_linecont_prog = NULL;
3440 vim_free(block->b_syn_linecont_pat);
3441 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003442#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003443 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003444#endif
3445
3446 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003447 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003449
3450 /* Reset the counter for ":syn include" */
3451 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003452}
3453
3454/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003455 * Get rid of ownsyntax for window "wp".
3456 */
3457 void
3458reset_synblock(wp)
3459 win_T *wp;
3460{
3461 if (wp->w_s != &wp->w_buffer->b_s)
3462 {
3463 syntax_clear(wp->w_s);
3464 vim_free(wp->w_s);
3465 wp->w_s = &wp->w_buffer->b_s;
3466 }
3467}
3468
3469/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470 * Clear syncing info for one buffer.
3471 */
3472 static void
3473syntax_sync_clear()
3474{
3475 int i;
3476
3477 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003478 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3479 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3480 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481
Bram Moolenaar860cae12010-06-05 23:22:07 +02003482 curwin->w_s->b_syn_sync_flags = 0;
3483 curwin->w_s->b_syn_sync_minlines = 0;
3484 curwin->w_s->b_syn_sync_maxlines = 0;
3485 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486
Bram Moolenaar860cae12010-06-05 23:22:07 +02003487 vim_free(curwin->w_s->b_syn_linecont_prog);
3488 curwin->w_s->b_syn_linecont_prog = NULL;
3489 vim_free(curwin->w_s->b_syn_linecont_pat);
3490 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003491
Bram Moolenaar860cae12010-06-05 23:22:07 +02003492 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003493}
3494
3495/*
3496 * Remove one pattern from the buffer's pattern list.
3497 */
3498 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003499syn_remove_pattern(block, idx)
3500 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501 int idx;
3502{
3503 synpat_T *spp;
3504
Bram Moolenaar860cae12010-06-05 23:22:07 +02003505 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003506#ifdef FEAT_FOLDING
3507 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003512 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3513 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514}
3515
3516/*
3517 * Clear and free one syntax pattern. When clearing all, must be called from
3518 * last to first!
3519 */
3520 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003521syn_clear_pattern(block, i)
3522 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003523 int i;
3524{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3526 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003528 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003529 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003530 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3531 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3532 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 }
3534}
3535
3536/*
3537 * Clear and free one syntax cluster.
3538 */
3539 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003540syn_clear_cluster(block, i)
3541 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003542 int i;
3543{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003544 vim_free(SYN_CLSTR(block)[i].scl_name);
3545 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3546 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003547}
3548
3549/*
3550 * Handle ":syntax clear" command.
3551 */
3552 static void
3553syn_cmd_clear(eap, syncing)
3554 exarg_T *eap;
3555 int syncing;
3556{
3557 char_u *arg = eap->arg;
3558 char_u *arg_end;
3559 int id;
3560
3561 eap->nextcmd = find_nextcmd(arg);
3562 if (eap->skip)
3563 return;
3564
3565 /*
3566 * We have to disable this within ":syn include @group filename",
3567 * because otherwise @group would get deleted.
3568 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3569 * clear".
3570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003571 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572 return;
3573
3574 if (ends_excmd(*arg))
3575 {
3576 /*
3577 * No argument: Clear all syntax items.
3578 */
3579 if (syncing)
3580 syntax_sync_clear();
3581 else
3582 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583 syntax_clear(curwin->w_s);
3584 if (curwin->w_s == &curwin->w_buffer->b_s)
3585 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003586 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 }
3588 }
3589 else
3590 {
3591 /*
3592 * Clear the group IDs that are in the argument.
3593 */
3594 while (!ends_excmd(*arg))
3595 {
3596 arg_end = skiptowhite(arg);
3597 if (*arg == '@')
3598 {
3599 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3600 if (id == 0)
3601 {
3602 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3603 break;
3604 }
3605 else
3606 {
3607 /*
3608 * We can't physically delete a cluster without changing
3609 * the IDs of other clusters, so we do the next best thing
3610 * and make it empty.
3611 */
3612 short scl_id = id - SYNID_CLUSTER;
3613
Bram Moolenaar860cae12010-06-05 23:22:07 +02003614 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3615 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616 }
3617 }
3618 else
3619 {
3620 id = syn_namen2id(arg, (int)(arg_end - arg));
3621 if (id == 0)
3622 {
3623 EMSG2(_(e_nogroup), arg);
3624 break;
3625 }
3626 else
3627 syn_clear_one(id, syncing);
3628 }
3629 arg = skipwhite(arg_end);
3630 }
3631 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003632 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003633 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634}
3635
3636/*
3637 * Clear one syntax group for the current buffer.
3638 */
3639 static void
3640syn_clear_one(id, syncing)
3641 int id;
3642 int syncing;
3643{
3644 synpat_T *spp;
3645 int idx;
3646
3647 /* Clear keywords only when not ":syn sync clear group-name" */
3648 if (!syncing)
3649 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3651 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003652 }
3653
3654 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003655 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003657 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3659 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003660 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661 }
3662}
3663
3664/*
3665 * Handle ":syntax on" command.
3666 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667 static void
3668syn_cmd_on(eap, syncing)
3669 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003670 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671{
3672 syn_cmd_onoff(eap, "syntax");
3673}
3674
3675/*
3676 * Handle ":syntax enable" command.
3677 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678 static void
3679syn_cmd_enable(eap, syncing)
3680 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003681 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682{
3683 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3684 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003685 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686}
3687
3688/*
3689 * Handle ":syntax reset" command.
3690 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691 static void
3692syn_cmd_reset(eap, syncing)
3693 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003694 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695{
3696 eap->nextcmd = check_nextcmd(eap->arg);
3697 if (!eap->skip)
3698 {
3699 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3700 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003701 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 }
3703}
3704
3705/*
3706 * Handle ":syntax manual" command.
3707 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708 static void
3709syn_cmd_manual(eap, syncing)
3710 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003711 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712{
3713 syn_cmd_onoff(eap, "manual");
3714}
3715
3716/*
3717 * Handle ":syntax off" command.
3718 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 static void
3720syn_cmd_off(eap, syncing)
3721 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003722 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723{
3724 syn_cmd_onoff(eap, "nosyntax");
3725}
3726
3727 static void
3728syn_cmd_onoff(eap, name)
3729 exarg_T *eap;
3730 char *name;
3731{
3732 char_u buf[100];
3733
3734 eap->nextcmd = check_nextcmd(eap->arg);
3735 if (!eap->skip)
3736 {
3737 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003738 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 do_cmdline_cmd(buf);
3740 }
3741}
3742
3743/*
3744 * Handle ":syntax [list]" command: list current syntax words.
3745 */
3746 static void
3747syn_cmd_list(eap, syncing)
3748 exarg_T *eap;
3749 int syncing; /* when TRUE: list syncing items */
3750{
3751 char_u *arg = eap->arg;
3752 int id;
3753 char_u *arg_end;
3754
3755 eap->nextcmd = find_nextcmd(arg);
3756 if (eap->skip)
3757 return;
3758
Bram Moolenaar860cae12010-06-05 23:22:07 +02003759 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 {
3761 MSG(_("No Syntax items defined for this buffer"));
3762 return;
3763 }
3764
3765 if (syncing)
3766 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003767 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768 {
3769 MSG_PUTS(_("syncing on C-style comments"));
3770 syn_lines_msg();
3771 syn_match_msg();
3772 return;
3773 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003774 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003776 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777 MSG_PUTS(_("no syncing"));
3778 else
3779 {
3780 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003781 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 MSG_PUTS(_(" lines before top line"));
3783 syn_match_msg();
3784 }
3785 return;
3786 }
3787 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003788 if (curwin->w_s->b_syn_sync_minlines > 0
3789 || curwin->w_s->b_syn_sync_maxlines > 0
3790 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003791 {
3792 MSG_PUTS(_("\nsyncing on items"));
3793 syn_lines_msg();
3794 syn_match_msg();
3795 }
3796 }
3797 else
3798 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3799 if (ends_excmd(*arg))
3800 {
3801 /*
3802 * No argument: List all group IDs and all syntax clusters.
3803 */
3804 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3805 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003806 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807 syn_list_cluster(id);
3808 }
3809 else
3810 {
3811 /*
3812 * List the group IDs and syntax clusters that are in the argument.
3813 */
3814 while (!ends_excmd(*arg) && !got_int)
3815 {
3816 arg_end = skiptowhite(arg);
3817 if (*arg == '@')
3818 {
3819 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3820 if (id == 0)
3821 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3822 else
3823 syn_list_cluster(id - SYNID_CLUSTER);
3824 }
3825 else
3826 {
3827 id = syn_namen2id(arg, (int)(arg_end - arg));
3828 if (id == 0)
3829 EMSG2(_(e_nogroup), arg);
3830 else
3831 syn_list_one(id, syncing, TRUE);
3832 }
3833 arg = skipwhite(arg_end);
3834 }
3835 }
3836 eap->nextcmd = check_nextcmd(arg);
3837}
3838
3839 static void
3840syn_lines_msg()
3841{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003842 if (curwin->w_s->b_syn_sync_maxlines > 0
3843 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 {
3845 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847 {
3848 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003849 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3850 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 MSG_PUTS(", ");
3852 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 {
3855 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003856 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 }
3858 MSG_PUTS(_(" lines before top line"));
3859 }
3860}
3861
3862 static void
3863syn_match_msg()
3864{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003865 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 {
3867 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003868 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 MSG_PUTS(_(" line breaks"));
3870 }
3871}
3872
3873static int last_matchgroup;
3874
3875struct name_list
3876{
3877 int flag;
3878 char *name;
3879};
3880
3881static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3882
3883/*
3884 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3885 */
3886 static void
3887syn_list_one(id, syncing, link_only)
3888 int id;
3889 int syncing; /* when TRUE: list syncing items */
3890 int link_only; /* when TRUE; list link-only too */
3891{
3892 int attr;
3893 int idx;
3894 int did_header = FALSE;
3895 synpat_T *spp;
3896 static struct name_list namelist1[] =
3897 {
3898 {HL_DISPLAY, "display"},
3899 {HL_CONTAINED, "contained"},
3900 {HL_ONELINE, "oneline"},
3901 {HL_KEEPEND, "keepend"},
3902 {HL_EXTEND, "extend"},
3903 {HL_EXCLUDENL, "excludenl"},
3904 {HL_TRANSP, "transparent"},
3905 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003906#ifdef FEAT_CONCEAL
3907 {HL_CONCEAL, "conceal"},
3908 {HL_CONCEALENDS, "concealends"},
3909#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 {0, NULL}
3911 };
3912 static struct name_list namelist2[] =
3913 {
3914 {HL_SKIPWHITE, "skipwhite"},
3915 {HL_SKIPNL, "skipnl"},
3916 {HL_SKIPEMPTY, "skipempty"},
3917 {0, NULL}
3918 };
3919
3920 attr = hl_attr(HLF_D); /* highlight like directories */
3921
3922 /* list the keywords for "id" */
3923 if (!syncing)
3924 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003925 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3926 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 did_header, attr);
3928 }
3929
3930 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003931 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003933 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3935 continue;
3936
3937 (void)syn_list_header(did_header, 999, id);
3938 did_header = TRUE;
3939 last_matchgroup = 0;
3940 if (spp->sp_type == SPTYPE_MATCH)
3941 {
3942 put_pattern("match", ' ', spp, attr);
3943 msg_putchar(' ');
3944 }
3945 else if (spp->sp_type == SPTYPE_START)
3946 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003947 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3948 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3949 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3950 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3951 while (idx < curwin->w_s->b_syn_patterns.ga_len
3952 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3953 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 --idx;
3955 msg_putchar(' ');
3956 }
3957 syn_list_flags(namelist1, spp->sp_flags, attr);
3958
3959 if (spp->sp_cont_list != NULL)
3960 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3961
3962 if (spp->sp_syn.cont_in_list != NULL)
3963 put_id_list((char_u *)"containedin",
3964 spp->sp_syn.cont_in_list, attr);
3965
3966 if (spp->sp_next_list != NULL)
3967 {
3968 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3969 syn_list_flags(namelist2, spp->sp_flags, attr);
3970 }
3971 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3972 {
3973 if (spp->sp_flags & HL_SYNC_HERE)
3974 msg_puts_attr((char_u *)"grouphere", attr);
3975 else
3976 msg_puts_attr((char_u *)"groupthere", attr);
3977 msg_putchar(' ');
3978 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003979 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003980 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3981 else
3982 MSG_PUTS("NONE");
3983 msg_putchar(' ');
3984 }
3985 }
3986
3987 /* list the link, if there is one */
3988 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3989 {
3990 (void)syn_list_header(did_header, 999, id);
3991 msg_puts_attr((char_u *)"links to", attr);
3992 msg_putchar(' ');
3993 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3994 }
3995}
3996
3997 static void
3998syn_list_flags(nl, flags, attr)
3999 struct name_list *nl;
4000 int flags;
4001 int attr;
4002{
4003 int i;
4004
4005 for (i = 0; nl[i].flag != 0; ++i)
4006 if (flags & nl[i].flag)
4007 {
4008 msg_puts_attr((char_u *)nl[i].name, attr);
4009 msg_putchar(' ');
4010 }
4011}
4012
4013/*
4014 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4015 */
4016 static void
4017syn_list_cluster(id)
4018 int id;
4019{
4020 int endcol = 15;
4021
4022 /* slight hack: roughly duplicate the guts of syn_list_header() */
4023 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004024 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004025
4026 if (msg_col >= endcol) /* output at least one space */
4027 endcol = msg_col + 1;
4028 if (Columns <= endcol) /* avoid hang for tiny window */
4029 endcol = Columns - 1;
4030
4031 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004032 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004034 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 hl_attr(HLF_D));
4036 }
4037 else
4038 {
4039 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4040 msg_puts((char_u *)"=NONE");
4041 }
4042}
4043
4044 static void
4045put_id_list(name, list, attr)
4046 char_u *name;
4047 short *list;
4048 int attr;
4049{
4050 short *p;
4051
4052 msg_puts_attr(name, attr);
4053 msg_putchar('=');
4054 for (p = list; *p; ++p)
4055 {
4056 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4057 {
4058 if (p[1])
4059 MSG_PUTS("ALLBUT");
4060 else
4061 MSG_PUTS("ALL");
4062 }
4063 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4064 {
4065 MSG_PUTS("TOP");
4066 }
4067 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4068 {
4069 MSG_PUTS("CONTAINED");
4070 }
4071 else if (*p >= SYNID_CLUSTER)
4072 {
4073 short scl_id = *p - SYNID_CLUSTER;
4074
4075 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004076 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077 }
4078 else
4079 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4080 if (p[1])
4081 msg_putchar(',');
4082 }
4083 msg_putchar(' ');
4084}
4085
4086 static void
4087put_pattern(s, c, spp, attr)
4088 char *s;
4089 int c;
4090 synpat_T *spp;
4091 int attr;
4092{
4093 long n;
4094 int mask;
4095 int first;
4096 static char *sepchars = "/+=-#@\"|'^&";
4097 int i;
4098
4099 /* May have to write "matchgroup=group" */
4100 if (last_matchgroup != spp->sp_syn_match_id)
4101 {
4102 last_matchgroup = spp->sp_syn_match_id;
4103 msg_puts_attr((char_u *)"matchgroup", attr);
4104 msg_putchar('=');
4105 if (last_matchgroup == 0)
4106 msg_outtrans((char_u *)"NONE");
4107 else
4108 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4109 msg_putchar(' ');
4110 }
4111
4112 /* output the name of the pattern and an '=' or ' ' */
4113 msg_puts_attr((char_u *)s, attr);
4114 msg_putchar(c);
4115
4116 /* output the pattern, in between a char that is not in the pattern */
4117 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4118 if (sepchars[++i] == NUL)
4119 {
4120 i = 0; /* no good char found, just use the first one */
4121 break;
4122 }
4123 msg_putchar(sepchars[i]);
4124 msg_outtrans(spp->sp_pattern);
4125 msg_putchar(sepchars[i]);
4126
4127 /* output any pattern options */
4128 first = TRUE;
4129 for (i = 0; i < SPO_COUNT; ++i)
4130 {
4131 mask = (1 << i);
4132 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4133 {
4134 if (!first)
4135 msg_putchar(','); /* separate with commas */
4136 msg_puts((char_u *)spo_name_tab[i]);
4137 n = spp->sp_offsets[i];
4138 if (i != SPO_LC_OFF)
4139 {
4140 if (spp->sp_off_flags & mask)
4141 msg_putchar('s');
4142 else
4143 msg_putchar('e');
4144 if (n > 0)
4145 msg_putchar('+');
4146 }
4147 if (n || i == SPO_LC_OFF)
4148 msg_outnum(n);
4149 first = FALSE;
4150 }
4151 }
4152 msg_putchar(' ');
4153}
4154
4155/*
4156 * List or clear the keywords for one syntax group.
4157 * Return TRUE if the header has been printed.
4158 */
4159 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004160syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004162 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004163 int did_header; /* header has already been printed */
4164 int attr;
4165{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 hashitem_T *hi;
4168 keyentry_T *kp;
4169 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 int prev_contained = 0;
4171 short *prev_next_list = NULL;
4172 short *prev_cont_in_list = NULL;
4173 int prev_skipnl = 0;
4174 int prev_skipwhite = 0;
4175 int prev_skipempty = 0;
4176
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 /*
4178 * Unfortunately, this list of keywords is not sorted on alphabet but on
4179 * hash value...
4180 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004181 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004182 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004184 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004186 --todo;
4187 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004189 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004191 if (prev_contained != (kp->flags & HL_CONTAINED)
4192 || prev_skipnl != (kp->flags & HL_SKIPNL)
4193 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4194 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4195 || prev_cont_in_list != kp->k_syn.cont_in_list
4196 || prev_next_list != kp->next_list)
4197 outlen = 9999;
4198 else
4199 outlen = (int)STRLEN(kp->keyword);
4200 /* output "contained" and "nextgroup" on each line */
4201 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004203 prev_contained = 0;
4204 prev_next_list = NULL;
4205 prev_cont_in_list = NULL;
4206 prev_skipnl = 0;
4207 prev_skipwhite = 0;
4208 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004210 did_header = TRUE;
4211 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004212 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004213 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004215 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004217 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004219 put_id_list((char_u *)"containedin",
4220 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004222 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004224 if (kp->next_list != prev_next_list)
4225 {
4226 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4227 msg_putchar(' ');
4228 prev_next_list = kp->next_list;
4229 if (kp->flags & HL_SKIPNL)
4230 {
4231 msg_puts_attr((char_u *)"skipnl", attr);
4232 msg_putchar(' ');
4233 prev_skipnl = (kp->flags & HL_SKIPNL);
4234 }
4235 if (kp->flags & HL_SKIPWHITE)
4236 {
4237 msg_puts_attr((char_u *)"skipwhite", attr);
4238 msg_putchar(' ');
4239 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4240 }
4241 if (kp->flags & HL_SKIPEMPTY)
4242 {
4243 msg_puts_attr((char_u *)"skipempty", attr);
4244 msg_putchar(' ');
4245 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4246 }
4247 }
4248 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250 }
4251 }
4252 }
4253
4254 return did_header;
4255}
4256
4257 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004258syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004260 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004261{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004262 hashitem_T *hi;
4263 keyentry_T *kp;
4264 keyentry_T *kp_prev;
4265 keyentry_T *kp_next;
4266 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267
Bram Moolenaardad6b692005-01-25 22:14:34 +00004268 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004269 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004270 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004272 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004273 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004274 --todo;
4275 kp_prev = NULL;
4276 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004278 if (kp->k_syn.id == id)
4279 {
4280 kp_next = kp->ke_next;
4281 if (kp_prev == NULL)
4282 {
4283 if (kp_next == NULL)
4284 hash_remove(ht, hi);
4285 else
4286 hi->hi_key = KE2HIKEY(kp_next);
4287 }
4288 else
4289 kp_prev->ke_next = kp_next;
4290 vim_free(kp->next_list);
4291 vim_free(kp->k_syn.cont_in_list);
4292 vim_free(kp);
4293 kp = kp_next;
4294 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 {
4297 kp_prev = kp;
4298 kp = kp->ke_next;
4299 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300 }
4301 }
4302 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004303 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304}
4305
4306/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 */
4309 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004310clear_keywtab(ht)
4311 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004313 hashitem_T *hi;
4314 int todo;
4315 keyentry_T *kp;
4316 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004318 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004319 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321 if (!HASHITEM_EMPTY(hi))
4322 {
4323 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004324 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 kp_next = kp->ke_next;
4327 vim_free(kp->next_list);
4328 vim_free(kp->k_syn.cont_in_list);
4329 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 hash_clear(ht);
4334 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335}
4336
4337/*
4338 * Add a keyword to the list of keywords.
4339 */
4340 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004341add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342 char_u *name; /* name of keyword */
4343 int id; /* group ID for this keyword */
4344 int flags; /* flags for this keyword */
4345 short *cont_in_list; /* containedin for this keyword */
4346 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004347 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 keyentry_T *kp;
4350 hashtab_T *ht;
4351 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004352 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004353 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004354 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355
Bram Moolenaar860cae12010-06-05 23:22:07 +02004356 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004357 name_ic = str_foldcase(name, (int)STRLEN(name),
4358 name_folded, MAXKEYWLEN + 1);
4359 else
4360 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004361 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4362 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004364 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 kp->k_syn.id = id;
4366 kp->k_syn.inc_tag = current_syn_inc_tag;
4367 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004368 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004370 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004371 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004372 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373
Bram Moolenaar860cae12010-06-05 23:22:07 +02004374 if (curwin->w_s->b_syn_ic)
4375 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004376 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004377 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378
Bram Moolenaardad6b692005-01-25 22:14:34 +00004379 hash = hash_hash(kp->keyword);
4380 hi = hash_lookup(ht, kp->keyword, hash);
4381 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 /* new keyword, add to hashtable */
4384 kp->ke_next = NULL;
4385 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004389 /* keyword already exists, prepend to list */
4390 kp->ke_next = HI2KE(hi);
4391 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393}
4394
4395/*
4396 * Get the start and end of the group name argument.
4397 * Return a pointer to the first argument.
4398 * Return NULL if the end of the command was found instead of further args.
4399 */
4400 static char_u *
4401get_group_name(arg, name_end)
4402 char_u *arg; /* start of the argument */
4403 char_u **name_end; /* pointer to end of the name */
4404{
4405 char_u *rest;
4406
4407 *name_end = skiptowhite(arg);
4408 rest = skipwhite(*name_end);
4409
4410 /*
4411 * Check if there are enough arguments. The first argument may be a
4412 * pattern, where '|' is allowed, so only check for NUL.
4413 */
4414 if (ends_excmd(*arg) || *rest == NUL)
4415 return NULL;
4416 return rest;
4417}
4418
4419/*
4420 * Check for syntax command option arguments.
4421 * This can be called at any place in the list of arguments, and just picks
4422 * out the arguments that are known. Can be called several times in a row to
4423 * collect all options in between other arguments.
4424 * Return a pointer to the next argument (which isn't an option).
4425 * Return NULL for any error;
4426 */
4427 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004428get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004429 char_u *arg; /* next argument to be checked */
4430 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004431 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433 char_u *gname_start, *gname;
4434 int syn_id;
4435 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004436 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 int i;
4438 int fidx;
4439 static struct flag
4440 {
4441 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004442 int argtype;
4443 int flags;
4444 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4445 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4446 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4447 {"eExXtTeEnNdD", 0, HL_EXTEND},
4448 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4449 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4450 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4451 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4452 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4453 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4454 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4455 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4456 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004457 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4458 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4459 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004460 {"cCoOnNtTaAiInNsS", 1, 0},
4461 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4462 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004464 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004465
4466 if (arg == NULL) /* already detected error */
4467 return NULL;
4468
Bram Moolenaar860cae12010-06-05 23:22:07 +02004469#ifdef FEAT_CONCEAL
4470 if (curwin->w_s->b_syn_conceal)
4471 opt->flags |= HL_CONCEAL;
4472#endif
4473
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 for (;;)
4475 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004476 /*
4477 * This is used very often when a large number of keywords is defined.
4478 * Need to skip quickly when no option name is found.
4479 * Also avoid tolower(), it's slow.
4480 */
4481 if (strchr(first_letters, *arg) == NULL)
4482 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004483
4484 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4485 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004486 p = flagtab[fidx].name;
4487 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4488 if (arg[len] != p[i] && arg[len] != p[i + 1])
4489 break;
4490 if (p[i] == NUL && (vim_iswhite(arg[len])
4491 || (flagtab[fidx].argtype > 0
4492 ? arg[len] == '='
4493 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004495 if (opt->keyword
4496 && (flagtab[fidx].flags == HL_DISPLAY
4497 || flagtab[fidx].flags == HL_FOLD
4498 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 /* treat "display", "fold" and "extend" as a keyword */
4500 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 break;
4502 }
4503 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004504 if (fidx < 0) /* no match found */
4505 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004507 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004508 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004509 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 {
4511 EMSG(_("E395: contains argument not accepted here"));
4512 return NULL;
4513 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004514 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515 return NULL;
4516 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520 return NULL;
4521 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004522 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 return NULL;
4526 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004527 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4528 {
4529#ifdef FEAT_MBYTE
4530 /* cchar=? */
4531 if (has_mbyte)
4532 {
4533# ifdef FEAT_CONCEAL
4534 *conceal_char = mb_ptr2char(arg + 6);
4535# endif
4536 arg += mb_ptr2len(arg + 6) - 1;
4537 }
4538 else
4539#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004540 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004541#ifdef FEAT_CONCEAL
4542 *conceal_char = arg[6];
4543#else
4544 ;
4545#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004546 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004547#ifdef FEAT_CONCEAL
4548 if (!vim_isprintc_strict(*conceal_char))
4549 {
4550 EMSG(_("E844: invalid cchar value"));
4551 return NULL;
4552 }
4553#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004554 arg = skipwhite(arg + 7);
4555 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004557 {
4558 opt->flags |= flagtab[fidx].flags;
4559 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004561 if (flagtab[fidx].flags == HL_SYNC_HERE
4562 || flagtab[fidx].flags == HL_SYNC_THERE)
4563 {
4564 if (opt->sync_idx == NULL)
4565 {
4566 EMSG(_("E393: group[t]here not accepted here"));
4567 return NULL;
4568 }
4569 gname_start = arg;
4570 arg = skiptowhite(arg);
4571 if (gname_start == arg)
4572 return NULL;
4573 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4574 if (gname == NULL)
4575 return NULL;
4576 if (STRCMP(gname, "NONE") == 0)
4577 *opt->sync_idx = NONE_IDX;
4578 else
4579 {
4580 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004581 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4582 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4583 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004584 {
4585 *opt->sync_idx = i;
4586 break;
4587 }
4588 if (i < 0)
4589 {
4590 EMSG2(_("E394: Didn't find region item for %s"), gname);
4591 vim_free(gname);
4592 return NULL;
4593 }
4594 }
4595
4596 vim_free(gname);
4597 arg = skipwhite(arg);
4598 }
4599#ifdef FEAT_FOLDING
4600 else if (flagtab[fidx].flags == HL_FOLD
4601 && foldmethodIsSyntax(curwin))
4602 /* Need to update folds later. */
4603 foldUpdateAll(curwin);
4604#endif
4605 }
4606 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004607
4608 return arg;
4609}
4610
4611/*
4612 * Adjustments to syntax item when declared in a ":syn include"'d file.
4613 * Set the contained flag, and if the item is not already contained, add it
4614 * to the specified top-level group, if any.
4615 */
4616 static void
4617syn_incl_toplevel(id, flagsp)
4618 int id;
4619 int *flagsp;
4620{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004621 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004622 return;
4623 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004624 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004625 {
4626 /* We have to alloc this, because syn_combine_list() will free it. */
4627 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004628 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629
4630 if (grp_list != NULL)
4631 {
4632 grp_list[0] = id;
4633 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004634 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004635 CLUSTER_ADD);
4636 }
4637 }
4638}
4639
4640/*
4641 * Handle ":syntax include [@{group-name}] filename" command.
4642 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 static void
4644syn_cmd_include(eap, syncing)
4645 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004646 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647{
4648 char_u *arg = eap->arg;
4649 int sgl_id = 1;
4650 char_u *group_name_end;
4651 char_u *rest;
4652 char_u *errormsg = NULL;
4653 int prev_toplvl_grp;
4654 int prev_syn_inc_tag;
4655 int source = FALSE;
4656
4657 eap->nextcmd = find_nextcmd(arg);
4658 if (eap->skip)
4659 return;
4660
4661 if (arg[0] == '@')
4662 {
4663 ++arg;
4664 rest = get_group_name(arg, &group_name_end);
4665 if (rest == NULL)
4666 {
4667 EMSG((char_u *)_("E397: Filename required"));
4668 return;
4669 }
4670 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004671 if (sgl_id == 0)
4672 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004673 /* separate_nextcmd() and expand_filename() depend on this */
4674 eap->arg = rest;
4675 }
4676
4677 /*
4678 * Everything that's left, up to the next command, should be the
4679 * filename to include.
4680 */
4681 eap->argt |= (XFILE | NOSPC);
4682 separate_nextcmd(eap);
4683 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4684 {
4685 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4686 * file. Need to expand the file name first. In other cases
4687 * ":runtime!" is used. */
4688 source = TRUE;
4689 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4690 {
4691 if (errormsg != NULL)
4692 EMSG(errormsg);
4693 return;
4694 }
4695 }
4696
4697 /*
4698 * Save and restore the existing top-level grouplist id and ":syn
4699 * include" tag around the actual inclusion.
4700 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004701 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4702 {
4703 EMSG((char_u *)_("E847: Too many syntax includes"));
4704 return;
4705 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004706 prev_syn_inc_tag = current_syn_inc_tag;
4707 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004708 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4709 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004710 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4711 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004712 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004713 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004714 current_syn_inc_tag = prev_syn_inc_tag;
4715}
4716
4717/*
4718 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4719 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004720 static void
4721syn_cmd_keyword(eap, syncing)
4722 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004723 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724{
4725 char_u *arg = eap->arg;
4726 char_u *group_name_end;
4727 int syn_id;
4728 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004729 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004731 char_u *kw;
4732 syn_opt_arg_T syn_opt_arg;
4733 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004734 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735
4736 rest = get_group_name(arg, &group_name_end);
4737
4738 if (rest != NULL)
4739 {
4740 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004741 if (syn_id != 0)
4742 /* allocate a buffer, for removing backslashes in the keyword */
4743 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744 if (keyword_copy != NULL)
4745 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004746 syn_opt_arg.flags = 0;
4747 syn_opt_arg.keyword = TRUE;
4748 syn_opt_arg.sync_idx = NULL;
4749 syn_opt_arg.has_cont_list = FALSE;
4750 syn_opt_arg.cont_in_list = NULL;
4751 syn_opt_arg.next_list = NULL;
4752
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753 /*
4754 * The options given apply to ALL keywords, so all options must be
4755 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004756 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004758 cnt = 0;
4759 p = keyword_copy;
4760 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004762 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004763 if (rest == NULL || ends_excmd(*rest))
4764 break;
4765 /* Copy the keyword, removing backslashes, and add a NUL. */
4766 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004767 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004768 if (*rest == '\\' && rest[1] != NUL)
4769 ++rest;
4770 *p++ = *rest++;
4771 }
4772 *p++ = NUL;
4773 ++cnt;
4774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004776 if (!eap->skip)
4777 {
4778 /* Adjust flags for use of ":syn include". */
4779 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4780
4781 /*
4782 * 2: Add an entry for each keyword.
4783 */
4784 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4785 {
4786 for (p = vim_strchr(kw, '['); ; )
4787 {
4788 if (p != NULL)
4789 *p = NUL;
4790 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004791 syn_opt_arg.cont_in_list,
4792 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004793 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004794 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004795 if (p[1] == NUL)
4796 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004797 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004798 kw = p + 2; /* skip over the NUL */
4799 break;
4800 }
4801 if (p[1] == ']')
4802 {
4803 kw = p + 1; /* skip over the "]" */
4804 break;
4805 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004806#ifdef FEAT_MBYTE
4807 if (has_mbyte)
4808 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004809 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004810
4811 mch_memmove(p, p + 1, l);
4812 p += l;
4813 }
4814 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004816 {
4817 p[0] = p[1];
4818 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004819 }
4820 }
4821 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004823
Bram Moolenaar071d4272004-06-13 20:20:40 +00004824 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004825 vim_free(syn_opt_arg.cont_in_list);
4826 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827 }
4828 }
4829
4830 if (rest != NULL)
4831 eap->nextcmd = check_nextcmd(rest);
4832 else
4833 EMSG2(_(e_invarg2), arg);
4834
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004835 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004836 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837}
4838
4839/*
4840 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4841 *
4842 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4843 */
4844 static void
4845syn_cmd_match(eap, syncing)
4846 exarg_T *eap;
4847 int syncing; /* TRUE for ":syntax sync match .. " */
4848{
4849 char_u *arg = eap->arg;
4850 char_u *group_name_end;
4851 char_u *rest;
4852 synpat_T item; /* the item found in the line */
4853 int syn_id;
4854 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004855 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004856 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004857 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004858
4859 /* Isolate the group name, check for validity */
4860 rest = get_group_name(arg, &group_name_end);
4861
4862 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004863 syn_opt_arg.flags = 0;
4864 syn_opt_arg.keyword = FALSE;
4865 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4866 syn_opt_arg.has_cont_list = TRUE;
4867 syn_opt_arg.cont_list = NULL;
4868 syn_opt_arg.cont_in_list = NULL;
4869 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004870 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871
4872 /* get the pattern. */
4873 init_syn_patterns();
4874 vim_memset(&item, 0, sizeof(item));
4875 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004876 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4877 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878
4879 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004880 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881
4882 if (rest != NULL) /* all arguments are valid */
4883 {
4884 /*
4885 * Check for trailing command and illegal trailing arguments.
4886 */
4887 eap->nextcmd = check_nextcmd(rest);
4888 if (!ends_excmd(*rest) || eap->skip)
4889 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004890 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004891 && (syn_id = syn_check_group(arg,
4892 (int)(group_name_end - arg))) != 0)
4893 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004894 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 /*
4896 * Store the pattern in the syn_items list
4897 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004898 idx = curwin->w_s->b_syn_patterns.ga_len;
4899 SYN_ITEMS(curwin->w_s)[idx] = item;
4900 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4901 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4902 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4903 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4904 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4905 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4906 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4907 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004908 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004909#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004910 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004911#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004912 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004913 curwin->w_s->b_syn_containedin = TRUE;
4914 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4915 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916
4917 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004918 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004919 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004920#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004921 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004922 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923#endif
4924
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004925 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004926 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927 return; /* don't free the progs and patterns now */
4928 }
4929 }
4930
4931 /*
4932 * Something failed, free the allocated memory.
4933 */
4934 vim_free(item.sp_prog);
4935 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004936 vim_free(syn_opt_arg.cont_list);
4937 vim_free(syn_opt_arg.cont_in_list);
4938 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939
4940 if (rest == NULL)
4941 EMSG2(_(e_invarg2), arg);
4942}
4943
4944/*
4945 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4946 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4947 */
4948 static void
4949syn_cmd_region(eap, syncing)
4950 exarg_T *eap;
4951 int syncing; /* TRUE for ":syntax sync region .." */
4952{
4953 char_u *arg = eap->arg;
4954 char_u *group_name_end;
4955 char_u *rest; /* next arg, NULL on error */
4956 char_u *key_end;
4957 char_u *key = NULL;
4958 char_u *p;
4959 int item;
4960#define ITEM_START 0
4961#define ITEM_SKIP 1
4962#define ITEM_END 2
4963#define ITEM_MATCHGROUP 3
4964 struct pat_ptr
4965 {
4966 synpat_T *pp_synp; /* pointer to syn_pattern */
4967 int pp_matchgroup_id; /* matchgroup ID */
4968 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4969 } *(pat_ptrs[3]);
4970 /* patterns found in the line */
4971 struct pat_ptr *ppp;
4972 struct pat_ptr *ppp_next;
4973 int pat_count = 0; /* nr of syn_patterns found */
4974 int syn_id;
4975 int matchgroup_id = 0;
4976 int not_enough = FALSE; /* not enough arguments */
4977 int illegal = FALSE; /* illegal arguments */
4978 int success = FALSE;
4979 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004980 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004981 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004982
4983 /* Isolate the group name, check for validity */
4984 rest = get_group_name(arg, &group_name_end);
4985
4986 pat_ptrs[0] = NULL;
4987 pat_ptrs[1] = NULL;
4988 pat_ptrs[2] = NULL;
4989
4990 init_syn_patterns();
4991
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004992 syn_opt_arg.flags = 0;
4993 syn_opt_arg.keyword = FALSE;
4994 syn_opt_arg.sync_idx = NULL;
4995 syn_opt_arg.has_cont_list = TRUE;
4996 syn_opt_arg.cont_list = NULL;
4997 syn_opt_arg.cont_in_list = NULL;
4998 syn_opt_arg.next_list = NULL;
4999
Bram Moolenaar071d4272004-06-13 20:20:40 +00005000 /*
5001 * get the options, patterns and matchgroup.
5002 */
5003 while (rest != NULL && !ends_excmd(*rest))
5004 {
5005 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005006 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005007 if (rest == NULL || ends_excmd(*rest))
5008 break;
5009
5010 /* must be a pattern or matchgroup then */
5011 key_end = rest;
5012 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5013 ++key_end;
5014 vim_free(key);
5015 key = vim_strnsave_up(rest, (int)(key_end - rest));
5016 if (key == NULL) /* out of memory */
5017 {
5018 rest = NULL;
5019 break;
5020 }
5021 if (STRCMP(key, "MATCHGROUP") == 0)
5022 item = ITEM_MATCHGROUP;
5023 else if (STRCMP(key, "START") == 0)
5024 item = ITEM_START;
5025 else if (STRCMP(key, "END") == 0)
5026 item = ITEM_END;
5027 else if (STRCMP(key, "SKIP") == 0)
5028 {
5029 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5030 {
5031 illegal = TRUE;
5032 break;
5033 }
5034 item = ITEM_SKIP;
5035 }
5036 else
5037 break;
5038 rest = skipwhite(key_end);
5039 if (*rest != '=')
5040 {
5041 rest = NULL;
5042 EMSG2(_("E398: Missing '=': %s"), arg);
5043 break;
5044 }
5045 rest = skipwhite(rest + 1);
5046 if (*rest == NUL)
5047 {
5048 not_enough = TRUE;
5049 break;
5050 }
5051
5052 if (item == ITEM_MATCHGROUP)
5053 {
5054 p = skiptowhite(rest);
5055 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5056 matchgroup_id = 0;
5057 else
5058 {
5059 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5060 if (matchgroup_id == 0)
5061 {
5062 illegal = TRUE;
5063 break;
5064 }
5065 }
5066 rest = skipwhite(p);
5067 }
5068 else
5069 {
5070 /*
5071 * Allocate room for a syn_pattern, and link it in the list of
5072 * syn_patterns for this item, at the start (because the list is
5073 * used from end to start).
5074 */
5075 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5076 if (ppp == NULL)
5077 {
5078 rest = NULL;
5079 break;
5080 }
5081 ppp->pp_next = pat_ptrs[item];
5082 pat_ptrs[item] = ppp;
5083 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5084 if (ppp->pp_synp == NULL)
5085 {
5086 rest = NULL;
5087 break;
5088 }
5089
5090 /*
5091 * Get the syntax pattern and the following offset(s).
5092 */
5093 /* Enable the appropriate \z specials. */
5094 if (item == ITEM_START)
5095 reg_do_extmatch = REX_SET;
5096 else if (item == ITEM_SKIP || item == ITEM_END)
5097 reg_do_extmatch = REX_USE;
5098 rest = get_syn_pattern(rest, ppp->pp_synp);
5099 reg_do_extmatch = 0;
5100 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005101 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005102 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5103 ppp->pp_matchgroup_id = matchgroup_id;
5104 ++pat_count;
5105 }
5106 }
5107 vim_free(key);
5108 if (illegal || not_enough)
5109 rest = NULL;
5110
5111 /*
5112 * Must have a "start" and "end" pattern.
5113 */
5114 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5115 pat_ptrs[ITEM_END] == NULL))
5116 {
5117 not_enough = TRUE;
5118 rest = NULL;
5119 }
5120
5121 if (rest != NULL)
5122 {
5123 /*
5124 * Check for trailing garbage or command.
5125 * If OK, add the item.
5126 */
5127 eap->nextcmd = check_nextcmd(rest);
5128 if (!ends_excmd(*rest) || eap->skip)
5129 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005130 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 && (syn_id = syn_check_group(arg,
5132 (int)(group_name_end - arg))) != 0)
5133 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005134 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 /*
5136 * Store the start/skip/end in the syn_items list
5137 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005138 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005139 for (item = ITEM_START; item <= ITEM_END; ++item)
5140 {
5141 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5142 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005143 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5144 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5145 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146 (item == ITEM_START) ? SPTYPE_START :
5147 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005148 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5149 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005150 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5151 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005152 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005153 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005154#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005155 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005156#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 if (item == ITEM_START)
5158 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005159 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005160 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005161 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005162 syn_opt_arg.cont_in_list;
5163 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005164 curwin->w_s->b_syn_containedin = TRUE;
5165 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005166 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005167 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005168 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005169 ++idx;
5170#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005171 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005172 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005173#endif
5174 }
5175 }
5176
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005177 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005178 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 success = TRUE; /* don't free the progs and patterns now */
5180 }
5181 }
5182
5183 /*
5184 * Free the allocated memory.
5185 */
5186 for (item = ITEM_START; item <= ITEM_END; ++item)
5187 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5188 {
5189 if (!success)
5190 {
5191 vim_free(ppp->pp_synp->sp_prog);
5192 vim_free(ppp->pp_synp->sp_pattern);
5193 }
5194 vim_free(ppp->pp_synp);
5195 ppp_next = ppp->pp_next;
5196 vim_free(ppp);
5197 }
5198
5199 if (!success)
5200 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005201 vim_free(syn_opt_arg.cont_list);
5202 vim_free(syn_opt_arg.cont_in_list);
5203 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005204 if (not_enough)
5205 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5206 else if (illegal || rest == NULL)
5207 EMSG2(_(e_invarg2), arg);
5208 }
5209}
5210
5211/*
5212 * A simple syntax group ID comparison function suitable for use in qsort()
5213 */
5214 static int
5215#ifdef __BORLANDC__
5216_RTLENTRYF
5217#endif
5218syn_compare_stub(v1, v2)
5219 const void *v1;
5220 const void *v2;
5221{
5222 const short *s1 = v1;
5223 const short *s2 = v2;
5224
5225 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5226}
5227
5228/*
5229 * Combines lists of syntax clusters.
5230 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5231 */
5232 static void
5233syn_combine_list(clstr1, clstr2, list_op)
5234 short **clstr1;
5235 short **clstr2;
5236 int list_op;
5237{
5238 int count1 = 0;
5239 int count2 = 0;
5240 short *g1;
5241 short *g2;
5242 short *clstr = NULL;
5243 int count;
5244 int round;
5245
5246 /*
5247 * Handle degenerate cases.
5248 */
5249 if (*clstr2 == NULL)
5250 return;
5251 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5252 {
5253 if (list_op == CLUSTER_REPLACE)
5254 vim_free(*clstr1);
5255 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5256 *clstr1 = *clstr2;
5257 else
5258 vim_free(*clstr2);
5259 return;
5260 }
5261
5262 for (g1 = *clstr1; *g1; g1++)
5263 ++count1;
5264 for (g2 = *clstr2; *g2; g2++)
5265 ++count2;
5266
5267 /*
5268 * For speed purposes, sort both lists.
5269 */
5270 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5271 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5272
5273 /*
5274 * We proceed in two passes; in round 1, we count the elements to place
5275 * in the new list, and in round 2, we allocate and populate the new
5276 * list. For speed, we use a mergesort-like method, adding the smaller
5277 * of the current elements in each list to the new list.
5278 */
5279 for (round = 1; round <= 2; round++)
5280 {
5281 g1 = *clstr1;
5282 g2 = *clstr2;
5283 count = 0;
5284
5285 /*
5286 * First, loop through the lists until one of them is empty.
5287 */
5288 while (*g1 && *g2)
5289 {
5290 /*
5291 * We always want to add from the first list.
5292 */
5293 if (*g1 < *g2)
5294 {
5295 if (round == 2)
5296 clstr[count] = *g1;
5297 count++;
5298 g1++;
5299 continue;
5300 }
5301 /*
5302 * We only want to add from the second list if we're adding the
5303 * lists.
5304 */
5305 if (list_op == CLUSTER_ADD)
5306 {
5307 if (round == 2)
5308 clstr[count] = *g2;
5309 count++;
5310 }
5311 if (*g1 == *g2)
5312 g1++;
5313 g2++;
5314 }
5315
5316 /*
5317 * Now add the leftovers from whichever list didn't get finished
5318 * first. As before, we only want to add from the second list if
5319 * we're adding the lists.
5320 */
5321 for (; *g1; g1++, count++)
5322 if (round == 2)
5323 clstr[count] = *g1;
5324 if (list_op == CLUSTER_ADD)
5325 for (; *g2; g2++, count++)
5326 if (round == 2)
5327 clstr[count] = *g2;
5328
5329 if (round == 1)
5330 {
5331 /*
5332 * If the group ended up empty, we don't need to allocate any
5333 * space for it.
5334 */
5335 if (count == 0)
5336 {
5337 clstr = NULL;
5338 break;
5339 }
5340 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5341 if (clstr == NULL)
5342 break;
5343 clstr[count] = 0;
5344 }
5345 }
5346
5347 /*
5348 * Finally, put the new list in place.
5349 */
5350 vim_free(*clstr1);
5351 vim_free(*clstr2);
5352 *clstr1 = clstr;
5353}
5354
5355/*
5356 * Lookup a syntax cluster name and return it's ID.
5357 * If it is not found, 0 is returned.
5358 */
5359 static int
5360syn_scl_name2id(name)
5361 char_u *name;
5362{
5363 int i;
5364 char_u *name_u;
5365
5366 /* Avoid using stricmp() too much, it's slow on some systems */
5367 name_u = vim_strsave_up(name);
5368 if (name_u == NULL)
5369 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005370 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5371 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5372 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005373 break;
5374 vim_free(name_u);
5375 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5376}
5377
5378/*
5379 * Like syn_scl_name2id(), but take a pointer + length argument.
5380 */
5381 static int
5382syn_scl_namen2id(linep, len)
5383 char_u *linep;
5384 int len;
5385{
5386 char_u *name;
5387 int id = 0;
5388
5389 name = vim_strnsave(linep, len);
5390 if (name != NULL)
5391 {
5392 id = syn_scl_name2id(name);
5393 vim_free(name);
5394 }
5395 return id;
5396}
5397
5398/*
5399 * Find syntax cluster name in the table and return it's ID.
5400 * The argument is a pointer to the name and the length of the name.
5401 * If it doesn't exist yet, a new entry is created.
5402 * Return 0 for failure.
5403 */
5404 static int
5405syn_check_cluster(pp, len)
5406 char_u *pp;
5407 int len;
5408{
5409 int id;
5410 char_u *name;
5411
5412 name = vim_strnsave(pp, len);
5413 if (name == NULL)
5414 return 0;
5415
5416 id = syn_scl_name2id(name);
5417 if (id == 0) /* doesn't exist yet */
5418 id = syn_add_cluster(name);
5419 else
5420 vim_free(name);
5421 return id;
5422}
5423
5424/*
5425 * Add new syntax cluster and return it's ID.
5426 * "name" must be an allocated string, it will be consumed.
5427 * Return 0 for failure.
5428 */
5429 static int
5430syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005431 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005432{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005433 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005434
5435 /*
5436 * First call for this growarray: init growing array.
5437 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005438 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005439 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005440 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5441 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005442 }
5443
Bram Moolenaar42431a72011-04-01 14:44:59 +02005444 len = curwin->w_s->b_syn_clusters.ga_len;
5445 if (len >= MAX_CLUSTER_ID)
5446 {
5447 EMSG((char_u *)_("E848: Too many syntax clusters"));
5448 vim_free(name);
5449 return 0;
5450 }
5451
Bram Moolenaar071d4272004-06-13 20:20:40 +00005452 /*
5453 * Make room for at least one other cluster entry.
5454 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005455 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456 {
5457 vim_free(name);
5458 return 0;
5459 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005460
Bram Moolenaar860cae12010-06-05 23:22:07 +02005461 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5462 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5463 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5464 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5465 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005466
Bram Moolenaar217ad922005-03-20 22:37:15 +00005467 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005468 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005469 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005470 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005471
Bram Moolenaar071d4272004-06-13 20:20:40 +00005472 return len + SYNID_CLUSTER;
5473}
5474
5475/*
5476 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5477 * [add={groupname},..] [remove={groupname},..]".
5478 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005479 static void
5480syn_cmd_cluster(eap, syncing)
5481 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005482 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005483{
5484 char_u *arg = eap->arg;
5485 char_u *group_name_end;
5486 char_u *rest;
5487 int scl_id;
5488 short *clstr_list;
5489 int got_clstr = FALSE;
5490 int opt_len;
5491 int list_op;
5492
5493 eap->nextcmd = find_nextcmd(arg);
5494 if (eap->skip)
5495 return;
5496
5497 rest = get_group_name(arg, &group_name_end);
5498
5499 if (rest != NULL)
5500 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005501 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5502 if (scl_id == 0)
5503 return;
5504 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005505
5506 for (;;)
5507 {
5508 if (STRNICMP(rest, "add", 3) == 0
5509 && (vim_iswhite(rest[3]) || rest[3] == '='))
5510 {
5511 opt_len = 3;
5512 list_op = CLUSTER_ADD;
5513 }
5514 else if (STRNICMP(rest, "remove", 6) == 0
5515 && (vim_iswhite(rest[6]) || rest[6] == '='))
5516 {
5517 opt_len = 6;
5518 list_op = CLUSTER_SUBTRACT;
5519 }
5520 else if (STRNICMP(rest, "contains", 8) == 0
5521 && (vim_iswhite(rest[8]) || rest[8] == '='))
5522 {
5523 opt_len = 8;
5524 list_op = CLUSTER_REPLACE;
5525 }
5526 else
5527 break;
5528
5529 clstr_list = NULL;
5530 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5531 {
5532 EMSG2(_(e_invarg2), rest);
5533 break;
5534 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005535 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005536 &clstr_list, list_op);
5537 got_clstr = TRUE;
5538 }
5539
5540 if (got_clstr)
5541 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005542 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005543 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005544 }
5545 }
5546
5547 if (!got_clstr)
5548 EMSG(_("E400: No cluster specified"));
5549 if (rest == NULL || !ends_excmd(*rest))
5550 EMSG2(_(e_invarg2), arg);
5551}
5552
5553/*
5554 * On first call for current buffer: Init growing array.
5555 */
5556 static void
5557init_syn_patterns()
5558{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005559 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5560 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561}
5562
5563/*
5564 * Get one pattern for a ":syntax match" or ":syntax region" command.
5565 * Stores the pattern and program in a synpat_T.
5566 * Returns a pointer to the next argument, or NULL in case of an error.
5567 */
5568 static char_u *
5569get_syn_pattern(arg, ci)
5570 char_u *arg;
5571 synpat_T *ci;
5572{
5573 char_u *end;
5574 int *p;
5575 int idx;
5576 char_u *cpo_save;
5577
5578 /* need at least three chars */
5579 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5580 return NULL;
5581
5582 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5583 if (*end != *arg) /* end delimiter not found */
5584 {
5585 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5586 return NULL;
5587 }
5588 /* store the pattern and compiled regexp program */
5589 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5590 return NULL;
5591
5592 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5593 cpo_save = p_cpo;
5594 p_cpo = (char_u *)"";
5595 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5596 p_cpo = cpo_save;
5597
5598 if (ci->sp_prog == NULL)
5599 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005600 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005601
5602 /*
5603 * Check for a match, highlight or region offset.
5604 */
5605 ++end;
5606 do
5607 {
5608 for (idx = SPO_COUNT; --idx >= 0; )
5609 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5610 break;
5611 if (idx >= 0)
5612 {
5613 p = &(ci->sp_offsets[idx]);
5614 if (idx != SPO_LC_OFF)
5615 switch (end[3])
5616 {
5617 case 's': break;
5618 case 'b': break;
5619 case 'e': idx += SPO_COUNT; break;
5620 default: idx = -1; break;
5621 }
5622 if (idx >= 0)
5623 {
5624 ci->sp_off_flags |= (1 << idx);
5625 if (idx == SPO_LC_OFF) /* lc=99 */
5626 {
5627 end += 3;
5628 *p = getdigits(&end);
5629
5630 /* "lc=" offset automatically sets "ms=" offset */
5631 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5632 {
5633 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5634 ci->sp_offsets[SPO_MS_OFF] = *p;
5635 }
5636 }
5637 else /* yy=x+99 */
5638 {
5639 end += 4;
5640 if (*end == '+')
5641 {
5642 ++end;
5643 *p = getdigits(&end); /* positive offset */
5644 }
5645 else if (*end == '-')
5646 {
5647 ++end;
5648 *p = -getdigits(&end); /* negative offset */
5649 }
5650 }
5651 if (*end != ',')
5652 break;
5653 ++end;
5654 }
5655 }
5656 } while (idx >= 0);
5657
5658 if (!ends_excmd(*end) && !vim_iswhite(*end))
5659 {
5660 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5661 return NULL;
5662 }
5663 return skipwhite(end);
5664}
5665
5666/*
5667 * Handle ":syntax sync .." command.
5668 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005669 static void
5670syn_cmd_sync(eap, syncing)
5671 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005672 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005673{
5674 char_u *arg_start = eap->arg;
5675 char_u *arg_end;
5676 char_u *key = NULL;
5677 char_u *next_arg;
5678 int illegal = FALSE;
5679 int finished = FALSE;
5680 long n;
5681 char_u *cpo_save;
5682
5683 if (ends_excmd(*arg_start))
5684 {
5685 syn_cmd_list(eap, TRUE);
5686 return;
5687 }
5688
5689 while (!ends_excmd(*arg_start))
5690 {
5691 arg_end = skiptowhite(arg_start);
5692 next_arg = skipwhite(arg_end);
5693 vim_free(key);
5694 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5695 if (STRCMP(key, "CCOMMENT") == 0)
5696 {
5697 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005698 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699 if (!ends_excmd(*next_arg))
5700 {
5701 arg_end = skiptowhite(next_arg);
5702 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005703 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 (int)(arg_end - next_arg));
5705 next_arg = skipwhite(arg_end);
5706 }
5707 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005708 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709 }
5710 else if ( STRNCMP(key, "LINES", 5) == 0
5711 || STRNCMP(key, "MINLINES", 8) == 0
5712 || STRNCMP(key, "MAXLINES", 8) == 0
5713 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5714 {
5715 if (key[4] == 'S')
5716 arg_end = key + 6;
5717 else if (key[0] == 'L')
5718 arg_end = key + 11;
5719 else
5720 arg_end = key + 9;
5721 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5722 {
5723 illegal = TRUE;
5724 break;
5725 }
5726 n = getdigits(&arg_end);
5727 if (!eap->skip)
5728 {
5729 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005730 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005732 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005734 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005735 }
5736 }
5737 else if (STRCMP(key, "FROMSTART") == 0)
5738 {
5739 if (!eap->skip)
5740 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005741 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5742 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005743 }
5744 }
5745 else if (STRCMP(key, "LINECONT") == 0)
5746 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005747 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 {
5749 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5750 finished = TRUE;
5751 break;
5752 }
5753 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5754 if (*arg_end != *next_arg) /* end delimiter not found */
5755 {
5756 illegal = TRUE;
5757 break;
5758 }
5759
5760 if (!eap->skip)
5761 {
5762 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005763 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005764 (int)(arg_end - next_arg - 1))) == NULL)
5765 {
5766 finished = TRUE;
5767 break;
5768 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005769 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005770
5771 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5772 cpo_save = p_cpo;
5773 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005774 curwin->w_s->b_syn_linecont_prog =
5775 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005776 p_cpo = cpo_save;
5777
Bram Moolenaar860cae12010-06-05 23:22:07 +02005778 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005779 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005780 vim_free(curwin->w_s->b_syn_linecont_pat);
5781 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005782 finished = TRUE;
5783 break;
5784 }
5785 }
5786 next_arg = skipwhite(arg_end + 1);
5787 }
5788 else
5789 {
5790 eap->arg = next_arg;
5791 if (STRCMP(key, "MATCH") == 0)
5792 syn_cmd_match(eap, TRUE);
5793 else if (STRCMP(key, "REGION") == 0)
5794 syn_cmd_region(eap, TRUE);
5795 else if (STRCMP(key, "CLEAR") == 0)
5796 syn_cmd_clear(eap, TRUE);
5797 else
5798 illegal = TRUE;
5799 finished = TRUE;
5800 break;
5801 }
5802 arg_start = next_arg;
5803 }
5804 vim_free(key);
5805 if (illegal)
5806 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5807 else if (!finished)
5808 {
5809 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005810 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005811 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005812 }
5813}
5814
5815/*
5816 * Convert a line of highlight group names into a list of group ID numbers.
5817 * "arg" should point to the "contains" or "nextgroup" keyword.
5818 * "arg" is advanced to after the last group name.
5819 * Careful: the argument is modified (NULs added).
5820 * returns FAIL for some error, OK for success.
5821 */
5822 static int
5823get_id_list(arg, keylen, list)
5824 char_u **arg;
5825 int keylen; /* length of keyword */
5826 short **list; /* where to store the resulting list, if not
5827 NULL, the list is silently skipped! */
5828{
5829 char_u *p = NULL;
5830 char_u *end;
5831 int round;
5832 int count;
5833 int total_count = 0;
5834 short *retval = NULL;
5835 char_u *name;
5836 regmatch_T regmatch;
5837 int id;
5838 int i;
5839 int failed = FALSE;
5840
5841 /*
5842 * We parse the list twice:
5843 * round == 1: count the number of items, allocate the array.
5844 * round == 2: fill the array with the items.
5845 * In round 1 new groups may be added, causing the number of items to
5846 * grow when a regexp is used. In that case round 1 is done once again.
5847 */
5848 for (round = 1; round <= 2; ++round)
5849 {
5850 /*
5851 * skip "contains"
5852 */
5853 p = skipwhite(*arg + keylen);
5854 if (*p != '=')
5855 {
5856 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5857 break;
5858 }
5859 p = skipwhite(p + 1);
5860 if (ends_excmd(*p))
5861 {
5862 EMSG2(_("E406: Empty argument: %s"), *arg);
5863 break;
5864 }
5865
5866 /*
5867 * parse the arguments after "contains"
5868 */
5869 count = 0;
5870 while (!ends_excmd(*p))
5871 {
5872 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5873 ;
5874 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5875 if (name == NULL)
5876 {
5877 failed = TRUE;
5878 break;
5879 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005880 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005881 if ( STRCMP(name + 1, "ALLBUT") == 0
5882 || STRCMP(name + 1, "ALL") == 0
5883 || STRCMP(name + 1, "TOP") == 0
5884 || STRCMP(name + 1, "CONTAINED") == 0)
5885 {
5886 if (TOUPPER_ASC(**arg) != 'C')
5887 {
5888 EMSG2(_("E407: %s not allowed here"), name + 1);
5889 failed = TRUE;
5890 vim_free(name);
5891 break;
5892 }
5893 if (count != 0)
5894 {
5895 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5896 failed = TRUE;
5897 vim_free(name);
5898 break;
5899 }
5900 if (name[1] == 'A')
5901 id = SYNID_ALLBUT;
5902 else if (name[1] == 'T')
5903 id = SYNID_TOP;
5904 else
5905 id = SYNID_CONTAINED;
5906 id += current_syn_inc_tag;
5907 }
5908 else if (name[1] == '@')
5909 {
5910 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5911 }
5912 else
5913 {
5914 /*
5915 * Handle full group name.
5916 */
5917 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5918 id = syn_check_group(name + 1, (int)(end - p));
5919 else
5920 {
5921 /*
5922 * Handle match of regexp with group names.
5923 */
5924 *name = '^';
5925 STRCAT(name, "$");
5926 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5927 if (regmatch.regprog == NULL)
5928 {
5929 failed = TRUE;
5930 vim_free(name);
5931 break;
5932 }
5933
5934 regmatch.rm_ic = TRUE;
5935 id = 0;
5936 for (i = highlight_ga.ga_len; --i >= 0; )
5937 {
5938 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5939 (colnr_T)0))
5940 {
5941 if (round == 2)
5942 {
5943 /* Got more items than expected; can happen
5944 * when adding items that match:
5945 * "contains=a.*b,axb".
5946 * Go back to first round */
5947 if (count >= total_count)
5948 {
5949 vim_free(retval);
5950 round = 1;
5951 }
5952 else
5953 retval[count] = i + 1;
5954 }
5955 ++count;
5956 id = -1; /* remember that we found one */
5957 }
5958 }
5959 vim_free(regmatch.regprog);
5960 }
5961 }
5962 vim_free(name);
5963 if (id == 0)
5964 {
5965 EMSG2(_("E409: Unknown group name: %s"), p);
5966 failed = TRUE;
5967 break;
5968 }
5969 if (id > 0)
5970 {
5971 if (round == 2)
5972 {
5973 /* Got more items than expected, go back to first round */
5974 if (count >= total_count)
5975 {
5976 vim_free(retval);
5977 round = 1;
5978 }
5979 else
5980 retval[count] = id;
5981 }
5982 ++count;
5983 }
5984 p = skipwhite(end);
5985 if (*p != ',')
5986 break;
5987 p = skipwhite(p + 1); /* skip comma in between arguments */
5988 }
5989 if (failed)
5990 break;
5991 if (round == 1)
5992 {
5993 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5994 if (retval == NULL)
5995 break;
5996 retval[count] = 0; /* zero means end of the list */
5997 total_count = count;
5998 }
5999 }
6000
6001 *arg = p;
6002 if (failed || retval == NULL)
6003 {
6004 vim_free(retval);
6005 return FAIL;
6006 }
6007
6008 if (*list == NULL)
6009 *list = retval;
6010 else
6011 vim_free(retval); /* list already found, don't overwrite it */
6012
6013 return OK;
6014}
6015
6016/*
6017 * Make a copy of an ID list.
6018 */
6019 static short *
6020copy_id_list(list)
6021 short *list;
6022{
6023 int len;
6024 int count;
6025 short *retval;
6026
6027 if (list == NULL)
6028 return NULL;
6029
6030 for (count = 0; list[count]; ++count)
6031 ;
6032 len = (count + 1) * sizeof(short);
6033 retval = (short *)alloc((unsigned)len);
6034 if (retval != NULL)
6035 mch_memmove(retval, list, (size_t)len);
6036
6037 return retval;
6038}
6039
6040/*
6041 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6042 * "cur_si" can be NULL if not checking the "containedin" list.
6043 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6044 * the current item.
6045 * This function is called very often, keep it fast!!
6046 */
6047 static int
6048in_id_list(cur_si, list, ssp, contained)
6049 stateitem_T *cur_si; /* current item or NULL */
6050 short *list; /* id list */
6051 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6052 int contained; /* group id is contained */
6053{
6054 int retval;
6055 short *scl_list;
6056 short item;
6057 short id = ssp->id;
6058 static int depth = 0;
6059 int r;
6060
6061 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006062 if (cur_si != NULL && ssp->cont_in_list != NULL
6063 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006064 {
6065 /* Ignore transparent items without a contains argument. Double check
6066 * that we don't go back past the first one. */
6067 while ((cur_si->si_flags & HL_TRANS_CONT)
6068 && cur_si > (stateitem_T *)(current_state.ga_data))
6069 --cur_si;
6070 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6071 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006072 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6073 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006074 return TRUE;
6075 }
6076
6077 if (list == NULL)
6078 return FALSE;
6079
6080 /*
6081 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6082 * inside anything. Only allow not-contained groups.
6083 */
6084 if (list == ID_LIST_ALL)
6085 return !contained;
6086
6087 /*
6088 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6089 * contains list. We also require that "id" is at the same ":syn include"
6090 * level as the list.
6091 */
6092 item = *list;
6093 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6094 {
6095 if (item < SYNID_TOP)
6096 {
6097 /* ALL or ALLBUT: accept all groups in the same file */
6098 if (item - SYNID_ALLBUT != ssp->inc_tag)
6099 return FALSE;
6100 }
6101 else if (item < SYNID_CONTAINED)
6102 {
6103 /* TOP: accept all not-contained groups in the same file */
6104 if (item - SYNID_TOP != ssp->inc_tag || contained)
6105 return FALSE;
6106 }
6107 else
6108 {
6109 /* CONTAINED: accept all contained groups in the same file */
6110 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6111 return FALSE;
6112 }
6113 item = *++list;
6114 retval = FALSE;
6115 }
6116 else
6117 retval = TRUE;
6118
6119 /*
6120 * Return "retval" if id is in the contains list.
6121 */
6122 while (item != 0)
6123 {
6124 if (item == id)
6125 return retval;
6126 if (item >= SYNID_CLUSTER)
6127 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006128 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006129 /* restrict recursiveness to 30 to avoid an endless loop for a
6130 * cluster that includes itself (indirectly) */
6131 if (scl_list != NULL && depth < 30)
6132 {
6133 ++depth;
6134 r = in_id_list(NULL, scl_list, ssp, contained);
6135 --depth;
6136 if (r)
6137 return retval;
6138 }
6139 }
6140 item = *++list;
6141 }
6142 return !retval;
6143}
6144
6145struct subcommand
6146{
6147 char *name; /* subcommand name */
6148 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6149};
6150
6151static struct subcommand subcommands[] =
6152{
6153 {"case", syn_cmd_case},
6154 {"clear", syn_cmd_clear},
6155 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006156 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006157 {"enable", syn_cmd_enable},
6158 {"include", syn_cmd_include},
6159 {"keyword", syn_cmd_keyword},
6160 {"list", syn_cmd_list},
6161 {"manual", syn_cmd_manual},
6162 {"match", syn_cmd_match},
6163 {"on", syn_cmd_on},
6164 {"off", syn_cmd_off},
6165 {"region", syn_cmd_region},
6166 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006167 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006168 {"sync", syn_cmd_sync},
6169 {"", syn_cmd_list},
6170 {NULL, NULL}
6171};
6172
6173/*
6174 * ":syntax".
6175 * This searches the subcommands[] table for the subcommand name, and calls a
6176 * syntax_subcommand() function to do the rest.
6177 */
6178 void
6179ex_syntax(eap)
6180 exarg_T *eap;
6181{
6182 char_u *arg = eap->arg;
6183 char_u *subcmd_end;
6184 char_u *subcmd_name;
6185 int i;
6186
6187 syn_cmdlinep = eap->cmdlinep;
6188
6189 /* isolate subcommand name */
6190 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6191 ;
6192 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6193 if (subcmd_name != NULL)
6194 {
6195 if (eap->skip) /* skip error messages for all subcommands */
6196 ++emsg_skip;
6197 for (i = 0; ; ++i)
6198 {
6199 if (subcommands[i].name == NULL)
6200 {
6201 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6202 break;
6203 }
6204 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6205 {
6206 eap->arg = skipwhite(subcmd_end);
6207 (subcommands[i].func)(eap, FALSE);
6208 break;
6209 }
6210 }
6211 vim_free(subcmd_name);
6212 if (eap->skip)
6213 --emsg_skip;
6214 }
6215}
6216
Bram Moolenaar860cae12010-06-05 23:22:07 +02006217 void
6218ex_ownsyntax(eap)
6219 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006220{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006221 char_u *old_value;
6222 char_u *new_value;
6223
Bram Moolenaar860cae12010-06-05 23:22:07 +02006224 if (curwin->w_s == &curwin->w_buffer->b_s)
6225 {
6226 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6227 memset(curwin->w_s, 0, sizeof(synblock_T));
6228#ifdef FEAT_SPELL
6229 curwin->w_p_spell = FALSE; /* No spell checking */
6230 clear_string_option(&curwin->w_s->b_p_spc);
6231 clear_string_option(&curwin->w_s->b_p_spf);
6232 vim_free(curwin->w_s->b_cap_prog);
6233 curwin->w_s->b_cap_prog = NULL;
6234 clear_string_option(&curwin->w_s->b_p_spl);
6235#endif
6236 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006237
6238 /* save value of b:current_syntax */
6239 old_value = get_var_value((char_u *)"b:current_syntax");
6240 if (old_value != NULL)
6241 old_value = vim_strsave(old_value);
6242
6243 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6244 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006245 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006246
6247 /* move value of b:current_syntax to w:current_syntax */
6248 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006249 if (new_value != NULL)
6250 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006251
6252 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006253 if (old_value == NULL)
6254 do_unlet((char_u *)"b:current_syntax", TRUE);
6255 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006256 {
6257 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6258 vim_free(old_value);
6259 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006260}
6261
6262 int
6263syntax_present(win)
6264 win_T *win;
6265{
6266 return (win->w_s->b_syn_patterns.ga_len != 0
6267 || win->w_s->b_syn_clusters.ga_len != 0
6268 || win->w_s->b_keywtab.ht_used > 0
6269 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006270}
6271
6272#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6273
6274static enum
6275{
6276 EXP_SUBCMD, /* expand ":syn" sub-commands */
6277 EXP_CASE /* expand ":syn case" arguments */
6278} expand_what;
6279
Bram Moolenaar4f688582007-07-24 12:34:30 +00006280/*
6281 * Reset include_link, include_default, include_none to 0.
6282 * Called when we are done expanding.
6283 */
6284 void
6285reset_expand_highlight()
6286{
6287 include_link = include_default = include_none = 0;
6288}
6289
6290/*
6291 * Handle command line completion for :match and :echohl command: Add "None"
6292 * as highlight group.
6293 */
6294 void
6295set_context_in_echohl_cmd(xp, arg)
6296 expand_T *xp;
6297 char_u *arg;
6298{
6299 xp->xp_context = EXPAND_HIGHLIGHT;
6300 xp->xp_pattern = arg;
6301 include_none = 1;
6302}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006303
6304/*
6305 * Handle command line completion for :syntax command.
6306 */
6307 void
6308set_context_in_syntax_cmd(xp, arg)
6309 expand_T *xp;
6310 char_u *arg;
6311{
6312 char_u *p;
6313
6314 /* Default: expand subcommands */
6315 xp->xp_context = EXPAND_SYNTAX;
6316 expand_what = EXP_SUBCMD;
6317 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006318 include_link = 0;
6319 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320
6321 /* (part of) subcommand already typed */
6322 if (*arg != NUL)
6323 {
6324 p = skiptowhite(arg);
6325 if (*p != NUL) /* past first word */
6326 {
6327 xp->xp_pattern = skipwhite(p);
6328 if (*skiptowhite(xp->xp_pattern) != NUL)
6329 xp->xp_context = EXPAND_NOTHING;
6330 else if (STRNICMP(arg, "case", p - arg) == 0)
6331 expand_what = EXP_CASE;
6332 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6333 || STRNICMP(arg, "region", p - arg) == 0
6334 || STRNICMP(arg, "match", p - arg) == 0
6335 || STRNICMP(arg, "list", p - arg) == 0)
6336 xp->xp_context = EXPAND_HIGHLIGHT;
6337 else
6338 xp->xp_context = EXPAND_NOTHING;
6339 }
6340 }
6341}
6342
6343static char *(case_args[]) = {"match", "ignore", NULL};
6344
6345/*
6346 * Function given to ExpandGeneric() to obtain the list syntax names for
6347 * expansion.
6348 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006349 char_u *
6350get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006351 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352 int idx;
6353{
6354 if (expand_what == EXP_SUBCMD)
6355 return (char_u *)subcommands[idx].name;
6356 return (char_u *)case_args[idx];
6357}
6358
6359#endif /* FEAT_CMDL_COMPL */
6360
Bram Moolenaar071d4272004-06-13 20:20:40 +00006361/*
6362 * Function called for expression evaluation: get syntax ID at file position.
6363 */
6364 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006365syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006366 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006368 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006369 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006370 int *spellp; /* return: can do spell checking */
6371 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006372{
6373 /* When the position is not after the current position and in the same
6374 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006375 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006376 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006377 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006378 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006379
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006380 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006381
6382 return (trans ? current_trans_id : current_id);
6383}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384
Bram Moolenaar860cae12010-06-05 23:22:07 +02006385#if defined(FEAT_CONCEAL) || defined(PROTO)
6386/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006387 * Get extra information about the syntax item. Must be called right after
6388 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006389 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006390 * Returns the current flags.
6391 */
6392 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006393get_syntax_info(seqnrp)
6394 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006395{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006396 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006397 return current_flags;
6398}
6399
6400/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006401 * Return conceal substitution character
6402 */
6403 int
6404syn_get_sub_char()
6405{
6406 return current_sub_char;
6407}
6408#endif
6409
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006410#if defined(FEAT_EVAL) || defined(PROTO)
6411/*
6412 * Return the syntax ID at position "i" in the current stack.
6413 * The caller must have called syn_get_id() before to fill the stack.
6414 * Returns -1 when "i" is out of range.
6415 */
6416 int
6417syn_get_stack_item(i)
6418 int i;
6419{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006420 if (i >= current_state.ga_len)
6421 {
6422 /* Need to invalidate the state, because we didn't properly finish it
6423 * for the last character, "keep_state" was TRUE. */
6424 invalidate_current_state();
6425 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006426 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006427 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006428 return CUR_STATE(i).si_id;
6429}
6430#endif
6431
Bram Moolenaar071d4272004-06-13 20:20:40 +00006432#if defined(FEAT_FOLDING) || defined(PROTO)
6433/*
6434 * Function called to get folding level for line "lnum" in window "wp".
6435 */
6436 int
6437syn_get_foldlevel(wp, lnum)
6438 win_T *wp;
6439 long lnum;
6440{
6441 int level = 0;
6442 int i;
6443
6444 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006445 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006446 {
6447 syntax_start(wp, lnum);
6448
6449 for (i = 0; i < current_state.ga_len; ++i)
6450 if (CUR_STATE(i).si_flags & HL_FOLD)
6451 ++level;
6452 }
6453 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006454 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006455 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006456 if (level < 0)
6457 level = 0;
6458 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459 return level;
6460}
6461#endif
6462
6463#endif /* FEAT_SYN_HL */
6464
6465
6466/**************************************
6467 * Highlighting stuff *
6468 **************************************/
6469
6470/*
6471 * The default highlight groups. These are compiled-in for fast startup and
6472 * they still work when the runtime files can't be found.
6473 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006474 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6475 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006476 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006477#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006478# define CENT(a, b) b
6479#else
6480# define CENT(a, b) a
6481#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482static char *(highlight_init_both[]) =
6483 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006484 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6485 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6486 CENT("IncSearch term=reverse cterm=reverse",
6487 "IncSearch term=reverse cterm=reverse gui=reverse"),
6488 CENT("ModeMsg term=bold cterm=bold",
6489 "ModeMsg term=bold cterm=bold gui=bold"),
6490 CENT("NonText term=bold ctermfg=Blue",
6491 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6492 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6493 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6494 CENT("StatusLineNC term=reverse cterm=reverse",
6495 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006496#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006497 CENT("VertSplit term=reverse cterm=reverse",
6498 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006499#endif
6500#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006501 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6502 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006503#endif
6504#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006505 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6506 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006507#endif
6508#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006509 CENT("PmenuThumb cterm=reverse",
6510 "PmenuThumb cterm=reverse gui=reverse"),
6511 CENT("PmenuSbar ctermbg=Grey",
6512 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006513#endif
6514#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006515 CENT("TabLineSel term=bold cterm=bold",
6516 "TabLineSel term=bold cterm=bold gui=bold"),
6517 CENT("TabLineFill term=reverse cterm=reverse",
6518 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006519#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006520#ifdef FEAT_GUI
6521 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006522 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524 NULL
6525 };
6526
6527static char *(highlight_init_light[]) =
6528 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006529 CENT("Directory term=bold ctermfg=DarkBlue",
6530 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6531 CENT("LineNr term=underline ctermfg=Brown",
6532 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6533 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6534 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6535 CENT("Question term=standout ctermfg=DarkGreen",
6536 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6537 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6538 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006539#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006540 CENT("SpellBad term=reverse ctermbg=LightRed",
6541 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6542 CENT("SpellCap term=reverse ctermbg=LightBlue",
6543 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6544 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6545 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6546 CENT("SpellLocal term=underline ctermbg=Cyan",
6547 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006548#endif
6549#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006550 CENT("Pmenu ctermbg=LightMagenta",
6551 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6552 CENT("PmenuSel ctermbg=LightGrey",
6553 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006554#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006555 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6556 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6557 CENT("Title term=bold ctermfg=DarkMagenta",
6558 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6559 CENT("WarningMsg term=standout ctermfg=DarkRed",
6560 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006561#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006562 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6563 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006564#endif
6565#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006566 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6567 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6568 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6569 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006570#endif
6571#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006572 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6573 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006574#endif
6575#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006576 CENT("Visual term=reverse",
6577 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006578#endif
6579#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006580 CENT("DiffAdd term=bold ctermbg=LightBlue",
6581 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6582 CENT("DiffChange term=bold ctermbg=LightMagenta",
6583 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6584 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6585 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006586#endif
6587#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006588 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6589 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006590#endif
6591#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006592 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006593 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006594 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006595 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006596 CENT("ColorColumn term=reverse ctermbg=LightRed",
6597 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006598#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006599#ifdef FEAT_CONCEAL
6600 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6601 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6602#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006603#ifdef FEAT_AUTOCMD
6604 CENT("MatchParen term=reverse ctermbg=Cyan",
6605 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6606#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006607#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006609#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006610 NULL
6611 };
6612
6613static char *(highlight_init_dark[]) =
6614 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006615 CENT("Directory term=bold ctermfg=LightCyan",
6616 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6617 CENT("LineNr term=underline ctermfg=Yellow",
6618 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6619 CENT("MoreMsg term=bold ctermfg=LightGreen",
6620 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6621 CENT("Question term=standout ctermfg=LightGreen",
6622 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6623 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6624 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6625 CENT("SpecialKey term=bold ctermfg=LightBlue",
6626 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006627#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006628 CENT("SpellBad term=reverse ctermbg=Red",
6629 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6630 CENT("SpellCap term=reverse ctermbg=Blue",
6631 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6632 CENT("SpellRare term=reverse ctermbg=Magenta",
6633 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6634 CENT("SpellLocal term=underline ctermbg=Cyan",
6635 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006636#endif
6637#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006638 CENT("Pmenu ctermbg=Magenta",
6639 "Pmenu ctermbg=Magenta guibg=Magenta"),
6640 CENT("PmenuSel ctermbg=DarkGrey",
6641 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006642#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006643 CENT("Title term=bold ctermfg=LightMagenta",
6644 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6645 CENT("WarningMsg term=standout ctermfg=LightRed",
6646 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006647#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006648 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6649 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006650#endif
6651#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006652 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6653 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6654 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6655 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006656#endif
6657#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006658 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6659 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006660#endif
6661#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006662 CENT("Visual term=reverse",
6663 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006664#endif
6665#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006666 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6667 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6668 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6669 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6670 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6671 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006672#endif
6673#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006674 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6675 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006676#endif
6677#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006678 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006679 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006680 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006681 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006682 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6683 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006684#endif
6685#ifdef FEAT_AUTOCMD
6686 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6687 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006688#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006689#ifdef FEAT_CONCEAL
6690 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6691 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6692#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006693#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006694 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006695#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006696 NULL
6697 };
6698
6699 void
6700init_highlight(both, reset)
6701 int both; /* include groups where 'bg' doesn't matter */
6702 int reset; /* clear group first */
6703{
6704 int i;
6705 char **pp;
6706 static int had_both = FALSE;
6707#ifdef FEAT_EVAL
6708 char_u *p;
6709
6710 /*
6711 * Try finding the color scheme file. Used when a color file was loaded
6712 * and 'background' or 't_Co' is changed.
6713 */
6714 p = get_var_value((char_u *)"g:colors_name");
6715 if (p != NULL && load_colors(p) == OK)
6716 return;
6717#endif
6718
6719 /*
6720 * Didn't use a color file, use the compiled-in colors.
6721 */
6722 if (both)
6723 {
6724 had_both = TRUE;
6725 pp = highlight_init_both;
6726 for (i = 0; pp[i] != NULL; ++i)
6727 do_highlight((char_u *)pp[i], reset, TRUE);
6728 }
6729 else if (!had_both)
6730 /* Don't do anything before the call with both == TRUE from main().
6731 * Not everything has been setup then, and that call will overrule
6732 * everything anyway. */
6733 return;
6734
6735 if (*p_bg == 'l')
6736 pp = highlight_init_light;
6737 else
6738 pp = highlight_init_dark;
6739 for (i = 0; pp[i] != NULL; ++i)
6740 do_highlight((char_u *)pp[i], reset, TRUE);
6741
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006742 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006743 * depend on the number of colors available.
6744 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006745 * to avoid Statement highlighted text disappears.
6746 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006747 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006748 do_highlight((char_u *)(*p_bg == 'l'
6749 ? "Visual cterm=NONE ctermbg=LightGrey"
6750 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006751 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006752 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006753 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6754 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006755 if (*p_bg == 'l')
6756 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6757 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006758
Bram Moolenaar071d4272004-06-13 20:20:40 +00006759#ifdef FEAT_SYN_HL
6760 /*
6761 * If syntax highlighting is enabled load the highlighting for it.
6762 */
6763 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006764 {
6765 static int recursive = 0;
6766
6767 if (recursive >= 5)
6768 EMSG(_("E679: recursive loop loading syncolor.vim"));
6769 else
6770 {
6771 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006772 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006773 --recursive;
6774 }
6775 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006776#endif
6777}
6778
6779/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006780 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006781 * Return OK for success, FAIL for failure.
6782 */
6783 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006784load_colors(name)
6785 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006786{
6787 char_u *buf;
6788 int retval = FAIL;
6789 static int recursive = FALSE;
6790
6791 /* When being called recursively, this is probably because setting
6792 * 'background' caused the highlighting to be reloaded. This means it is
6793 * working, thus we should return OK. */
6794 if (recursive)
6795 return OK;
6796
6797 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006798 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006799 if (buf != NULL)
6800 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006801 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006802 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006803 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006804#ifdef FEAT_AUTOCMD
6805 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6806#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006807 }
6808 recursive = FALSE;
6809
6810 return retval;
6811}
6812
6813/*
6814 * Handle the ":highlight .." command.
6815 * When using ":hi clear" this is called recursively for each group with
6816 * "forceit" and "init" both TRUE.
6817 */
6818 void
6819do_highlight(line, forceit, init)
6820 char_u *line;
6821 int forceit;
6822 int init; /* TRUE when called for initializing */
6823{
6824 char_u *name_end;
6825 char_u *p;
6826 char_u *linep;
6827 char_u *key_start;
6828 char_u *arg_start;
6829 char_u *key = NULL, *arg = NULL;
6830 long i;
6831 int off;
6832 int len;
6833 int attr;
6834 int id;
6835 int idx;
6836 int dodefault = FALSE;
6837 int doclear = FALSE;
6838 int dolink = FALSE;
6839 int error = FALSE;
6840 int color;
6841 int is_normal_group = FALSE; /* "Normal" group */
6842#ifdef FEAT_GUI_X11
6843 int is_menu_group = FALSE; /* "Menu" group */
6844 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6845 int is_tooltip_group = FALSE; /* "Tooltip" group */
6846 int do_colors = FALSE; /* need to update colors? */
6847#else
6848# define is_menu_group 0
6849# define is_tooltip_group 0
6850#endif
6851
6852 /*
6853 * If no argument, list current highlighting.
6854 */
6855 if (ends_excmd(*line))
6856 {
6857 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6858 /* TODO: only call when the group has attributes set */
6859 highlight_list_one((int)i);
6860 return;
6861 }
6862
6863 /*
6864 * Isolate the name.
6865 */
6866 name_end = skiptowhite(line);
6867 linep = skipwhite(name_end);
6868
6869 /*
6870 * Check for "default" argument.
6871 */
6872 if (STRNCMP(line, "default", name_end - line) == 0)
6873 {
6874 dodefault = TRUE;
6875 line = linep;
6876 name_end = skiptowhite(line);
6877 linep = skipwhite(name_end);
6878 }
6879
6880 /*
6881 * Check for "clear" or "link" argument.
6882 */
6883 if (STRNCMP(line, "clear", name_end - line) == 0)
6884 doclear = TRUE;
6885 if (STRNCMP(line, "link", name_end - line) == 0)
6886 dolink = TRUE;
6887
6888 /*
6889 * ":highlight {group-name}": list highlighting for one group.
6890 */
6891 if (!doclear && !dolink && ends_excmd(*linep))
6892 {
6893 id = syn_namen2id(line, (int)(name_end - line));
6894 if (id == 0)
6895 EMSG2(_("E411: highlight group not found: %s"), line);
6896 else
6897 highlight_list_one(id);
6898 return;
6899 }
6900
6901 /*
6902 * Handle ":highlight link {from} {to}" command.
6903 */
6904 if (dolink)
6905 {
6906 char_u *from_start = linep;
6907 char_u *from_end;
6908 char_u *to_start;
6909 char_u *to_end;
6910 int from_id;
6911 int to_id;
6912
6913 from_end = skiptowhite(from_start);
6914 to_start = skipwhite(from_end);
6915 to_end = skiptowhite(to_start);
6916
6917 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6918 {
6919 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6920 from_start);
6921 return;
6922 }
6923
6924 if (!ends_excmd(*skipwhite(to_end)))
6925 {
6926 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6927 return;
6928 }
6929
6930 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6931 if (STRNCMP(to_start, "NONE", 4) == 0)
6932 to_id = 0;
6933 else
6934 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6935
6936 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6937 {
6938 /*
6939 * Don't allow a link when there already is some highlighting
6940 * for the group, unless '!' is used
6941 */
6942 if (to_id > 0 && !forceit && !init
6943 && hl_has_settings(from_id - 1, dodefault))
6944 {
6945 if (sourcing_name == NULL && !dodefault)
6946 EMSG(_("E414: group has settings, highlight link ignored"));
6947 }
6948 else
6949 {
6950 if (!init)
6951 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6952 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006953#ifdef FEAT_EVAL
6954 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6955#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006956 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006957 }
6958 }
6959
6960 /* Only call highlight_changed() once, after sourcing a syntax file */
6961 need_highlight_changed = TRUE;
6962
6963 return;
6964 }
6965
6966 if (doclear)
6967 {
6968 /*
6969 * ":highlight clear [group]" command.
6970 */
6971 line = linep;
6972 if (ends_excmd(*line))
6973 {
6974#ifdef FEAT_GUI
6975 /* First, we do not destroy the old values, but allocate the new
6976 * ones and update the display. THEN we destroy the old values.
6977 * If we destroy the old values first, then the old values
6978 * (such as GuiFont's or GuiFontset's) will still be displayed but
6979 * invalid because they were free'd.
6980 */
6981 if (gui.in_use)
6982 {
6983# ifdef FEAT_BEVAL_TIP
6984 gui_init_tooltip_font();
6985# endif
6986# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6987 gui_init_menu_font();
6988# endif
6989 }
6990# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6991 gui_mch_def_colors();
6992# endif
6993# ifdef FEAT_GUI_X11
6994# ifdef FEAT_MENU
6995
6996 /* This only needs to be done when there is no Menu highlight
6997 * group defined by default, which IS currently the case.
6998 */
6999 gui_mch_new_menu_colors();
7000# endif
7001 if (gui.in_use)
7002 {
7003 gui_new_scrollbar_colors();
7004# ifdef FEAT_BEVAL
7005 gui_mch_new_tooltip_colors();
7006# endif
7007# ifdef FEAT_MENU
7008 gui_mch_new_menu_font();
7009# endif
7010 }
7011# endif
7012
7013 /* Ok, we're done allocating the new default graphics items.
7014 * The screen should already be refreshed at this point.
7015 * It is now Ok to clear out the old data.
7016 */
7017#endif
7018#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007019 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007020#endif
7021 restore_cterm_colors();
7022
7023 /*
7024 * Clear all default highlight groups and load the defaults.
7025 */
7026 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7027 highlight_clear(idx);
7028 init_highlight(TRUE, TRUE);
7029#ifdef FEAT_GUI
7030 if (gui.in_use)
7031 highlight_gui_started();
7032#endif
7033 highlight_changed();
7034 redraw_later_clear();
7035 return;
7036 }
7037 name_end = skiptowhite(line);
7038 linep = skipwhite(name_end);
7039 }
7040
7041 /*
7042 * Find the group name in the table. If it does not exist yet, add it.
7043 */
7044 id = syn_check_group(line, (int)(name_end - line));
7045 if (id == 0) /* failed (out of memory) */
7046 return;
7047 idx = id - 1; /* index is ID minus one */
7048
7049 /* Return if "default" was used and the group already has settings. */
7050 if (dodefault && hl_has_settings(idx, TRUE))
7051 return;
7052
7053 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7054 is_normal_group = TRUE;
7055#ifdef FEAT_GUI_X11
7056 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7057 is_menu_group = TRUE;
7058 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7059 is_scrollbar_group = TRUE;
7060 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7061 is_tooltip_group = TRUE;
7062#endif
7063
7064 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7065 if (doclear || (forceit && init))
7066 {
7067 highlight_clear(idx);
7068 if (!doclear)
7069 HL_TABLE()[idx].sg_set = 0;
7070 }
7071
7072 if (!doclear)
7073 while (!ends_excmd(*linep))
7074 {
7075 key_start = linep;
7076 if (*linep == '=')
7077 {
7078 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7079 error = TRUE;
7080 break;
7081 }
7082
7083 /*
7084 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7085 * "guibg").
7086 */
7087 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7088 ++linep;
7089 vim_free(key);
7090 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7091 if (key == NULL)
7092 {
7093 error = TRUE;
7094 break;
7095 }
7096 linep = skipwhite(linep);
7097
7098 if (STRCMP(key, "NONE") == 0)
7099 {
7100 if (!init || HL_TABLE()[idx].sg_set == 0)
7101 {
7102 if (!init)
7103 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7104 highlight_clear(idx);
7105 }
7106 continue;
7107 }
7108
7109 /*
7110 * Check for the equal sign.
7111 */
7112 if (*linep != '=')
7113 {
7114 EMSG2(_("E416: missing equal sign: %s"), key_start);
7115 error = TRUE;
7116 break;
7117 }
7118 ++linep;
7119
7120 /*
7121 * Isolate the argument.
7122 */
7123 linep = skipwhite(linep);
7124 if (*linep == '\'') /* guifg='color name' */
7125 {
7126 arg_start = ++linep;
7127 linep = vim_strchr(linep, '\'');
7128 if (linep == NULL)
7129 {
7130 EMSG2(_(e_invarg2), key_start);
7131 error = TRUE;
7132 break;
7133 }
7134 }
7135 else
7136 {
7137 arg_start = linep;
7138 linep = skiptowhite(linep);
7139 }
7140 if (linep == arg_start)
7141 {
7142 EMSG2(_("E417: missing argument: %s"), key_start);
7143 error = TRUE;
7144 break;
7145 }
7146 vim_free(arg);
7147 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7148 if (arg == NULL)
7149 {
7150 error = TRUE;
7151 break;
7152 }
7153 if (*linep == '\'')
7154 ++linep;
7155
7156 /*
7157 * Store the argument.
7158 */
7159 if ( STRCMP(key, "TERM") == 0
7160 || STRCMP(key, "CTERM") == 0
7161 || STRCMP(key, "GUI") == 0)
7162 {
7163 attr = 0;
7164 off = 0;
7165 while (arg[off] != NUL)
7166 {
7167 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7168 {
7169 len = (int)STRLEN(hl_name_table[i]);
7170 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7171 {
7172 attr |= hl_attr_table[i];
7173 off += len;
7174 break;
7175 }
7176 }
7177 if (i < 0)
7178 {
7179 EMSG2(_("E418: Illegal value: %s"), arg);
7180 error = TRUE;
7181 break;
7182 }
7183 if (arg[off] == ',') /* another one follows */
7184 ++off;
7185 }
7186 if (error)
7187 break;
7188 if (*key == 'T')
7189 {
7190 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7191 {
7192 if (!init)
7193 HL_TABLE()[idx].sg_set |= SG_TERM;
7194 HL_TABLE()[idx].sg_term = attr;
7195 }
7196 }
7197 else if (*key == 'C')
7198 {
7199 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7200 {
7201 if (!init)
7202 HL_TABLE()[idx].sg_set |= SG_CTERM;
7203 HL_TABLE()[idx].sg_cterm = attr;
7204 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7205 }
7206 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007207#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208 else
7209 {
7210 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7211 {
7212 if (!init)
7213 HL_TABLE()[idx].sg_set |= SG_GUI;
7214 HL_TABLE()[idx].sg_gui = attr;
7215 }
7216 }
7217#endif
7218 }
7219 else if (STRCMP(key, "FONT") == 0)
7220 {
7221 /* in non-GUI fonts are simply ignored */
7222#ifdef FEAT_GUI
7223 if (!gui.shell_created)
7224 {
7225 /* GUI not started yet, always accept the name. */
7226 vim_free(HL_TABLE()[idx].sg_font_name);
7227 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7228 }
7229 else
7230 {
7231 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7232# ifdef FEAT_XFONTSET
7233 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7234# endif
7235 /* First, save the current font/fontset.
7236 * Then try to allocate the font/fontset.
7237 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7238 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7239 */
7240
7241 HL_TABLE()[idx].sg_font = NOFONT;
7242# ifdef FEAT_XFONTSET
7243 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7244# endif
7245 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7246 is_tooltip_group);
7247
7248# ifdef FEAT_XFONTSET
7249 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7250 {
7251 /* New fontset was accepted. Free the old one, if there was
7252 * one.
7253 */
7254 gui_mch_free_fontset(temp_sg_fontset);
7255 vim_free(HL_TABLE()[idx].sg_font_name);
7256 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7257 }
7258 else
7259 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7260# endif
7261 if (HL_TABLE()[idx].sg_font != NOFONT)
7262 {
7263 /* New font was accepted. Free the old one, if there was
7264 * one.
7265 */
7266 gui_mch_free_font(temp_sg_font);
7267 vim_free(HL_TABLE()[idx].sg_font_name);
7268 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7269 }
7270 else
7271 HL_TABLE()[idx].sg_font = temp_sg_font;
7272 }
7273#endif
7274 }
7275 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7276 {
7277 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7278 {
7279 if (!init)
7280 HL_TABLE()[idx].sg_set |= SG_CTERM;
7281
7282 /* When setting the foreground color, and previously the "bold"
7283 * flag was set for a light color, reset it now */
7284 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7285 {
7286 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7287 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7288 }
7289
7290 if (VIM_ISDIGIT(*arg))
7291 color = atoi((char *)arg);
7292 else if (STRICMP(arg, "fg") == 0)
7293 {
7294 if (cterm_normal_fg_color)
7295 color = cterm_normal_fg_color - 1;
7296 else
7297 {
7298 EMSG(_("E419: FG color unknown"));
7299 error = TRUE;
7300 break;
7301 }
7302 }
7303 else if (STRICMP(arg, "bg") == 0)
7304 {
7305 if (cterm_normal_bg_color > 0)
7306 color = cterm_normal_bg_color - 1;
7307 else
7308 {
7309 EMSG(_("E420: BG color unknown"));
7310 error = TRUE;
7311 break;
7312 }
7313 }
7314 else
7315 {
7316 static char *(color_names[28]) = {
7317 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7318 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7319 "Gray", "Grey",
7320 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7321 "Blue", "LightBlue", "Green", "LightGreen",
7322 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7323 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7324 static int color_numbers_16[28] = {0, 1, 2, 3,
7325 4, 5, 6, 6,
7326 7, 7,
7327 7, 7, 8, 8,
7328 9, 9, 10, 10,
7329 11, 11, 12, 12, 13,
7330 13, 14, 14, 15, -1};
7331 /* for xterm with 88 colors... */
7332 static int color_numbers_88[28] = {0, 4, 2, 6,
7333 1, 5, 32, 72,
7334 84, 84,
7335 7, 7, 82, 82,
7336 12, 43, 10, 61,
7337 14, 63, 9, 74, 13,
7338 75, 11, 78, 15, -1};
7339 /* for xterm with 256 colors... */
7340 static int color_numbers_256[28] = {0, 4, 2, 6,
7341 1, 5, 130, 130,
7342 248, 248,
7343 7, 7, 242, 242,
7344 12, 81, 10, 121,
7345 14, 159, 9, 224, 13,
7346 225, 11, 229, 15, -1};
7347 /* for terminals with less than 16 colors... */
7348 static int color_numbers_8[28] = {0, 4, 2, 6,
7349 1, 5, 3, 3,
7350 7, 7,
7351 7, 7, 0+8, 0+8,
7352 4+8, 4+8, 2+8, 2+8,
7353 6+8, 6+8, 1+8, 1+8, 5+8,
7354 5+8, 3+8, 3+8, 7+8, -1};
7355#if defined(__QNXNTO__)
7356 static int *color_numbers_8_qansi = color_numbers_8;
7357 /* On qnx, the 8 & 16 color arrays are the same */
7358 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7359 color_numbers_8_qansi = color_numbers_16;
7360#endif
7361
7362 /* reduce calls to STRICMP a bit, it can be slow */
7363 off = TOUPPER_ASC(*arg);
7364 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7365 if (off == color_names[i][0]
7366 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7367 break;
7368 if (i < 0)
7369 {
7370 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7371 error = TRUE;
7372 break;
7373 }
7374
7375 /* Use the _16 table to check if its a valid color name. */
7376 color = color_numbers_16[i];
7377 if (color >= 0)
7378 {
7379 if (t_colors == 8)
7380 {
7381 /* t_Co is 8: use the 8 colors table */
7382#if defined(__QNXNTO__)
7383 color = color_numbers_8_qansi[i];
7384#else
7385 color = color_numbers_8[i];
7386#endif
7387 if (key[5] == 'F')
7388 {
7389 /* set/reset bold attribute to get light foreground
7390 * colors (on some terminals, e.g. "linux") */
7391 if (color & 8)
7392 {
7393 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7394 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7395 }
7396 else
7397 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7398 }
7399 color &= 7; /* truncate to 8 colors */
7400 }
7401 else if (t_colors == 16 || t_colors == 88
7402 || t_colors == 256)
7403 {
7404 /*
7405 * Guess: if the termcap entry ends in 'm', it is
7406 * probably an xterm-like terminal. Use the changed
7407 * order for colors.
7408 */
7409 if (*T_CAF != NUL)
7410 p = T_CAF;
7411 else
7412 p = T_CSF;
7413 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7414 switch (t_colors)
7415 {
7416 case 16:
7417 color = color_numbers_8[i];
7418 break;
7419 case 88:
7420 color = color_numbers_88[i];
7421 break;
7422 case 256:
7423 color = color_numbers_256[i];
7424 break;
7425 }
7426 }
7427 }
7428 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007429 /* Add one to the argument, to avoid zero. Zero is used for
7430 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007431 if (key[5] == 'F')
7432 {
7433 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7434 if (is_normal_group)
7435 {
7436 cterm_normal_fg_color = color + 1;
7437 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7438#ifdef FEAT_GUI
7439 /* Don't do this if the GUI is used. */
7440 if (!gui.in_use && !gui.starting)
7441#endif
7442 {
7443 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007444 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007445 term_fg_color(color);
7446 }
7447 }
7448 }
7449 else
7450 {
7451 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7452 if (is_normal_group)
7453 {
7454 cterm_normal_bg_color = color + 1;
7455#ifdef FEAT_GUI
7456 /* Don't mess with 'background' if the GUI is used. */
7457 if (!gui.in_use && !gui.starting)
7458#endif
7459 {
7460 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007461 if (color >= 0)
7462 {
7463 if (termcap_active)
7464 term_bg_color(color);
7465 if (t_colors < 16)
7466 i = (color == 0 || color == 4);
7467 else
7468 i = (color < 7 || color == 8);
7469 /* Set the 'background' option if the value is
7470 * wrong. */
7471 if (i != (*p_bg == 'd'))
7472 set_option_value((char_u *)"bg", 0L,
7473 i ? (char_u *)"dark"
7474 : (char_u *)"light", 0);
7475 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007476 }
7477 }
7478 }
7479 }
7480 }
7481 else if (STRCMP(key, "GUIFG") == 0)
7482 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007483#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007484 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007485 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007486 if (!init)
7487 HL_TABLE()[idx].sg_set |= SG_GUI;
7488
Bram Moolenaar61623362010-07-14 22:04:22 +02007489# ifdef FEAT_GUI
7490 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007491 i = color_name2handle(arg);
7492 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7493 {
7494 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007495# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007496 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7497 if (STRCMP(arg, "NONE"))
7498 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7499 else
7500 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007501# ifdef FEAT_GUI
7502# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007503 if (is_menu_group)
7504 gui.menu_fg_pixel = i;
7505 if (is_scrollbar_group)
7506 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007507# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007508 if (is_tooltip_group)
7509 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007510# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007511 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007512# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007513 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007514# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007515 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007516#endif
7517 }
7518 else if (STRCMP(key, "GUIBG") == 0)
7519 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007520#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007521 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007522 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007523 if (!init)
7524 HL_TABLE()[idx].sg_set |= SG_GUI;
7525
Bram Moolenaar61623362010-07-14 22:04:22 +02007526# ifdef FEAT_GUI
7527 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007528 i = color_name2handle(arg);
7529 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7530 {
7531 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007532# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007533 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7534 if (STRCMP(arg, "NONE") != 0)
7535 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7536 else
7537 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007538# ifdef FEAT_GUI
7539# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007540 if (is_menu_group)
7541 gui.menu_bg_pixel = i;
7542 if (is_scrollbar_group)
7543 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007544# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007545 if (is_tooltip_group)
7546 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007547# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007548 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007549# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007550 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007551# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007552 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007553#endif
7554 }
7555 else if (STRCMP(key, "GUISP") == 0)
7556 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007557#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007558 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7559 {
7560 if (!init)
7561 HL_TABLE()[idx].sg_set |= SG_GUI;
7562
Bram Moolenaar61623362010-07-14 22:04:22 +02007563# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007564 i = color_name2handle(arg);
7565 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7566 {
7567 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007568# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007569 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7570 if (STRCMP(arg, "NONE") != 0)
7571 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7572 else
7573 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007574# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007575 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007576# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007577 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007578#endif
7579 }
7580 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7581 {
7582 char_u buf[100];
7583 char_u *tname;
7584
7585 if (!init)
7586 HL_TABLE()[idx].sg_set |= SG_TERM;
7587
7588 /*
7589 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007590 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591 */
7592 if (STRNCMP(arg, "t_", 2) == 0)
7593 {
7594 off = 0;
7595 buf[0] = 0;
7596 while (arg[off] != NUL)
7597 {
7598 /* Isolate one termcap name */
7599 for (len = 0; arg[off + len] &&
7600 arg[off + len] != ','; ++len)
7601 ;
7602 tname = vim_strnsave(arg + off, len);
7603 if (tname == NULL) /* out of memory */
7604 {
7605 error = TRUE;
7606 break;
7607 }
7608 /* lookup the escape sequence for the item */
7609 p = get_term_code(tname);
7610 vim_free(tname);
7611 if (p == NULL) /* ignore non-existing things */
7612 p = (char_u *)"";
7613
7614 /* Append it to the already found stuff */
7615 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7616 {
7617 EMSG2(_("E422: terminal code too long: %s"), arg);
7618 error = TRUE;
7619 break;
7620 }
7621 STRCAT(buf, p);
7622
7623 /* Advance to the next item */
7624 off += len;
7625 if (arg[off] == ',') /* another one follows */
7626 ++off;
7627 }
7628 }
7629 else
7630 {
7631 /*
7632 * Copy characters from arg[] to buf[], translating <> codes.
7633 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007634 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007635 {
7636 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007637 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007638 off += len;
7639 else /* copy as normal char */
7640 buf[off++] = *p++;
7641 }
7642 buf[off] = NUL;
7643 }
7644 if (error)
7645 break;
7646
7647 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7648 p = NULL;
7649 else
7650 p = vim_strsave(buf);
7651 if (key[2] == 'A')
7652 {
7653 vim_free(HL_TABLE()[idx].sg_start);
7654 HL_TABLE()[idx].sg_start = p;
7655 }
7656 else
7657 {
7658 vim_free(HL_TABLE()[idx].sg_stop);
7659 HL_TABLE()[idx].sg_stop = p;
7660 }
7661 }
7662 else
7663 {
7664 EMSG2(_("E423: Illegal argument: %s"), key_start);
7665 error = TRUE;
7666 break;
7667 }
7668
7669 /*
7670 * When highlighting has been given for a group, don't link it.
7671 */
7672 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7673 HL_TABLE()[idx].sg_link = 0;
7674
7675 /*
7676 * Continue with next argument.
7677 */
7678 linep = skipwhite(linep);
7679 }
7680
7681 /*
7682 * If there is an error, and it's a new entry, remove it from the table.
7683 */
7684 if (error && idx == highlight_ga.ga_len)
7685 syn_unadd_group();
7686 else
7687 {
7688 if (is_normal_group)
7689 {
7690 HL_TABLE()[idx].sg_term_attr = 0;
7691 HL_TABLE()[idx].sg_cterm_attr = 0;
7692#ifdef FEAT_GUI
7693 HL_TABLE()[idx].sg_gui_attr = 0;
7694 /*
7695 * Need to update all groups, because they might be using "bg"
7696 * and/or "fg", which have been changed now.
7697 */
7698 if (gui.in_use)
7699 highlight_gui_started();
7700#endif
7701 }
7702#ifdef FEAT_GUI_X11
7703# ifdef FEAT_MENU
7704 else if (is_menu_group)
7705 {
7706 if (gui.in_use && do_colors)
7707 gui_mch_new_menu_colors();
7708 }
7709# endif
7710 else if (is_scrollbar_group)
7711 {
7712 if (gui.in_use && do_colors)
7713 gui_new_scrollbar_colors();
7714 }
7715# ifdef FEAT_BEVAL
7716 else if (is_tooltip_group)
7717 {
7718 if (gui.in_use && do_colors)
7719 gui_mch_new_tooltip_colors();
7720 }
7721# endif
7722#endif
7723 else
7724 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007725#ifdef FEAT_EVAL
7726 HL_TABLE()[idx].sg_scriptID = current_SID;
7727#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007728 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007729 }
7730 vim_free(key);
7731 vim_free(arg);
7732
7733 /* Only call highlight_changed() once, after sourcing a syntax file */
7734 need_highlight_changed = TRUE;
7735}
7736
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007737#if defined(EXITFREE) || defined(PROTO)
7738 void
7739free_highlight()
7740{
7741 int i;
7742
7743 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007744 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007745 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007746 vim_free(HL_TABLE()[i].sg_name);
7747 vim_free(HL_TABLE()[i].sg_name_u);
7748 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007749 ga_clear(&highlight_ga);
7750}
7751#endif
7752
Bram Moolenaar071d4272004-06-13 20:20:40 +00007753/*
7754 * Reset the cterm colors to what they were before Vim was started, if
7755 * possible. Otherwise reset them to zero.
7756 */
7757 void
7758restore_cterm_colors()
7759{
7760#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7761 /* Since t_me has been set, this probably means that the user
7762 * wants to use this as default colors. Need to reset default
7763 * background/foreground colors. */
7764 mch_set_normal_colors();
7765#else
7766 cterm_normal_fg_color = 0;
7767 cterm_normal_fg_bold = 0;
7768 cterm_normal_bg_color = 0;
7769#endif
7770}
7771
7772/*
7773 * Return TRUE if highlight group "idx" has any settings.
7774 * When "check_link" is TRUE also check for an existing link.
7775 */
7776 static int
7777hl_has_settings(idx, check_link)
7778 int idx;
7779 int check_link;
7780{
7781 return ( HL_TABLE()[idx].sg_term_attr != 0
7782 || HL_TABLE()[idx].sg_cterm_attr != 0
7783#ifdef FEAT_GUI
7784 || HL_TABLE()[idx].sg_gui_attr != 0
7785#endif
7786 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7787}
7788
7789/*
7790 * Clear highlighting for one group.
7791 */
7792 static void
7793highlight_clear(idx)
7794 int idx;
7795{
7796 HL_TABLE()[idx].sg_term = 0;
7797 vim_free(HL_TABLE()[idx].sg_start);
7798 HL_TABLE()[idx].sg_start = NULL;
7799 vim_free(HL_TABLE()[idx].sg_stop);
7800 HL_TABLE()[idx].sg_stop = NULL;
7801 HL_TABLE()[idx].sg_term_attr = 0;
7802 HL_TABLE()[idx].sg_cterm = 0;
7803 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7804 HL_TABLE()[idx].sg_cterm_fg = 0;
7805 HL_TABLE()[idx].sg_cterm_bg = 0;
7806 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007807#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7810 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7812 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007813 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7814 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007815#endif
7816#ifdef FEAT_GUI
7817 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7818 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7819 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7821 HL_TABLE()[idx].sg_font = NOFONT;
7822# ifdef FEAT_XFONTSET
7823 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7824 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7825# endif
7826 vim_free(HL_TABLE()[idx].sg_font_name);
7827 HL_TABLE()[idx].sg_font_name = NULL;
7828 HL_TABLE()[idx].sg_gui_attr = 0;
7829#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007830#ifdef FEAT_EVAL
7831 /* Clear the script ID only when there is no link, since that is not
7832 * cleared. */
7833 if (HL_TABLE()[idx].sg_link == 0)
7834 HL_TABLE()[idx].sg_scriptID = 0;
7835#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836}
7837
7838#if defined(FEAT_GUI) || defined(PROTO)
7839/*
7840 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007841 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007842 * "Tooltip" colors.
7843 */
7844 void
7845set_normal_colors()
7846{
7847 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007848 &gui.norm_pixel, &gui.back_pixel,
7849 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 {
7851 gui_mch_new_colors();
7852 must_redraw = CLEAR;
7853 }
7854#ifdef FEAT_GUI_X11
7855 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007856 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7857 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 {
7859# ifdef FEAT_MENU
7860 gui_mch_new_menu_colors();
7861# endif
7862 must_redraw = CLEAR;
7863 }
7864# ifdef FEAT_BEVAL
7865 if (set_group_colors((char_u *)"Tooltip",
7866 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7867 FALSE, FALSE, TRUE))
7868 {
7869# ifdef FEAT_TOOLBAR
7870 gui_mch_new_tooltip_colors();
7871# endif
7872 must_redraw = CLEAR;
7873 }
7874#endif
7875 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007876 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7877 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007878 {
7879 gui_new_scrollbar_colors();
7880 must_redraw = CLEAR;
7881 }
7882#endif
7883}
7884
7885/*
7886 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7887 */
7888 static int
7889set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7890 char_u *name;
7891 guicolor_T *fgp;
7892 guicolor_T *bgp;
7893 int do_menu;
7894 int use_norm;
7895 int do_tooltip;
7896{
7897 int idx;
7898
7899 idx = syn_name2id(name) - 1;
7900 if (idx >= 0)
7901 {
7902 gui_do_one_color(idx, do_menu, do_tooltip);
7903
7904 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7905 *fgp = HL_TABLE()[idx].sg_gui_fg;
7906 else if (use_norm)
7907 *fgp = gui.def_norm_pixel;
7908 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7909 *bgp = HL_TABLE()[idx].sg_gui_bg;
7910 else if (use_norm)
7911 *bgp = gui.def_back_pixel;
7912 return TRUE;
7913 }
7914 return FALSE;
7915}
7916
7917/*
7918 * Get the font of the "Normal" group.
7919 * Returns "" when it's not found or not set.
7920 */
7921 char_u *
7922hl_get_font_name()
7923{
7924 int id;
7925 char_u *s;
7926
7927 id = syn_name2id((char_u *)"Normal");
7928 if (id > 0)
7929 {
7930 s = HL_TABLE()[id - 1].sg_font_name;
7931 if (s != NULL)
7932 return s;
7933 }
7934 return (char_u *)"";
7935}
7936
7937/*
7938 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7939 * actually chosen to be used.
7940 */
7941 void
7942hl_set_font_name(font_name)
7943 char_u *font_name;
7944{
7945 int id;
7946
7947 id = syn_name2id((char_u *)"Normal");
7948 if (id > 0)
7949 {
7950 vim_free(HL_TABLE()[id - 1].sg_font_name);
7951 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7952 }
7953}
7954
7955/*
7956 * Set background color for "Normal" group. Called by gui_set_bg_color()
7957 * when the color is known.
7958 */
7959 void
7960hl_set_bg_color_name(name)
7961 char_u *name; /* must have been allocated */
7962{
7963 int id;
7964
7965 if (name != NULL)
7966 {
7967 id = syn_name2id((char_u *)"Normal");
7968 if (id > 0)
7969 {
7970 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7971 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7972 }
7973 }
7974}
7975
7976/*
7977 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7978 * when the color is known.
7979 */
7980 void
7981hl_set_fg_color_name(name)
7982 char_u *name; /* must have been allocated */
7983{
7984 int id;
7985
7986 if (name != NULL)
7987 {
7988 id = syn_name2id((char_u *)"Normal");
7989 if (id > 0)
7990 {
7991 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7992 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7993 }
7994 }
7995}
7996
7997/*
7998 * Return the handle for a color name.
7999 * Returns INVALCOLOR when failed.
8000 */
8001 static guicolor_T
8002color_name2handle(name)
8003 char_u *name;
8004{
8005 if (STRCMP(name, "NONE") == 0)
8006 return INVALCOLOR;
8007
8008 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8009 return gui.norm_pixel;
8010 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8011 return gui.back_pixel;
8012
8013 return gui_get_color(name);
8014}
8015
8016/*
8017 * Return the handle for a font name.
8018 * Returns NOFONT when failed.
8019 */
8020 static GuiFont
8021font_name2handle(name)
8022 char_u *name;
8023{
8024 if (STRCMP(name, "NONE") == 0)
8025 return NOFONT;
8026
8027 return gui_mch_get_font(name, TRUE);
8028}
8029
8030# ifdef FEAT_XFONTSET
8031/*
8032 * Return the handle for a fontset name.
8033 * Returns NOFONTSET when failed.
8034 */
8035 static GuiFontset
8036fontset_name2handle(name, fixed_width)
8037 char_u *name;
8038 int fixed_width;
8039{
8040 if (STRCMP(name, "NONE") == 0)
8041 return NOFONTSET;
8042
8043 return gui_mch_get_fontset(name, TRUE, fixed_width);
8044}
8045# endif
8046
8047/*
8048 * Get the font or fontset for one highlight group.
8049 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008050 static void
8051hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8052 int idx;
8053 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008054 int do_normal; /* set normal font */
8055 int do_menu UNUSED; /* set menu font */
8056 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008057{
8058# ifdef FEAT_XFONTSET
8059 /* If 'guifontset' is not empty, first try using the name as a
8060 * fontset. If that doesn't work, use it as a font name. */
8061 if (*p_guifontset != NUL
8062# ifdef FONTSET_ALWAYS
8063 || do_menu
8064# endif
8065# ifdef FEAT_BEVAL_TIP
8066 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8067 || do_tooltip
8068# endif
8069 )
8070 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8071# ifdef FONTSET_ALWAYS
8072 || do_menu
8073# endif
8074# ifdef FEAT_BEVAL_TIP
8075 || do_tooltip
8076# endif
8077 );
8078 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8079 {
8080 /* If it worked and it's the Normal group, use it as the
8081 * normal fontset. Same for the Menu group. */
8082 if (do_normal)
8083 gui_init_font(arg, TRUE);
8084# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8085 if (do_menu)
8086 {
8087# ifdef FONTSET_ALWAYS
8088 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8089# else
8090 /* YIKES! This is a bug waiting to crash the program */
8091 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8092# endif
8093 gui_mch_new_menu_font();
8094 }
8095# ifdef FEAT_BEVAL
8096 if (do_tooltip)
8097 {
8098 /* The Athena widget set cannot currently handle switching between
8099 * displaying a single font and a fontset.
8100 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008101 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008102 * XFontStruct is used.
8103 */
8104 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8105 gui_mch_new_tooltip_font();
8106 }
8107# endif
8108# endif
8109 }
8110 else
8111# endif
8112 {
8113 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8114 /* If it worked and it's the Normal group, use it as the
8115 * normal font. Same for the Menu group. */
8116 if (HL_TABLE()[idx].sg_font != NOFONT)
8117 {
8118 if (do_normal)
8119 gui_init_font(arg, FALSE);
8120#ifndef FONTSET_ALWAYS
8121# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8122 if (do_menu)
8123 {
8124 gui.menu_font = HL_TABLE()[idx].sg_font;
8125 gui_mch_new_menu_font();
8126 }
8127# endif
8128#endif
8129 }
8130 }
8131}
8132
8133#endif /* FEAT_GUI */
8134
8135/*
8136 * Table with the specifications for an attribute number.
8137 * Note that this table is used by ALL buffers. This is required because the
8138 * GUI can redraw at any time for any buffer.
8139 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008140static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141
8142#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8143
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008144static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145
8146#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8147
8148#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008149static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008150
8151#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8152#endif
8153
8154/*
8155 * Return the attr number for a set of colors and font.
8156 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8157 * if the combination is new.
8158 * Return 0 for error (no more room).
8159 */
8160 static int
8161get_attr_entry(table, aep)
8162 garray_T *table;
8163 attrentry_T *aep;
8164{
8165 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008166 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008167 static int recursive = FALSE;
8168
8169 /*
8170 * Init the table, in case it wasn't done yet.
8171 */
8172 table->ga_itemsize = sizeof(attrentry_T);
8173 table->ga_growsize = 7;
8174
8175 /*
8176 * Try to find an entry with the same specifications.
8177 */
8178 for (i = 0; i < table->ga_len; ++i)
8179 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008180 taep = &(((attrentry_T *)table->ga_data)[i]);
8181 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182 && (
8183#ifdef FEAT_GUI
8184 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008185 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8186 && aep->ae_u.gui.bg_color
8187 == taep->ae_u.gui.bg_color
8188 && aep->ae_u.gui.sp_color
8189 == taep->ae_u.gui.sp_color
8190 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008191# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008192 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193# endif
8194 ))
8195 ||
8196#endif
8197 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008198 && (aep->ae_u.term.start == NULL)
8199 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200 && (aep->ae_u.term.start == NULL
8201 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008202 taep->ae_u.term.start) == 0)
8203 && (aep->ae_u.term.stop == NULL)
8204 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008205 && (aep->ae_u.term.stop == NULL
8206 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008207 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008209 && aep->ae_u.cterm.fg_color
8210 == taep->ae_u.cterm.fg_color
8211 && aep->ae_u.cterm.bg_color
8212 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008213 ))
8214
8215 return i + ATTR_OFF;
8216 }
8217
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008218 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219 {
8220 /*
8221 * Running out of attribute entries! remove all attributes, and
8222 * compute new ones for all groups.
8223 * When called recursively, we are really out of numbers.
8224 */
8225 if (recursive)
8226 {
8227 EMSG(_("E424: Too many different highlighting attributes in use"));
8228 return 0;
8229 }
8230 recursive = TRUE;
8231
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008232 clear_hl_tables();
8233
Bram Moolenaar071d4272004-06-13 20:20:40 +00008234 must_redraw = CLEAR;
8235
8236 for (i = 0; i < highlight_ga.ga_len; ++i)
8237 set_hl_attr(i);
8238
8239 recursive = FALSE;
8240 }
8241
8242 /*
8243 * This is a new combination of colors and font, add an entry.
8244 */
8245 if (ga_grow(table, 1) == FAIL)
8246 return 0;
8247
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008248 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8249 vim_memset(taep, 0, sizeof(attrentry_T));
8250 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008251#ifdef FEAT_GUI
8252 if (table == &gui_attr_table)
8253 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008254 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8255 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8256 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8257 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008258# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008259 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008260# endif
8261 }
8262#endif
8263 if (table == &term_attr_table)
8264 {
8265 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008266 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008267 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008268 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008269 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008270 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008271 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008272 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008273 }
8274 else if (table == &cterm_attr_table)
8275 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008276 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8277 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008278 }
8279 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008280 return (table->ga_len - 1 + ATTR_OFF);
8281}
8282
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008283/*
8284 * Clear all highlight tables.
8285 */
8286 void
8287clear_hl_tables()
8288{
8289 int i;
8290 attrentry_T *taep;
8291
8292#ifdef FEAT_GUI
8293 ga_clear(&gui_attr_table);
8294#endif
8295 for (i = 0; i < term_attr_table.ga_len; ++i)
8296 {
8297 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8298 vim_free(taep->ae_u.term.start);
8299 vim_free(taep->ae_u.term.stop);
8300 }
8301 ga_clear(&term_attr_table);
8302 ga_clear(&cterm_attr_table);
8303}
8304
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008305#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008306/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008307 * Combine special attributes (e.g., for spelling) with other attributes
8308 * (e.g., for syntax highlighting).
8309 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008310 * This creates a new group when required.
8311 * Since we expect there to be few spelling mistakes we don't cache the
8312 * result.
8313 * Return the resulting attributes.
8314 */
8315 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008316hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008317 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008318 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008319{
8320 attrentry_T *char_aep = NULL;
8321 attrentry_T *spell_aep;
8322 attrentry_T new_en;
8323
8324 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008325 return prim_attr;
8326 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8327 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008328#ifdef FEAT_GUI
8329 if (gui.in_use)
8330 {
8331 if (char_attr > HL_ALL)
8332 char_aep = syn_gui_attr2entry(char_attr);
8333 if (char_aep != NULL)
8334 new_en = *char_aep;
8335 else
8336 {
8337 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008338 new_en.ae_u.gui.fg_color = INVALCOLOR;
8339 new_en.ae_u.gui.bg_color = INVALCOLOR;
8340 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008341 if (char_attr <= HL_ALL)
8342 new_en.ae_attr = char_attr;
8343 }
8344
Bram Moolenaar30abd282005-06-22 22:35:10 +00008345 if (prim_attr <= HL_ALL)
8346 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008347 else
8348 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008349 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008350 if (spell_aep != NULL)
8351 {
8352 new_en.ae_attr |= spell_aep->ae_attr;
8353 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8354 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8355 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8356 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8357 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8358 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8359 if (spell_aep->ae_u.gui.font != NOFONT)
8360 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8361# ifdef FEAT_XFONTSET
8362 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8363 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8364# endif
8365 }
8366 }
8367 return get_attr_entry(&gui_attr_table, &new_en);
8368 }
8369#endif
8370
8371 if (t_colors > 1)
8372 {
8373 if (char_attr > HL_ALL)
8374 char_aep = syn_cterm_attr2entry(char_attr);
8375 if (char_aep != NULL)
8376 new_en = *char_aep;
8377 else
8378 {
8379 vim_memset(&new_en, 0, sizeof(new_en));
8380 if (char_attr <= HL_ALL)
8381 new_en.ae_attr = char_attr;
8382 }
8383
Bram Moolenaar30abd282005-06-22 22:35:10 +00008384 if (prim_attr <= HL_ALL)
8385 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008386 else
8387 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008388 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008389 if (spell_aep != NULL)
8390 {
8391 new_en.ae_attr |= spell_aep->ae_attr;
8392 if (spell_aep->ae_u.cterm.fg_color > 0)
8393 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8394 if (spell_aep->ae_u.cterm.bg_color > 0)
8395 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8396 }
8397 }
8398 return get_attr_entry(&cterm_attr_table, &new_en);
8399 }
8400
8401 if (char_attr > HL_ALL)
8402 char_aep = syn_term_attr2entry(char_attr);
8403 if (char_aep != NULL)
8404 new_en = *char_aep;
8405 else
8406 {
8407 vim_memset(&new_en, 0, sizeof(new_en));
8408 if (char_attr <= HL_ALL)
8409 new_en.ae_attr = char_attr;
8410 }
8411
Bram Moolenaar30abd282005-06-22 22:35:10 +00008412 if (prim_attr <= HL_ALL)
8413 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008414 else
8415 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008416 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008417 if (spell_aep != NULL)
8418 {
8419 new_en.ae_attr |= spell_aep->ae_attr;
8420 if (spell_aep->ae_u.term.start != NULL)
8421 {
8422 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8423 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8424 }
8425 }
8426 }
8427 return get_attr_entry(&term_attr_table, &new_en);
8428}
8429#endif
8430
Bram Moolenaar071d4272004-06-13 20:20:40 +00008431#ifdef FEAT_GUI
8432
8433 attrentry_T *
8434syn_gui_attr2entry(attr)
8435 int attr;
8436{
8437 attr -= ATTR_OFF;
8438 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8439 return NULL;
8440 return &(GUI_ATTR_ENTRY(attr));
8441}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008442#endif /* FEAT_GUI */
8443
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008444/*
8445 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8446 * Only to be used when "attr" > HL_ALL.
8447 */
8448 int
8449syn_attr2attr(attr)
8450 int attr;
8451{
8452 attrentry_T *aep;
8453
8454#ifdef FEAT_GUI
8455 if (gui.in_use)
8456 aep = syn_gui_attr2entry(attr);
8457 else
8458#endif
8459 if (t_colors > 1)
8460 aep = syn_cterm_attr2entry(attr);
8461 else
8462 aep = syn_term_attr2entry(attr);
8463
8464 if (aep == NULL) /* highlighting not set */
8465 return 0;
8466 return aep->ae_attr;
8467}
8468
8469
Bram Moolenaar071d4272004-06-13 20:20:40 +00008470 attrentry_T *
8471syn_term_attr2entry(attr)
8472 int attr;
8473{
8474 attr -= ATTR_OFF;
8475 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8476 return NULL;
8477 return &(TERM_ATTR_ENTRY(attr));
8478}
8479
8480 attrentry_T *
8481syn_cterm_attr2entry(attr)
8482 int attr;
8483{
8484 attr -= ATTR_OFF;
8485 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8486 return NULL;
8487 return &(CTERM_ATTR_ENTRY(attr));
8488}
8489
8490#define LIST_ATTR 1
8491#define LIST_STRING 2
8492#define LIST_INT 3
8493
8494 static void
8495highlight_list_one(id)
8496 int id;
8497{
8498 struct hl_group *sgp;
8499 int didh = FALSE;
8500
8501 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8502
8503 didh = highlight_list_arg(id, didh, LIST_ATTR,
8504 sgp->sg_term, NULL, "term");
8505 didh = highlight_list_arg(id, didh, LIST_STRING,
8506 0, sgp->sg_start, "start");
8507 didh = highlight_list_arg(id, didh, LIST_STRING,
8508 0, sgp->sg_stop, "stop");
8509
8510 didh = highlight_list_arg(id, didh, LIST_ATTR,
8511 sgp->sg_cterm, NULL, "cterm");
8512 didh = highlight_list_arg(id, didh, LIST_INT,
8513 sgp->sg_cterm_fg, NULL, "ctermfg");
8514 didh = highlight_list_arg(id, didh, LIST_INT,
8515 sgp->sg_cterm_bg, NULL, "ctermbg");
8516
Bram Moolenaar61623362010-07-14 22:04:22 +02008517#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 didh = highlight_list_arg(id, didh, LIST_ATTR,
8519 sgp->sg_gui, NULL, "gui");
8520 didh = highlight_list_arg(id, didh, LIST_STRING,
8521 0, sgp->sg_gui_fg_name, "guifg");
8522 didh = highlight_list_arg(id, didh, LIST_STRING,
8523 0, sgp->sg_gui_bg_name, "guibg");
8524 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008525 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008526#endif
8527#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008528 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008529 0, sgp->sg_font_name, "font");
8530#endif
8531
Bram Moolenaar661b1822005-07-28 22:36:45 +00008532 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008533 {
8534 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008535 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008536 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8537 msg_putchar(' ');
8538 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8539 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008540
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008541 if (!didh)
8542 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008543#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008544 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008545 last_set_msg(sgp->sg_scriptID);
8546#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008547}
8548
8549 static int
8550highlight_list_arg(id, didh, type, iarg, sarg, name)
8551 int id;
8552 int didh;
8553 int type;
8554 int iarg;
8555 char_u *sarg;
8556 char *name;
8557{
8558 char_u buf[100];
8559 char_u *ts;
8560 int i;
8561
Bram Moolenaar661b1822005-07-28 22:36:45 +00008562 if (got_int)
8563 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008564 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8565 {
8566 ts = buf;
8567 if (type == LIST_INT)
8568 sprintf((char *)buf, "%d", iarg - 1);
8569 else if (type == LIST_STRING)
8570 ts = sarg;
8571 else /* type == LIST_ATTR */
8572 {
8573 buf[0] = NUL;
8574 for (i = 0; hl_attr_table[i] != 0; ++i)
8575 {
8576 if (iarg & hl_attr_table[i])
8577 {
8578 if (buf[0] != NUL)
8579 STRCAT(buf, ",");
8580 STRCAT(buf, hl_name_table[i]);
8581 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8582 }
8583 }
8584 }
8585
8586 (void)syn_list_header(didh,
8587 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8588 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008589 if (!got_int)
8590 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008591 if (*name != NUL)
8592 {
8593 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8594 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8595 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008596 msg_outtrans(ts);
8597 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008598 }
8599 return didh;
8600}
8601
8602#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8603/*
8604 * Return "1" if highlight group "id" has attribute "flag".
8605 * Return NULL otherwise.
8606 */
8607 char_u *
8608highlight_has_attr(id, flag, modec)
8609 int id;
8610 int flag;
8611 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8612{
8613 int attr;
8614
8615 if (id <= 0 || id > highlight_ga.ga_len)
8616 return NULL;
8617
Bram Moolenaar61623362010-07-14 22:04:22 +02008618#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008619 if (modec == 'g')
8620 attr = HL_TABLE()[id - 1].sg_gui;
8621 else
8622#endif
8623 if (modec == 'c')
8624 attr = HL_TABLE()[id - 1].sg_cterm;
8625 else
8626 attr = HL_TABLE()[id - 1].sg_term;
8627
8628 if (attr & flag)
8629 return (char_u *)"1";
8630 return NULL;
8631}
8632#endif
8633
8634#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8635/*
8636 * Return color name of highlight group "id".
8637 */
8638 char_u *
8639highlight_color(id, what, modec)
8640 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008641 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008642 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8643{
8644 static char_u name[20];
8645 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008646 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008647 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008648 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008649
8650 if (id <= 0 || id > highlight_ga.ga_len)
8651 return NULL;
8652
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008653 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008654 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008655 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008656 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008657 font = TRUE;
8658 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008659 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008660 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8661 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662 if (modec == 'g')
8663 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008664# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008665 /* return font name */
8666 if (font)
8667 return HL_TABLE()[id - 1].sg_font_name;
8668
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008670 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008671 {
8672 guicolor_T color;
8673 long_u rgb;
8674 static char_u buf[10];
8675
8676 if (fg)
8677 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008678 else if (sp)
8679 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008680 else
8681 color = HL_TABLE()[id - 1].sg_gui_bg;
8682 if (color == INVALCOLOR)
8683 return NULL;
8684 rgb = gui_mch_get_rgb(color);
8685 sprintf((char *)buf, "#%02x%02x%02x",
8686 (unsigned)(rgb >> 16),
8687 (unsigned)(rgb >> 8) & 255,
8688 (unsigned)rgb & 255);
8689 return buf;
8690 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008691#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008692 if (fg)
8693 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008694 if (sp)
8695 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8697 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008698 if (font || sp)
8699 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008700 if (modec == 'c')
8701 {
8702 if (fg)
8703 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8704 else
8705 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8706 sprintf((char *)name, "%d", n);
8707 return name;
8708 }
8709 /* term doesn't have color */
8710 return NULL;
8711}
8712#endif
8713
8714#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8715 || defined(PROTO)
8716/*
8717 * Return color name of highlight group "id" as RGB value.
8718 */
8719 long_u
8720highlight_gui_color_rgb(id, fg)
8721 int id;
8722 int fg; /* TRUE = fg, FALSE = bg */
8723{
8724 guicolor_T color;
8725
8726 if (id <= 0 || id > highlight_ga.ga_len)
8727 return 0L;
8728
8729 if (fg)
8730 color = HL_TABLE()[id - 1].sg_gui_fg;
8731 else
8732 color = HL_TABLE()[id - 1].sg_gui_bg;
8733
8734 if (color == INVALCOLOR)
8735 return 0L;
8736
8737 return gui_mch_get_rgb(color);
8738}
8739#endif
8740
8741/*
8742 * Output the syntax list header.
8743 * Return TRUE when started a new line.
8744 */
8745 static int
8746syn_list_header(did_header, outlen, id)
8747 int did_header; /* did header already */
8748 int outlen; /* length of string that comes */
8749 int id; /* highlight group id */
8750{
8751 int endcol = 19;
8752 int newline = TRUE;
8753
8754 if (!did_header)
8755 {
8756 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008757 if (got_int)
8758 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008759 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8760 endcol = 15;
8761 }
8762 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008763 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008764 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008765 if (got_int)
8766 return TRUE;
8767 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008768 else
8769 {
8770 if (msg_col >= endcol) /* wrap around is like starting a new line */
8771 newline = FALSE;
8772 }
8773
8774 if (msg_col >= endcol) /* output at least one space */
8775 endcol = msg_col + 1;
8776 if (Columns <= endcol) /* avoid hang for tiny window */
8777 endcol = Columns - 1;
8778
8779 msg_advance(endcol);
8780
8781 /* Show "xxx" with the attributes. */
8782 if (!did_header)
8783 {
8784 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8785 msg_putchar(' ');
8786 }
8787
8788 return newline;
8789}
8790
8791/*
8792 * Set the attribute numbers for a highlight group.
8793 * Called after one of the attributes has changed.
8794 */
8795 static void
8796set_hl_attr(idx)
8797 int idx; /* index in array */
8798{
8799 attrentry_T at_en;
8800 struct hl_group *sgp = HL_TABLE() + idx;
8801
8802 /* The "Normal" group doesn't need an attribute number */
8803 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8804 return;
8805
8806#ifdef FEAT_GUI
8807 /*
8808 * For the GUI mode: If there are other than "normal" highlighting
8809 * attributes, need to allocate an attr number.
8810 */
8811 if (sgp->sg_gui_fg == INVALCOLOR
8812 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008813 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008814 && sgp->sg_font == NOFONT
8815# ifdef FEAT_XFONTSET
8816 && sgp->sg_fontset == NOFONTSET
8817# endif
8818 )
8819 {
8820 sgp->sg_gui_attr = sgp->sg_gui;
8821 }
8822 else
8823 {
8824 at_en.ae_attr = sgp->sg_gui;
8825 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8826 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008827 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008828 at_en.ae_u.gui.font = sgp->sg_font;
8829# ifdef FEAT_XFONTSET
8830 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8831# endif
8832 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8833 }
8834#endif
8835 /*
8836 * For the term mode: If there are other than "normal" highlighting
8837 * attributes, need to allocate an attr number.
8838 */
8839 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8840 sgp->sg_term_attr = sgp->sg_term;
8841 else
8842 {
8843 at_en.ae_attr = sgp->sg_term;
8844 at_en.ae_u.term.start = sgp->sg_start;
8845 at_en.ae_u.term.stop = sgp->sg_stop;
8846 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8847 }
8848
8849 /*
8850 * For the color term mode: If there are other than "normal"
8851 * highlighting attributes, need to allocate an attr number.
8852 */
8853 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8854 sgp->sg_cterm_attr = sgp->sg_cterm;
8855 else
8856 {
8857 at_en.ae_attr = sgp->sg_cterm;
8858 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8859 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8860 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8861 }
8862}
8863
8864/*
8865 * Lookup a highlight group name and return it's ID.
8866 * If it is not found, 0 is returned.
8867 */
8868 int
8869syn_name2id(name)
8870 char_u *name;
8871{
8872 int i;
8873 char_u name_u[200];
8874
8875 /* Avoid using stricmp() too much, it's slow on some systems */
8876 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8877 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008878 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008879 vim_strup(name_u);
8880 for (i = highlight_ga.ga_len; --i >= 0; )
8881 if (HL_TABLE()[i].sg_name_u != NULL
8882 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8883 break;
8884 return i + 1;
8885}
8886
8887#if defined(FEAT_EVAL) || defined(PROTO)
8888/*
8889 * Return TRUE if highlight group "name" exists.
8890 */
8891 int
8892highlight_exists(name)
8893 char_u *name;
8894{
8895 return (syn_name2id(name) > 0);
8896}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008897
8898# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8899/*
8900 * Return the name of highlight group "id".
8901 * When not a valid ID return an empty string.
8902 */
8903 char_u *
8904syn_id2name(id)
8905 int id;
8906{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008907 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008908 return (char_u *)"";
8909 return HL_TABLE()[id - 1].sg_name;
8910}
8911# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008912#endif
8913
8914/*
8915 * Like syn_name2id(), but take a pointer + length argument.
8916 */
8917 int
8918syn_namen2id(linep, len)
8919 char_u *linep;
8920 int len;
8921{
8922 char_u *name;
8923 int id = 0;
8924
8925 name = vim_strnsave(linep, len);
8926 if (name != NULL)
8927 {
8928 id = syn_name2id(name);
8929 vim_free(name);
8930 }
8931 return id;
8932}
8933
8934/*
8935 * Find highlight group name in the table and return it's ID.
8936 * The argument is a pointer to the name and the length of the name.
8937 * If it doesn't exist yet, a new entry is created.
8938 * Return 0 for failure.
8939 */
8940 int
8941syn_check_group(pp, len)
8942 char_u *pp;
8943 int len;
8944{
8945 int id;
8946 char_u *name;
8947
8948 name = vim_strnsave(pp, len);
8949 if (name == NULL)
8950 return 0;
8951
8952 id = syn_name2id(name);
8953 if (id == 0) /* doesn't exist yet */
8954 id = syn_add_group(name);
8955 else
8956 vim_free(name);
8957 return id;
8958}
8959
8960/*
8961 * Add new highlight group and return it's ID.
8962 * "name" must be an allocated string, it will be consumed.
8963 * Return 0 for failure.
8964 */
8965 static int
8966syn_add_group(name)
8967 char_u *name;
8968{
8969 char_u *p;
8970
8971 /* Check that the name is ASCII letters, digits and underscore. */
8972 for (p = name; *p != NUL; ++p)
8973 {
8974 if (!vim_isprintc(*p))
8975 {
8976 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008977 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008978 return 0;
8979 }
8980 else if (!ASCII_ISALNUM(*p) && *p != '_')
8981 {
8982 /* This is an error, but since there previously was no check only
8983 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008984 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008985 MSG(_("W18: Invalid character in group name"));
8986 break;
8987 }
8988 }
8989
8990 /*
8991 * First call for this growarray: init growing array.
8992 */
8993 if (highlight_ga.ga_data == NULL)
8994 {
8995 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8996 highlight_ga.ga_growsize = 10;
8997 }
8998
Bram Moolenaar42431a72011-04-01 14:44:59 +02008999 if (highlight_ga.ga_len >= MAX_SYNID)
9000 {
9001 EMSG(_("E849: Too many syntax groups"));
9002 vim_free(name);
9003 return 0;
9004 }
9005
Bram Moolenaar071d4272004-06-13 20:20:40 +00009006 /*
9007 * Make room for at least one other syntax_highlight entry.
9008 */
9009 if (ga_grow(&highlight_ga, 1) == FAIL)
9010 {
9011 vim_free(name);
9012 return 0;
9013 }
9014
9015 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9016 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9017 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9018#ifdef FEAT_GUI
9019 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9020 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009021 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009022#endif
9023 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009024
9025 return highlight_ga.ga_len; /* ID is index plus one */
9026}
9027
9028/*
9029 * When, just after calling syn_add_group(), an error is discovered, this
9030 * function deletes the new name.
9031 */
9032 static void
9033syn_unadd_group()
9034{
9035 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009036 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9037 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9038}
9039
9040/*
9041 * Translate a group ID to highlight attributes.
9042 */
9043 int
9044syn_id2attr(hl_id)
9045 int hl_id;
9046{
9047 int attr;
9048 struct hl_group *sgp;
9049
9050 hl_id = syn_get_final_id(hl_id);
9051 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9052
9053#ifdef FEAT_GUI
9054 /*
9055 * Only use GUI attr when the GUI is being used.
9056 */
9057 if (gui.in_use)
9058 attr = sgp->sg_gui_attr;
9059 else
9060#endif
9061 if (t_colors > 1)
9062 attr = sgp->sg_cterm_attr;
9063 else
9064 attr = sgp->sg_term_attr;
9065
9066 return attr;
9067}
9068
9069#ifdef FEAT_GUI
9070/*
9071 * Get the GUI colors and attributes for a group ID.
9072 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9073 */
9074 int
9075syn_id2colors(hl_id, fgp, bgp)
9076 int hl_id;
9077 guicolor_T *fgp;
9078 guicolor_T *bgp;
9079{
9080 struct hl_group *sgp;
9081
9082 hl_id = syn_get_final_id(hl_id);
9083 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9084
9085 *fgp = sgp->sg_gui_fg;
9086 *bgp = sgp->sg_gui_bg;
9087 return sgp->sg_gui;
9088}
9089#endif
9090
9091/*
9092 * Translate a group ID to the final group ID (following links).
9093 */
9094 int
9095syn_get_final_id(hl_id)
9096 int hl_id;
9097{
9098 int count;
9099 struct hl_group *sgp;
9100
9101 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9102 return 0; /* Can be called from eval!! */
9103
9104 /*
9105 * Follow links until there is no more.
9106 * Look out for loops! Break after 100 links.
9107 */
9108 for (count = 100; --count >= 0; )
9109 {
9110 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9111 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9112 break;
9113 hl_id = sgp->sg_link;
9114 }
9115
9116 return hl_id;
9117}
9118
9119#ifdef FEAT_GUI
9120/*
9121 * Call this function just after the GUI has started.
9122 * It finds the font and color handles for the highlighting groups.
9123 */
9124 void
9125highlight_gui_started()
9126{
9127 int idx;
9128
9129 /* First get the colors from the "Normal" and "Menu" group, if set */
9130 set_normal_colors();
9131
9132 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9133 gui_do_one_color(idx, FALSE, FALSE);
9134
9135 highlight_changed();
9136}
9137
9138 static void
9139gui_do_one_color(idx, do_menu, do_tooltip)
9140 int idx;
9141 int do_menu; /* TRUE: might set the menu font */
9142 int do_tooltip; /* TRUE: might set the tooltip font */
9143{
9144 int didit = FALSE;
9145
9146 if (HL_TABLE()[idx].sg_font_name != NULL)
9147 {
9148 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9149 do_tooltip);
9150 didit = TRUE;
9151 }
9152 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9153 {
9154 HL_TABLE()[idx].sg_gui_fg =
9155 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9156 didit = TRUE;
9157 }
9158 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9159 {
9160 HL_TABLE()[idx].sg_gui_bg =
9161 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9162 didit = TRUE;
9163 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009164 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9165 {
9166 HL_TABLE()[idx].sg_gui_sp =
9167 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9168 didit = TRUE;
9169 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009170 if (didit) /* need to get a new attr number */
9171 set_hl_attr(idx);
9172}
9173
9174#endif
9175
9176/*
9177 * Translate the 'highlight' option into attributes in highlight_attr[] and
9178 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9179 * corresponding highlights to use on top of HLF_SNC is computed.
9180 * Called only when the 'highlight' option has been changed and upon first
9181 * screen redraw after any :highlight command.
9182 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9183 */
9184 int
9185highlight_changed()
9186{
9187 int hlf;
9188 int i;
9189 char_u *p;
9190 int attr;
9191 char_u *end;
9192 int id;
9193#ifdef USER_HIGHLIGHT
9194 char_u userhl[10];
9195# ifdef FEAT_STL_OPT
9196 int id_SNC = -1;
9197 int id_S = -1;
9198 int hlcnt;
9199# endif
9200#endif
9201 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9202
9203 need_highlight_changed = FALSE;
9204
9205 /*
9206 * Clear all attributes.
9207 */
9208 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9209 highlight_attr[hlf] = 0;
9210
9211 /*
9212 * First set all attributes to their default value.
9213 * Then use the attributes from the 'highlight' option.
9214 */
9215 for (i = 0; i < 2; ++i)
9216 {
9217 if (i)
9218 p = p_hl;
9219 else
9220 p = get_highlight_default();
9221 if (p == NULL) /* just in case */
9222 continue;
9223
9224 while (*p)
9225 {
9226 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9227 if (hl_flags[hlf] == *p)
9228 break;
9229 ++p;
9230 if (hlf == (int)HLF_COUNT || *p == NUL)
9231 return FAIL;
9232
9233 /*
9234 * Allow several hl_flags to be combined, like "bu" for
9235 * bold-underlined.
9236 */
9237 attr = 0;
9238 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9239 {
9240 if (vim_iswhite(*p)) /* ignore white space */
9241 continue;
9242
9243 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9244 return FAIL;
9245
9246 switch (*p)
9247 {
9248 case 'b': attr |= HL_BOLD;
9249 break;
9250 case 'i': attr |= HL_ITALIC;
9251 break;
9252 case '-':
9253 case 'n': /* no highlighting */
9254 break;
9255 case 'r': attr |= HL_INVERSE;
9256 break;
9257 case 's': attr |= HL_STANDOUT;
9258 break;
9259 case 'u': attr |= HL_UNDERLINE;
9260 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009261 case 'c': attr |= HL_UNDERCURL;
9262 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009263 case ':': ++p; /* highlight group name */
9264 if (attr || *p == NUL) /* no combinations */
9265 return FAIL;
9266 end = vim_strchr(p, ',');
9267 if (end == NULL)
9268 end = p + STRLEN(p);
9269 id = syn_check_group(p, (int)(end - p));
9270 if (id == 0)
9271 return FAIL;
9272 attr = syn_id2attr(id);
9273 p = end - 1;
9274#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9275 if (hlf == (int)HLF_SNC)
9276 id_SNC = syn_get_final_id(id);
9277 else if (hlf == (int)HLF_S)
9278 id_S = syn_get_final_id(id);
9279#endif
9280 break;
9281 default: return FAIL;
9282 }
9283 }
9284 highlight_attr[hlf] = attr;
9285
9286 p = skip_to_option_part(p); /* skip comma and spaces */
9287 }
9288 }
9289
9290#ifdef USER_HIGHLIGHT
9291 /* Setup the user highlights
9292 *
9293 * Temporarily utilize 10 more hl entries. Have to be in there
9294 * simultaneously in case of table overflows in get_attr_entry()
9295 */
9296# ifdef FEAT_STL_OPT
9297 if (ga_grow(&highlight_ga, 10) == FAIL)
9298 return FAIL;
9299 hlcnt = highlight_ga.ga_len;
9300 if (id_S == 0)
9301 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009302 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009303 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9304 id_S = hlcnt + 10;
9305 }
9306# endif
9307 for (i = 0; i < 9; i++)
9308 {
9309 sprintf((char *)userhl, "User%d", i + 1);
9310 id = syn_name2id(userhl);
9311 if (id == 0)
9312 {
9313 highlight_user[i] = 0;
9314# ifdef FEAT_STL_OPT
9315 highlight_stlnc[i] = 0;
9316# endif
9317 }
9318 else
9319 {
9320# ifdef FEAT_STL_OPT
9321 struct hl_group *hlt = HL_TABLE();
9322# endif
9323
9324 highlight_user[i] = syn_id2attr(id);
9325# ifdef FEAT_STL_OPT
9326 if (id_SNC == 0)
9327 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009328 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009329 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9330 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009331# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009332 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9333# endif
9334 }
9335 else
9336 mch_memmove(&hlt[hlcnt + i],
9337 &hlt[id_SNC - 1],
9338 sizeof(struct hl_group));
9339 hlt[hlcnt + i].sg_link = 0;
9340
9341 /* Apply difference between UserX and HLF_S to HLF_SNC */
9342 hlt[hlcnt + i].sg_term ^=
9343 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9344 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9345 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9346 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9347 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9348 hlt[hlcnt + i].sg_cterm ^=
9349 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9350 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9351 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9352 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9353 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009354# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 hlt[hlcnt + i].sg_gui ^=
9356 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009357# endif
9358# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009359 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9360 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9361 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9362 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009363 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9364 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009365 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9366 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9367# ifdef FEAT_XFONTSET
9368 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9369 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9370# endif
9371# endif
9372 highlight_ga.ga_len = hlcnt + i + 1;
9373 set_hl_attr(hlcnt + i); /* At long last we can apply */
9374 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9375# endif
9376 }
9377 }
9378# ifdef FEAT_STL_OPT
9379 highlight_ga.ga_len = hlcnt;
9380# endif
9381
9382#endif /* USER_HIGHLIGHT */
9383
9384 return OK;
9385}
9386
Bram Moolenaar4f688582007-07-24 12:34:30 +00009387#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009388
9389static void highlight_list __ARGS((void));
9390static void highlight_list_two __ARGS((int cnt, int attr));
9391
9392/*
9393 * Handle command line completion for :highlight command.
9394 */
9395 void
9396set_context_in_highlight_cmd(xp, arg)
9397 expand_T *xp;
9398 char_u *arg;
9399{
9400 char_u *p;
9401
9402 /* Default: expand group names */
9403 xp->xp_context = EXPAND_HIGHLIGHT;
9404 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009405 include_link = 2;
9406 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407
9408 /* (part of) subcommand already typed */
9409 if (*arg != NUL)
9410 {
9411 p = skiptowhite(arg);
9412 if (*p != NUL) /* past "default" or group name */
9413 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009414 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009415 if (STRNCMP("default", arg, p - arg) == 0)
9416 {
9417 arg = skipwhite(p);
9418 xp->xp_pattern = arg;
9419 p = skiptowhite(arg);
9420 }
9421 if (*p != NUL) /* past group name */
9422 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009423 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009424 if (arg[1] == 'i' && arg[0] == 'N')
9425 highlight_list();
9426 if (STRNCMP("link", arg, p - arg) == 0
9427 || STRNCMP("clear", arg, p - arg) == 0)
9428 {
9429 xp->xp_pattern = skipwhite(p);
9430 p = skiptowhite(xp->xp_pattern);
9431 if (*p != NUL) /* past first group name */
9432 {
9433 xp->xp_pattern = skipwhite(p);
9434 p = skiptowhite(xp->xp_pattern);
9435 }
9436 }
9437 if (*p != NUL) /* past group name(s) */
9438 xp->xp_context = EXPAND_NOTHING;
9439 }
9440 }
9441 }
9442}
9443
9444/*
9445 * List highlighting matches in a nice way.
9446 */
9447 static void
9448highlight_list()
9449{
9450 int i;
9451
9452 for (i = 10; --i >= 0; )
9453 highlight_list_two(i, hl_attr(HLF_D));
9454 for (i = 40; --i >= 0; )
9455 highlight_list_two(99, 0);
9456}
9457
9458 static void
9459highlight_list_two(cnt, attr)
9460 int cnt;
9461 int attr;
9462{
9463 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9464 msg_clr_eos();
9465 out_flush();
9466 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9467}
9468
9469#endif /* FEAT_CMDL_COMPL */
9470
9471#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9472 || defined(FEAT_SIGNS) || defined(PROTO)
9473/*
9474 * Function given to ExpandGeneric() to obtain the list of group names.
9475 * Also used for synIDattr() function.
9476 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009477 char_u *
9478get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009479 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009480 int idx;
9481{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009482#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009483 if (idx == highlight_ga.ga_len && include_none != 0)
9484 return (char_u *)"none";
9485 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009486 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009487 if (idx == highlight_ga.ga_len + include_none + include_default
9488 && include_link != 0)
9489 return (char_u *)"link";
9490 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9491 && include_link != 0)
9492 return (char_u *)"clear";
9493#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009494 if (idx < 0 || idx >= highlight_ga.ga_len)
9495 return NULL;
9496 return HL_TABLE()[idx].sg_name;
9497}
9498#endif
9499
Bram Moolenaar4f688582007-07-24 12:34:30 +00009500#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009501/*
9502 * Free all the highlight group fonts.
9503 * Used when quitting for systems which need it.
9504 */
9505 void
9506free_highlight_fonts()
9507{
9508 int idx;
9509
9510 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9511 {
9512 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9513 HL_TABLE()[idx].sg_font = NOFONT;
9514# ifdef FEAT_XFONTSET
9515 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9516 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9517# endif
9518 }
9519
9520 gui_mch_free_font(gui.norm_font);
9521# ifdef FEAT_XFONTSET
9522 gui_mch_free_fontset(gui.fontset);
9523# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009524# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009525 gui_mch_free_font(gui.bold_font);
9526 gui_mch_free_font(gui.ital_font);
9527 gui_mch_free_font(gui.boldital_font);
9528# endif
9529}
9530#endif
9531
9532/**************************************
9533 * End of Highlighting stuff *
9534 **************************************/