blob: cb87a020224a6bf1f1669f90e00f667974c44254 [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
148 int sp_char; /* conceal substitute character */
149#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
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200199static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200200static int current_flags = 0;
201static 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:
222 * 0 - 9999 normal syntax groups
223 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
224 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
225 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
226 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
227 */
228#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
229#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
230#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
231#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
232
233/*
234 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
235 * expand_filename(). Most of the other syntax commands don't need it, so
236 * instead of passing it to them, we stow it here.
237 */
238static char_u **syn_cmdlinep;
239
240/*
241 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200242 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243 * rules in each ":syn include"'d file.
244 */
245static int current_syn_inc_tag = 0;
246static int running_syn_inc_tag = 0;
247
248/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000249 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
250 * This avoids adding a pointer to the hashtable item.
251 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
252 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
253 * HI2KE() converts a hashitem pointer to a var pointer.
254 */
255static keyentry_T dumkey;
256#define KE2HIKEY(kp) ((kp)->keyword)
257#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
258#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
259
260/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261 * To reduce the time spent in keepend(), remember at which level in the state
262 * stack the first item with "keepend" is present. When "-1", there is no
263 * "keepend" on the stack.
264 */
265static int keepend_level = -1;
266
267/*
268 * For the current state we need to remember more than just the idx.
269 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
270 * (The end positions have the column number of the next char)
271 */
272typedef struct state_item
273{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000274 int si_idx; /* index of syntax pattern or
275 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000277 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278 int si_m_lnum; /* lnum of the match */
279 int si_m_startcol; /* starting column of the match */
280 lpos_T si_m_endpos; /* just after end posn of the match */
281 lpos_T si_h_startpos; /* start position of the highlighting */
282 lpos_T si_h_endpos; /* end position of the highlighting */
283 lpos_T si_eoe_pos; /* end position of end pattern */
284 int si_end_idx; /* group ID for end pattern or zero */
285 int si_ends; /* if match ends before si_m_endpos */
286 int si_attr; /* attributes in this state */
287 long si_flags; /* HL_HAS_EOL flag in this state, and
288 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200289#ifdef FEAT_CONCEAL
290 int si_char; /* substitution character for conceal */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200291 int si_seqnr; /* sequence number */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200292#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 short *si_cont_list; /* list of contained groups */
294 short *si_next_list; /* nextgroup IDs after this item ends */
295 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
296 * pattern */
297} stateitem_T;
298
299#define KEYWORD_IDX -1 /* value of si_idx for keywords */
300#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
301 but contained groups */
302
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200303#ifdef FEAT_CONCEAL
304static int next_seqnr = 0; /* value to use for si_seqnr */
305#endif
306
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000308 * Struct to reduce the number of arguments to get_syn_options(), it's used
309 * very often.
310 */
311typedef struct
312{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000313 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000314 int keyword; /* TRUE for ":syn keyword" */
315 int *sync_idx; /* syntax item for "grouphere" argument, NULL
316 if not allowed */
317 char has_cont_list; /* TRUE if "cont_list" can be used */
318 short *cont_list; /* group IDs for "contains" argument */
319 short *cont_in_list; /* group IDs for "containedin" argument */
320 short *next_list; /* group IDs for "nextgroup" argument */
321} syn_opt_arg_T;
322
323/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 * The next possible match in the current line for any pattern is remembered,
325 * to avoid having to try for a match in each column.
326 * If next_match_idx == -1, not tried (in this line) yet.
327 * If next_match_col == MAXCOL, no match found in this line.
328 * (All end positions have the column of the char after the end)
329 */
330static int next_match_col; /* column for start of next match */
331static lpos_T next_match_m_endpos; /* position for end of next match */
332static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
333static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
334static int next_match_idx; /* index of matched item */
335static long next_match_flags; /* flags for next match */
336static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
337static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
338static int next_match_end_idx; /* ID of group for end pattn or zero */
339static reg_extmatch_T *next_match_extmatch = NULL;
340
341/*
342 * A state stack is an array of integers or stateitem_T, stored in a
343 * garray_T. A state stack is invalid if it's itemsize entry is zero.
344 */
345#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
346#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
347
348/*
349 * The current state (within the line) of the recognition engine.
350 * When current_state.ga_itemsize is 0 the current state is invalid.
351 */
352static win_T *syn_win; /* current window for highlighting */
353static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200354static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355static linenr_T current_lnum = 0; /* lnum of current state */
356static colnr_T current_col = 0; /* column of current state */
357static int current_state_stored = 0; /* TRUE if stored current state
358 * after setting current_finished */
359static int current_finished = 0; /* current line has been finished */
360static garray_T current_state /* current stack of state_items */
361 = {0, 0, 0, 0, NULL};
362static short *current_next_list = NULL; /* when non-zero, nextgroup list */
363static int current_next_flags = 0; /* flags for current_next_list */
364static int current_line_id = 0; /* unique number for current line */
365
366#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
367
368static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
369static int syn_match_linecont __ARGS((linenr_T lnum));
370static void syn_start_line __ARGS((void));
371static void syn_update_ends __ARGS((int startofline));
372static void syn_stack_alloc __ARGS((void));
373static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200374static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000376static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377static void load_current_state __ARGS((synstate_T *from));
378static void invalidate_current_state __ARGS((void));
379static int syn_stack_equal __ARGS((synstate_T *sp));
380static void validate_current_state __ARGS((void));
381static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000382static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383static int did_match_already __ARGS((int idx, garray_T *gap));
384static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
385static void check_state_ends __ARGS((void));
386static void update_si_attr __ARGS((int idx));
387static void check_keepend __ARGS((void));
388static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
389static short *copy_id_list __ARGS((short *list));
390static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
391static int push_current_state __ARGS((int idx));
392static void pop_current_state __ARGS((void));
393
Bram Moolenaar860cae12010-06-05 23:22:07 +0200394static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395static 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));
396static void clear_syn_state __ARGS((synstate_T *p));
397static void clear_current_state __ARGS((void));
398
399static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
400static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
401static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
402static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
403static char_u *syn_getcurline __ARGS((void));
404static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200405static 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 +0000406static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000407static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200409static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
410static void syn_clear_pattern __ARGS((synblock_T *block, int i));
411static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200413static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414static void syn_clear_one __ARGS((int id, int syncing));
415static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
416static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
417static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
418static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
419static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
420static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
421static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
422static void syn_lines_msg __ARGS((void));
423static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200424static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425static void syn_list_one __ARGS((int id, int syncing, int link_only));
426static void syn_list_cluster __ARGS((int id));
427static void put_id_list __ARGS((char_u *name, short *list, int attr));
428static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000429static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
430static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
431static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200432static 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 +0000433static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200434static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
436static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
437static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
438static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
439#ifdef __BORLANDC__
440static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
441#else
442static int syn_compare_stub __ARGS((const void *v1, const void *v2));
443#endif
444static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
445static int syn_scl_name2id __ARGS((char_u *name));
446static int syn_scl_namen2id __ARGS((char_u *linep, int len));
447static int syn_check_cluster __ARGS((char_u *pp, int len));
448static int syn_add_cluster __ARGS((char_u *name));
449static void init_syn_patterns __ARGS((void));
450static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
451static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
452static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
453static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
454static void syn_incl_toplevel __ARGS((int id, int *flagsp));
455
456/*
457 * Start the syntax recognition for a line. This function is normally called
458 * from the screen updating, once for each displayed line.
459 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
460 * it. Careful: curbuf and curwin are likely to point to another buffer and
461 * window.
462 */
463 void
464syntax_start(wp, lnum)
465 win_T *wp;
466 linenr_T lnum;
467{
468 synstate_T *p;
469 synstate_T *last_valid = NULL;
470 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000471 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 linenr_T parsed_lnum;
473 linenr_T first_stored;
474 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000475 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200477#ifdef FEAT_CONCEAL
478 current_sub_char = NUL;
479#endif
480
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 /*
482 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000483 * Also do this when a change was made, the current state may be invalid
484 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 {
488 invalidate_current_state();
489 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200490 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000492 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 syn_win = wp;
494
495 /*
496 * Allocate syntax stack when needed.
497 */
498 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200499 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000500 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200501 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502
503 /*
504 * If the state of the end of the previous line is useful, store it.
505 */
506 if (VALID_STATE(&current_state)
507 && current_lnum < lnum
508 && current_lnum < syn_buf->b_ml.ml_line_count)
509 {
510 (void)syn_finish_line(FALSE);
511 if (!current_state_stored)
512 {
513 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000514 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 }
516
517 /*
518 * If the current_lnum is now the same as "lnum", keep the current
519 * state (this happens very often!). Otherwise invalidate
520 * current_state and figure it out below.
521 */
522 if (current_lnum != lnum)
523 invalidate_current_state();
524 }
525 else
526 invalidate_current_state();
527
528 /*
529 * Try to synchronize from a saved state in b_sst_array[].
530 * Only do this if lnum is not before and not to far beyond a saved state.
531 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 {
534 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200535 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000536 {
537 if (p->sst_lnum > lnum)
538 break;
539 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
540 {
541 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200542 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 last_min_valid = p;
544 }
545 }
546 if (last_min_valid != NULL)
547 load_current_state(last_min_valid);
548 }
549
550 /*
551 * If "lnum" is before or far beyond a line with a saved state, need to
552 * re-synchronize.
553 */
554 if (INVALID_STATE(&current_state))
555 {
556 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200557 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 }
559 else
560 first_stored = current_lnum;
561
562 /*
563 * Advance from the sync point or saved state until the current line.
564 * Save some entries for syncing with later on.
565 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200566 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000567 dist = 999999;
568 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200569 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570 while (current_lnum < lnum)
571 {
572 syn_start_line();
573 (void)syn_finish_line(FALSE);
574 ++current_lnum;
575
576 /* If we parsed at least "minlines" lines or started at a valid
577 * state, the current state is considered valid. */
578 if (current_lnum >= first_stored)
579 {
580 /* Check if the saved state entry is for the current line and is
581 * equal to the current state. If so, then validate all saved
582 * states that depended on a change before the parsed line. */
583 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000584 prev = syn_stack_find_entry(current_lnum - 1);
585 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200586 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000588 sp = prev;
589 while (sp != NULL && sp->sst_lnum < current_lnum)
590 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 if (sp != NULL
592 && sp->sst_lnum == current_lnum
593 && syn_stack_equal(sp))
594 {
595 parsed_lnum = current_lnum;
596 prev = sp;
597 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
598 {
599 if (sp->sst_lnum <= lnum)
600 /* valid state before desired line, use this one */
601 prev = sp;
602 else if (sp->sst_change_lnum == 0)
603 /* past saved states depending on change, break here. */
604 break;
605 sp->sst_change_lnum = 0;
606 sp = sp->sst_next;
607 }
608 load_current_state(prev);
609 }
610 /* Store the state at this line when it's the first one, the line
611 * where we start parsing, or some distance from the previously
612 * saved state. But only when parsed at least 'minlines'. */
613 else if (prev == NULL
614 || current_lnum == lnum
615 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000616 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 }
618
619 /* This can take a long time: break when CTRL-C pressed. The current
620 * state will be wrong then. */
621 line_breakcheck();
622 if (got_int)
623 {
624 current_lnum = lnum;
625 break;
626 }
627 }
628
629 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630}
631
632/*
633 * We cannot simply discard growarrays full of state_items or buf_states; we
634 * have to manually release their extmatch pointers first.
635 */
636 static void
637clear_syn_state(p)
638 synstate_T *p;
639{
640 int i;
641 garray_T *gap;
642
643 if (p->sst_stacksize > SST_FIX_STATES)
644 {
645 gap = &(p->sst_union.sst_ga);
646 for (i = 0; i < gap->ga_len; i++)
647 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
648 ga_clear(gap);
649 }
650 else
651 {
652 for (i = 0; i < p->sst_stacksize; i++)
653 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
654 }
655}
656
657/*
658 * Cleanup the current_state stack.
659 */
660 static void
661clear_current_state()
662{
663 int i;
664 stateitem_T *sip;
665
666 sip = (stateitem_T *)(current_state.ga_data);
667 for (i = 0; i < current_state.ga_len; i++)
668 unref_extmatch(sip[i].si_extmatch);
669 ga_clear(&current_state);
670}
671
672/*
673 * Try to find a synchronisation point for line "lnum".
674 *
675 * This sets current_lnum and the current state. One of three methods is
676 * used:
677 * 1. Search backwards for the end of a C-comment.
678 * 2. Search backwards for given sync patterns.
679 * 3. Simply start on a given number of lines above "lnum".
680 */
681 static void
682syn_sync(wp, start_lnum, last_valid)
683 win_T *wp;
684 linenr_T start_lnum;
685 synstate_T *last_valid;
686{
687 buf_T *curbuf_save;
688 win_T *curwin_save;
689 pos_T cursor_save;
690 int idx;
691 linenr_T lnum;
692 linenr_T end_lnum;
693 linenr_T break_lnum;
694 int had_sync_point;
695 stateitem_T *cur_si;
696 synpat_T *spp;
697 char_u *line;
698 int found_flags = 0;
699 int found_match_idx = 0;
700 linenr_T found_current_lnum = 0;
701 int found_current_col= 0;
702 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000703 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000704
705 /*
706 * Clear any current state that might be hanging around.
707 */
708 invalidate_current_state();
709
710 /*
711 * Start at least "minlines" back. Default starting point for parsing is
712 * there.
713 * Start further back, to avoid that scrolling backwards will result in
714 * resyncing for every line. Now it resyncs only one out of N lines,
715 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
716 * Watch out for overflow when minlines is MAXLNUM.
717 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200718 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 start_lnum = 1;
720 else
721 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200722 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200724 else if (syn_block->b_syn_sync_minlines < 10)
725 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200727 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
728 if (syn_block->b_syn_sync_maxlines != 0
729 && lnum > syn_block->b_syn_sync_maxlines)
730 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 if (lnum >= start_lnum)
732 start_lnum = 1;
733 else
734 start_lnum -= lnum;
735 }
736 current_lnum = start_lnum;
737
738 /*
739 * 1. Search backwards for the end of a C-style comment.
740 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200741 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
743 /* Need to make syn_buf the current buffer for a moment, to be able to
744 * use find_start_comment(). */
745 curwin_save = curwin;
746 curwin = wp;
747 curbuf_save = curbuf;
748 curbuf = syn_buf;
749
750 /*
751 * Skip lines that end in a backslash.
752 */
753 for ( ; start_lnum > 1; --start_lnum)
754 {
755 line = ml_get(start_lnum - 1);
756 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
757 break;
758 }
759 current_lnum = start_lnum;
760
761 /* set cursor to start of search */
762 cursor_save = wp->w_cursor;
763 wp->w_cursor.lnum = start_lnum;
764 wp->w_cursor.col = 0;
765
766 /*
767 * If the line is inside a comment, need to find the syntax item that
768 * defines the comment.
769 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
770 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200773 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
774 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
775 == syn_block->b_syn_sync_id
776 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 {
778 validate_current_state();
779 if (push_current_state(idx) == OK)
780 update_si_attr(current_state.ga_len - 1);
781 break;
782 }
783 }
784
785 /* restore cursor and buffer */
786 wp->w_cursor = cursor_save;
787 curwin = curwin_save;
788 curbuf = curbuf_save;
789 }
790
791 /*
792 * 2. Search backwards for given sync patterns.
793 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200794 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200796 if (syn_block->b_syn_sync_maxlines != 0
797 && start_lnum > syn_block->b_syn_sync_maxlines)
798 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 else
800 break_lnum = 0;
801
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000802 found_m_endpos.lnum = 0;
803 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 end_lnum = start_lnum;
805 lnum = start_lnum;
806 while (--lnum > break_lnum)
807 {
808 /* This can take a long time: break when CTRL-C pressed. */
809 line_breakcheck();
810 if (got_int)
811 {
812 invalidate_current_state();
813 current_lnum = start_lnum;
814 break;
815 }
816
817 /* Check if we have run into a valid saved state stack now. */
818 if (last_valid != NULL && lnum == last_valid->sst_lnum)
819 {
820 load_current_state(last_valid);
821 break;
822 }
823
824 /*
825 * Check if the previous line has the line-continuation pattern.
826 */
827 if (lnum > 1 && syn_match_linecont(lnum - 1))
828 continue;
829
830 /*
831 * Start with nothing on the state stack
832 */
833 validate_current_state();
834
835 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
836 {
837 syn_start_line();
838 for (;;)
839 {
840 had_sync_point = syn_finish_line(TRUE);
841 /*
842 * When a sync point has been found, remember where, and
843 * continue to look for another one, further on in the line.
844 */
845 if (had_sync_point && current_state.ga_len)
846 {
847 cur_si = &CUR_STATE(current_state.ga_len - 1);
848 if (cur_si->si_m_endpos.lnum > start_lnum)
849 {
850 /* ignore match that goes to after where started */
851 current_lnum = end_lnum;
852 break;
853 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000854 if (cur_si->si_idx < 0)
855 {
856 /* Cannot happen? */
857 found_flags = 0;
858 found_match_idx = KEYWORD_IDX;
859 }
860 else
861 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200862 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000863 found_flags = spp->sp_flags;
864 found_match_idx = spp->sp_sync_idx;
865 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 found_current_lnum = current_lnum;
867 found_current_col = current_col;
868 found_m_endpos = cur_si->si_m_endpos;
869 /*
870 * Continue after the match (be aware of a zero-length
871 * match).
872 */
873 if (found_m_endpos.lnum > current_lnum)
874 {
875 current_lnum = found_m_endpos.lnum;
876 current_col = found_m_endpos.col;
877 if (current_lnum >= end_lnum)
878 break;
879 }
880 else if (found_m_endpos.col > current_col)
881 current_col = found_m_endpos.col;
882 else
883 ++current_col;
884
885 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000886 * an item that ends here, need to do that now. Be
887 * careful not to go past the NUL. */
888 prev_current_col = current_col;
889 if (syn_getcurline()[current_col] != NUL)
890 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000892 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893 }
894 else
895 break;
896 }
897 }
898
899 /*
900 * If a sync point was encountered, break here.
901 */
902 if (found_flags)
903 {
904 /*
905 * Put the item that was specified by the sync point on the
906 * state stack. If there was no item specified, make the
907 * state stack empty.
908 */
909 clear_current_state();
910 if (found_match_idx >= 0
911 && push_current_state(found_match_idx) == OK)
912 update_si_attr(current_state.ga_len - 1);
913
914 /*
915 * When using "grouphere", continue from the sync point
916 * match, until the end of the line. Parsing starts at
917 * the next line.
918 * For "groupthere" the parsing starts at start_lnum.
919 */
920 if (found_flags & HL_SYNC_HERE)
921 {
922 if (current_state.ga_len)
923 {
924 cur_si = &CUR_STATE(current_state.ga_len - 1);
925 cur_si->si_h_startpos.lnum = found_current_lnum;
926 cur_si->si_h_startpos.col = found_current_col;
927 update_si_end(cur_si, (int)current_col, TRUE);
928 check_keepend();
929 }
930 current_col = found_m_endpos.col;
931 current_lnum = found_m_endpos.lnum;
932 (void)syn_finish_line(FALSE);
933 ++current_lnum;
934 }
935 else
936 current_lnum = start_lnum;
937
938 break;
939 }
940
941 end_lnum = lnum;
942 invalidate_current_state();
943 }
944
945 /* Ran into start of the file or exceeded maximum number of lines */
946 if (lnum <= break_lnum)
947 {
948 invalidate_current_state();
949 current_lnum = break_lnum + 1;
950 }
951 }
952
953 validate_current_state();
954}
955
956/*
957 * Return TRUE if the line-continuation pattern matches in line "lnum".
958 */
959 static int
960syn_match_linecont(lnum)
961 linenr_T lnum;
962{
963 regmmatch_T regmatch;
964
Bram Moolenaar860cae12010-06-05 23:22:07 +0200965 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200967 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
968 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 return syn_regexec(&regmatch, lnum, (colnr_T)0);
970 }
971 return FALSE;
972}
973
974/*
975 * Prepare the current state for the start of a line.
976 */
977 static void
978syn_start_line()
979{
980 current_finished = FALSE;
981 current_col = 0;
982
983 /*
984 * Need to update the end of a start/skip/end that continues from the
985 * previous line and regions that have "keepend".
986 */
987 if (current_state.ga_len > 0)
988 syn_update_ends(TRUE);
989
990 next_match_idx = -1;
991 ++current_line_id;
992}
993
994/*
995 * Check for items in the stack that need their end updated.
996 * When "startofline" is TRUE the last item is always updated.
997 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
998 */
999 static void
1000syn_update_ends(startofline)
1001 int startofline;
1002{
1003 stateitem_T *cur_si;
1004 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001005 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006
1007 if (startofline)
1008 {
1009 /* Check for a match carried over from a previous line with a
1010 * contained region. The match ends as soon as the region ends. */
1011 for (i = 0; i < current_state.ga_len; ++i)
1012 {
1013 cur_si = &CUR_STATE(i);
1014 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001015 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 == SPTYPE_MATCH
1017 && cur_si->si_m_endpos.lnum < current_lnum)
1018 {
1019 cur_si->si_flags |= HL_MATCHCONT;
1020 cur_si->si_m_endpos.lnum = 0;
1021 cur_si->si_m_endpos.col = 0;
1022 cur_si->si_h_endpos = cur_si->si_m_endpos;
1023 cur_si->si_ends = TRUE;
1024 }
1025 }
1026 }
1027
1028 /*
1029 * Need to update the end of a start/skip/end that continues from the
1030 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001031 * influence contained items. If we've just removed "extend"
1032 * (startofline == 0) then we should update ends of normal regions
1033 * contained inside "keepend" because "extend" could have extended
1034 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035 * Then check for items ending in column 0.
1036 */
1037 i = current_state.ga_len - 1;
1038 if (keepend_level >= 0)
1039 for ( ; i > keepend_level; --i)
1040 if (CUR_STATE(i).si_flags & HL_EXTEND)
1041 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001042
1043 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044 for ( ; i < current_state.ga_len; ++i)
1045 {
1046 cur_si = &CUR_STATE(i);
1047 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001048 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 || (i == current_state.ga_len - 1 && startofline))
1050 {
1051 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1052 cur_si->si_h_startpos.lnum = current_lnum;
1053
1054 if (!(cur_si->si_flags & HL_MATCHCONT))
1055 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001056
1057 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1058 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059 }
1060 }
1061 check_keepend();
1062 check_state_ends();
1063}
1064
1065/****************************************
1066 * Handling of the state stack cache.
1067 */
1068
1069/*
1070 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1071 *
1072 * To speed up syntax highlighting, the state stack for the start of some
1073 * lines is cached. These entries can be used to start parsing at that point.
1074 *
1075 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1076 * valid entries. b_sst_first points to the first one, then follow sst_next.
1077 * The entries are sorted on line number. The first entry is often for line 2
1078 * (line 1 always starts with an empty stack).
1079 * There is also a list for free entries. This construction is used to avoid
1080 * having to allocate and free memory blocks too often.
1081 *
1082 * When making changes to the buffer, this is logged in b_mod_*. When calling
1083 * update_screen() to update the display, it will call
1084 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1085 * entries. The entries which are inside the changed area are removed,
1086 * because they must be recomputed. Entries below the changed have their line
1087 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1088 * set to indicate that a check must be made if the changed lines would change
1089 * the cached entry.
1090 *
1091 * When later displaying lines, an entry is stored for each line. Displayed
1092 * lines are likely to be displayed again, in which case the state at the
1093 * start of the line is needed.
1094 * For not displayed lines, an entry is stored for every so many lines. These
1095 * entries will be used e.g., when scrolling backwards. The distance between
1096 * entries depends on the number of lines in the buffer. For small buffers
1097 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1098 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1099 */
1100
Bram Moolenaar860cae12010-06-05 23:22:07 +02001101 static void
1102syn_stack_free_block(block)
1103 synblock_T *block;
1104{
1105 synstate_T *p;
1106
1107 if (block->b_sst_array != NULL)
1108 {
1109 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1110 clear_syn_state(p);
1111 vim_free(block->b_sst_array);
1112 block->b_sst_array = NULL;
1113 block->b_sst_len = 0;
1114 }
1115}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116/*
1117 * Free b_sst_array[] for buffer "buf".
1118 * Used when syntax items changed to force resyncing everywhere.
1119 */
1120 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001121syn_stack_free_all(block)
1122 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 win_T *wp;
1125
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126 syn_stack_free_block(block);
1127
1128
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129#ifdef FEAT_FOLDING
1130 /* When using "syntax" fold method, must update all folds. */
1131 FOR_ALL_WINDOWS(wp)
1132 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001133 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 foldUpdateAll(wp);
1135 }
1136#endif
1137}
1138
1139/*
1140 * Allocate the syntax state stack for syn_buf when needed.
1141 * If the number of entries in b_sst_array[] is much too big or a bit too
1142 * small, reallocate it.
1143 * Also used to allocate b_sst_array[] for the first time.
1144 */
1145 static void
1146syn_stack_alloc()
1147{
1148 long len;
1149 synstate_T *to, *from;
1150 synstate_T *sstp;
1151
1152 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1153 if (len < SST_MIN_ENTRIES)
1154 len = SST_MIN_ENTRIES;
1155 else if (len > SST_MAX_ENTRIES)
1156 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001157 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 {
1159 /* Allocate 50% too much, to avoid reallocating too often. */
1160 len = syn_buf->b_ml.ml_line_count;
1161 len = (len + len / 2) / SST_DIST + Rows * 2;
1162 if (len < SST_MIN_ENTRIES)
1163 len = SST_MIN_ENTRIES;
1164 else if (len > SST_MAX_ENTRIES)
1165 len = SST_MAX_ENTRIES;
1166
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 {
1169 /* When shrinking the array, cleanup the existing stack.
1170 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 && syn_stack_cleanup())
1173 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1175 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 }
1177
1178 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1179 if (sstp == NULL) /* out of memory! */
1180 return;
1181
1182 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001183 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 {
1185 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001186 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 from = from->sst_next)
1188 {
1189 ++to;
1190 *to = *from;
1191 to->sst_next = to + 1;
1192 }
1193 }
1194 if (to != sstp - 1)
1195 {
1196 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 syn_block->b_sst_first = sstp;
1198 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 }
1200 else
1201 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 syn_block->b_sst_first = NULL;
1203 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 }
1205
1206 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 while (++to < sstp + len)
1209 to->sst_next = to + 1;
1210 (sstp + len - 1)->sst_next = NULL;
1211
Bram Moolenaar860cae12010-06-05 23:22:07 +02001212 vim_free(syn_block->b_sst_array);
1213 syn_block->b_sst_array = sstp;
1214 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 }
1216}
1217
1218/*
1219 * Check for changes in a buffer to affect stored syntax states. Uses the
1220 * b_mod_* fields.
1221 * Called from update_screen(), before screen is being updated, once for each
1222 * displayed buffer.
1223 */
1224 void
1225syn_stack_apply_changes(buf)
1226 buf_T *buf;
1227{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 win_T *wp;
1229
1230 syn_stack_apply_changes_block(&buf->b_s, buf);
1231
1232 FOR_ALL_WINDOWS(wp)
1233 {
1234 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1235 syn_stack_apply_changes_block(wp->w_s, buf);
1236 }
1237}
1238
1239 static void
1240syn_stack_apply_changes_block(block, buf)
1241 synblock_T *block;
1242 buf_T *buf;
1243{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 synstate_T *p, *prev, *np;
1245 linenr_T n;
1246
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 return;
1249
1250 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 {
1255 n = p->sst_lnum + buf->b_mod_xlines;
1256 if (n <= buf->b_mod_bot)
1257 {
1258 /* this state is inside the changed area, remove it */
1259 np = p->sst_next;
1260 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001261 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 else
1263 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 p = np;
1266 continue;
1267 }
1268 /* This state is below the changed area. Remember the line
1269 * that needs to be parsed before this entry can be made valid
1270 * again. */
1271 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1272 {
1273 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1274 p->sst_change_lnum += buf->b_mod_xlines;
1275 else
1276 p->sst_change_lnum = buf->b_mod_top;
1277 }
1278 if (p->sst_change_lnum == 0
1279 || p->sst_change_lnum < buf->b_mod_bot)
1280 p->sst_change_lnum = buf->b_mod_bot;
1281
1282 p->sst_lnum = n;
1283 }
1284 prev = p;
1285 p = p->sst_next;
1286 }
1287}
1288
1289/*
1290 * Reduce the number of entries in the state stack for syn_buf.
1291 * Returns TRUE if at least one entry was freed.
1292 */
1293 static int
1294syn_stack_cleanup()
1295{
1296 synstate_T *p, *prev;
1297 disptick_T tick;
1298 int above;
1299 int dist;
1300 int retval = FALSE;
1301
Bram Moolenaar860cae12010-06-05 23:22:07 +02001302 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 return retval;
1304
1305 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001307 dist = 999999;
1308 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310
1311 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001312 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 * be removed. Set "above" when the "tick" for the oldest entry is above
1314 * "b_sst_lasttick" (the display tick wraps around).
1315 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001318 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1320 {
1321 if (prev->sst_lnum + dist > p->sst_lnum)
1322 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 {
1325 if (!above || p->sst_tick < tick)
1326 tick = p->sst_tick;
1327 above = TRUE;
1328 }
1329 else if (!above && p->sst_tick < tick)
1330 tick = p->sst_tick;
1331 }
1332 }
1333
1334 /*
1335 * Go through the list to make the entries for the oldest tick at an
1336 * interval of several lines.
1337 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1340 {
1341 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1342 {
1343 /* Move this entry from used list to free list */
1344 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001345 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 p = prev;
1347 retval = TRUE;
1348 }
1349 }
1350 return retval;
1351}
1352
1353/*
1354 * Free the allocated memory for a syn_state item.
1355 * Move the entry into the free list.
1356 */
1357 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358syn_stack_free_entry(block, p)
1359 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001360 synstate_T *p;
1361{
1362 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001363 p->sst_next = block->b_sst_firstfree;
1364 block->b_sst_firstfree = p;
1365 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366}
1367
1368/*
1369 * Find an entry in the list of state stacks at or before "lnum".
1370 * Returns NULL when there is no entry or the first entry is after "lnum".
1371 */
1372 static synstate_T *
1373syn_stack_find_entry(lnum)
1374 linenr_T lnum;
1375{
1376 synstate_T *p, *prev;
1377
1378 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 {
1381 if (p->sst_lnum == lnum)
1382 return p;
1383 if (p->sst_lnum > lnum)
1384 break;
1385 }
1386 return prev;
1387}
1388
1389/*
1390 * Try saving the current state in b_sst_array[].
1391 * The current state must be valid for the start of the current_lnum line!
1392 */
1393 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001394store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395{
1396 int i;
1397 synstate_T *p;
1398 bufstate_T *bp;
1399 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001400 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401
1402 /*
1403 * If the current state contains a start or end pattern that continues
1404 * from the previous line, we can't use it. Don't store it then.
1405 */
1406 for (i = current_state.ga_len - 1; i >= 0; --i)
1407 {
1408 cur_si = &CUR_STATE(i);
1409 if (cur_si->si_h_startpos.lnum >= current_lnum
1410 || cur_si->si_m_endpos.lnum >= current_lnum
1411 || cur_si->si_h_endpos.lnum >= current_lnum
1412 || (cur_si->si_end_idx
1413 && cur_si->si_eoe_pos.lnum >= current_lnum))
1414 break;
1415 }
1416 if (i >= 0)
1417 {
1418 if (sp != NULL)
1419 {
1420 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001421 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 else
1425 {
1426 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001427 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 if (p->sst_next == sp)
1429 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001430 if (p != NULL) /* just in case */
1431 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001433 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 sp = NULL;
1435 }
1436 }
1437 else if (sp == NULL || sp->sst_lnum != current_lnum)
1438 {
1439 /*
1440 * Add a new entry
1441 */
1442 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001443 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 {
1445 (void)syn_stack_cleanup();
1446 /* "sp" may have been moved to the freelist now */
1447 sp = syn_stack_find_entry(current_lnum);
1448 }
1449 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001450 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 sp = NULL;
1452 else
1453 {
1454 /* Take the first item from the free list and put it in the used
1455 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001456 p = syn_block->b_sst_firstfree;
1457 syn_block->b_sst_firstfree = p->sst_next;
1458 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 if (sp == NULL)
1460 {
1461 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001462 p->sst_next = syn_block->b_sst_first;
1463 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 }
1465 else
1466 {
1467 /* insert in list after *sp */
1468 p->sst_next = sp->sst_next;
1469 sp->sst_next = p;
1470 }
1471 sp = p;
1472 sp->sst_stacksize = 0;
1473 sp->sst_lnum = current_lnum;
1474 }
1475 }
1476 if (sp != NULL)
1477 {
1478 /* When overwriting an existing state stack, clear it first */
1479 clear_syn_state(sp);
1480 sp->sst_stacksize = current_state.ga_len;
1481 if (current_state.ga_len > SST_FIX_STATES)
1482 {
1483 /* Need to clear it, might be something remaining from when the
1484 * length was less than SST_FIX_STATES. */
1485 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1486 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1487 sp->sst_stacksize = 0;
1488 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1491 }
1492 else
1493 bp = sp->sst_union.sst_stack;
1494 for (i = 0; i < sp->sst_stacksize; ++i)
1495 {
1496 bp[i].bs_idx = CUR_STATE(i).si_idx;
1497 bp[i].bs_flags = CUR_STATE(i).si_flags;
1498 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1499 }
1500 sp->sst_next_flags = current_next_flags;
1501 sp->sst_next_list = current_next_list;
1502 sp->sst_tick = display_tick;
1503 sp->sst_change_lnum = 0;
1504 }
1505 current_state_stored = TRUE;
1506 return sp;
1507}
1508
1509/*
1510 * Copy a state stack from "from" in b_sst_array[] to current_state;
1511 */
1512 static void
1513load_current_state(from)
1514 synstate_T *from;
1515{
1516 int i;
1517 bufstate_T *bp;
1518
1519 clear_current_state();
1520 validate_current_state();
1521 keepend_level = -1;
1522 if (from->sst_stacksize
1523 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1524 {
1525 if (from->sst_stacksize > SST_FIX_STATES)
1526 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1527 else
1528 bp = from->sst_union.sst_stack;
1529 for (i = 0; i < from->sst_stacksize; ++i)
1530 {
1531 CUR_STATE(i).si_idx = bp[i].bs_idx;
1532 CUR_STATE(i).si_flags = bp[i].bs_flags;
1533 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1534 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1535 keepend_level = i;
1536 CUR_STATE(i).si_ends = FALSE;
1537 CUR_STATE(i).si_m_lnum = 0;
1538 if (CUR_STATE(i).si_idx >= 0)
1539 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001540 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 else
1542 CUR_STATE(i).si_next_list = NULL;
1543 update_si_attr(i);
1544 }
1545 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 }
1547 current_next_list = from->sst_next_list;
1548 current_next_flags = from->sst_next_flags;
1549 current_lnum = from->sst_lnum;
1550}
1551
1552/*
1553 * Compare saved state stack "*sp" with the current state.
1554 * Return TRUE when they are equal.
1555 */
1556 static int
1557syn_stack_equal(sp)
1558 synstate_T *sp;
1559{
1560 int i, j;
1561 bufstate_T *bp;
1562 reg_extmatch_T *six, *bsx;
1563
1564 /* First a quick check if the stacks have the same size end nextlist. */
1565 if (sp->sst_stacksize == current_state.ga_len
1566 && sp->sst_next_list == current_next_list)
1567 {
1568 /* Need to compare all states on both stacks. */
1569 if (sp->sst_stacksize > SST_FIX_STATES)
1570 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1571 else
1572 bp = sp->sst_union.sst_stack;
1573
1574 for (i = current_state.ga_len; --i >= 0; )
1575 {
1576 /* If the item has another index the state is different. */
1577 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1578 break;
1579 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1580 {
1581 /* When the extmatch pointers are different, the strings in
1582 * them can still be the same. Check if the extmatch
1583 * references are equal. */
1584 bsx = bp[i].bs_extmatch;
1585 six = CUR_STATE(i).si_extmatch;
1586 /* If one of the extmatch pointers is NULL the states are
1587 * different. */
1588 if (bsx == NULL || six == NULL)
1589 break;
1590 for (j = 0; j < NSUBEXP; ++j)
1591 {
1592 /* Check each referenced match string. They must all be
1593 * equal. */
1594 if (bsx->matches[j] != six->matches[j])
1595 {
1596 /* If the pointer is different it can still be the
1597 * same text. Compare the strings, ignore case when
1598 * the start item has the sp_ic flag set. */
1599 if (bsx->matches[j] == NULL
1600 || six->matches[j] == NULL)
1601 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001602 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001603 ? MB_STRICMP(bsx->matches[j],
1604 six->matches[j]) != 0
1605 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1606 break;
1607 }
1608 }
1609 if (j != NSUBEXP)
1610 break;
1611 }
1612 }
1613 if (i < 0)
1614 return TRUE;
1615 }
1616 return FALSE;
1617}
1618
1619/*
1620 * We stop parsing syntax above line "lnum". If the stored state at or below
1621 * this line depended on a change before it, it now depends on the line below
1622 * the last parsed line.
1623 * The window looks like this:
1624 * line which changed
1625 * displayed line
1626 * displayed line
1627 * lnum -> line below window
1628 */
1629 void
1630syntax_end_parsing(lnum)
1631 linenr_T lnum;
1632{
1633 synstate_T *sp;
1634
1635 sp = syn_stack_find_entry(lnum);
1636 if (sp != NULL && sp->sst_lnum < lnum)
1637 sp = sp->sst_next;
1638
1639 if (sp != NULL && sp->sst_change_lnum != 0)
1640 sp->sst_change_lnum = lnum;
1641}
1642
1643/*
1644 * End of handling of the state stack.
1645 ****************************************/
1646
1647 static void
1648invalidate_current_state()
1649{
1650 clear_current_state();
1651 current_state.ga_itemsize = 0; /* mark current_state invalid */
1652 current_next_list = NULL;
1653 keepend_level = -1;
1654}
1655
1656 static void
1657validate_current_state()
1658{
1659 current_state.ga_itemsize = sizeof(stateitem_T);
1660 current_state.ga_growsize = 3;
1661}
1662
1663/*
1664 * Return TRUE if the syntax at start of lnum changed since last time.
1665 * This will only be called just after get_syntax_attr() for the previous
1666 * line, to check if the next line needs to be redrawn too.
1667 */
1668 int
1669syntax_check_changed(lnum)
1670 linenr_T lnum;
1671{
1672 int retval = TRUE;
1673 synstate_T *sp;
1674
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675 /*
1676 * Check the state stack when:
1677 * - lnum is just below the previously syntaxed line.
1678 * - lnum is not before the lines with saved states.
1679 * - lnum is not past the lines with saved states.
1680 * - lnum is at or before the last changed line.
1681 */
1682 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1683 {
1684 sp = syn_stack_find_entry(lnum);
1685 if (sp != NULL && sp->sst_lnum == lnum)
1686 {
1687 /*
1688 * finish the previous line (needed when not all of the line was
1689 * drawn)
1690 */
1691 (void)syn_finish_line(FALSE);
1692
1693 /*
1694 * Compare the current state with the previously saved state of
1695 * the line.
1696 */
1697 if (syn_stack_equal(sp))
1698 retval = FALSE;
1699
1700 /*
1701 * Store the current state in b_sst_array[] for later use.
1702 */
1703 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001704 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705 }
1706 }
1707
Bram Moolenaar071d4272004-06-13 20:20:40 +00001708 return retval;
1709}
1710
1711/*
1712 * Finish the current line.
1713 * This doesn't return any attributes, it only gets the state at the end of
1714 * the line. It can start anywhere in the line, as long as the current state
1715 * is valid.
1716 */
1717 static int
1718syn_finish_line(syncing)
1719 int syncing; /* called for syncing */
1720{
1721 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001722 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723
1724 if (!current_finished)
1725 {
1726 while (!current_finished)
1727 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001728 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 /*
1730 * When syncing, and found some item, need to check the item.
1731 */
1732 if (syncing && current_state.ga_len)
1733 {
1734 /*
1735 * Check for match with sync item.
1736 */
1737 cur_si = &CUR_STATE(current_state.ga_len - 1);
1738 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001739 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001740 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1741 return TRUE;
1742
1743 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001744 * that ends here, need to do that now. Be careful not to go
1745 * past the NUL. */
1746 prev_current_col = current_col;
1747 if (syn_getcurline()[current_col] != NUL)
1748 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001749 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001750 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001751 }
1752 ++current_col;
1753 }
1754 }
1755 return FALSE;
1756}
1757
1758/*
1759 * Return highlight attributes for next character.
1760 * Must first call syntax_start() once for the line.
1761 * "col" is normally 0 for the first use in a line, and increments by one each
1762 * time. It's allowed to skip characters and to stop before the end of the
1763 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001764 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1765 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 */
1767 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001768get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001769 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001770 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001771 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772{
1773 int attr = 0;
1774
Bram Moolenaar349955a2007-08-14 21:07:36 +00001775 if (can_spell != NULL)
1776 /* Default: Only do spelling when there is no @Spell cluster or when
1777 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001778 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1779 ? (syn_block->b_spell_cluster_id == 0)
1780 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001781
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001783 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 return 0;
1785
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001786 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001787 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001788 {
1789 clear_current_state();
1790#ifdef FEAT_EVAL
1791 current_id = 0;
1792 current_trans_id = 0;
1793#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001794#ifdef FEAT_CONCEAL
1795 current_flags = 0;
1796#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001797 return 0;
1798 }
1799
Bram Moolenaar071d4272004-06-13 20:20:40 +00001800 /* Make sure current_state is valid */
1801 if (INVALID_STATE(&current_state))
1802 validate_current_state();
1803
1804 /*
1805 * Skip from the current column to "col", get the attributes for "col".
1806 */
1807 while (current_col <= col)
1808 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001809 attr = syn_current_attr(FALSE, TRUE, can_spell,
1810 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 ++current_col;
1812 }
1813
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 return attr;
1815}
1816
1817/*
1818 * Get syntax attributes for current_lnum, current_col.
1819 */
1820 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001821syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 int syncing; /* When 1: called for syncing */
1823 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001824 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001825 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826{
1827 int syn_id;
1828 lpos_T endpos; /* was: char_u *endp; */
1829 lpos_T hl_startpos; /* was: int hl_startcol; */
1830 lpos_T hl_endpos;
1831 lpos_T eos_pos; /* end-of-start match (start region) */
1832 lpos_T eoe_pos; /* end-of-end pattern */
1833 int end_idx; /* group ID for end pattern */
1834 int idx;
1835 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001836 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 int startcol;
1838 int endcol;
1839 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001840 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841 short *next_list;
1842 int found_match; /* found usable match */
1843 static int try_next_column = FALSE; /* must try in next col */
1844 int do_keywords;
1845 regmmatch_T regmatch;
1846 lpos_T pos;
1847 int lc_col;
1848 reg_extmatch_T *cur_extmatch = NULL;
1849 char_u *line; /* current line. NOTE: becomes invalid after
1850 looking for a pattern match! */
1851
1852 /* variables for zero-width matches that have a "nextgroup" argument */
1853 int keep_next_list;
1854 int zero_width_next_list = FALSE;
1855 garray_T zero_width_next_ga;
1856
1857 /*
1858 * No character, no attributes! Past end of line?
1859 * Do try matching with an empty line (could be the start of a region).
1860 */
1861 line = syn_getcurline();
1862 if (line[current_col] == NUL && current_col != 0)
1863 {
1864 /*
1865 * If we found a match after the last column, use it.
1866 */
1867 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1868 && next_match_col != MAXCOL)
1869 (void)push_next_match(NULL);
1870
1871 current_finished = TRUE;
1872 current_state_stored = FALSE;
1873 return 0;
1874 }
1875
1876 /* if the current or next character is NUL, we will finish the line now */
1877 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1878 {
1879 current_finished = TRUE;
1880 current_state_stored = FALSE;
1881 }
1882
1883 /*
1884 * When in the previous column there was a match but it could not be used
1885 * (empty match or already matched in this column) need to try again in
1886 * the next column.
1887 */
1888 if (try_next_column)
1889 {
1890 next_match_idx = -1;
1891 try_next_column = FALSE;
1892 }
1893
1894 /* Only check for keywords when not syncing and there are some. */
1895 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001896 && (syn_block->b_keywtab.ht_used > 0
1897 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898
1899 /* Init the list of zero-width matches with a nextlist. This is used to
1900 * avoid matching the same item in the same position twice. */
1901 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1902
1903 /*
1904 * Repeat matching keywords and patterns, to find contained items at the
1905 * same column. This stops when there are no extra matches at the current
1906 * column.
1907 */
1908 do
1909 {
1910 found_match = FALSE;
1911 keep_next_list = FALSE;
1912 syn_id = 0;
1913
1914 /*
1915 * 1. Check for a current state.
1916 * Only when there is no current state, or if the current state may
1917 * contain other things, we need to check for keywords and patterns.
1918 * Always need to check for contained items if some item has the
1919 * "containedin" argument (takes extra time!).
1920 */
1921 if (current_state.ga_len)
1922 cur_si = &CUR_STATE(current_state.ga_len - 1);
1923 else
1924 cur_si = NULL;
1925
Bram Moolenaar860cae12010-06-05 23:22:07 +02001926 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001927 || cur_si->si_cont_list != NULL)
1928 {
1929 /*
1930 * 2. Check for keywords, if on a keyword char after a non-keyword
1931 * char. Don't do this when syncing.
1932 */
1933 if (do_keywords)
1934 {
1935 line = syn_getcurline();
1936 if (vim_iswordc_buf(line + current_col, syn_buf)
1937 && (current_col == 0
1938 || !vim_iswordc_buf(line + current_col - 1
1939#ifdef FEAT_MBYTE
1940 - (has_mbyte
1941 ? (*mb_head_off)(line, line + current_col - 1)
1942 : 0)
1943#endif
1944 , syn_buf)))
1945 {
1946 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001947 &endcol, &flags, &next_list, cur_si,
1948 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001949 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001950 {
1951 if (push_current_state(KEYWORD_IDX) == OK)
1952 {
1953 cur_si = &CUR_STATE(current_state.ga_len - 1);
1954 cur_si->si_m_startcol = current_col;
1955 cur_si->si_h_startpos.lnum = current_lnum;
1956 cur_si->si_h_startpos.col = 0; /* starts right away */
1957 cur_si->si_m_endpos.lnum = current_lnum;
1958 cur_si->si_m_endpos.col = endcol;
1959 cur_si->si_h_endpos.lnum = current_lnum;
1960 cur_si->si_h_endpos.col = endcol;
1961 cur_si->si_ends = TRUE;
1962 cur_si->si_end_idx = 0;
1963 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001964#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001965 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001966 cur_si->si_char = cchar;
1967 if (current_state.ga_len > 1)
1968 cur_si->si_flags |=
1969 CUR_STATE(current_state.ga_len - 2).si_flags
1970 & HL_CONCEAL;
1971#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 cur_si->si_id = syn_id;
1973 cur_si->si_trans_id = syn_id;
1974 if (flags & HL_TRANSP)
1975 {
1976 if (current_state.ga_len < 2)
1977 {
1978 cur_si->si_attr = 0;
1979 cur_si->si_trans_id = 0;
1980 }
1981 else
1982 {
1983 cur_si->si_attr = CUR_STATE(
1984 current_state.ga_len - 2).si_attr;
1985 cur_si->si_trans_id = CUR_STATE(
1986 current_state.ga_len - 2).si_trans_id;
1987 }
1988 }
1989 else
1990 cur_si->si_attr = syn_id2attr(syn_id);
1991 cur_si->si_cont_list = NULL;
1992 cur_si->si_next_list = next_list;
1993 check_keepend();
1994 }
1995 else
1996 vim_free(next_list);
1997 }
1998 }
1999 }
2000
2001 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002002 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002003 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002004 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002005 {
2006 /*
2007 * If we didn't check for a match yet, or we are past it, check
2008 * for any match with a pattern.
2009 */
2010 if (next_match_idx < 0 || next_match_col < (int)current_col)
2011 {
2012 /*
2013 * Check all relevant patterns for a match at this
2014 * position. This is complicated, because matching with a
2015 * pattern takes quite a bit of time, thus we want to
2016 * avoid doing it when it's not needed.
2017 */
2018 next_match_idx = 0; /* no match in this line yet */
2019 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002020 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002021 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002022 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 if ( spp->sp_syncing == syncing
2024 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2025 && (spp->sp_type == SPTYPE_MATCH
2026 || spp->sp_type == SPTYPE_START)
2027 && (current_next_list != NULL
2028 ? in_id_list(NULL, current_next_list,
2029 &spp->sp_syn, 0)
2030 : (cur_si == NULL
2031 ? !(spp->sp_flags & HL_CONTAINED)
2032 : in_id_list(cur_si,
2033 cur_si->si_cont_list, &spp->sp_syn,
2034 spp->sp_flags & HL_CONTAINED))))
2035 {
2036 /* If we already tried matching in this line, and
2037 * there isn't a match before next_match_col, skip
2038 * this item. */
2039 if (spp->sp_line_id == current_line_id
2040 && spp->sp_startcol >= next_match_col)
2041 continue;
2042 spp->sp_line_id = current_line_id;
2043
2044 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2045 if (lc_col < 0)
2046 lc_col = 0;
2047
2048 regmatch.rmm_ic = spp->sp_ic;
2049 regmatch.regprog = spp->sp_prog;
2050 if (!syn_regexec(&regmatch, current_lnum,
2051 (colnr_T)lc_col))
2052 {
2053 /* no match in this line, try another one */
2054 spp->sp_startcol = MAXCOL;
2055 continue;
2056 }
2057
2058 /*
2059 * Compute the first column of the match.
2060 */
2061 syn_add_start_off(&pos, &regmatch,
2062 spp, SPO_MS_OFF, -1);
2063 if (pos.lnum > current_lnum)
2064 {
2065 /* must have used end of match in a next line,
2066 * we can't handle that */
2067 spp->sp_startcol = MAXCOL;
2068 continue;
2069 }
2070 startcol = pos.col;
2071
2072 /* remember the next column where this pattern
2073 * matches in the current line */
2074 spp->sp_startcol = startcol;
2075
2076 /*
2077 * If a previously found match starts at a lower
2078 * column number, don't use this one.
2079 */
2080 if (startcol >= next_match_col)
2081 continue;
2082
2083 /*
2084 * If we matched this pattern at this position
2085 * before, skip it. Must retry in the next
2086 * column, because it may match from there.
2087 */
2088 if (did_match_already(idx, &zero_width_next_ga))
2089 {
2090 try_next_column = TRUE;
2091 continue;
2092 }
2093
2094 endpos.lnum = regmatch.endpos[0].lnum;
2095 endpos.col = regmatch.endpos[0].col;
2096
2097 /* Compute the highlight start. */
2098 syn_add_start_off(&hl_startpos, &regmatch,
2099 spp, SPO_HS_OFF, -1);
2100
2101 /* Compute the region start. */
2102 /* Default is to use the end of the match. */
2103 syn_add_end_off(&eos_pos, &regmatch,
2104 spp, SPO_RS_OFF, 0);
2105
2106 /*
2107 * Grab the external submatches before they get
2108 * overwritten. Reference count doesn't change.
2109 */
2110 unref_extmatch(cur_extmatch);
2111 cur_extmatch = re_extmatch_out;
2112 re_extmatch_out = NULL;
2113
2114 flags = 0;
2115 eoe_pos.lnum = 0; /* avoid warning */
2116 eoe_pos.col = 0;
2117 end_idx = 0;
2118 hl_endpos.lnum = 0;
2119
2120 /*
2121 * For a "oneline" the end must be found in the
2122 * same line too. Search for it after the end of
2123 * the match with the start pattern. Set the
2124 * resulting end positions at the same time.
2125 */
2126 if (spp->sp_type == SPTYPE_START
2127 && (spp->sp_flags & HL_ONELINE))
2128 {
2129 lpos_T startpos;
2130
2131 startpos = endpos;
2132 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2133 &flags, &eoe_pos, &end_idx, cur_extmatch);
2134 if (endpos.lnum == 0)
2135 continue; /* not found */
2136 }
2137
2138 /*
2139 * For a "match" the size must be > 0 after the
2140 * end offset needs has been added. Except when
2141 * syncing.
2142 */
2143 else if (spp->sp_type == SPTYPE_MATCH)
2144 {
2145 syn_add_end_off(&hl_endpos, &regmatch, spp,
2146 SPO_HE_OFF, 0);
2147 syn_add_end_off(&endpos, &regmatch, spp,
2148 SPO_ME_OFF, 0);
2149 if (endpos.lnum == current_lnum
2150 && (int)endpos.col + syncing < startcol)
2151 {
2152 /*
2153 * If an empty string is matched, may need
2154 * to try matching again at next column.
2155 */
2156 if (regmatch.startpos[0].col
2157 == regmatch.endpos[0].col)
2158 try_next_column = TRUE;
2159 continue;
2160 }
2161 }
2162
2163 /*
2164 * keep the best match so far in next_match_*
2165 */
2166 /* Highlighting must start after startpos and end
2167 * before endpos. */
2168 if (hl_startpos.lnum == current_lnum
2169 && (int)hl_startpos.col < startcol)
2170 hl_startpos.col = startcol;
2171 limit_pos_zero(&hl_endpos, &endpos);
2172
2173 next_match_idx = idx;
2174 next_match_col = startcol;
2175 next_match_m_endpos = endpos;
2176 next_match_h_endpos = hl_endpos;
2177 next_match_h_startpos = hl_startpos;
2178 next_match_flags = flags;
2179 next_match_eos_pos = eos_pos;
2180 next_match_eoe_pos = eoe_pos;
2181 next_match_end_idx = end_idx;
2182 unref_extmatch(next_match_extmatch);
2183 next_match_extmatch = cur_extmatch;
2184 cur_extmatch = NULL;
2185 }
2186 }
2187 }
2188
2189 /*
2190 * If we found a match at the current column, use it.
2191 */
2192 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2193 {
2194 synpat_T *lspp;
2195
2196 /* When a zero-width item matched which has a nextgroup,
2197 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002198 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002199 if (next_match_m_endpos.lnum == current_lnum
2200 && next_match_m_endpos.col == current_col
2201 && lspp->sp_next_list != NULL)
2202 {
2203 current_next_list = lspp->sp_next_list;
2204 current_next_flags = lspp->sp_flags;
2205 keep_next_list = TRUE;
2206 zero_width_next_list = TRUE;
2207
2208 /* Add the index to a list, so that we can check
2209 * later that we don't match it again (and cause an
2210 * endless loop). */
2211 if (ga_grow(&zero_width_next_ga, 1) == OK)
2212 {
2213 ((int *)(zero_width_next_ga.ga_data))
2214 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002215 }
2216 next_match_idx = -1;
2217 }
2218 else
2219 cur_si = push_next_match(cur_si);
2220 found_match = TRUE;
2221 }
2222 }
2223 }
2224
2225 /*
2226 * Handle searching for nextgroup match.
2227 */
2228 if (current_next_list != NULL && !keep_next_list)
2229 {
2230 /*
2231 * If a nextgroup was not found, continue looking for one if:
2232 * - this is an empty line and the "skipempty" option was given
2233 * - we are on white space and the "skipwhite" option was given
2234 */
2235 if (!found_match)
2236 {
2237 line = syn_getcurline();
2238 if (((current_next_flags & HL_SKIPWHITE)
2239 && vim_iswhite(line[current_col]))
2240 || ((current_next_flags & HL_SKIPEMPTY)
2241 && *line == NUL))
2242 break;
2243 }
2244
2245 /*
2246 * If a nextgroup was found: Use it, and continue looking for
2247 * contained matches.
2248 * If a nextgroup was not found: Continue looking for a normal
2249 * match.
2250 * When did set current_next_list for a zero-width item and no
2251 * match was found don't loop (would get stuck).
2252 */
2253 current_next_list = NULL;
2254 next_match_idx = -1;
2255 if (!zero_width_next_list)
2256 found_match = TRUE;
2257 }
2258
2259 } while (found_match);
2260
2261 /*
2262 * Use attributes from the current state, if within its highlighting.
2263 * If not, use attributes from the current-but-one state, etc.
2264 */
2265 current_attr = 0;
2266#ifdef FEAT_EVAL
2267 current_id = 0;
2268 current_trans_id = 0;
2269#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002270#ifdef FEAT_CONCEAL
2271 current_flags = 0;
2272#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273 if (cur_si != NULL)
2274 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002275#ifndef FEAT_EVAL
2276 int current_trans_id = 0;
2277#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2279 {
2280 sip = &CUR_STATE(idx);
2281 if ((current_lnum > sip->si_h_startpos.lnum
2282 || (current_lnum == sip->si_h_startpos.lnum
2283 && current_col >= sip->si_h_startpos.col))
2284 && (sip->si_h_endpos.lnum == 0
2285 || current_lnum < sip->si_h_endpos.lnum
2286 || (current_lnum == sip->si_h_endpos.lnum
2287 && current_col < sip->si_h_endpos.col)))
2288 {
2289 current_attr = sip->si_attr;
2290#ifdef FEAT_EVAL
2291 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002293 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002294#ifdef FEAT_CONCEAL
2295 current_flags = sip->si_flags;
2296 current_sub_char = sip->si_char;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002297 current_seqnr = sip->si_seqnr;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002298#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002299 break;
2300 }
2301 }
2302
Bram Moolenaar217ad922005-03-20 22:37:15 +00002303 if (can_spell != NULL)
2304 {
2305 struct sp_syn sps;
2306
2307 /*
2308 * set "can_spell" to TRUE if spell checking is supposed to be
2309 * done in the current item.
2310 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002311 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002312 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002313 /* There is no @Spell cluster: Do spelling for items without
2314 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002315 if (syn_block->b_nospell_cluster_id == 0
2316 || current_trans_id == 0)
2317 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002318 else
2319 {
2320 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002321 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002322 sps.cont_in_list = NULL;
2323 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2324 }
2325 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002326 else
2327 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002328 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002329 * the @Spell cluster. But not when @NoSpell is also there.
2330 * At the toplevel only spell check when ":syn spell toplevel"
2331 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002332 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002333 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002334 else
2335 {
2336 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002337 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002338 sps.cont_in_list = NULL;
2339 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2340
Bram Moolenaar860cae12010-06-05 23:22:07 +02002341 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002342 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002343 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002344 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2345 *can_spell = FALSE;
2346 }
2347 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002348 }
2349 }
2350
2351
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 /*
2353 * Check for end of current state (and the states before it) at the
2354 * next column. Don't do this for syncing, because we would miss a
2355 * single character match.
2356 * First check if the current state ends at the current column. It
2357 * may be for an empty match and a containing item might end in the
2358 * current column.
2359 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002360 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002361 {
2362 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002363 if (current_state.ga_len > 0
2364 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 {
2366 ++current_col;
2367 check_state_ends();
2368 --current_col;
2369 }
2370 }
2371 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002372 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002373 /* Default: Only do spelling when there is no @Spell cluster or when
2374 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002375 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2376 ? (syn_block->b_spell_cluster_id == 0)
2377 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002379 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380 if (current_next_list != NULL
2381 && syn_getcurline()[current_col + 1] == NUL
2382 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2383 current_next_list = NULL;
2384
2385 if (zero_width_next_ga.ga_len > 0)
2386 ga_clear(&zero_width_next_ga);
2387
2388 /* No longer need external matches. But keep next_match_extmatch. */
2389 unref_extmatch(re_extmatch_out);
2390 re_extmatch_out = NULL;
2391 unref_extmatch(cur_extmatch);
2392
2393 return current_attr;
2394}
2395
2396
2397/*
2398 * Check if we already matched pattern "idx" at the current column.
2399 */
2400 static int
2401did_match_already(idx, gap)
2402 int idx;
2403 garray_T *gap;
2404{
2405 int i;
2406
2407 for (i = current_state.ga_len; --i >= 0; )
2408 if (CUR_STATE(i).si_m_startcol == (int)current_col
2409 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2410 && CUR_STATE(i).si_idx == idx)
2411 return TRUE;
2412
2413 /* Zero-width matches with a nextgroup argument are not put on the syntax
2414 * stack, and can only be matched once anyway. */
2415 for (i = gap->ga_len; --i >= 0; )
2416 if (((int *)(gap->ga_data))[i] == idx)
2417 return TRUE;
2418
2419 return FALSE;
2420}
2421
2422/*
2423 * Push the next match onto the stack.
2424 */
2425 static stateitem_T *
2426push_next_match(cur_si)
2427 stateitem_T *cur_si;
2428{
2429 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002430#ifdef FEAT_CONCEAL
2431 int save_flags;
2432#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433
Bram Moolenaar860cae12010-06-05 23:22:07 +02002434 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435
2436 /*
2437 * Push the item in current_state stack;
2438 */
2439 if (push_current_state(next_match_idx) == OK)
2440 {
2441 /*
2442 * If it's a start-skip-end type that crosses lines, figure out how
2443 * much it continues in this line. Otherwise just fill in the length.
2444 */
2445 cur_si = &CUR_STATE(current_state.ga_len - 1);
2446 cur_si->si_h_startpos = next_match_h_startpos;
2447 cur_si->si_m_startcol = current_col;
2448 cur_si->si_m_lnum = current_lnum;
2449 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002450#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002451 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002452 cur_si->si_char = spp->sp_char;
2453 if (current_state.ga_len > 1)
2454 cur_si->si_flags |=
2455 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 cur_si->si_next_list = spp->sp_next_list;
2458 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2459 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2460 {
2461 /* Try to find the end pattern in the current line */
2462 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2463 check_keepend();
2464 }
2465 else
2466 {
2467 cur_si->si_m_endpos = next_match_m_endpos;
2468 cur_si->si_h_endpos = next_match_h_endpos;
2469 cur_si->si_ends = TRUE;
2470 cur_si->si_flags |= next_match_flags;
2471 cur_si->si_eoe_pos = next_match_eoe_pos;
2472 cur_si->si_end_idx = next_match_end_idx;
2473 }
2474 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2475 keepend_level = current_state.ga_len - 1;
2476 check_keepend();
2477 update_si_attr(current_state.ga_len - 1);
2478
Bram Moolenaar860cae12010-06-05 23:22:07 +02002479#ifdef FEAT_CONCEAL
2480 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2481#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002482 /*
2483 * If the start pattern has another highlight group, push another item
2484 * on the stack for the start pattern.
2485 */
2486 if ( spp->sp_type == SPTYPE_START
2487 && spp->sp_syn_match_id != 0
2488 && push_current_state(next_match_idx) == OK)
2489 {
2490 cur_si = &CUR_STATE(current_state.ga_len - 1);
2491 cur_si->si_h_startpos = next_match_h_startpos;
2492 cur_si->si_m_startcol = current_col;
2493 cur_si->si_m_lnum = current_lnum;
2494 cur_si->si_m_endpos = next_match_eos_pos;
2495 cur_si->si_h_endpos = next_match_eos_pos;
2496 cur_si->si_ends = TRUE;
2497 cur_si->si_end_idx = 0;
2498 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002499#ifdef FEAT_CONCEAL
2500 cur_si->si_flags |= save_flags;
2501 if (cur_si->si_flags & HL_CONCEALENDS)
2502 cur_si->si_flags |= HL_CONCEAL;
2503#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002504 cur_si->si_next_list = NULL;
2505 check_keepend();
2506 update_si_attr(current_state.ga_len - 1);
2507 }
2508 }
2509
2510 next_match_idx = -1; /* try other match next time */
2511
2512 return cur_si;
2513}
2514
2515/*
2516 * Check for end of current state (and the states before it).
2517 */
2518 static void
2519check_state_ends()
2520{
2521 stateitem_T *cur_si;
2522 int had_extend = FALSE;
2523
2524 cur_si = &CUR_STATE(current_state.ga_len - 1);
2525 for (;;)
2526 {
2527 if (cur_si->si_ends
2528 && (cur_si->si_m_endpos.lnum < current_lnum
2529 || (cur_si->si_m_endpos.lnum == current_lnum
2530 && cur_si->si_m_endpos.col <= current_col)))
2531 {
2532 /*
2533 * If there is an end pattern group ID, highlight the end pattern
2534 * now. No need to pop the current item from the stack.
2535 * Only do this if the end pattern continues beyond the current
2536 * position.
2537 */
2538 if (cur_si->si_end_idx
2539 && (cur_si->si_eoe_pos.lnum > current_lnum
2540 || (cur_si->si_eoe_pos.lnum == current_lnum
2541 && cur_si->si_eoe_pos.col > current_col)))
2542 {
2543 cur_si->si_idx = cur_si->si_end_idx;
2544 cur_si->si_end_idx = 0;
2545 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2546 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2547 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002548#ifdef FEAT_CONCEAL
2549 if (cur_si->si_flags & HL_CONCEALENDS)
2550 cur_si->si_flags |= HL_CONCEAL;
2551#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002553
2554 /* what matches next may be different now, clear it */
2555 next_match_idx = 0;
2556 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002557 break;
2558 }
2559 else
2560 {
2561 /* handle next_list, unless at end of line and no "skipnl" or
2562 * "skipempty" */
2563 current_next_list = cur_si->si_next_list;
2564 current_next_flags = cur_si->si_flags;
2565 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2566 && syn_getcurline()[current_col] == NUL)
2567 current_next_list = NULL;
2568
2569 /* When the ended item has "extend", another item with
2570 * "keepend" now needs to check for its end. */
2571 if (cur_si->si_flags & HL_EXTEND)
2572 had_extend = TRUE;
2573
2574 pop_current_state();
2575
2576 if (current_state.ga_len == 0)
2577 break;
2578
Bram Moolenaar81993f42008-01-11 20:27:45 +00002579 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 {
2581 syn_update_ends(FALSE);
2582 if (current_state.ga_len == 0)
2583 break;
2584 }
2585
2586 cur_si = &CUR_STATE(current_state.ga_len - 1);
2587
2588 /*
2589 * Only for a region the search for the end continues after
2590 * the end of the contained item. If the contained match
2591 * included the end-of-line, break here, the region continues.
2592 * Don't do this when:
2593 * - "keepend" is used for the contained item
2594 * - not at the end of the line (could be end="x$"me=e-1).
2595 * - "excludenl" is used (HL_HAS_EOL won't be set)
2596 */
2597 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002598 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002599 == SPTYPE_START
2600 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2601 {
2602 update_si_end(cur_si, (int)current_col, TRUE);
2603 check_keepend();
2604 if ((current_next_flags & HL_HAS_EOL)
2605 && keepend_level < 0
2606 && syn_getcurline()[current_col] == NUL)
2607 break;
2608 }
2609 }
2610 }
2611 else
2612 break;
2613 }
2614}
2615
2616/*
2617 * Update an entry in the current_state stack for a match or region. This
2618 * fills in si_attr, si_next_list and si_cont_list.
2619 */
2620 static void
2621update_si_attr(idx)
2622 int idx;
2623{
2624 stateitem_T *sip = &CUR_STATE(idx);
2625 synpat_T *spp;
2626
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002627 /* This should not happen... */
2628 if (sip->si_idx < 0)
2629 return;
2630
Bram Moolenaar860cae12010-06-05 23:22:07 +02002631 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 if (sip->si_flags & HL_MATCH)
2633 sip->si_id = spp->sp_syn_match_id;
2634 else
2635 sip->si_id = spp->sp_syn.id;
2636 sip->si_attr = syn_id2attr(sip->si_id);
2637 sip->si_trans_id = sip->si_id;
2638 if (sip->si_flags & HL_MATCH)
2639 sip->si_cont_list = NULL;
2640 else
2641 sip->si_cont_list = spp->sp_cont_list;
2642
2643 /*
2644 * For transparent items, take attr from outer item.
2645 * Also take cont_list, if there is none.
2646 * Don't do this for the matchgroup of a start or end pattern.
2647 */
2648 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2649 {
2650 if (idx == 0)
2651 {
2652 sip->si_attr = 0;
2653 sip->si_trans_id = 0;
2654 if (sip->si_cont_list == NULL)
2655 sip->si_cont_list = ID_LIST_ALL;
2656 }
2657 else
2658 {
2659 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2660 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002661 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2662 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002663 if (sip->si_cont_list == NULL)
2664 {
2665 sip->si_flags |= HL_TRANS_CONT;
2666 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2667 }
2668 }
2669 }
2670}
2671
2672/*
2673 * Check the current stack for patterns with "keepend" flag.
2674 * Propagate the match-end to contained items, until a "skipend" item is found.
2675 */
2676 static void
2677check_keepend()
2678{
2679 int i;
2680 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002681 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682 stateitem_T *sip;
2683
2684 /*
2685 * This check can consume a lot of time; only do it from the level where
2686 * there really is a keepend.
2687 */
2688 if (keepend_level < 0)
2689 return;
2690
2691 /*
2692 * Find the last index of an "extend" item. "keepend" items before that
2693 * won't do anything. If there is no "extend" item "i" will be
2694 * "keepend_level" and all "keepend" items will work normally.
2695 */
2696 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2697 if (CUR_STATE(i).si_flags & HL_EXTEND)
2698 break;
2699
2700 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002701 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002702 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002703 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 for ( ; i < current_state.ga_len; ++i)
2705 {
2706 sip = &CUR_STATE(i);
2707 if (maxpos.lnum != 0)
2708 {
2709 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002710 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2712 sip->si_ends = TRUE;
2713 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002714 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2715 {
2716 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002717 || maxpos.lnum > sip->si_m_endpos.lnum
2718 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002719 && maxpos.col > sip->si_m_endpos.col))
2720 maxpos = sip->si_m_endpos;
2721 if (maxpos_h.lnum == 0
2722 || maxpos_h.lnum > sip->si_h_endpos.lnum
2723 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2724 && maxpos_h.col > sip->si_h_endpos.col))
2725 maxpos_h = sip->si_h_endpos;
2726 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727 }
2728}
2729
2730/*
2731 * Update an entry in the current_state stack for a start-skip-end pattern.
2732 * This finds the end of the current item, if it's in the current line.
2733 *
2734 * Return the flags for the matched END.
2735 */
2736 static void
2737update_si_end(sip, startcol, force)
2738 stateitem_T *sip;
2739 int startcol; /* where to start searching for the end */
2740 int force; /* when TRUE overrule a previous end */
2741{
2742 lpos_T startpos;
2743 lpos_T endpos;
2744 lpos_T hl_endpos;
2745 lpos_T end_endpos;
2746 int end_idx;
2747
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002748 /* return quickly for a keyword */
2749 if (sip->si_idx < 0)
2750 return;
2751
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 /* Don't update when it's already done. Can be a match of an end pattern
2753 * that started in a previous line. Watch out: can also be a "keepend"
2754 * from a containing item. */
2755 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2756 return;
2757
2758 /*
2759 * We need to find the end of the region. It may continue in the next
2760 * line.
2761 */
2762 end_idx = 0;
2763 startpos.lnum = current_lnum;
2764 startpos.col = startcol;
2765 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2766 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2767
2768 if (endpos.lnum == 0)
2769 {
2770 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002771 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 {
2773 /* a "oneline" never continues in the next line */
2774 sip->si_ends = TRUE;
2775 sip->si_m_endpos.lnum = current_lnum;
2776 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2777 }
2778 else
2779 {
2780 /* continues in the next line */
2781 sip->si_ends = FALSE;
2782 sip->si_m_endpos.lnum = 0;
2783 }
2784 sip->si_h_endpos = sip->si_m_endpos;
2785 }
2786 else
2787 {
2788 /* match within this line */
2789 sip->si_m_endpos = endpos;
2790 sip->si_h_endpos = hl_endpos;
2791 sip->si_eoe_pos = end_endpos;
2792 sip->si_ends = TRUE;
2793 sip->si_end_idx = end_idx;
2794 }
2795}
2796
2797/*
2798 * Add a new state to the current state stack.
2799 * It is cleared and the index set to "idx".
2800 * Return FAIL if it's not possible (out of memory).
2801 */
2802 static int
2803push_current_state(idx)
2804 int idx;
2805{
2806 if (ga_grow(&current_state, 1) == FAIL)
2807 return FAIL;
2808 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2809 CUR_STATE(current_state.ga_len).si_idx = idx;
2810 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002811 return OK;
2812}
2813
2814/*
2815 * Remove a state from the current_state stack.
2816 */
2817 static void
2818pop_current_state()
2819{
2820 if (current_state.ga_len)
2821 {
2822 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2823 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002824 }
2825 /* after the end of a pattern, try matching a keyword or pattern */
2826 next_match_idx = -1;
2827
2828 /* if first state with "keepend" is popped, reset keepend_level */
2829 if (keepend_level >= current_state.ga_len)
2830 keepend_level = -1;
2831}
2832
2833/*
2834 * Find the end of a start/skip/end syntax region after "startpos".
2835 * Only checks one line.
2836 * Also handles a match item that continued from a previous line.
2837 * If not found, the syntax item continues in the next line. m_endpos->lnum
2838 * will be 0.
2839 * If found, the end of the region and the end of the highlighting is
2840 * computed.
2841 */
2842 static void
2843find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2844 end_idx, start_ext)
2845 int idx; /* index of the pattern */
2846 lpos_T *startpos; /* where to start looking for an END match */
2847 lpos_T *m_endpos; /* return: end of match */
2848 lpos_T *hl_endpos; /* return: end of highlighting */
2849 long *flagsp; /* return: flags of matching END */
2850 lpos_T *end_endpos; /* return: end of end pattern match */
2851 int *end_idx; /* return: group ID for end pat. match, or 0 */
2852 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2853{
2854 colnr_T matchcol;
2855 synpat_T *spp, *spp_skip;
2856 int start_idx;
2857 int best_idx;
2858 regmmatch_T regmatch;
2859 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2860 lpos_T pos;
2861 char_u *line;
2862 int had_match = FALSE;
2863
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002864 /* just in case we are invoked for a keyword */
2865 if (idx < 0)
2866 return;
2867
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868 /*
2869 * Check for being called with a START pattern.
2870 * Can happen with a match that continues to the next line, because it
2871 * contained a region.
2872 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002873 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002874 if (spp->sp_type != SPTYPE_START)
2875 {
2876 *hl_endpos = *startpos;
2877 return;
2878 }
2879
2880 /*
2881 * Find the SKIP or first END pattern after the last START pattern.
2882 */
2883 for (;;)
2884 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002885 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 if (spp->sp_type != SPTYPE_START)
2887 break;
2888 ++idx;
2889 }
2890
2891 /*
2892 * Lookup the SKIP pattern (if present)
2893 */
2894 if (spp->sp_type == SPTYPE_SKIP)
2895 {
2896 spp_skip = spp;
2897 ++idx;
2898 }
2899 else
2900 spp_skip = NULL;
2901
2902 /* Setup external matches for syn_regexec(). */
2903 unref_extmatch(re_extmatch_in);
2904 re_extmatch_in = ref_extmatch(start_ext);
2905
2906 matchcol = startpos->col; /* start looking for a match at sstart */
2907 start_idx = idx; /* remember the first END pattern. */
2908 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2909 for (;;)
2910 {
2911 /*
2912 * Find end pattern that matches first after "matchcol".
2913 */
2914 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002915 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 {
2917 int lc_col = matchcol;
2918
Bram Moolenaar860cae12010-06-05 23:22:07 +02002919 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002920 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2921 break;
2922 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2923 if (lc_col < 0)
2924 lc_col = 0;
2925
2926 regmatch.rmm_ic = spp->sp_ic;
2927 regmatch.regprog = spp->sp_prog;
2928 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2929 {
2930 if (best_idx == -1 || regmatch.startpos[0].col
2931 < best_regmatch.startpos[0].col)
2932 {
2933 best_idx = idx;
2934 best_regmatch.startpos[0] = regmatch.startpos[0];
2935 best_regmatch.endpos[0] = regmatch.endpos[0];
2936 }
2937 }
2938 }
2939
2940 /*
2941 * If all end patterns have been tried, and there is no match, the
2942 * item continues until end-of-line.
2943 */
2944 if (best_idx == -1)
2945 break;
2946
2947 /*
2948 * If the skip pattern matches before the end pattern,
2949 * continue searching after the skip pattern.
2950 */
2951 if (spp_skip != NULL)
2952 {
2953 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2954
2955 if (lc_col < 0)
2956 lc_col = 0;
2957 regmatch.rmm_ic = spp_skip->sp_ic;
2958 regmatch.regprog = spp_skip->sp_prog;
2959 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2960 && regmatch.startpos[0].col
2961 <= best_regmatch.startpos[0].col)
2962 {
2963 /* Add offset to skip pattern match */
2964 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2965
2966 /* If the skip pattern goes on to the next line, there is no
2967 * match with an end pattern in this line. */
2968 if (pos.lnum > startpos->lnum)
2969 break;
2970
2971 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2972
2973 /* take care of an empty match or negative offset */
2974 if (pos.col <= matchcol)
2975 ++matchcol;
2976 else if (pos.col <= regmatch.endpos[0].col)
2977 matchcol = pos.col;
2978 else
2979 /* Be careful not to jump over the NUL at the end-of-line */
2980 for (matchcol = regmatch.endpos[0].col;
2981 line[matchcol] != NUL && matchcol < pos.col;
2982 ++matchcol)
2983 ;
2984
2985 /* if the skip pattern includes end-of-line, break here */
2986 if (line[matchcol] == NUL)
2987 break;
2988
2989 continue; /* start with first end pattern again */
2990 }
2991 }
2992
2993 /*
2994 * Match from start pattern to end pattern.
2995 * Correct for match and highlight offset of end pattern.
2996 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002997 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002998 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2999 /* can't end before the start */
3000 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3001 m_endpos->col = startpos->col;
3002
3003 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3004 /* can't end before the start */
3005 if (end_endpos->lnum == startpos->lnum
3006 && end_endpos->col < startpos->col)
3007 end_endpos->col = startpos->col;
3008 /* can't end after the match */
3009 limit_pos(end_endpos, m_endpos);
3010
3011 /*
3012 * If the end group is highlighted differently, adjust the pointers.
3013 */
3014 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3015 {
3016 *end_idx = best_idx;
3017 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3018 {
3019 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3020 hl_endpos->col = best_regmatch.endpos[0].col;
3021 }
3022 else
3023 {
3024 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3025 hl_endpos->col = best_regmatch.startpos[0].col;
3026 }
3027 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3028
3029 /* can't end before the start */
3030 if (hl_endpos->lnum == startpos->lnum
3031 && hl_endpos->col < startpos->col)
3032 hl_endpos->col = startpos->col;
3033 limit_pos(hl_endpos, m_endpos);
3034
3035 /* now the match ends where the highlighting ends, it is turned
3036 * into the matchgroup for the end */
3037 *m_endpos = *hl_endpos;
3038 }
3039 else
3040 {
3041 *end_idx = 0;
3042 *hl_endpos = *end_endpos;
3043 }
3044
3045 *flagsp = spp->sp_flags;
3046
3047 had_match = TRUE;
3048 break;
3049 }
3050
3051 /* no match for an END pattern in this line */
3052 if (!had_match)
3053 m_endpos->lnum = 0;
3054
3055 /* Remove external matches. */
3056 unref_extmatch(re_extmatch_in);
3057 re_extmatch_in = NULL;
3058}
3059
3060/*
3061 * Limit "pos" not to be after "limit".
3062 */
3063 static void
3064limit_pos(pos, limit)
3065 lpos_T *pos;
3066 lpos_T *limit;
3067{
3068 if (pos->lnum > limit->lnum)
3069 *pos = *limit;
3070 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3071 pos->col = limit->col;
3072}
3073
3074/*
3075 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3076 */
3077 static void
3078limit_pos_zero(pos, limit)
3079 lpos_T *pos;
3080 lpos_T *limit;
3081{
3082 if (pos->lnum == 0)
3083 *pos = *limit;
3084 else
3085 limit_pos(pos, limit);
3086}
3087
3088/*
3089 * Add offset to matched text for end of match or highlight.
3090 */
3091 static void
3092syn_add_end_off(result, regmatch, spp, idx, extra)
3093 lpos_T *result; /* returned position */
3094 regmmatch_T *regmatch; /* start/end of match */
3095 synpat_T *spp; /* matched pattern */
3096 int idx; /* index of offset */
3097 int extra; /* extra chars for offset to start */
3098{
3099 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003100 int off;
3101 char_u *base;
3102 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003103
3104 if (spp->sp_off_flags & (1 << idx))
3105 {
3106 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003107 col = regmatch->startpos[0].col;
3108 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003109 }
3110 else
3111 {
3112 result->lnum = regmatch->endpos[0].lnum;
3113 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003114 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003115 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003116 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3117 * is a matchgroup. Watch out for match with last NL in the buffer. */
3118 if (result->lnum > syn_buf->b_ml.ml_line_count)
3119 col = 0;
3120 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003121 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003122 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3123 p = base + col;
3124 if (off > 0)
3125 {
3126 while (off-- > 0 && *p != NUL)
3127 mb_ptr_adv(p);
3128 }
3129 else if (off < 0)
3130 {
3131 while (off++ < 0 && base < p)
3132 mb_ptr_back(base, p);
3133 }
3134 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003135 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003136 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003137}
3138
3139/*
3140 * Add offset to matched text for start of match or highlight.
3141 * Avoid resulting column to become negative.
3142 */
3143 static void
3144syn_add_start_off(result, regmatch, spp, idx, extra)
3145 lpos_T *result; /* returned position */
3146 regmmatch_T *regmatch; /* start/end of match */
3147 synpat_T *spp;
3148 int idx;
3149 int extra; /* extra chars for offset to end */
3150{
3151 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003152 int off;
3153 char_u *base;
3154 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003155
3156 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3157 {
3158 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003159 col = regmatch->endpos[0].col;
3160 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161 }
3162 else
3163 {
3164 result->lnum = regmatch->startpos[0].lnum;
3165 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003166 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003167 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003168 if (result->lnum > syn_buf->b_ml.ml_line_count)
3169 {
3170 /* a "\n" at the end of the pattern may take us below the last line */
3171 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003172 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003173 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003174 if (off != 0)
3175 {
3176 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3177 p = base + col;
3178 if (off > 0)
3179 {
3180 while (off-- && *p != NUL)
3181 mb_ptr_adv(p);
3182 }
3183 else if (off < 0)
3184 {
3185 while (off++ && base < p)
3186 mb_ptr_back(base, p);
3187 }
3188 col = (int)(p - base);
3189 }
3190 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003191}
3192
3193/*
3194 * Get current line in syntax buffer.
3195 */
3196 static char_u *
3197syn_getcurline()
3198{
3199 return ml_get_buf(syn_buf, current_lnum, FALSE);
3200}
3201
3202/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003203 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003204 * Returns TRUE when there is a match.
3205 */
3206 static int
3207syn_regexec(rmp, lnum, col)
3208 regmmatch_T *rmp;
3209 linenr_T lnum;
3210 colnr_T col;
3211{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003212 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003213 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003214 {
3215 rmp->startpos[0].lnum += lnum;
3216 rmp->endpos[0].lnum += lnum;
3217 return TRUE;
3218 }
3219 return FALSE;
3220}
3221
3222/*
3223 * Check one position in a line for a matching keyword.
3224 * The caller must check if a keyword can start at startcol.
3225 * Return it's ID if found, 0 otherwise.
3226 */
3227 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003228check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 char_u *line;
3230 int startcol; /* position in line to check for keyword */
3231 int *endcolp; /* return: character after found keyword */
3232 long *flagsp; /* return: flags of matching keyword */
3233 short **next_listp; /* return: next_list of matching keyword */
3234 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003235 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003237 keyentry_T *kp;
3238 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003240 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003242 hashtab_T *ht;
3243 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003244
3245 /* Find first character after the keyword. First character was already
3246 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003247 kwp = line + startcol;
3248 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003249 do
3250 {
3251#ifdef FEAT_MBYTE
3252 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003253 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 else
3255#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003256 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003258 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259
Bram Moolenaardad6b692005-01-25 22:14:34 +00003260 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261 return 0;
3262
3263 /*
3264 * Must make a copy of the keyword, so we can add a NUL and make it
3265 * lowercase.
3266 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003267 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268
3269 /*
3270 * Try twice:
3271 * 1. matching case
3272 * 2. ignoring case
3273 */
3274 for (round = 1; round <= 2; ++round)
3275 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003276 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003277 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003279 if (round == 2) /* ignore case */
3280 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003281
3282 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003283 * Find keywords that match. There can be several with different
3284 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285 * When current_next_list is non-zero accept only that group, otherwise:
3286 * Accept a not-contained keyword at toplevel.
3287 * Accept a keyword at other levels only if it is in the contains list.
3288 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003289 hi = hash_find(ht, keyword);
3290 if (!HASHITEM_EMPTY(hi))
3291 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003293 if (current_next_list != 0
3294 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3295 : (cur_si == NULL
3296 ? !(kp->flags & HL_CONTAINED)
3297 : in_id_list(cur_si, cur_si->si_cont_list,
3298 &kp->k_syn, kp->flags & HL_CONTAINED)))
3299 {
3300 *endcolp = startcol + kwlen;
3301 *flagsp = kp->flags;
3302 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003303#ifdef FEAT_CONCEAL
3304 *ccharp = kp->k_char;
3305#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003306 return kp->k_syn.id;
3307 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308 }
3309 }
3310 return 0;
3311}
3312
3313/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003314 * Handle ":syntax conceal" command.
3315 */
3316 static void
3317syn_cmd_conceal(eap, syncing)
3318 exarg_T *eap UNUSED;
3319 int syncing UNUSED;
3320{
3321#ifdef FEAT_CONCEAL
3322 char_u *arg = eap->arg;
3323 char_u *next;
3324
3325 eap->nextcmd = find_nextcmd(arg);
3326 if (eap->skip)
3327 return;
3328
3329 next = skiptowhite(arg);
3330 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3331 curwin->w_s->b_syn_conceal = TRUE;
3332 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3333 curwin->w_s->b_syn_conceal = FALSE;
3334 else
3335 EMSG2(_("E390: Illegal argument: %s"), arg);
3336#endif
3337}
3338
3339/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 * Handle ":syntax case" command.
3341 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003342 static void
3343syn_cmd_case(eap, syncing)
3344 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003345 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346{
3347 char_u *arg = eap->arg;
3348 char_u *next;
3349
3350 eap->nextcmd = find_nextcmd(arg);
3351 if (eap->skip)
3352 return;
3353
3354 next = skiptowhite(arg);
3355 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003356 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003358 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 else
3360 EMSG2(_("E390: Illegal argument: %s"), arg);
3361}
3362
3363/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003364 * Handle ":syntax spell" command.
3365 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003366 static void
3367syn_cmd_spell(eap, syncing)
3368 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003369 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003370{
3371 char_u *arg = eap->arg;
3372 char_u *next;
3373
3374 eap->nextcmd = find_nextcmd(arg);
3375 if (eap->skip)
3376 return;
3377
3378 next = skiptowhite(arg);
3379 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003380 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003381 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003382 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003383 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003384 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003385 else
3386 EMSG2(_("E390: Illegal argument: %s"), arg);
3387}
3388
3389/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390 * Clear all syntax info for one buffer.
3391 */
3392 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003393syntax_clear(block)
3394 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395{
3396 int i;
3397
Bram Moolenaar860cae12010-06-05 23:22:07 +02003398 block->b_syn_error = FALSE; /* clear previous error */
3399 block->b_syn_ic = FALSE; /* Use case, by default */
3400 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3401 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003402
3403 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003404 clear_keywtab(&block->b_keywtab);
3405 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003406
3407 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3409 syn_clear_pattern(block, i);
3410 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411
3412 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3414 syn_clear_cluster(block, i);
3415 ga_clear(&block->b_syn_clusters);
3416 block->b_spell_cluster_id = 0;
3417 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003418
Bram Moolenaar860cae12010-06-05 23:22:07 +02003419 block->b_syn_sync_flags = 0;
3420 block->b_syn_sync_minlines = 0;
3421 block->b_syn_sync_maxlines = 0;
3422 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003423
Bram Moolenaar860cae12010-06-05 23:22:07 +02003424 vim_free(block->b_syn_linecont_prog);
3425 block->b_syn_linecont_prog = NULL;
3426 vim_free(block->b_syn_linecont_pat);
3427 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003429 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430#endif
3431
3432 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003433 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003434 invalidate_current_state();
3435}
3436
3437/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003438 * Get rid of ownsyntax for window "wp".
3439 */
3440 void
3441reset_synblock(wp)
3442 win_T *wp;
3443{
3444 if (wp->w_s != &wp->w_buffer->b_s)
3445 {
3446 syntax_clear(wp->w_s);
3447 vim_free(wp->w_s);
3448 wp->w_s = &wp->w_buffer->b_s;
3449 }
3450}
3451
3452/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453 * Clear syncing info for one buffer.
3454 */
3455 static void
3456syntax_sync_clear()
3457{
3458 int i;
3459
3460 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003461 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3462 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3463 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003464
Bram Moolenaar860cae12010-06-05 23:22:07 +02003465 curwin->w_s->b_syn_sync_flags = 0;
3466 curwin->w_s->b_syn_sync_minlines = 0;
3467 curwin->w_s->b_syn_sync_maxlines = 0;
3468 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469
Bram Moolenaar860cae12010-06-05 23:22:07 +02003470 vim_free(curwin->w_s->b_syn_linecont_prog);
3471 curwin->w_s->b_syn_linecont_prog = NULL;
3472 vim_free(curwin->w_s->b_syn_linecont_pat);
3473 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476}
3477
3478/*
3479 * Remove one pattern from the buffer's pattern list.
3480 */
3481 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003482syn_remove_pattern(block, idx)
3483 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484 int idx;
3485{
3486 synpat_T *spp;
3487
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489#ifdef FEAT_FOLDING
3490 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003491 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003495 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3496 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497}
3498
3499/*
3500 * Clear and free one syntax pattern. When clearing all, must be called from
3501 * last to first!
3502 */
3503 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003504syn_clear_pattern(block, i)
3505 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003506 int i;
3507{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3509 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003513 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3514 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3515 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516 }
3517}
3518
3519/*
3520 * Clear and free one syntax cluster.
3521 */
3522 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003523syn_clear_cluster(block, i)
3524 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525 int i;
3526{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003527 vim_free(SYN_CLSTR(block)[i].scl_name);
3528 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3529 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530}
3531
3532/*
3533 * Handle ":syntax clear" command.
3534 */
3535 static void
3536syn_cmd_clear(eap, syncing)
3537 exarg_T *eap;
3538 int syncing;
3539{
3540 char_u *arg = eap->arg;
3541 char_u *arg_end;
3542 int id;
3543
3544 eap->nextcmd = find_nextcmd(arg);
3545 if (eap->skip)
3546 return;
3547
3548 /*
3549 * We have to disable this within ":syn include @group filename",
3550 * because otherwise @group would get deleted.
3551 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3552 * clear".
3553 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003554 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003555 return;
3556
3557 if (ends_excmd(*arg))
3558 {
3559 /*
3560 * No argument: Clear all syntax items.
3561 */
3562 if (syncing)
3563 syntax_sync_clear();
3564 else
3565 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003566 syntax_clear(curwin->w_s);
3567 if (curwin->w_s == &curwin->w_buffer->b_s)
3568 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003569 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570 }
3571 }
3572 else
3573 {
3574 /*
3575 * Clear the group IDs that are in the argument.
3576 */
3577 while (!ends_excmd(*arg))
3578 {
3579 arg_end = skiptowhite(arg);
3580 if (*arg == '@')
3581 {
3582 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3583 if (id == 0)
3584 {
3585 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3586 break;
3587 }
3588 else
3589 {
3590 /*
3591 * We can't physically delete a cluster without changing
3592 * the IDs of other clusters, so we do the next best thing
3593 * and make it empty.
3594 */
3595 short scl_id = id - SYNID_CLUSTER;
3596
Bram Moolenaar860cae12010-06-05 23:22:07 +02003597 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3598 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003599 }
3600 }
3601 else
3602 {
3603 id = syn_namen2id(arg, (int)(arg_end - arg));
3604 if (id == 0)
3605 {
3606 EMSG2(_(e_nogroup), arg);
3607 break;
3608 }
3609 else
3610 syn_clear_one(id, syncing);
3611 }
3612 arg = skipwhite(arg_end);
3613 }
3614 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003615 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003616 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617}
3618
3619/*
3620 * Clear one syntax group for the current buffer.
3621 */
3622 static void
3623syn_clear_one(id, syncing)
3624 int id;
3625 int syncing;
3626{
3627 synpat_T *spp;
3628 int idx;
3629
3630 /* Clear keywords only when not ":syn sync clear group-name" */
3631 if (!syncing)
3632 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003633 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3634 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635 }
3636
3637 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003640 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003641 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3642 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003644 }
3645}
3646
3647/*
3648 * Handle ":syntax on" command.
3649 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650 static void
3651syn_cmd_on(eap, syncing)
3652 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003653 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654{
3655 syn_cmd_onoff(eap, "syntax");
3656}
3657
3658/*
3659 * Handle ":syntax enable" command.
3660 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661 static void
3662syn_cmd_enable(eap, syncing)
3663 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003664 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665{
3666 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3667 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003668 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669}
3670
3671/*
3672 * Handle ":syntax reset" command.
3673 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003674 static void
3675syn_cmd_reset(eap, syncing)
3676 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003677 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678{
3679 eap->nextcmd = check_nextcmd(eap->arg);
3680 if (!eap->skip)
3681 {
3682 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3683 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003684 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685 }
3686}
3687
3688/*
3689 * Handle ":syntax manual" command.
3690 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691 static void
3692syn_cmd_manual(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 syn_cmd_onoff(eap, "manual");
3697}
3698
3699/*
3700 * Handle ":syntax off" command.
3701 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 static void
3703syn_cmd_off(eap, syncing)
3704 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003705 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706{
3707 syn_cmd_onoff(eap, "nosyntax");
3708}
3709
3710 static void
3711syn_cmd_onoff(eap, name)
3712 exarg_T *eap;
3713 char *name;
3714{
3715 char_u buf[100];
3716
3717 eap->nextcmd = check_nextcmd(eap->arg);
3718 if (!eap->skip)
3719 {
3720 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003721 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003722 do_cmdline_cmd(buf);
3723 }
3724}
3725
3726/*
3727 * Handle ":syntax [list]" command: list current syntax words.
3728 */
3729 static void
3730syn_cmd_list(eap, syncing)
3731 exarg_T *eap;
3732 int syncing; /* when TRUE: list syncing items */
3733{
3734 char_u *arg = eap->arg;
3735 int id;
3736 char_u *arg_end;
3737
3738 eap->nextcmd = find_nextcmd(arg);
3739 if (eap->skip)
3740 return;
3741
Bram Moolenaar860cae12010-06-05 23:22:07 +02003742 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743 {
3744 MSG(_("No Syntax items defined for this buffer"));
3745 return;
3746 }
3747
3748 if (syncing)
3749 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003750 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 {
3752 MSG_PUTS(_("syncing on C-style comments"));
3753 syn_lines_msg();
3754 syn_match_msg();
3755 return;
3756 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003757 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003759 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 MSG_PUTS(_("no syncing"));
3761 else
3762 {
3763 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003764 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765 MSG_PUTS(_(" lines before top line"));
3766 syn_match_msg();
3767 }
3768 return;
3769 }
3770 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003771 if (curwin->w_s->b_syn_sync_minlines > 0
3772 || curwin->w_s->b_syn_sync_maxlines > 0
3773 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774 {
3775 MSG_PUTS(_("\nsyncing on items"));
3776 syn_lines_msg();
3777 syn_match_msg();
3778 }
3779 }
3780 else
3781 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3782 if (ends_excmd(*arg))
3783 {
3784 /*
3785 * No argument: List all group IDs and all syntax clusters.
3786 */
3787 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3788 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003789 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790 syn_list_cluster(id);
3791 }
3792 else
3793 {
3794 /*
3795 * List the group IDs and syntax clusters that are in the argument.
3796 */
3797 while (!ends_excmd(*arg) && !got_int)
3798 {
3799 arg_end = skiptowhite(arg);
3800 if (*arg == '@')
3801 {
3802 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3803 if (id == 0)
3804 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3805 else
3806 syn_list_cluster(id - SYNID_CLUSTER);
3807 }
3808 else
3809 {
3810 id = syn_namen2id(arg, (int)(arg_end - arg));
3811 if (id == 0)
3812 EMSG2(_(e_nogroup), arg);
3813 else
3814 syn_list_one(id, syncing, TRUE);
3815 }
3816 arg = skipwhite(arg_end);
3817 }
3818 }
3819 eap->nextcmd = check_nextcmd(arg);
3820}
3821
3822 static void
3823syn_lines_msg()
3824{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003825 if (curwin->w_s->b_syn_sync_maxlines > 0
3826 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003827 {
3828 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003829 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830 {
3831 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003832 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3833 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003834 MSG_PUTS(", ");
3835 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003836 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 {
3838 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003839 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 }
3841 MSG_PUTS(_(" lines before top line"));
3842 }
3843}
3844
3845 static void
3846syn_match_msg()
3847{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 {
3850 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003851 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852 MSG_PUTS(_(" line breaks"));
3853 }
3854}
3855
3856static int last_matchgroup;
3857
3858struct name_list
3859{
3860 int flag;
3861 char *name;
3862};
3863
3864static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3865
3866/*
3867 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3868 */
3869 static void
3870syn_list_one(id, syncing, link_only)
3871 int id;
3872 int syncing; /* when TRUE: list syncing items */
3873 int link_only; /* when TRUE; list link-only too */
3874{
3875 int attr;
3876 int idx;
3877 int did_header = FALSE;
3878 synpat_T *spp;
3879 static struct name_list namelist1[] =
3880 {
3881 {HL_DISPLAY, "display"},
3882 {HL_CONTAINED, "contained"},
3883 {HL_ONELINE, "oneline"},
3884 {HL_KEEPEND, "keepend"},
3885 {HL_EXTEND, "extend"},
3886 {HL_EXCLUDENL, "excludenl"},
3887 {HL_TRANSP, "transparent"},
3888 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003889#ifdef FEAT_CONCEAL
3890 {HL_CONCEAL, "conceal"},
3891 {HL_CONCEALENDS, "concealends"},
3892#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003893 {0, NULL}
3894 };
3895 static struct name_list namelist2[] =
3896 {
3897 {HL_SKIPWHITE, "skipwhite"},
3898 {HL_SKIPNL, "skipnl"},
3899 {HL_SKIPEMPTY, "skipempty"},
3900 {0, NULL}
3901 };
3902
3903 attr = hl_attr(HLF_D); /* highlight like directories */
3904
3905 /* list the keywords for "id" */
3906 if (!syncing)
3907 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003908 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3909 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 did_header, attr);
3911 }
3912
3913 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003914 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003915 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003916 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3918 continue;
3919
3920 (void)syn_list_header(did_header, 999, id);
3921 did_header = TRUE;
3922 last_matchgroup = 0;
3923 if (spp->sp_type == SPTYPE_MATCH)
3924 {
3925 put_pattern("match", ' ', spp, attr);
3926 msg_putchar(' ');
3927 }
3928 else if (spp->sp_type == SPTYPE_START)
3929 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003930 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3931 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3932 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3933 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3934 while (idx < curwin->w_s->b_syn_patterns.ga_len
3935 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3936 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 --idx;
3938 msg_putchar(' ');
3939 }
3940 syn_list_flags(namelist1, spp->sp_flags, attr);
3941
3942 if (spp->sp_cont_list != NULL)
3943 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3944
3945 if (spp->sp_syn.cont_in_list != NULL)
3946 put_id_list((char_u *)"containedin",
3947 spp->sp_syn.cont_in_list, attr);
3948
3949 if (spp->sp_next_list != NULL)
3950 {
3951 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3952 syn_list_flags(namelist2, spp->sp_flags, attr);
3953 }
3954 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3955 {
3956 if (spp->sp_flags & HL_SYNC_HERE)
3957 msg_puts_attr((char_u *)"grouphere", attr);
3958 else
3959 msg_puts_attr((char_u *)"groupthere", attr);
3960 msg_putchar(' ');
3961 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003962 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3964 else
3965 MSG_PUTS("NONE");
3966 msg_putchar(' ');
3967 }
3968 }
3969
3970 /* list the link, if there is one */
3971 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3972 {
3973 (void)syn_list_header(did_header, 999, id);
3974 msg_puts_attr((char_u *)"links to", attr);
3975 msg_putchar(' ');
3976 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3977 }
3978}
3979
3980 static void
3981syn_list_flags(nl, flags, attr)
3982 struct name_list *nl;
3983 int flags;
3984 int attr;
3985{
3986 int i;
3987
3988 for (i = 0; nl[i].flag != 0; ++i)
3989 if (flags & nl[i].flag)
3990 {
3991 msg_puts_attr((char_u *)nl[i].name, attr);
3992 msg_putchar(' ');
3993 }
3994}
3995
3996/*
3997 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3998 */
3999 static void
4000syn_list_cluster(id)
4001 int id;
4002{
4003 int endcol = 15;
4004
4005 /* slight hack: roughly duplicate the guts of syn_list_header() */
4006 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004007 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004008
4009 if (msg_col >= endcol) /* output at least one space */
4010 endcol = msg_col + 1;
4011 if (Columns <= endcol) /* avoid hang for tiny window */
4012 endcol = Columns - 1;
4013
4014 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004015 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004016 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004017 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 hl_attr(HLF_D));
4019 }
4020 else
4021 {
4022 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4023 msg_puts((char_u *)"=NONE");
4024 }
4025}
4026
4027 static void
4028put_id_list(name, list, attr)
4029 char_u *name;
4030 short *list;
4031 int attr;
4032{
4033 short *p;
4034
4035 msg_puts_attr(name, attr);
4036 msg_putchar('=');
4037 for (p = list; *p; ++p)
4038 {
4039 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4040 {
4041 if (p[1])
4042 MSG_PUTS("ALLBUT");
4043 else
4044 MSG_PUTS("ALL");
4045 }
4046 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4047 {
4048 MSG_PUTS("TOP");
4049 }
4050 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4051 {
4052 MSG_PUTS("CONTAINED");
4053 }
4054 else if (*p >= SYNID_CLUSTER)
4055 {
4056 short scl_id = *p - SYNID_CLUSTER;
4057
4058 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004059 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060 }
4061 else
4062 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4063 if (p[1])
4064 msg_putchar(',');
4065 }
4066 msg_putchar(' ');
4067}
4068
4069 static void
4070put_pattern(s, c, spp, attr)
4071 char *s;
4072 int c;
4073 synpat_T *spp;
4074 int attr;
4075{
4076 long n;
4077 int mask;
4078 int first;
4079 static char *sepchars = "/+=-#@\"|'^&";
4080 int i;
4081
4082 /* May have to write "matchgroup=group" */
4083 if (last_matchgroup != spp->sp_syn_match_id)
4084 {
4085 last_matchgroup = spp->sp_syn_match_id;
4086 msg_puts_attr((char_u *)"matchgroup", attr);
4087 msg_putchar('=');
4088 if (last_matchgroup == 0)
4089 msg_outtrans((char_u *)"NONE");
4090 else
4091 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4092 msg_putchar(' ');
4093 }
4094
4095 /* output the name of the pattern and an '=' or ' ' */
4096 msg_puts_attr((char_u *)s, attr);
4097 msg_putchar(c);
4098
4099 /* output the pattern, in between a char that is not in the pattern */
4100 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4101 if (sepchars[++i] == NUL)
4102 {
4103 i = 0; /* no good char found, just use the first one */
4104 break;
4105 }
4106 msg_putchar(sepchars[i]);
4107 msg_outtrans(spp->sp_pattern);
4108 msg_putchar(sepchars[i]);
4109
4110 /* output any pattern options */
4111 first = TRUE;
4112 for (i = 0; i < SPO_COUNT; ++i)
4113 {
4114 mask = (1 << i);
4115 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4116 {
4117 if (!first)
4118 msg_putchar(','); /* separate with commas */
4119 msg_puts((char_u *)spo_name_tab[i]);
4120 n = spp->sp_offsets[i];
4121 if (i != SPO_LC_OFF)
4122 {
4123 if (spp->sp_off_flags & mask)
4124 msg_putchar('s');
4125 else
4126 msg_putchar('e');
4127 if (n > 0)
4128 msg_putchar('+');
4129 }
4130 if (n || i == SPO_LC_OFF)
4131 msg_outnum(n);
4132 first = FALSE;
4133 }
4134 }
4135 msg_putchar(' ');
4136}
4137
4138/*
4139 * List or clear the keywords for one syntax group.
4140 * Return TRUE if the header has been printed.
4141 */
4142 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004143syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004144 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004145 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 int did_header; /* header has already been printed */
4147 int attr;
4148{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004149 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004150 hashitem_T *hi;
4151 keyentry_T *kp;
4152 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 int prev_contained = 0;
4154 short *prev_next_list = NULL;
4155 short *prev_cont_in_list = NULL;
4156 int prev_skipnl = 0;
4157 int prev_skipwhite = 0;
4158 int prev_skipempty = 0;
4159
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160 /*
4161 * Unfortunately, this list of keywords is not sorted on alphabet but on
4162 * hash value...
4163 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004164 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004165 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004169 --todo;
4170 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004172 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004174 if (prev_contained != (kp->flags & HL_CONTAINED)
4175 || prev_skipnl != (kp->flags & HL_SKIPNL)
4176 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4177 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4178 || prev_cont_in_list != kp->k_syn.cont_in_list
4179 || prev_next_list != kp->next_list)
4180 outlen = 9999;
4181 else
4182 outlen = (int)STRLEN(kp->keyword);
4183 /* output "contained" and "nextgroup" on each line */
4184 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004186 prev_contained = 0;
4187 prev_next_list = NULL;
4188 prev_cont_in_list = NULL;
4189 prev_skipnl = 0;
4190 prev_skipwhite = 0;
4191 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004193 did_header = TRUE;
4194 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004196 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004197 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004198 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004200 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004202 put_id_list((char_u *)"containedin",
4203 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004205 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004207 if (kp->next_list != prev_next_list)
4208 {
4209 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4210 msg_putchar(' ');
4211 prev_next_list = kp->next_list;
4212 if (kp->flags & HL_SKIPNL)
4213 {
4214 msg_puts_attr((char_u *)"skipnl", attr);
4215 msg_putchar(' ');
4216 prev_skipnl = (kp->flags & HL_SKIPNL);
4217 }
4218 if (kp->flags & HL_SKIPWHITE)
4219 {
4220 msg_puts_attr((char_u *)"skipwhite", attr);
4221 msg_putchar(' ');
4222 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4223 }
4224 if (kp->flags & HL_SKIPEMPTY)
4225 {
4226 msg_puts_attr((char_u *)"skipempty", attr);
4227 msg_putchar(' ');
4228 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4229 }
4230 }
4231 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004232 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233 }
4234 }
4235 }
4236
4237 return did_header;
4238}
4239
4240 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004241syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004243 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004245 hashitem_T *hi;
4246 keyentry_T *kp;
4247 keyentry_T *kp_prev;
4248 keyentry_T *kp_next;
4249 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250
Bram Moolenaardad6b692005-01-25 22:14:34 +00004251 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004252 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004253 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004255 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004257 --todo;
4258 kp_prev = NULL;
4259 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004261 if (kp->k_syn.id == id)
4262 {
4263 kp_next = kp->ke_next;
4264 if (kp_prev == NULL)
4265 {
4266 if (kp_next == NULL)
4267 hash_remove(ht, hi);
4268 else
4269 hi->hi_key = KE2HIKEY(kp_next);
4270 }
4271 else
4272 kp_prev->ke_next = kp_next;
4273 vim_free(kp->next_list);
4274 vim_free(kp->k_syn.cont_in_list);
4275 vim_free(kp);
4276 kp = kp_next;
4277 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 {
4280 kp_prev = kp;
4281 kp = kp->ke_next;
4282 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283 }
4284 }
4285 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004286 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004287}
4288
4289/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004290 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 */
4292 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004293clear_keywtab(ht)
4294 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 hashitem_T *hi;
4297 int todo;
4298 keyentry_T *kp;
4299 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004301 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004302 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004304 if (!HASHITEM_EMPTY(hi))
4305 {
4306 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004309 kp_next = kp->ke_next;
4310 vim_free(kp->next_list);
4311 vim_free(kp->k_syn.cont_in_list);
4312 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004314 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004316 hash_clear(ht);
4317 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318}
4319
4320/*
4321 * Add a keyword to the list of keywords.
4322 */
4323 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004324add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325 char_u *name; /* name of keyword */
4326 int id; /* group ID for this keyword */
4327 int flags; /* flags for this keyword */
4328 short *cont_in_list; /* containedin for this keyword */
4329 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004330 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 keyentry_T *kp;
4333 hashtab_T *ht;
4334 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004335 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004337 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338
Bram Moolenaar860cae12010-06-05 23:22:07 +02004339 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004340 name_ic = str_foldcase(name, (int)STRLEN(name),
4341 name_folded, MAXKEYWLEN + 1);
4342 else
4343 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004344 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4345 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004347 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004348 kp->k_syn.id = id;
4349 kp->k_syn.inc_tag = current_syn_inc_tag;
4350 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004351 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004354 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356
Bram Moolenaar860cae12010-06-05 23:22:07 +02004357 if (curwin->w_s->b_syn_ic)
4358 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004360 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 hash = hash_hash(kp->keyword);
4363 hi = hash_lookup(ht, kp->keyword, hash);
4364 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 /* new keyword, add to hashtable */
4367 kp->ke_next = NULL;
4368 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004372 /* keyword already exists, prepend to list */
4373 kp->ke_next = HI2KE(hi);
4374 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004376}
4377
4378/*
4379 * Get the start and end of the group name argument.
4380 * Return a pointer to the first argument.
4381 * Return NULL if the end of the command was found instead of further args.
4382 */
4383 static char_u *
4384get_group_name(arg, name_end)
4385 char_u *arg; /* start of the argument */
4386 char_u **name_end; /* pointer to end of the name */
4387{
4388 char_u *rest;
4389
4390 *name_end = skiptowhite(arg);
4391 rest = skipwhite(*name_end);
4392
4393 /*
4394 * Check if there are enough arguments. The first argument may be a
4395 * pattern, where '|' is allowed, so only check for NUL.
4396 */
4397 if (ends_excmd(*arg) || *rest == NUL)
4398 return NULL;
4399 return rest;
4400}
4401
4402/*
4403 * Check for syntax command option arguments.
4404 * This can be called at any place in the list of arguments, and just picks
4405 * out the arguments that are known. Can be called several times in a row to
4406 * collect all options in between other arguments.
4407 * Return a pointer to the next argument (which isn't an option).
4408 * Return NULL for any error;
4409 */
4410 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004411get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004412 char_u *arg; /* next argument to be checked */
4413 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004414 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 char_u *gname_start, *gname;
4417 int syn_id;
4418 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004419 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 int i;
4421 int fidx;
4422 static struct flag
4423 {
4424 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004425 int argtype;
4426 int flags;
4427 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4428 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4429 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4430 {"eExXtTeEnNdD", 0, HL_EXTEND},
4431 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4432 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4433 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4434 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4435 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4436 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4437 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4438 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4439 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004440 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4441 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4442 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004443 {"cCoOnNtTaAiInNsS", 1, 0},
4444 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4445 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004447 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448
4449 if (arg == NULL) /* already detected error */
4450 return NULL;
4451
Bram Moolenaar860cae12010-06-05 23:22:07 +02004452#ifdef FEAT_CONCEAL
4453 if (curwin->w_s->b_syn_conceal)
4454 opt->flags |= HL_CONCEAL;
4455#endif
4456
Bram Moolenaar071d4272004-06-13 20:20:40 +00004457 for (;;)
4458 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004459 /*
4460 * This is used very often when a large number of keywords is defined.
4461 * Need to skip quickly when no option name is found.
4462 * Also avoid tolower(), it's slow.
4463 */
4464 if (strchr(first_letters, *arg) == NULL)
4465 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466
4467 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4468 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004469 p = flagtab[fidx].name;
4470 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4471 if (arg[len] != p[i] && arg[len] != p[i + 1])
4472 break;
4473 if (p[i] == NUL && (vim_iswhite(arg[len])
4474 || (flagtab[fidx].argtype > 0
4475 ? arg[len] == '='
4476 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004478 if (opt->keyword
4479 && (flagtab[fidx].flags == HL_DISPLAY
4480 || flagtab[fidx].flags == HL_FOLD
4481 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 /* treat "display", "fold" and "extend" as a keyword */
4483 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 break;
4485 }
4486 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004487 if (fidx < 0) /* no match found */
4488 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004489
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004490 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004492 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 {
4494 EMSG(_("E395: contains argument not accepted here"));
4495 return NULL;
4496 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498 return NULL;
4499 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004500 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004502#if 0 /* cannot happen */
4503 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 {
4505 EMSG(_("E396: containedin argument not accepted here"));
4506 return NULL;
4507 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004508#endif
4509 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 return NULL;
4511 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004512 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004514 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515 return NULL;
4516 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004517 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4518 {
4519#ifdef FEAT_MBYTE
4520 /* cchar=? */
4521 if (has_mbyte)
4522 {
4523# ifdef FEAT_CONCEAL
4524 *conceal_char = mb_ptr2char(arg + 6);
4525# endif
4526 arg += mb_ptr2len(arg + 6) - 1;
4527 }
4528 else
4529#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004530 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004531#ifdef FEAT_CONCEAL
4532 *conceal_char = arg[6];
4533#else
4534 ;
4535#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004536 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004537 arg = skipwhite(arg + 7);
4538 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004540 {
4541 opt->flags |= flagtab[fidx].flags;
4542 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004544 if (flagtab[fidx].flags == HL_SYNC_HERE
4545 || flagtab[fidx].flags == HL_SYNC_THERE)
4546 {
4547 if (opt->sync_idx == NULL)
4548 {
4549 EMSG(_("E393: group[t]here not accepted here"));
4550 return NULL;
4551 }
4552 gname_start = arg;
4553 arg = skiptowhite(arg);
4554 if (gname_start == arg)
4555 return NULL;
4556 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4557 if (gname == NULL)
4558 return NULL;
4559 if (STRCMP(gname, "NONE") == 0)
4560 *opt->sync_idx = NONE_IDX;
4561 else
4562 {
4563 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004564 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4565 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4566 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004567 {
4568 *opt->sync_idx = i;
4569 break;
4570 }
4571 if (i < 0)
4572 {
4573 EMSG2(_("E394: Didn't find region item for %s"), gname);
4574 vim_free(gname);
4575 return NULL;
4576 }
4577 }
4578
4579 vim_free(gname);
4580 arg = skipwhite(arg);
4581 }
4582#ifdef FEAT_FOLDING
4583 else if (flagtab[fidx].flags == HL_FOLD
4584 && foldmethodIsSyntax(curwin))
4585 /* Need to update folds later. */
4586 foldUpdateAll(curwin);
4587#endif
4588 }
4589 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004590
4591 return arg;
4592}
4593
4594/*
4595 * Adjustments to syntax item when declared in a ":syn include"'d file.
4596 * Set the contained flag, and if the item is not already contained, add it
4597 * to the specified top-level group, if any.
4598 */
4599 static void
4600syn_incl_toplevel(id, flagsp)
4601 int id;
4602 int *flagsp;
4603{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004604 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 return;
4606 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004607 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608 {
4609 /* We have to alloc this, because syn_combine_list() will free it. */
4610 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004611 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612
4613 if (grp_list != NULL)
4614 {
4615 grp_list[0] = id;
4616 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004617 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618 CLUSTER_ADD);
4619 }
4620 }
4621}
4622
4623/*
4624 * Handle ":syntax include [@{group-name}] filename" command.
4625 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626 static void
4627syn_cmd_include(eap, syncing)
4628 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004629 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004630{
4631 char_u *arg = eap->arg;
4632 int sgl_id = 1;
4633 char_u *group_name_end;
4634 char_u *rest;
4635 char_u *errormsg = NULL;
4636 int prev_toplvl_grp;
4637 int prev_syn_inc_tag;
4638 int source = FALSE;
4639
4640 eap->nextcmd = find_nextcmd(arg);
4641 if (eap->skip)
4642 return;
4643
4644 if (arg[0] == '@')
4645 {
4646 ++arg;
4647 rest = get_group_name(arg, &group_name_end);
4648 if (rest == NULL)
4649 {
4650 EMSG((char_u *)_("E397: Filename required"));
4651 return;
4652 }
4653 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4654 /* separate_nextcmd() and expand_filename() depend on this */
4655 eap->arg = rest;
4656 }
4657
4658 /*
4659 * Everything that's left, up to the next command, should be the
4660 * filename to include.
4661 */
4662 eap->argt |= (XFILE | NOSPC);
4663 separate_nextcmd(eap);
4664 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4665 {
4666 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4667 * file. Need to expand the file name first. In other cases
4668 * ":runtime!" is used. */
4669 source = TRUE;
4670 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4671 {
4672 if (errormsg != NULL)
4673 EMSG(errormsg);
4674 return;
4675 }
4676 }
4677
4678 /*
4679 * Save and restore the existing top-level grouplist id and ":syn
4680 * include" tag around the actual inclusion.
4681 */
4682 prev_syn_inc_tag = current_syn_inc_tag;
4683 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004684 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4685 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004686 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4687 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004689 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004690 current_syn_inc_tag = prev_syn_inc_tag;
4691}
4692
4693/*
4694 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4695 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696 static void
4697syn_cmd_keyword(eap, syncing)
4698 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004699 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700{
4701 char_u *arg = eap->arg;
4702 char_u *group_name_end;
4703 int syn_id;
4704 char_u *rest;
4705 char_u *keyword_copy;
4706 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004707 char_u *kw;
4708 syn_opt_arg_T syn_opt_arg;
4709 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004710 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004711
4712 rest = get_group_name(arg, &group_name_end);
4713
4714 if (rest != NULL)
4715 {
4716 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4717
4718 /* allocate a buffer, for removing the backslashes in the keyword */
4719 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4720 if (keyword_copy != NULL)
4721 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004722 syn_opt_arg.flags = 0;
4723 syn_opt_arg.keyword = TRUE;
4724 syn_opt_arg.sync_idx = NULL;
4725 syn_opt_arg.has_cont_list = FALSE;
4726 syn_opt_arg.cont_in_list = NULL;
4727 syn_opt_arg.next_list = NULL;
4728
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729 /*
4730 * The options given apply to ALL keywords, so all options must be
4731 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004732 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004734 cnt = 0;
4735 p = keyword_copy;
4736 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004738 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004739 if (rest == NULL || ends_excmd(*rest))
4740 break;
4741 /* Copy the keyword, removing backslashes, and add a NUL. */
4742 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004743 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004744 if (*rest == '\\' && rest[1] != NUL)
4745 ++rest;
4746 *p++ = *rest++;
4747 }
4748 *p++ = NUL;
4749 ++cnt;
4750 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004751
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004752 if (!eap->skip)
4753 {
4754 /* Adjust flags for use of ":syn include". */
4755 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4756
4757 /*
4758 * 2: Add an entry for each keyword.
4759 */
4760 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4761 {
4762 for (p = vim_strchr(kw, '['); ; )
4763 {
4764 if (p != NULL)
4765 *p = NUL;
4766 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004767 syn_opt_arg.cont_in_list,
4768 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004769 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004770 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004771 if (p[1] == NUL)
4772 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004773 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004774 kw = p + 2; /* skip over the NUL */
4775 break;
4776 }
4777 if (p[1] == ']')
4778 {
4779 kw = p + 1; /* skip over the "]" */
4780 break;
4781 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004782#ifdef FEAT_MBYTE
4783 if (has_mbyte)
4784 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004785 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004786
4787 mch_memmove(p, p + 1, l);
4788 p += l;
4789 }
4790 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004792 {
4793 p[0] = p[1];
4794 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795 }
4796 }
4797 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004798 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004799
Bram Moolenaar071d4272004-06-13 20:20:40 +00004800 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004801 vim_free(syn_opt_arg.cont_in_list);
4802 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803 }
4804 }
4805
4806 if (rest != NULL)
4807 eap->nextcmd = check_nextcmd(rest);
4808 else
4809 EMSG2(_(e_invarg2), arg);
4810
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004811 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004812 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813}
4814
4815/*
4816 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4817 *
4818 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4819 */
4820 static void
4821syn_cmd_match(eap, syncing)
4822 exarg_T *eap;
4823 int syncing; /* TRUE for ":syntax sync match .. " */
4824{
4825 char_u *arg = eap->arg;
4826 char_u *group_name_end;
4827 char_u *rest;
4828 synpat_T item; /* the item found in the line */
4829 int syn_id;
4830 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004831 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004833 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004834
4835 /* Isolate the group name, check for validity */
4836 rest = get_group_name(arg, &group_name_end);
4837
4838 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004839 syn_opt_arg.flags = 0;
4840 syn_opt_arg.keyword = FALSE;
4841 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4842 syn_opt_arg.has_cont_list = TRUE;
4843 syn_opt_arg.cont_list = NULL;
4844 syn_opt_arg.cont_in_list = NULL;
4845 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004846 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004847
4848 /* get the pattern. */
4849 init_syn_patterns();
4850 vim_memset(&item, 0, sizeof(item));
4851 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004852 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4853 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004854
4855 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004856 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857
4858 if (rest != NULL) /* all arguments are valid */
4859 {
4860 /*
4861 * Check for trailing command and illegal trailing arguments.
4862 */
4863 eap->nextcmd = check_nextcmd(rest);
4864 if (!ends_excmd(*rest) || eap->skip)
4865 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004866 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867 && (syn_id = syn_check_group(arg,
4868 (int)(group_name_end - arg))) != 0)
4869 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004870 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004871 /*
4872 * Store the pattern in the syn_items list
4873 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004874 idx = curwin->w_s->b_syn_patterns.ga_len;
4875 SYN_ITEMS(curwin->w_s)[idx] = item;
4876 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4877 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4878 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4879 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4880 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4881 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4882 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4883 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004884 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004885#ifdef FEAT_CONCEAL
4886 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4887#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004888 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004889 curwin->w_s->b_syn_containedin = TRUE;
4890 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4891 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892
4893 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004894 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004895 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004898 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899#endif
4900
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004901 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004902 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004903 return; /* don't free the progs and patterns now */
4904 }
4905 }
4906
4907 /*
4908 * Something failed, free the allocated memory.
4909 */
4910 vim_free(item.sp_prog);
4911 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004912 vim_free(syn_opt_arg.cont_list);
4913 vim_free(syn_opt_arg.cont_in_list);
4914 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915
4916 if (rest == NULL)
4917 EMSG2(_(e_invarg2), arg);
4918}
4919
4920/*
4921 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4922 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4923 */
4924 static void
4925syn_cmd_region(eap, syncing)
4926 exarg_T *eap;
4927 int syncing; /* TRUE for ":syntax sync region .." */
4928{
4929 char_u *arg = eap->arg;
4930 char_u *group_name_end;
4931 char_u *rest; /* next arg, NULL on error */
4932 char_u *key_end;
4933 char_u *key = NULL;
4934 char_u *p;
4935 int item;
4936#define ITEM_START 0
4937#define ITEM_SKIP 1
4938#define ITEM_END 2
4939#define ITEM_MATCHGROUP 3
4940 struct pat_ptr
4941 {
4942 synpat_T *pp_synp; /* pointer to syn_pattern */
4943 int pp_matchgroup_id; /* matchgroup ID */
4944 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4945 } *(pat_ptrs[3]);
4946 /* patterns found in the line */
4947 struct pat_ptr *ppp;
4948 struct pat_ptr *ppp_next;
4949 int pat_count = 0; /* nr of syn_patterns found */
4950 int syn_id;
4951 int matchgroup_id = 0;
4952 int not_enough = FALSE; /* not enough arguments */
4953 int illegal = FALSE; /* illegal arguments */
4954 int success = FALSE;
4955 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004956 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004957 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958
4959 /* Isolate the group name, check for validity */
4960 rest = get_group_name(arg, &group_name_end);
4961
4962 pat_ptrs[0] = NULL;
4963 pat_ptrs[1] = NULL;
4964 pat_ptrs[2] = NULL;
4965
4966 init_syn_patterns();
4967
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004968 syn_opt_arg.flags = 0;
4969 syn_opt_arg.keyword = FALSE;
4970 syn_opt_arg.sync_idx = NULL;
4971 syn_opt_arg.has_cont_list = TRUE;
4972 syn_opt_arg.cont_list = NULL;
4973 syn_opt_arg.cont_in_list = NULL;
4974 syn_opt_arg.next_list = NULL;
4975
Bram Moolenaar071d4272004-06-13 20:20:40 +00004976 /*
4977 * get the options, patterns and matchgroup.
4978 */
4979 while (rest != NULL && !ends_excmd(*rest))
4980 {
4981 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004982 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004983 if (rest == NULL || ends_excmd(*rest))
4984 break;
4985
4986 /* must be a pattern or matchgroup then */
4987 key_end = rest;
4988 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4989 ++key_end;
4990 vim_free(key);
4991 key = vim_strnsave_up(rest, (int)(key_end - rest));
4992 if (key == NULL) /* out of memory */
4993 {
4994 rest = NULL;
4995 break;
4996 }
4997 if (STRCMP(key, "MATCHGROUP") == 0)
4998 item = ITEM_MATCHGROUP;
4999 else if (STRCMP(key, "START") == 0)
5000 item = ITEM_START;
5001 else if (STRCMP(key, "END") == 0)
5002 item = ITEM_END;
5003 else if (STRCMP(key, "SKIP") == 0)
5004 {
5005 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5006 {
5007 illegal = TRUE;
5008 break;
5009 }
5010 item = ITEM_SKIP;
5011 }
5012 else
5013 break;
5014 rest = skipwhite(key_end);
5015 if (*rest != '=')
5016 {
5017 rest = NULL;
5018 EMSG2(_("E398: Missing '=': %s"), arg);
5019 break;
5020 }
5021 rest = skipwhite(rest + 1);
5022 if (*rest == NUL)
5023 {
5024 not_enough = TRUE;
5025 break;
5026 }
5027
5028 if (item == ITEM_MATCHGROUP)
5029 {
5030 p = skiptowhite(rest);
5031 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5032 matchgroup_id = 0;
5033 else
5034 {
5035 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5036 if (matchgroup_id == 0)
5037 {
5038 illegal = TRUE;
5039 break;
5040 }
5041 }
5042 rest = skipwhite(p);
5043 }
5044 else
5045 {
5046 /*
5047 * Allocate room for a syn_pattern, and link it in the list of
5048 * syn_patterns for this item, at the start (because the list is
5049 * used from end to start).
5050 */
5051 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5052 if (ppp == NULL)
5053 {
5054 rest = NULL;
5055 break;
5056 }
5057 ppp->pp_next = pat_ptrs[item];
5058 pat_ptrs[item] = ppp;
5059 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5060 if (ppp->pp_synp == NULL)
5061 {
5062 rest = NULL;
5063 break;
5064 }
5065
5066 /*
5067 * Get the syntax pattern and the following offset(s).
5068 */
5069 /* Enable the appropriate \z specials. */
5070 if (item == ITEM_START)
5071 reg_do_extmatch = REX_SET;
5072 else if (item == ITEM_SKIP || item == ITEM_END)
5073 reg_do_extmatch = REX_USE;
5074 rest = get_syn_pattern(rest, ppp->pp_synp);
5075 reg_do_extmatch = 0;
5076 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005077 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5079 ppp->pp_matchgroup_id = matchgroup_id;
5080 ++pat_count;
5081 }
5082 }
5083 vim_free(key);
5084 if (illegal || not_enough)
5085 rest = NULL;
5086
5087 /*
5088 * Must have a "start" and "end" pattern.
5089 */
5090 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5091 pat_ptrs[ITEM_END] == NULL))
5092 {
5093 not_enough = TRUE;
5094 rest = NULL;
5095 }
5096
5097 if (rest != NULL)
5098 {
5099 /*
5100 * Check for trailing garbage or command.
5101 * If OK, add the item.
5102 */
5103 eap->nextcmd = check_nextcmd(rest);
5104 if (!ends_excmd(*rest) || eap->skip)
5105 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005106 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107 && (syn_id = syn_check_group(arg,
5108 (int)(group_name_end - arg))) != 0)
5109 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005110 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111 /*
5112 * Store the start/skip/end in the syn_items list
5113 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005114 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 for (item = ITEM_START; item <= ITEM_END; ++item)
5116 {
5117 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5118 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005119 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5120 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5121 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005122 (item == ITEM_START) ? SPTYPE_START :
5123 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005124 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5125 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5126 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5127 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005129#ifdef FEAT_CONCEAL
5130 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5131#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132 if (item == ITEM_START)
5133 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005134 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005135 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005136 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005137 syn_opt_arg.cont_in_list;
5138 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005139 curwin->w_s->b_syn_containedin = TRUE;
5140 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005141 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005143 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005144 ++idx;
5145#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005146 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005147 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005148#endif
5149 }
5150 }
5151
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005152 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005153 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 success = TRUE; /* don't free the progs and patterns now */
5155 }
5156 }
5157
5158 /*
5159 * Free the allocated memory.
5160 */
5161 for (item = ITEM_START; item <= ITEM_END; ++item)
5162 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5163 {
5164 if (!success)
5165 {
5166 vim_free(ppp->pp_synp->sp_prog);
5167 vim_free(ppp->pp_synp->sp_pattern);
5168 }
5169 vim_free(ppp->pp_synp);
5170 ppp_next = ppp->pp_next;
5171 vim_free(ppp);
5172 }
5173
5174 if (!success)
5175 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005176 vim_free(syn_opt_arg.cont_list);
5177 vim_free(syn_opt_arg.cont_in_list);
5178 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 if (not_enough)
5180 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5181 else if (illegal || rest == NULL)
5182 EMSG2(_(e_invarg2), arg);
5183 }
5184}
5185
5186/*
5187 * A simple syntax group ID comparison function suitable for use in qsort()
5188 */
5189 static int
5190#ifdef __BORLANDC__
5191_RTLENTRYF
5192#endif
5193syn_compare_stub(v1, v2)
5194 const void *v1;
5195 const void *v2;
5196{
5197 const short *s1 = v1;
5198 const short *s2 = v2;
5199
5200 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5201}
5202
5203/*
5204 * Combines lists of syntax clusters.
5205 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5206 */
5207 static void
5208syn_combine_list(clstr1, clstr2, list_op)
5209 short **clstr1;
5210 short **clstr2;
5211 int list_op;
5212{
5213 int count1 = 0;
5214 int count2 = 0;
5215 short *g1;
5216 short *g2;
5217 short *clstr = NULL;
5218 int count;
5219 int round;
5220
5221 /*
5222 * Handle degenerate cases.
5223 */
5224 if (*clstr2 == NULL)
5225 return;
5226 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5227 {
5228 if (list_op == CLUSTER_REPLACE)
5229 vim_free(*clstr1);
5230 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5231 *clstr1 = *clstr2;
5232 else
5233 vim_free(*clstr2);
5234 return;
5235 }
5236
5237 for (g1 = *clstr1; *g1; g1++)
5238 ++count1;
5239 for (g2 = *clstr2; *g2; g2++)
5240 ++count2;
5241
5242 /*
5243 * For speed purposes, sort both lists.
5244 */
5245 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5246 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5247
5248 /*
5249 * We proceed in two passes; in round 1, we count the elements to place
5250 * in the new list, and in round 2, we allocate and populate the new
5251 * list. For speed, we use a mergesort-like method, adding the smaller
5252 * of the current elements in each list to the new list.
5253 */
5254 for (round = 1; round <= 2; round++)
5255 {
5256 g1 = *clstr1;
5257 g2 = *clstr2;
5258 count = 0;
5259
5260 /*
5261 * First, loop through the lists until one of them is empty.
5262 */
5263 while (*g1 && *g2)
5264 {
5265 /*
5266 * We always want to add from the first list.
5267 */
5268 if (*g1 < *g2)
5269 {
5270 if (round == 2)
5271 clstr[count] = *g1;
5272 count++;
5273 g1++;
5274 continue;
5275 }
5276 /*
5277 * We only want to add from the second list if we're adding the
5278 * lists.
5279 */
5280 if (list_op == CLUSTER_ADD)
5281 {
5282 if (round == 2)
5283 clstr[count] = *g2;
5284 count++;
5285 }
5286 if (*g1 == *g2)
5287 g1++;
5288 g2++;
5289 }
5290
5291 /*
5292 * Now add the leftovers from whichever list didn't get finished
5293 * first. As before, we only want to add from the second list if
5294 * we're adding the lists.
5295 */
5296 for (; *g1; g1++, count++)
5297 if (round == 2)
5298 clstr[count] = *g1;
5299 if (list_op == CLUSTER_ADD)
5300 for (; *g2; g2++, count++)
5301 if (round == 2)
5302 clstr[count] = *g2;
5303
5304 if (round == 1)
5305 {
5306 /*
5307 * If the group ended up empty, we don't need to allocate any
5308 * space for it.
5309 */
5310 if (count == 0)
5311 {
5312 clstr = NULL;
5313 break;
5314 }
5315 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5316 if (clstr == NULL)
5317 break;
5318 clstr[count] = 0;
5319 }
5320 }
5321
5322 /*
5323 * Finally, put the new list in place.
5324 */
5325 vim_free(*clstr1);
5326 vim_free(*clstr2);
5327 *clstr1 = clstr;
5328}
5329
5330/*
5331 * Lookup a syntax cluster name and return it's ID.
5332 * If it is not found, 0 is returned.
5333 */
5334 static int
5335syn_scl_name2id(name)
5336 char_u *name;
5337{
5338 int i;
5339 char_u *name_u;
5340
5341 /* Avoid using stricmp() too much, it's slow on some systems */
5342 name_u = vim_strsave_up(name);
5343 if (name_u == NULL)
5344 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005345 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5346 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5347 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005348 break;
5349 vim_free(name_u);
5350 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5351}
5352
5353/*
5354 * Like syn_scl_name2id(), but take a pointer + length argument.
5355 */
5356 static int
5357syn_scl_namen2id(linep, len)
5358 char_u *linep;
5359 int len;
5360{
5361 char_u *name;
5362 int id = 0;
5363
5364 name = vim_strnsave(linep, len);
5365 if (name != NULL)
5366 {
5367 id = syn_scl_name2id(name);
5368 vim_free(name);
5369 }
5370 return id;
5371}
5372
5373/*
5374 * Find syntax cluster name in the table and return it's ID.
5375 * The argument is a pointer to the name and the length of the name.
5376 * If it doesn't exist yet, a new entry is created.
5377 * Return 0 for failure.
5378 */
5379 static int
5380syn_check_cluster(pp, len)
5381 char_u *pp;
5382 int len;
5383{
5384 int id;
5385 char_u *name;
5386
5387 name = vim_strnsave(pp, len);
5388 if (name == NULL)
5389 return 0;
5390
5391 id = syn_scl_name2id(name);
5392 if (id == 0) /* doesn't exist yet */
5393 id = syn_add_cluster(name);
5394 else
5395 vim_free(name);
5396 return id;
5397}
5398
5399/*
5400 * Add new syntax cluster and return it's ID.
5401 * "name" must be an allocated string, it will be consumed.
5402 * Return 0 for failure.
5403 */
5404 static int
5405syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005406 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005408 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005409
5410 /*
5411 * First call for this growarray: init growing array.
5412 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005413 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005414 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005415 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5416 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417 }
5418
5419 /*
5420 * Make room for at least one other cluster entry.
5421 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005422 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005423 {
5424 vim_free(name);
5425 return 0;
5426 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005427 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005428
Bram Moolenaar860cae12010-06-05 23:22:07 +02005429 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5430 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5431 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5432 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5433 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005434
Bram Moolenaar217ad922005-03-20 22:37:15 +00005435 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005436 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005437 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005438 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005439
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 return len + SYNID_CLUSTER;
5441}
5442
5443/*
5444 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5445 * [add={groupname},..] [remove={groupname},..]".
5446 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005447 static void
5448syn_cmd_cluster(eap, syncing)
5449 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005450 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005451{
5452 char_u *arg = eap->arg;
5453 char_u *group_name_end;
5454 char_u *rest;
5455 int scl_id;
5456 short *clstr_list;
5457 int got_clstr = FALSE;
5458 int opt_len;
5459 int list_op;
5460
5461 eap->nextcmd = find_nextcmd(arg);
5462 if (eap->skip)
5463 return;
5464
5465 rest = get_group_name(arg, &group_name_end);
5466
5467 if (rest != NULL)
5468 {
5469 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005470 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005471
5472 for (;;)
5473 {
5474 if (STRNICMP(rest, "add", 3) == 0
5475 && (vim_iswhite(rest[3]) || rest[3] == '='))
5476 {
5477 opt_len = 3;
5478 list_op = CLUSTER_ADD;
5479 }
5480 else if (STRNICMP(rest, "remove", 6) == 0
5481 && (vim_iswhite(rest[6]) || rest[6] == '='))
5482 {
5483 opt_len = 6;
5484 list_op = CLUSTER_SUBTRACT;
5485 }
5486 else if (STRNICMP(rest, "contains", 8) == 0
5487 && (vim_iswhite(rest[8]) || rest[8] == '='))
5488 {
5489 opt_len = 8;
5490 list_op = CLUSTER_REPLACE;
5491 }
5492 else
5493 break;
5494
5495 clstr_list = NULL;
5496 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5497 {
5498 EMSG2(_(e_invarg2), rest);
5499 break;
5500 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005501 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502 &clstr_list, list_op);
5503 got_clstr = TRUE;
5504 }
5505
5506 if (got_clstr)
5507 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005508 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005509 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005510 }
5511 }
5512
5513 if (!got_clstr)
5514 EMSG(_("E400: No cluster specified"));
5515 if (rest == NULL || !ends_excmd(*rest))
5516 EMSG2(_(e_invarg2), arg);
5517}
5518
5519/*
5520 * On first call for current buffer: Init growing array.
5521 */
5522 static void
5523init_syn_patterns()
5524{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005525 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5526 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005527}
5528
5529/*
5530 * Get one pattern for a ":syntax match" or ":syntax region" command.
5531 * Stores the pattern and program in a synpat_T.
5532 * Returns a pointer to the next argument, or NULL in case of an error.
5533 */
5534 static char_u *
5535get_syn_pattern(arg, ci)
5536 char_u *arg;
5537 synpat_T *ci;
5538{
5539 char_u *end;
5540 int *p;
5541 int idx;
5542 char_u *cpo_save;
5543
5544 /* need at least three chars */
5545 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5546 return NULL;
5547
5548 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5549 if (*end != *arg) /* end delimiter not found */
5550 {
5551 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5552 return NULL;
5553 }
5554 /* store the pattern and compiled regexp program */
5555 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5556 return NULL;
5557
5558 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5559 cpo_save = p_cpo;
5560 p_cpo = (char_u *)"";
5561 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5562 p_cpo = cpo_save;
5563
5564 if (ci->sp_prog == NULL)
5565 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005566 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005567
5568 /*
5569 * Check for a match, highlight or region offset.
5570 */
5571 ++end;
5572 do
5573 {
5574 for (idx = SPO_COUNT; --idx >= 0; )
5575 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5576 break;
5577 if (idx >= 0)
5578 {
5579 p = &(ci->sp_offsets[idx]);
5580 if (idx != SPO_LC_OFF)
5581 switch (end[3])
5582 {
5583 case 's': break;
5584 case 'b': break;
5585 case 'e': idx += SPO_COUNT; break;
5586 default: idx = -1; break;
5587 }
5588 if (idx >= 0)
5589 {
5590 ci->sp_off_flags |= (1 << idx);
5591 if (idx == SPO_LC_OFF) /* lc=99 */
5592 {
5593 end += 3;
5594 *p = getdigits(&end);
5595
5596 /* "lc=" offset automatically sets "ms=" offset */
5597 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5598 {
5599 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5600 ci->sp_offsets[SPO_MS_OFF] = *p;
5601 }
5602 }
5603 else /* yy=x+99 */
5604 {
5605 end += 4;
5606 if (*end == '+')
5607 {
5608 ++end;
5609 *p = getdigits(&end); /* positive offset */
5610 }
5611 else if (*end == '-')
5612 {
5613 ++end;
5614 *p = -getdigits(&end); /* negative offset */
5615 }
5616 }
5617 if (*end != ',')
5618 break;
5619 ++end;
5620 }
5621 }
5622 } while (idx >= 0);
5623
5624 if (!ends_excmd(*end) && !vim_iswhite(*end))
5625 {
5626 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5627 return NULL;
5628 }
5629 return skipwhite(end);
5630}
5631
5632/*
5633 * Handle ":syntax sync .." command.
5634 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005635 static void
5636syn_cmd_sync(eap, syncing)
5637 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005638 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005639{
5640 char_u *arg_start = eap->arg;
5641 char_u *arg_end;
5642 char_u *key = NULL;
5643 char_u *next_arg;
5644 int illegal = FALSE;
5645 int finished = FALSE;
5646 long n;
5647 char_u *cpo_save;
5648
5649 if (ends_excmd(*arg_start))
5650 {
5651 syn_cmd_list(eap, TRUE);
5652 return;
5653 }
5654
5655 while (!ends_excmd(*arg_start))
5656 {
5657 arg_end = skiptowhite(arg_start);
5658 next_arg = skipwhite(arg_end);
5659 vim_free(key);
5660 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5661 if (STRCMP(key, "CCOMMENT") == 0)
5662 {
5663 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005664 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 if (!ends_excmd(*next_arg))
5666 {
5667 arg_end = skiptowhite(next_arg);
5668 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005669 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670 (int)(arg_end - next_arg));
5671 next_arg = skipwhite(arg_end);
5672 }
5673 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005674 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 }
5676 else if ( STRNCMP(key, "LINES", 5) == 0
5677 || STRNCMP(key, "MINLINES", 8) == 0
5678 || STRNCMP(key, "MAXLINES", 8) == 0
5679 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5680 {
5681 if (key[4] == 'S')
5682 arg_end = key + 6;
5683 else if (key[0] == 'L')
5684 arg_end = key + 11;
5685 else
5686 arg_end = key + 9;
5687 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5688 {
5689 illegal = TRUE;
5690 break;
5691 }
5692 n = getdigits(&arg_end);
5693 if (!eap->skip)
5694 {
5695 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005696 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005697 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005698 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005699 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005700 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005701 }
5702 }
5703 else if (STRCMP(key, "FROMSTART") == 0)
5704 {
5705 if (!eap->skip)
5706 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005707 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5708 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709 }
5710 }
5711 else if (STRCMP(key, "LINECONT") == 0)
5712 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005713 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 {
5715 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5716 finished = TRUE;
5717 break;
5718 }
5719 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5720 if (*arg_end != *next_arg) /* end delimiter not found */
5721 {
5722 illegal = TRUE;
5723 break;
5724 }
5725
5726 if (!eap->skip)
5727 {
5728 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005729 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005730 (int)(arg_end - next_arg - 1))) == NULL)
5731 {
5732 finished = TRUE;
5733 break;
5734 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005735 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736
5737 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5738 cpo_save = p_cpo;
5739 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005740 curwin->w_s->b_syn_linecont_prog =
5741 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005742 p_cpo = cpo_save;
5743
Bram Moolenaar860cae12010-06-05 23:22:07 +02005744 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005745 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005746 vim_free(curwin->w_s->b_syn_linecont_pat);
5747 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 finished = TRUE;
5749 break;
5750 }
5751 }
5752 next_arg = skipwhite(arg_end + 1);
5753 }
5754 else
5755 {
5756 eap->arg = next_arg;
5757 if (STRCMP(key, "MATCH") == 0)
5758 syn_cmd_match(eap, TRUE);
5759 else if (STRCMP(key, "REGION") == 0)
5760 syn_cmd_region(eap, TRUE);
5761 else if (STRCMP(key, "CLEAR") == 0)
5762 syn_cmd_clear(eap, TRUE);
5763 else
5764 illegal = TRUE;
5765 finished = TRUE;
5766 break;
5767 }
5768 arg_start = next_arg;
5769 }
5770 vim_free(key);
5771 if (illegal)
5772 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5773 else if (!finished)
5774 {
5775 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005776 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005777 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005778 }
5779}
5780
5781/*
5782 * Convert a line of highlight group names into a list of group ID numbers.
5783 * "arg" should point to the "contains" or "nextgroup" keyword.
5784 * "arg" is advanced to after the last group name.
5785 * Careful: the argument is modified (NULs added).
5786 * returns FAIL for some error, OK for success.
5787 */
5788 static int
5789get_id_list(arg, keylen, list)
5790 char_u **arg;
5791 int keylen; /* length of keyword */
5792 short **list; /* where to store the resulting list, if not
5793 NULL, the list is silently skipped! */
5794{
5795 char_u *p = NULL;
5796 char_u *end;
5797 int round;
5798 int count;
5799 int total_count = 0;
5800 short *retval = NULL;
5801 char_u *name;
5802 regmatch_T regmatch;
5803 int id;
5804 int i;
5805 int failed = FALSE;
5806
5807 /*
5808 * We parse the list twice:
5809 * round == 1: count the number of items, allocate the array.
5810 * round == 2: fill the array with the items.
5811 * In round 1 new groups may be added, causing the number of items to
5812 * grow when a regexp is used. In that case round 1 is done once again.
5813 */
5814 for (round = 1; round <= 2; ++round)
5815 {
5816 /*
5817 * skip "contains"
5818 */
5819 p = skipwhite(*arg + keylen);
5820 if (*p != '=')
5821 {
5822 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5823 break;
5824 }
5825 p = skipwhite(p + 1);
5826 if (ends_excmd(*p))
5827 {
5828 EMSG2(_("E406: Empty argument: %s"), *arg);
5829 break;
5830 }
5831
5832 /*
5833 * parse the arguments after "contains"
5834 */
5835 count = 0;
5836 while (!ends_excmd(*p))
5837 {
5838 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5839 ;
5840 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5841 if (name == NULL)
5842 {
5843 failed = TRUE;
5844 break;
5845 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005846 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005847 if ( STRCMP(name + 1, "ALLBUT") == 0
5848 || STRCMP(name + 1, "ALL") == 0
5849 || STRCMP(name + 1, "TOP") == 0
5850 || STRCMP(name + 1, "CONTAINED") == 0)
5851 {
5852 if (TOUPPER_ASC(**arg) != 'C')
5853 {
5854 EMSG2(_("E407: %s not allowed here"), name + 1);
5855 failed = TRUE;
5856 vim_free(name);
5857 break;
5858 }
5859 if (count != 0)
5860 {
5861 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5862 failed = TRUE;
5863 vim_free(name);
5864 break;
5865 }
5866 if (name[1] == 'A')
5867 id = SYNID_ALLBUT;
5868 else if (name[1] == 'T')
5869 id = SYNID_TOP;
5870 else
5871 id = SYNID_CONTAINED;
5872 id += current_syn_inc_tag;
5873 }
5874 else if (name[1] == '@')
5875 {
5876 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5877 }
5878 else
5879 {
5880 /*
5881 * Handle full group name.
5882 */
5883 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5884 id = syn_check_group(name + 1, (int)(end - p));
5885 else
5886 {
5887 /*
5888 * Handle match of regexp with group names.
5889 */
5890 *name = '^';
5891 STRCAT(name, "$");
5892 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5893 if (regmatch.regprog == NULL)
5894 {
5895 failed = TRUE;
5896 vim_free(name);
5897 break;
5898 }
5899
5900 regmatch.rm_ic = TRUE;
5901 id = 0;
5902 for (i = highlight_ga.ga_len; --i >= 0; )
5903 {
5904 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5905 (colnr_T)0))
5906 {
5907 if (round == 2)
5908 {
5909 /* Got more items than expected; can happen
5910 * when adding items that match:
5911 * "contains=a.*b,axb".
5912 * Go back to first round */
5913 if (count >= total_count)
5914 {
5915 vim_free(retval);
5916 round = 1;
5917 }
5918 else
5919 retval[count] = i + 1;
5920 }
5921 ++count;
5922 id = -1; /* remember that we found one */
5923 }
5924 }
5925 vim_free(regmatch.regprog);
5926 }
5927 }
5928 vim_free(name);
5929 if (id == 0)
5930 {
5931 EMSG2(_("E409: Unknown group name: %s"), p);
5932 failed = TRUE;
5933 break;
5934 }
5935 if (id > 0)
5936 {
5937 if (round == 2)
5938 {
5939 /* Got more items than expected, go back to first round */
5940 if (count >= total_count)
5941 {
5942 vim_free(retval);
5943 round = 1;
5944 }
5945 else
5946 retval[count] = id;
5947 }
5948 ++count;
5949 }
5950 p = skipwhite(end);
5951 if (*p != ',')
5952 break;
5953 p = skipwhite(p + 1); /* skip comma in between arguments */
5954 }
5955 if (failed)
5956 break;
5957 if (round == 1)
5958 {
5959 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5960 if (retval == NULL)
5961 break;
5962 retval[count] = 0; /* zero means end of the list */
5963 total_count = count;
5964 }
5965 }
5966
5967 *arg = p;
5968 if (failed || retval == NULL)
5969 {
5970 vim_free(retval);
5971 return FAIL;
5972 }
5973
5974 if (*list == NULL)
5975 *list = retval;
5976 else
5977 vim_free(retval); /* list already found, don't overwrite it */
5978
5979 return OK;
5980}
5981
5982/*
5983 * Make a copy of an ID list.
5984 */
5985 static short *
5986copy_id_list(list)
5987 short *list;
5988{
5989 int len;
5990 int count;
5991 short *retval;
5992
5993 if (list == NULL)
5994 return NULL;
5995
5996 for (count = 0; list[count]; ++count)
5997 ;
5998 len = (count + 1) * sizeof(short);
5999 retval = (short *)alloc((unsigned)len);
6000 if (retval != NULL)
6001 mch_memmove(retval, list, (size_t)len);
6002
6003 return retval;
6004}
6005
6006/*
6007 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6008 * "cur_si" can be NULL if not checking the "containedin" list.
6009 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6010 * the current item.
6011 * This function is called very often, keep it fast!!
6012 */
6013 static int
6014in_id_list(cur_si, list, ssp, contained)
6015 stateitem_T *cur_si; /* current item or NULL */
6016 short *list; /* id list */
6017 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6018 int contained; /* group id is contained */
6019{
6020 int retval;
6021 short *scl_list;
6022 short item;
6023 short id = ssp->id;
6024 static int depth = 0;
6025 int r;
6026
6027 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006028 if (cur_si != NULL && ssp->cont_in_list != NULL
6029 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006030 {
6031 /* Ignore transparent items without a contains argument. Double check
6032 * that we don't go back past the first one. */
6033 while ((cur_si->si_flags & HL_TRANS_CONT)
6034 && cur_si > (stateitem_T *)(current_state.ga_data))
6035 --cur_si;
6036 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6037 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006038 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6039 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006040 return TRUE;
6041 }
6042
6043 if (list == NULL)
6044 return FALSE;
6045
6046 /*
6047 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6048 * inside anything. Only allow not-contained groups.
6049 */
6050 if (list == ID_LIST_ALL)
6051 return !contained;
6052
6053 /*
6054 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6055 * contains list. We also require that "id" is at the same ":syn include"
6056 * level as the list.
6057 */
6058 item = *list;
6059 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6060 {
6061 if (item < SYNID_TOP)
6062 {
6063 /* ALL or ALLBUT: accept all groups in the same file */
6064 if (item - SYNID_ALLBUT != ssp->inc_tag)
6065 return FALSE;
6066 }
6067 else if (item < SYNID_CONTAINED)
6068 {
6069 /* TOP: accept all not-contained groups in the same file */
6070 if (item - SYNID_TOP != ssp->inc_tag || contained)
6071 return FALSE;
6072 }
6073 else
6074 {
6075 /* CONTAINED: accept all contained groups in the same file */
6076 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6077 return FALSE;
6078 }
6079 item = *++list;
6080 retval = FALSE;
6081 }
6082 else
6083 retval = TRUE;
6084
6085 /*
6086 * Return "retval" if id is in the contains list.
6087 */
6088 while (item != 0)
6089 {
6090 if (item == id)
6091 return retval;
6092 if (item >= SYNID_CLUSTER)
6093 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006094 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006095 /* restrict recursiveness to 30 to avoid an endless loop for a
6096 * cluster that includes itself (indirectly) */
6097 if (scl_list != NULL && depth < 30)
6098 {
6099 ++depth;
6100 r = in_id_list(NULL, scl_list, ssp, contained);
6101 --depth;
6102 if (r)
6103 return retval;
6104 }
6105 }
6106 item = *++list;
6107 }
6108 return !retval;
6109}
6110
6111struct subcommand
6112{
6113 char *name; /* subcommand name */
6114 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6115};
6116
6117static struct subcommand subcommands[] =
6118{
6119 {"case", syn_cmd_case},
6120 {"clear", syn_cmd_clear},
6121 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006122 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006123 {"enable", syn_cmd_enable},
6124 {"include", syn_cmd_include},
6125 {"keyword", syn_cmd_keyword},
6126 {"list", syn_cmd_list},
6127 {"manual", syn_cmd_manual},
6128 {"match", syn_cmd_match},
6129 {"on", syn_cmd_on},
6130 {"off", syn_cmd_off},
6131 {"region", syn_cmd_region},
6132 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006133 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134 {"sync", syn_cmd_sync},
6135 {"", syn_cmd_list},
6136 {NULL, NULL}
6137};
6138
6139/*
6140 * ":syntax".
6141 * This searches the subcommands[] table for the subcommand name, and calls a
6142 * syntax_subcommand() function to do the rest.
6143 */
6144 void
6145ex_syntax(eap)
6146 exarg_T *eap;
6147{
6148 char_u *arg = eap->arg;
6149 char_u *subcmd_end;
6150 char_u *subcmd_name;
6151 int i;
6152
6153 syn_cmdlinep = eap->cmdlinep;
6154
6155 /* isolate subcommand name */
6156 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6157 ;
6158 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6159 if (subcmd_name != NULL)
6160 {
6161 if (eap->skip) /* skip error messages for all subcommands */
6162 ++emsg_skip;
6163 for (i = 0; ; ++i)
6164 {
6165 if (subcommands[i].name == NULL)
6166 {
6167 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6168 break;
6169 }
6170 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6171 {
6172 eap->arg = skipwhite(subcmd_end);
6173 (subcommands[i].func)(eap, FALSE);
6174 break;
6175 }
6176 }
6177 vim_free(subcmd_name);
6178 if (eap->skip)
6179 --emsg_skip;
6180 }
6181}
6182
Bram Moolenaar860cae12010-06-05 23:22:07 +02006183 void
6184ex_ownsyntax(eap)
6185 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006186{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006187 char_u *old_value;
6188 char_u *new_value;
6189
Bram Moolenaar860cae12010-06-05 23:22:07 +02006190 if (curwin->w_s == &curwin->w_buffer->b_s)
6191 {
6192 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6193 memset(curwin->w_s, 0, sizeof(synblock_T));
6194#ifdef FEAT_SPELL
6195 curwin->w_p_spell = FALSE; /* No spell checking */
6196 clear_string_option(&curwin->w_s->b_p_spc);
6197 clear_string_option(&curwin->w_s->b_p_spf);
6198 vim_free(curwin->w_s->b_cap_prog);
6199 curwin->w_s->b_cap_prog = NULL;
6200 clear_string_option(&curwin->w_s->b_p_spl);
6201#endif
6202 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006203
6204 /* save value of b:current_syntax */
6205 old_value = get_var_value((char_u *)"b:current_syntax");
6206 if (old_value != NULL)
6207 old_value = vim_strsave(old_value);
6208
6209 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6210 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006211 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006212
6213 /* move value of b:current_syntax to w:current_syntax */
6214 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006215 if (new_value != NULL)
6216 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006217
6218 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006219 if (old_value == NULL)
6220 do_unlet((char_u *)"b:current_syntax", TRUE);
6221 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006222 {
6223 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6224 vim_free(old_value);
6225 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006226}
6227
6228 int
6229syntax_present(win)
6230 win_T *win;
6231{
6232 return (win->w_s->b_syn_patterns.ga_len != 0
6233 || win->w_s->b_syn_clusters.ga_len != 0
6234 || win->w_s->b_keywtab.ht_used > 0
6235 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006236}
6237
6238#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6239
6240static enum
6241{
6242 EXP_SUBCMD, /* expand ":syn" sub-commands */
6243 EXP_CASE /* expand ":syn case" arguments */
6244} expand_what;
6245
Bram Moolenaar4f688582007-07-24 12:34:30 +00006246/*
6247 * Reset include_link, include_default, include_none to 0.
6248 * Called when we are done expanding.
6249 */
6250 void
6251reset_expand_highlight()
6252{
6253 include_link = include_default = include_none = 0;
6254}
6255
6256/*
6257 * Handle command line completion for :match and :echohl command: Add "None"
6258 * as highlight group.
6259 */
6260 void
6261set_context_in_echohl_cmd(xp, arg)
6262 expand_T *xp;
6263 char_u *arg;
6264{
6265 xp->xp_context = EXPAND_HIGHLIGHT;
6266 xp->xp_pattern = arg;
6267 include_none = 1;
6268}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006269
6270/*
6271 * Handle command line completion for :syntax command.
6272 */
6273 void
6274set_context_in_syntax_cmd(xp, arg)
6275 expand_T *xp;
6276 char_u *arg;
6277{
6278 char_u *p;
6279
6280 /* Default: expand subcommands */
6281 xp->xp_context = EXPAND_SYNTAX;
6282 expand_what = EXP_SUBCMD;
6283 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006284 include_link = 0;
6285 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286
6287 /* (part of) subcommand already typed */
6288 if (*arg != NUL)
6289 {
6290 p = skiptowhite(arg);
6291 if (*p != NUL) /* past first word */
6292 {
6293 xp->xp_pattern = skipwhite(p);
6294 if (*skiptowhite(xp->xp_pattern) != NUL)
6295 xp->xp_context = EXPAND_NOTHING;
6296 else if (STRNICMP(arg, "case", p - arg) == 0)
6297 expand_what = EXP_CASE;
6298 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6299 || STRNICMP(arg, "region", p - arg) == 0
6300 || STRNICMP(arg, "match", p - arg) == 0
6301 || STRNICMP(arg, "list", p - arg) == 0)
6302 xp->xp_context = EXPAND_HIGHLIGHT;
6303 else
6304 xp->xp_context = EXPAND_NOTHING;
6305 }
6306 }
6307}
6308
6309static char *(case_args[]) = {"match", "ignore", NULL};
6310
6311/*
6312 * Function given to ExpandGeneric() to obtain the list syntax names for
6313 * expansion.
6314 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006315 char_u *
6316get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006317 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006318 int idx;
6319{
6320 if (expand_what == EXP_SUBCMD)
6321 return (char_u *)subcommands[idx].name;
6322 return (char_u *)case_args[idx];
6323}
6324
6325#endif /* FEAT_CMDL_COMPL */
6326
Bram Moolenaar071d4272004-06-13 20:20:40 +00006327/*
6328 * Function called for expression evaluation: get syntax ID at file position.
6329 */
6330 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006331syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006332 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006333 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006334 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006335 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006336 int *spellp; /* return: can do spell checking */
6337 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338{
6339 /* When the position is not after the current position and in the same
6340 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006341 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006342 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006343 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006344 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006345
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006346 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006347
6348 return (trans ? current_trans_id : current_id);
6349}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350
Bram Moolenaar860cae12010-06-05 23:22:07 +02006351#if defined(FEAT_CONCEAL) || defined(PROTO)
6352/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006353 * Get extra information about the syntax item. Must be called right after
6354 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006355 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006356 * Returns the current flags.
6357 */
6358 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006359get_syntax_info(seqnrp)
6360 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006361{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006362 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006363 return current_flags;
6364}
6365
6366/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006367 * Return conceal substitution character
6368 */
6369 int
6370syn_get_sub_char()
6371{
6372 return current_sub_char;
6373}
6374#endif
6375
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006376#if defined(FEAT_EVAL) || defined(PROTO)
6377/*
6378 * Return the syntax ID at position "i" in the current stack.
6379 * The caller must have called syn_get_id() before to fill the stack.
6380 * Returns -1 when "i" is out of range.
6381 */
6382 int
6383syn_get_stack_item(i)
6384 int i;
6385{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006386 if (i >= current_state.ga_len)
6387 {
6388 /* Need to invalidate the state, because we didn't properly finish it
6389 * for the last character, "keep_state" was TRUE. */
6390 invalidate_current_state();
6391 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006392 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006393 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006394 return CUR_STATE(i).si_id;
6395}
6396#endif
6397
Bram Moolenaar071d4272004-06-13 20:20:40 +00006398#if defined(FEAT_FOLDING) || defined(PROTO)
6399/*
6400 * Function called to get folding level for line "lnum" in window "wp".
6401 */
6402 int
6403syn_get_foldlevel(wp, lnum)
6404 win_T *wp;
6405 long lnum;
6406{
6407 int level = 0;
6408 int i;
6409
6410 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006411 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006412 {
6413 syntax_start(wp, lnum);
6414
6415 for (i = 0; i < current_state.ga_len; ++i)
6416 if (CUR_STATE(i).si_flags & HL_FOLD)
6417 ++level;
6418 }
6419 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006420 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006421 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006422 if (level < 0)
6423 level = 0;
6424 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006425 return level;
6426}
6427#endif
6428
6429#endif /* FEAT_SYN_HL */
6430
6431
6432/**************************************
6433 * Highlighting stuff *
6434 **************************************/
6435
6436/*
6437 * The default highlight groups. These are compiled-in for fast startup and
6438 * they still work when the runtime files can't be found.
6439 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006440 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6441 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006443#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006444# define CENT(a, b) b
6445#else
6446# define CENT(a, b) a
6447#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448static char *(highlight_init_both[]) =
6449 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006450 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6451 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6452 CENT("IncSearch term=reverse cterm=reverse",
6453 "IncSearch term=reverse cterm=reverse gui=reverse"),
6454 CENT("ModeMsg term=bold cterm=bold",
6455 "ModeMsg term=bold cterm=bold gui=bold"),
6456 CENT("NonText term=bold ctermfg=Blue",
6457 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6458 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6459 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6460 CENT("StatusLineNC term=reverse cterm=reverse",
6461 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006462#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006463 CENT("VertSplit term=reverse cterm=reverse",
6464 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006465#endif
6466#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006467 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6468 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006469#endif
6470#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006471 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6472 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006473#endif
6474#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006475 CENT("PmenuThumb cterm=reverse",
6476 "PmenuThumb cterm=reverse gui=reverse"),
6477 CENT("PmenuSbar ctermbg=Grey",
6478 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006479#endif
6480#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006481 CENT("TabLineSel term=bold cterm=bold",
6482 "TabLineSel term=bold cterm=bold gui=bold"),
6483 CENT("TabLineFill term=reverse cterm=reverse",
6484 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006485#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486#ifdef FEAT_GUI
6487 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006488 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006489#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006490 NULL
6491 };
6492
6493static char *(highlight_init_light[]) =
6494 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006495 CENT("Directory term=bold ctermfg=DarkBlue",
6496 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6497 CENT("LineNr term=underline ctermfg=Brown",
6498 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6499 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6500 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6501 CENT("Question term=standout ctermfg=DarkGreen",
6502 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6503 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6504 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006505#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006506 CENT("SpellBad term=reverse ctermbg=LightRed",
6507 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6508 CENT("SpellCap term=reverse ctermbg=LightBlue",
6509 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6510 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6511 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6512 CENT("SpellLocal term=underline ctermbg=Cyan",
6513 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006514#endif
6515#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006516 CENT("Pmenu ctermbg=LightMagenta",
6517 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6518 CENT("PmenuSel ctermbg=LightGrey",
6519 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006520#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006521 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6522 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6523 CENT("Title term=bold ctermfg=DarkMagenta",
6524 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6525 CENT("WarningMsg term=standout ctermfg=DarkRed",
6526 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006527#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006528 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6529 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006530#endif
6531#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006532 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6533 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6534 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6535 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006536#endif
6537#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006538 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6539 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006540#endif
6541#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006542 CENT("Visual term=reverse",
6543 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006544#endif
6545#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006546 CENT("DiffAdd term=bold ctermbg=LightBlue",
6547 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6548 CENT("DiffChange term=bold ctermbg=LightMagenta",
6549 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6550 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6551 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006552#endif
6553#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006554 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6555 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006556#endif
6557#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006558 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006559 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006560 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006561 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006562 CENT("ColorColumn term=reverse ctermbg=LightRed",
6563 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006564#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006565#ifdef FEAT_CONCEAL
6566 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6567 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6568#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006569#ifdef FEAT_AUTOCMD
6570 CENT("MatchParen term=reverse ctermbg=Cyan",
6571 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6572#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006573#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006574 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006575#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006576 NULL
6577 };
6578
6579static char *(highlight_init_dark[]) =
6580 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006581 CENT("Directory term=bold ctermfg=LightCyan",
6582 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6583 CENT("LineNr term=underline ctermfg=Yellow",
6584 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6585 CENT("MoreMsg term=bold ctermfg=LightGreen",
6586 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6587 CENT("Question term=standout ctermfg=LightGreen",
6588 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6589 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6590 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6591 CENT("SpecialKey term=bold ctermfg=LightBlue",
6592 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006593#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006594 CENT("SpellBad term=reverse ctermbg=Red",
6595 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6596 CENT("SpellCap term=reverse ctermbg=Blue",
6597 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6598 CENT("SpellRare term=reverse ctermbg=Magenta",
6599 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6600 CENT("SpellLocal term=underline ctermbg=Cyan",
6601 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006602#endif
6603#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006604 CENT("Pmenu ctermbg=Magenta",
6605 "Pmenu ctermbg=Magenta guibg=Magenta"),
6606 CENT("PmenuSel ctermbg=DarkGrey",
6607 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006608#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006609 CENT("Title term=bold ctermfg=LightMagenta",
6610 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6611 CENT("WarningMsg term=standout ctermfg=LightRed",
6612 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006613#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006614 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6615 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006616#endif
6617#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006618 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6619 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6620 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6621 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006622#endif
6623#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006624 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6625 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006626#endif
6627#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006628 CENT("Visual term=reverse",
6629 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006630#endif
6631#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006632 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6633 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6634 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6635 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6636 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6637 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006638#endif
6639#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006640 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6641 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006642#endif
6643#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006644 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006645 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006646 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006647 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006648 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6649 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006650#endif
6651#ifdef FEAT_AUTOCMD
6652 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6653 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006654#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006655#ifdef FEAT_CONCEAL
6656 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6657 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6658#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006659#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006660 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006661#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006662 NULL
6663 };
6664
6665 void
6666init_highlight(both, reset)
6667 int both; /* include groups where 'bg' doesn't matter */
6668 int reset; /* clear group first */
6669{
6670 int i;
6671 char **pp;
6672 static int had_both = FALSE;
6673#ifdef FEAT_EVAL
6674 char_u *p;
6675
6676 /*
6677 * Try finding the color scheme file. Used when a color file was loaded
6678 * and 'background' or 't_Co' is changed.
6679 */
6680 p = get_var_value((char_u *)"g:colors_name");
6681 if (p != NULL && load_colors(p) == OK)
6682 return;
6683#endif
6684
6685 /*
6686 * Didn't use a color file, use the compiled-in colors.
6687 */
6688 if (both)
6689 {
6690 had_both = TRUE;
6691 pp = highlight_init_both;
6692 for (i = 0; pp[i] != NULL; ++i)
6693 do_highlight((char_u *)pp[i], reset, TRUE);
6694 }
6695 else if (!had_both)
6696 /* Don't do anything before the call with both == TRUE from main().
6697 * Not everything has been setup then, and that call will overrule
6698 * everything anyway. */
6699 return;
6700
6701 if (*p_bg == 'l')
6702 pp = highlight_init_light;
6703 else
6704 pp = highlight_init_dark;
6705 for (i = 0; pp[i] != NULL; ++i)
6706 do_highlight((char_u *)pp[i], reset, TRUE);
6707
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006708 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006709 * depend on the number of colors available.
6710 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006711 * to avoid Statement highlighted text disappears.
6712 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006713 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006714 do_highlight((char_u *)(*p_bg == 'l'
6715 ? "Visual cterm=NONE ctermbg=LightGrey"
6716 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006717 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006718 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006719 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6720 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006721 if (*p_bg == 'l')
6722 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6723 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006724
Bram Moolenaar071d4272004-06-13 20:20:40 +00006725#ifdef FEAT_SYN_HL
6726 /*
6727 * If syntax highlighting is enabled load the highlighting for it.
6728 */
6729 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006730 {
6731 static int recursive = 0;
6732
6733 if (recursive >= 5)
6734 EMSG(_("E679: recursive loop loading syncolor.vim"));
6735 else
6736 {
6737 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006738 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006739 --recursive;
6740 }
6741 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006742#endif
6743}
6744
6745/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006746 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006747 * Return OK for success, FAIL for failure.
6748 */
6749 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006750load_colors(name)
6751 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006752{
6753 char_u *buf;
6754 int retval = FAIL;
6755 static int recursive = FALSE;
6756
6757 /* When being called recursively, this is probably because setting
6758 * 'background' caused the highlighting to be reloaded. This means it is
6759 * working, thus we should return OK. */
6760 if (recursive)
6761 return OK;
6762
6763 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006764 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006765 if (buf != NULL)
6766 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006767 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006768 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006769 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006770#ifdef FEAT_AUTOCMD
6771 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6772#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006773 }
6774 recursive = FALSE;
6775
6776 return retval;
6777}
6778
6779/*
6780 * Handle the ":highlight .." command.
6781 * When using ":hi clear" this is called recursively for each group with
6782 * "forceit" and "init" both TRUE.
6783 */
6784 void
6785do_highlight(line, forceit, init)
6786 char_u *line;
6787 int forceit;
6788 int init; /* TRUE when called for initializing */
6789{
6790 char_u *name_end;
6791 char_u *p;
6792 char_u *linep;
6793 char_u *key_start;
6794 char_u *arg_start;
6795 char_u *key = NULL, *arg = NULL;
6796 long i;
6797 int off;
6798 int len;
6799 int attr;
6800 int id;
6801 int idx;
6802 int dodefault = FALSE;
6803 int doclear = FALSE;
6804 int dolink = FALSE;
6805 int error = FALSE;
6806 int color;
6807 int is_normal_group = FALSE; /* "Normal" group */
6808#ifdef FEAT_GUI_X11
6809 int is_menu_group = FALSE; /* "Menu" group */
6810 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6811 int is_tooltip_group = FALSE; /* "Tooltip" group */
6812 int do_colors = FALSE; /* need to update colors? */
6813#else
6814# define is_menu_group 0
6815# define is_tooltip_group 0
6816#endif
6817
6818 /*
6819 * If no argument, list current highlighting.
6820 */
6821 if (ends_excmd(*line))
6822 {
6823 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6824 /* TODO: only call when the group has attributes set */
6825 highlight_list_one((int)i);
6826 return;
6827 }
6828
6829 /*
6830 * Isolate the name.
6831 */
6832 name_end = skiptowhite(line);
6833 linep = skipwhite(name_end);
6834
6835 /*
6836 * Check for "default" argument.
6837 */
6838 if (STRNCMP(line, "default", name_end - line) == 0)
6839 {
6840 dodefault = TRUE;
6841 line = linep;
6842 name_end = skiptowhite(line);
6843 linep = skipwhite(name_end);
6844 }
6845
6846 /*
6847 * Check for "clear" or "link" argument.
6848 */
6849 if (STRNCMP(line, "clear", name_end - line) == 0)
6850 doclear = TRUE;
6851 if (STRNCMP(line, "link", name_end - line) == 0)
6852 dolink = TRUE;
6853
6854 /*
6855 * ":highlight {group-name}": list highlighting for one group.
6856 */
6857 if (!doclear && !dolink && ends_excmd(*linep))
6858 {
6859 id = syn_namen2id(line, (int)(name_end - line));
6860 if (id == 0)
6861 EMSG2(_("E411: highlight group not found: %s"), line);
6862 else
6863 highlight_list_one(id);
6864 return;
6865 }
6866
6867 /*
6868 * Handle ":highlight link {from} {to}" command.
6869 */
6870 if (dolink)
6871 {
6872 char_u *from_start = linep;
6873 char_u *from_end;
6874 char_u *to_start;
6875 char_u *to_end;
6876 int from_id;
6877 int to_id;
6878
6879 from_end = skiptowhite(from_start);
6880 to_start = skipwhite(from_end);
6881 to_end = skiptowhite(to_start);
6882
6883 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6884 {
6885 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6886 from_start);
6887 return;
6888 }
6889
6890 if (!ends_excmd(*skipwhite(to_end)))
6891 {
6892 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6893 return;
6894 }
6895
6896 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6897 if (STRNCMP(to_start, "NONE", 4) == 0)
6898 to_id = 0;
6899 else
6900 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6901
6902 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6903 {
6904 /*
6905 * Don't allow a link when there already is some highlighting
6906 * for the group, unless '!' is used
6907 */
6908 if (to_id > 0 && !forceit && !init
6909 && hl_has_settings(from_id - 1, dodefault))
6910 {
6911 if (sourcing_name == NULL && !dodefault)
6912 EMSG(_("E414: group has settings, highlight link ignored"));
6913 }
6914 else
6915 {
6916 if (!init)
6917 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6918 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006919#ifdef FEAT_EVAL
6920 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6921#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006922 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006923 }
6924 }
6925
6926 /* Only call highlight_changed() once, after sourcing a syntax file */
6927 need_highlight_changed = TRUE;
6928
6929 return;
6930 }
6931
6932 if (doclear)
6933 {
6934 /*
6935 * ":highlight clear [group]" command.
6936 */
6937 line = linep;
6938 if (ends_excmd(*line))
6939 {
6940#ifdef FEAT_GUI
6941 /* First, we do not destroy the old values, but allocate the new
6942 * ones and update the display. THEN we destroy the old values.
6943 * If we destroy the old values first, then the old values
6944 * (such as GuiFont's or GuiFontset's) will still be displayed but
6945 * invalid because they were free'd.
6946 */
6947 if (gui.in_use)
6948 {
6949# ifdef FEAT_BEVAL_TIP
6950 gui_init_tooltip_font();
6951# endif
6952# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6953 gui_init_menu_font();
6954# endif
6955 }
6956# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6957 gui_mch_def_colors();
6958# endif
6959# ifdef FEAT_GUI_X11
6960# ifdef FEAT_MENU
6961
6962 /* This only needs to be done when there is no Menu highlight
6963 * group defined by default, which IS currently the case.
6964 */
6965 gui_mch_new_menu_colors();
6966# endif
6967 if (gui.in_use)
6968 {
6969 gui_new_scrollbar_colors();
6970# ifdef FEAT_BEVAL
6971 gui_mch_new_tooltip_colors();
6972# endif
6973# ifdef FEAT_MENU
6974 gui_mch_new_menu_font();
6975# endif
6976 }
6977# endif
6978
6979 /* Ok, we're done allocating the new default graphics items.
6980 * The screen should already be refreshed at this point.
6981 * It is now Ok to clear out the old data.
6982 */
6983#endif
6984#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006985 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986#endif
6987 restore_cterm_colors();
6988
6989 /*
6990 * Clear all default highlight groups and load the defaults.
6991 */
6992 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6993 highlight_clear(idx);
6994 init_highlight(TRUE, TRUE);
6995#ifdef FEAT_GUI
6996 if (gui.in_use)
6997 highlight_gui_started();
6998#endif
6999 highlight_changed();
7000 redraw_later_clear();
7001 return;
7002 }
7003 name_end = skiptowhite(line);
7004 linep = skipwhite(name_end);
7005 }
7006
7007 /*
7008 * Find the group name in the table. If it does not exist yet, add it.
7009 */
7010 id = syn_check_group(line, (int)(name_end - line));
7011 if (id == 0) /* failed (out of memory) */
7012 return;
7013 idx = id - 1; /* index is ID minus one */
7014
7015 /* Return if "default" was used and the group already has settings. */
7016 if (dodefault && hl_has_settings(idx, TRUE))
7017 return;
7018
7019 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7020 is_normal_group = TRUE;
7021#ifdef FEAT_GUI_X11
7022 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7023 is_menu_group = TRUE;
7024 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7025 is_scrollbar_group = TRUE;
7026 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7027 is_tooltip_group = TRUE;
7028#endif
7029
7030 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7031 if (doclear || (forceit && init))
7032 {
7033 highlight_clear(idx);
7034 if (!doclear)
7035 HL_TABLE()[idx].sg_set = 0;
7036 }
7037
7038 if (!doclear)
7039 while (!ends_excmd(*linep))
7040 {
7041 key_start = linep;
7042 if (*linep == '=')
7043 {
7044 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7045 error = TRUE;
7046 break;
7047 }
7048
7049 /*
7050 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7051 * "guibg").
7052 */
7053 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7054 ++linep;
7055 vim_free(key);
7056 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7057 if (key == NULL)
7058 {
7059 error = TRUE;
7060 break;
7061 }
7062 linep = skipwhite(linep);
7063
7064 if (STRCMP(key, "NONE") == 0)
7065 {
7066 if (!init || HL_TABLE()[idx].sg_set == 0)
7067 {
7068 if (!init)
7069 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7070 highlight_clear(idx);
7071 }
7072 continue;
7073 }
7074
7075 /*
7076 * Check for the equal sign.
7077 */
7078 if (*linep != '=')
7079 {
7080 EMSG2(_("E416: missing equal sign: %s"), key_start);
7081 error = TRUE;
7082 break;
7083 }
7084 ++linep;
7085
7086 /*
7087 * Isolate the argument.
7088 */
7089 linep = skipwhite(linep);
7090 if (*linep == '\'') /* guifg='color name' */
7091 {
7092 arg_start = ++linep;
7093 linep = vim_strchr(linep, '\'');
7094 if (linep == NULL)
7095 {
7096 EMSG2(_(e_invarg2), key_start);
7097 error = TRUE;
7098 break;
7099 }
7100 }
7101 else
7102 {
7103 arg_start = linep;
7104 linep = skiptowhite(linep);
7105 }
7106 if (linep == arg_start)
7107 {
7108 EMSG2(_("E417: missing argument: %s"), key_start);
7109 error = TRUE;
7110 break;
7111 }
7112 vim_free(arg);
7113 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7114 if (arg == NULL)
7115 {
7116 error = TRUE;
7117 break;
7118 }
7119 if (*linep == '\'')
7120 ++linep;
7121
7122 /*
7123 * Store the argument.
7124 */
7125 if ( STRCMP(key, "TERM") == 0
7126 || STRCMP(key, "CTERM") == 0
7127 || STRCMP(key, "GUI") == 0)
7128 {
7129 attr = 0;
7130 off = 0;
7131 while (arg[off] != NUL)
7132 {
7133 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7134 {
7135 len = (int)STRLEN(hl_name_table[i]);
7136 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7137 {
7138 attr |= hl_attr_table[i];
7139 off += len;
7140 break;
7141 }
7142 }
7143 if (i < 0)
7144 {
7145 EMSG2(_("E418: Illegal value: %s"), arg);
7146 error = TRUE;
7147 break;
7148 }
7149 if (arg[off] == ',') /* another one follows */
7150 ++off;
7151 }
7152 if (error)
7153 break;
7154 if (*key == 'T')
7155 {
7156 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7157 {
7158 if (!init)
7159 HL_TABLE()[idx].sg_set |= SG_TERM;
7160 HL_TABLE()[idx].sg_term = attr;
7161 }
7162 }
7163 else if (*key == 'C')
7164 {
7165 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7166 {
7167 if (!init)
7168 HL_TABLE()[idx].sg_set |= SG_CTERM;
7169 HL_TABLE()[idx].sg_cterm = attr;
7170 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7171 }
7172 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007173#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007174 else
7175 {
7176 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7177 {
7178 if (!init)
7179 HL_TABLE()[idx].sg_set |= SG_GUI;
7180 HL_TABLE()[idx].sg_gui = attr;
7181 }
7182 }
7183#endif
7184 }
7185 else if (STRCMP(key, "FONT") == 0)
7186 {
7187 /* in non-GUI fonts are simply ignored */
7188#ifdef FEAT_GUI
7189 if (!gui.shell_created)
7190 {
7191 /* GUI not started yet, always accept the name. */
7192 vim_free(HL_TABLE()[idx].sg_font_name);
7193 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7194 }
7195 else
7196 {
7197 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7198# ifdef FEAT_XFONTSET
7199 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7200# endif
7201 /* First, save the current font/fontset.
7202 * Then try to allocate the font/fontset.
7203 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7204 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7205 */
7206
7207 HL_TABLE()[idx].sg_font = NOFONT;
7208# ifdef FEAT_XFONTSET
7209 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7210# endif
7211 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7212 is_tooltip_group);
7213
7214# ifdef FEAT_XFONTSET
7215 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7216 {
7217 /* New fontset was accepted. Free the old one, if there was
7218 * one.
7219 */
7220 gui_mch_free_fontset(temp_sg_fontset);
7221 vim_free(HL_TABLE()[idx].sg_font_name);
7222 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7223 }
7224 else
7225 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7226# endif
7227 if (HL_TABLE()[idx].sg_font != NOFONT)
7228 {
7229 /* New font was accepted. Free the old one, if there was
7230 * one.
7231 */
7232 gui_mch_free_font(temp_sg_font);
7233 vim_free(HL_TABLE()[idx].sg_font_name);
7234 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7235 }
7236 else
7237 HL_TABLE()[idx].sg_font = temp_sg_font;
7238 }
7239#endif
7240 }
7241 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7242 {
7243 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7244 {
7245 if (!init)
7246 HL_TABLE()[idx].sg_set |= SG_CTERM;
7247
7248 /* When setting the foreground color, and previously the "bold"
7249 * flag was set for a light color, reset it now */
7250 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7251 {
7252 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7253 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7254 }
7255
7256 if (VIM_ISDIGIT(*arg))
7257 color = atoi((char *)arg);
7258 else if (STRICMP(arg, "fg") == 0)
7259 {
7260 if (cterm_normal_fg_color)
7261 color = cterm_normal_fg_color - 1;
7262 else
7263 {
7264 EMSG(_("E419: FG color unknown"));
7265 error = TRUE;
7266 break;
7267 }
7268 }
7269 else if (STRICMP(arg, "bg") == 0)
7270 {
7271 if (cterm_normal_bg_color > 0)
7272 color = cterm_normal_bg_color - 1;
7273 else
7274 {
7275 EMSG(_("E420: BG color unknown"));
7276 error = TRUE;
7277 break;
7278 }
7279 }
7280 else
7281 {
7282 static char *(color_names[28]) = {
7283 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7284 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7285 "Gray", "Grey",
7286 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7287 "Blue", "LightBlue", "Green", "LightGreen",
7288 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7289 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7290 static int color_numbers_16[28] = {0, 1, 2, 3,
7291 4, 5, 6, 6,
7292 7, 7,
7293 7, 7, 8, 8,
7294 9, 9, 10, 10,
7295 11, 11, 12, 12, 13,
7296 13, 14, 14, 15, -1};
7297 /* for xterm with 88 colors... */
7298 static int color_numbers_88[28] = {0, 4, 2, 6,
7299 1, 5, 32, 72,
7300 84, 84,
7301 7, 7, 82, 82,
7302 12, 43, 10, 61,
7303 14, 63, 9, 74, 13,
7304 75, 11, 78, 15, -1};
7305 /* for xterm with 256 colors... */
7306 static int color_numbers_256[28] = {0, 4, 2, 6,
7307 1, 5, 130, 130,
7308 248, 248,
7309 7, 7, 242, 242,
7310 12, 81, 10, 121,
7311 14, 159, 9, 224, 13,
7312 225, 11, 229, 15, -1};
7313 /* for terminals with less than 16 colors... */
7314 static int color_numbers_8[28] = {0, 4, 2, 6,
7315 1, 5, 3, 3,
7316 7, 7,
7317 7, 7, 0+8, 0+8,
7318 4+8, 4+8, 2+8, 2+8,
7319 6+8, 6+8, 1+8, 1+8, 5+8,
7320 5+8, 3+8, 3+8, 7+8, -1};
7321#if defined(__QNXNTO__)
7322 static int *color_numbers_8_qansi = color_numbers_8;
7323 /* On qnx, the 8 & 16 color arrays are the same */
7324 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7325 color_numbers_8_qansi = color_numbers_16;
7326#endif
7327
7328 /* reduce calls to STRICMP a bit, it can be slow */
7329 off = TOUPPER_ASC(*arg);
7330 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7331 if (off == color_names[i][0]
7332 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7333 break;
7334 if (i < 0)
7335 {
7336 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7337 error = TRUE;
7338 break;
7339 }
7340
7341 /* Use the _16 table to check if its a valid color name. */
7342 color = color_numbers_16[i];
7343 if (color >= 0)
7344 {
7345 if (t_colors == 8)
7346 {
7347 /* t_Co is 8: use the 8 colors table */
7348#if defined(__QNXNTO__)
7349 color = color_numbers_8_qansi[i];
7350#else
7351 color = color_numbers_8[i];
7352#endif
7353 if (key[5] == 'F')
7354 {
7355 /* set/reset bold attribute to get light foreground
7356 * colors (on some terminals, e.g. "linux") */
7357 if (color & 8)
7358 {
7359 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7360 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7361 }
7362 else
7363 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7364 }
7365 color &= 7; /* truncate to 8 colors */
7366 }
7367 else if (t_colors == 16 || t_colors == 88
7368 || t_colors == 256)
7369 {
7370 /*
7371 * Guess: if the termcap entry ends in 'm', it is
7372 * probably an xterm-like terminal. Use the changed
7373 * order for colors.
7374 */
7375 if (*T_CAF != NUL)
7376 p = T_CAF;
7377 else
7378 p = T_CSF;
7379 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7380 switch (t_colors)
7381 {
7382 case 16:
7383 color = color_numbers_8[i];
7384 break;
7385 case 88:
7386 color = color_numbers_88[i];
7387 break;
7388 case 256:
7389 color = color_numbers_256[i];
7390 break;
7391 }
7392 }
7393 }
7394 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007395 /* Add one to the argument, to avoid zero. Zero is used for
7396 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007397 if (key[5] == 'F')
7398 {
7399 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7400 if (is_normal_group)
7401 {
7402 cterm_normal_fg_color = color + 1;
7403 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7404#ifdef FEAT_GUI
7405 /* Don't do this if the GUI is used. */
7406 if (!gui.in_use && !gui.starting)
7407#endif
7408 {
7409 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007410 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007411 term_fg_color(color);
7412 }
7413 }
7414 }
7415 else
7416 {
7417 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7418 if (is_normal_group)
7419 {
7420 cterm_normal_bg_color = color + 1;
7421#ifdef FEAT_GUI
7422 /* Don't mess with 'background' if the GUI is used. */
7423 if (!gui.in_use && !gui.starting)
7424#endif
7425 {
7426 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007427 if (color >= 0)
7428 {
7429 if (termcap_active)
7430 term_bg_color(color);
7431 if (t_colors < 16)
7432 i = (color == 0 || color == 4);
7433 else
7434 i = (color < 7 || color == 8);
7435 /* Set the 'background' option if the value is
7436 * wrong. */
7437 if (i != (*p_bg == 'd'))
7438 set_option_value((char_u *)"bg", 0L,
7439 i ? (char_u *)"dark"
7440 : (char_u *)"light", 0);
7441 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007442 }
7443 }
7444 }
7445 }
7446 }
7447 else if (STRCMP(key, "GUIFG") == 0)
7448 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007449#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007450 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007451 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007452 if (!init)
7453 HL_TABLE()[idx].sg_set |= SG_GUI;
7454
Bram Moolenaar61623362010-07-14 22:04:22 +02007455# ifdef FEAT_GUI
7456 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007457 i = color_name2handle(arg);
7458 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7459 {
7460 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007461# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007462 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7463 if (STRCMP(arg, "NONE"))
7464 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7465 else
7466 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007467# ifdef FEAT_GUI
7468# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007469 if (is_menu_group)
7470 gui.menu_fg_pixel = i;
7471 if (is_scrollbar_group)
7472 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007473# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007474 if (is_tooltip_group)
7475 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007476# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007477 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007478# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007479 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007480# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007481 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007482#endif
7483 }
7484 else if (STRCMP(key, "GUIBG") == 0)
7485 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007486#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007487 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007488 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007489 if (!init)
7490 HL_TABLE()[idx].sg_set |= SG_GUI;
7491
Bram Moolenaar61623362010-07-14 22:04:22 +02007492# ifdef FEAT_GUI
7493 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007494 i = color_name2handle(arg);
7495 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7496 {
7497 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007498# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007499 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7500 if (STRCMP(arg, "NONE") != 0)
7501 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7502 else
7503 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007504# ifdef FEAT_GUI
7505# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007506 if (is_menu_group)
7507 gui.menu_bg_pixel = i;
7508 if (is_scrollbar_group)
7509 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007510# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007511 if (is_tooltip_group)
7512 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007513# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007514 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007515# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007516 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007517# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007518 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007519#endif
7520 }
7521 else if (STRCMP(key, "GUISP") == 0)
7522 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007523#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007524 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7525 {
7526 if (!init)
7527 HL_TABLE()[idx].sg_set |= SG_GUI;
7528
Bram Moolenaar61623362010-07-14 22:04:22 +02007529# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007530 i = color_name2handle(arg);
7531 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7532 {
7533 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007534# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007535 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7536 if (STRCMP(arg, "NONE") != 0)
7537 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7538 else
7539 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007540# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007541 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007542# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007543 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007544#endif
7545 }
7546 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7547 {
7548 char_u buf[100];
7549 char_u *tname;
7550
7551 if (!init)
7552 HL_TABLE()[idx].sg_set |= SG_TERM;
7553
7554 /*
7555 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007556 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007557 */
7558 if (STRNCMP(arg, "t_", 2) == 0)
7559 {
7560 off = 0;
7561 buf[0] = 0;
7562 while (arg[off] != NUL)
7563 {
7564 /* Isolate one termcap name */
7565 for (len = 0; arg[off + len] &&
7566 arg[off + len] != ','; ++len)
7567 ;
7568 tname = vim_strnsave(arg + off, len);
7569 if (tname == NULL) /* out of memory */
7570 {
7571 error = TRUE;
7572 break;
7573 }
7574 /* lookup the escape sequence for the item */
7575 p = get_term_code(tname);
7576 vim_free(tname);
7577 if (p == NULL) /* ignore non-existing things */
7578 p = (char_u *)"";
7579
7580 /* Append it to the already found stuff */
7581 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7582 {
7583 EMSG2(_("E422: terminal code too long: %s"), arg);
7584 error = TRUE;
7585 break;
7586 }
7587 STRCAT(buf, p);
7588
7589 /* Advance to the next item */
7590 off += len;
7591 if (arg[off] == ',') /* another one follows */
7592 ++off;
7593 }
7594 }
7595 else
7596 {
7597 /*
7598 * Copy characters from arg[] to buf[], translating <> codes.
7599 */
7600 for (p = arg, off = 0; off < 100 && *p; )
7601 {
7602 len = trans_special(&p, buf + off, FALSE);
7603 if (len) /* recognized special char */
7604 off += len;
7605 else /* copy as normal char */
7606 buf[off++] = *p++;
7607 }
7608 buf[off] = NUL;
7609 }
7610 if (error)
7611 break;
7612
7613 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7614 p = NULL;
7615 else
7616 p = vim_strsave(buf);
7617 if (key[2] == 'A')
7618 {
7619 vim_free(HL_TABLE()[idx].sg_start);
7620 HL_TABLE()[idx].sg_start = p;
7621 }
7622 else
7623 {
7624 vim_free(HL_TABLE()[idx].sg_stop);
7625 HL_TABLE()[idx].sg_stop = p;
7626 }
7627 }
7628 else
7629 {
7630 EMSG2(_("E423: Illegal argument: %s"), key_start);
7631 error = TRUE;
7632 break;
7633 }
7634
7635 /*
7636 * When highlighting has been given for a group, don't link it.
7637 */
7638 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7639 HL_TABLE()[idx].sg_link = 0;
7640
7641 /*
7642 * Continue with next argument.
7643 */
7644 linep = skipwhite(linep);
7645 }
7646
7647 /*
7648 * If there is an error, and it's a new entry, remove it from the table.
7649 */
7650 if (error && idx == highlight_ga.ga_len)
7651 syn_unadd_group();
7652 else
7653 {
7654 if (is_normal_group)
7655 {
7656 HL_TABLE()[idx].sg_term_attr = 0;
7657 HL_TABLE()[idx].sg_cterm_attr = 0;
7658#ifdef FEAT_GUI
7659 HL_TABLE()[idx].sg_gui_attr = 0;
7660 /*
7661 * Need to update all groups, because they might be using "bg"
7662 * and/or "fg", which have been changed now.
7663 */
7664 if (gui.in_use)
7665 highlight_gui_started();
7666#endif
7667 }
7668#ifdef FEAT_GUI_X11
7669# ifdef FEAT_MENU
7670 else if (is_menu_group)
7671 {
7672 if (gui.in_use && do_colors)
7673 gui_mch_new_menu_colors();
7674 }
7675# endif
7676 else if (is_scrollbar_group)
7677 {
7678 if (gui.in_use && do_colors)
7679 gui_new_scrollbar_colors();
7680 }
7681# ifdef FEAT_BEVAL
7682 else if (is_tooltip_group)
7683 {
7684 if (gui.in_use && do_colors)
7685 gui_mch_new_tooltip_colors();
7686 }
7687# endif
7688#endif
7689 else
7690 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007691#ifdef FEAT_EVAL
7692 HL_TABLE()[idx].sg_scriptID = current_SID;
7693#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007694 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007695 }
7696 vim_free(key);
7697 vim_free(arg);
7698
7699 /* Only call highlight_changed() once, after sourcing a syntax file */
7700 need_highlight_changed = TRUE;
7701}
7702
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007703#if defined(EXITFREE) || defined(PROTO)
7704 void
7705free_highlight()
7706{
7707 int i;
7708
7709 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007710 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007711 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007712 vim_free(HL_TABLE()[i].sg_name);
7713 vim_free(HL_TABLE()[i].sg_name_u);
7714 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007715 ga_clear(&highlight_ga);
7716}
7717#endif
7718
Bram Moolenaar071d4272004-06-13 20:20:40 +00007719/*
7720 * Reset the cterm colors to what they were before Vim was started, if
7721 * possible. Otherwise reset them to zero.
7722 */
7723 void
7724restore_cterm_colors()
7725{
7726#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7727 /* Since t_me has been set, this probably means that the user
7728 * wants to use this as default colors. Need to reset default
7729 * background/foreground colors. */
7730 mch_set_normal_colors();
7731#else
7732 cterm_normal_fg_color = 0;
7733 cterm_normal_fg_bold = 0;
7734 cterm_normal_bg_color = 0;
7735#endif
7736}
7737
7738/*
7739 * Return TRUE if highlight group "idx" has any settings.
7740 * When "check_link" is TRUE also check for an existing link.
7741 */
7742 static int
7743hl_has_settings(idx, check_link)
7744 int idx;
7745 int check_link;
7746{
7747 return ( HL_TABLE()[idx].sg_term_attr != 0
7748 || HL_TABLE()[idx].sg_cterm_attr != 0
7749#ifdef FEAT_GUI
7750 || HL_TABLE()[idx].sg_gui_attr != 0
7751#endif
7752 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7753}
7754
7755/*
7756 * Clear highlighting for one group.
7757 */
7758 static void
7759highlight_clear(idx)
7760 int idx;
7761{
7762 HL_TABLE()[idx].sg_term = 0;
7763 vim_free(HL_TABLE()[idx].sg_start);
7764 HL_TABLE()[idx].sg_start = NULL;
7765 vim_free(HL_TABLE()[idx].sg_stop);
7766 HL_TABLE()[idx].sg_stop = NULL;
7767 HL_TABLE()[idx].sg_term_attr = 0;
7768 HL_TABLE()[idx].sg_cterm = 0;
7769 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7770 HL_TABLE()[idx].sg_cterm_fg = 0;
7771 HL_TABLE()[idx].sg_cterm_bg = 0;
7772 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007773#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007774 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007775 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7776 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7778 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007779 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7780 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007781#endif
7782#ifdef FEAT_GUI
7783 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7784 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7785 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007786 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7787 HL_TABLE()[idx].sg_font = NOFONT;
7788# ifdef FEAT_XFONTSET
7789 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7790 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7791# endif
7792 vim_free(HL_TABLE()[idx].sg_font_name);
7793 HL_TABLE()[idx].sg_font_name = NULL;
7794 HL_TABLE()[idx].sg_gui_attr = 0;
7795#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007796#ifdef FEAT_EVAL
7797 /* Clear the script ID only when there is no link, since that is not
7798 * cleared. */
7799 if (HL_TABLE()[idx].sg_link == 0)
7800 HL_TABLE()[idx].sg_scriptID = 0;
7801#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007802}
7803
7804#if defined(FEAT_GUI) || defined(PROTO)
7805/*
7806 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007807 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 * "Tooltip" colors.
7809 */
7810 void
7811set_normal_colors()
7812{
7813 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007814 &gui.norm_pixel, &gui.back_pixel,
7815 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007816 {
7817 gui_mch_new_colors();
7818 must_redraw = CLEAR;
7819 }
7820#ifdef FEAT_GUI_X11
7821 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007822 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7823 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007824 {
7825# ifdef FEAT_MENU
7826 gui_mch_new_menu_colors();
7827# endif
7828 must_redraw = CLEAR;
7829 }
7830# ifdef FEAT_BEVAL
7831 if (set_group_colors((char_u *)"Tooltip",
7832 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7833 FALSE, FALSE, TRUE))
7834 {
7835# ifdef FEAT_TOOLBAR
7836 gui_mch_new_tooltip_colors();
7837# endif
7838 must_redraw = CLEAR;
7839 }
7840#endif
7841 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007842 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7843 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007844 {
7845 gui_new_scrollbar_colors();
7846 must_redraw = CLEAR;
7847 }
7848#endif
7849}
7850
7851/*
7852 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7853 */
7854 static int
7855set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7856 char_u *name;
7857 guicolor_T *fgp;
7858 guicolor_T *bgp;
7859 int do_menu;
7860 int use_norm;
7861 int do_tooltip;
7862{
7863 int idx;
7864
7865 idx = syn_name2id(name) - 1;
7866 if (idx >= 0)
7867 {
7868 gui_do_one_color(idx, do_menu, do_tooltip);
7869
7870 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7871 *fgp = HL_TABLE()[idx].sg_gui_fg;
7872 else if (use_norm)
7873 *fgp = gui.def_norm_pixel;
7874 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7875 *bgp = HL_TABLE()[idx].sg_gui_bg;
7876 else if (use_norm)
7877 *bgp = gui.def_back_pixel;
7878 return TRUE;
7879 }
7880 return FALSE;
7881}
7882
7883/*
7884 * Get the font of the "Normal" group.
7885 * Returns "" when it's not found or not set.
7886 */
7887 char_u *
7888hl_get_font_name()
7889{
7890 int id;
7891 char_u *s;
7892
7893 id = syn_name2id((char_u *)"Normal");
7894 if (id > 0)
7895 {
7896 s = HL_TABLE()[id - 1].sg_font_name;
7897 if (s != NULL)
7898 return s;
7899 }
7900 return (char_u *)"";
7901}
7902
7903/*
7904 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7905 * actually chosen to be used.
7906 */
7907 void
7908hl_set_font_name(font_name)
7909 char_u *font_name;
7910{
7911 int id;
7912
7913 id = syn_name2id((char_u *)"Normal");
7914 if (id > 0)
7915 {
7916 vim_free(HL_TABLE()[id - 1].sg_font_name);
7917 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7918 }
7919}
7920
7921/*
7922 * Set background color for "Normal" group. Called by gui_set_bg_color()
7923 * when the color is known.
7924 */
7925 void
7926hl_set_bg_color_name(name)
7927 char_u *name; /* must have been allocated */
7928{
7929 int id;
7930
7931 if (name != NULL)
7932 {
7933 id = syn_name2id((char_u *)"Normal");
7934 if (id > 0)
7935 {
7936 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7937 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7938 }
7939 }
7940}
7941
7942/*
7943 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7944 * when the color is known.
7945 */
7946 void
7947hl_set_fg_color_name(name)
7948 char_u *name; /* must have been allocated */
7949{
7950 int id;
7951
7952 if (name != NULL)
7953 {
7954 id = syn_name2id((char_u *)"Normal");
7955 if (id > 0)
7956 {
7957 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7958 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7959 }
7960 }
7961}
7962
7963/*
7964 * Return the handle for a color name.
7965 * Returns INVALCOLOR when failed.
7966 */
7967 static guicolor_T
7968color_name2handle(name)
7969 char_u *name;
7970{
7971 if (STRCMP(name, "NONE") == 0)
7972 return INVALCOLOR;
7973
7974 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7975 return gui.norm_pixel;
7976 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7977 return gui.back_pixel;
7978
7979 return gui_get_color(name);
7980}
7981
7982/*
7983 * Return the handle for a font name.
7984 * Returns NOFONT when failed.
7985 */
7986 static GuiFont
7987font_name2handle(name)
7988 char_u *name;
7989{
7990 if (STRCMP(name, "NONE") == 0)
7991 return NOFONT;
7992
7993 return gui_mch_get_font(name, TRUE);
7994}
7995
7996# ifdef FEAT_XFONTSET
7997/*
7998 * Return the handle for a fontset name.
7999 * Returns NOFONTSET when failed.
8000 */
8001 static GuiFontset
8002fontset_name2handle(name, fixed_width)
8003 char_u *name;
8004 int fixed_width;
8005{
8006 if (STRCMP(name, "NONE") == 0)
8007 return NOFONTSET;
8008
8009 return gui_mch_get_fontset(name, TRUE, fixed_width);
8010}
8011# endif
8012
8013/*
8014 * Get the font or fontset for one highlight group.
8015 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008016 static void
8017hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8018 int idx;
8019 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008020 int do_normal; /* set normal font */
8021 int do_menu UNUSED; /* set menu font */
8022 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008023{
8024# ifdef FEAT_XFONTSET
8025 /* If 'guifontset' is not empty, first try using the name as a
8026 * fontset. If that doesn't work, use it as a font name. */
8027 if (*p_guifontset != NUL
8028# ifdef FONTSET_ALWAYS
8029 || do_menu
8030# endif
8031# ifdef FEAT_BEVAL_TIP
8032 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8033 || do_tooltip
8034# endif
8035 )
8036 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8037# ifdef FONTSET_ALWAYS
8038 || do_menu
8039# endif
8040# ifdef FEAT_BEVAL_TIP
8041 || do_tooltip
8042# endif
8043 );
8044 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8045 {
8046 /* If it worked and it's the Normal group, use it as the
8047 * normal fontset. Same for the Menu group. */
8048 if (do_normal)
8049 gui_init_font(arg, TRUE);
8050# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8051 if (do_menu)
8052 {
8053# ifdef FONTSET_ALWAYS
8054 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8055# else
8056 /* YIKES! This is a bug waiting to crash the program */
8057 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8058# endif
8059 gui_mch_new_menu_font();
8060 }
8061# ifdef FEAT_BEVAL
8062 if (do_tooltip)
8063 {
8064 /* The Athena widget set cannot currently handle switching between
8065 * displaying a single font and a fontset.
8066 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008067 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008068 * XFontStruct is used.
8069 */
8070 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8071 gui_mch_new_tooltip_font();
8072 }
8073# endif
8074# endif
8075 }
8076 else
8077# endif
8078 {
8079 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8080 /* If it worked and it's the Normal group, use it as the
8081 * normal font. Same for the Menu group. */
8082 if (HL_TABLE()[idx].sg_font != NOFONT)
8083 {
8084 if (do_normal)
8085 gui_init_font(arg, FALSE);
8086#ifndef FONTSET_ALWAYS
8087# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8088 if (do_menu)
8089 {
8090 gui.menu_font = HL_TABLE()[idx].sg_font;
8091 gui_mch_new_menu_font();
8092 }
8093# endif
8094#endif
8095 }
8096 }
8097}
8098
8099#endif /* FEAT_GUI */
8100
8101/*
8102 * Table with the specifications for an attribute number.
8103 * Note that this table is used by ALL buffers. This is required because the
8104 * GUI can redraw at any time for any buffer.
8105 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008106static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008107
8108#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8109
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008110static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008111
8112#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8113
8114#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008115static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008116
8117#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8118#endif
8119
8120/*
8121 * Return the attr number for a set of colors and font.
8122 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8123 * if the combination is new.
8124 * Return 0 for error (no more room).
8125 */
8126 static int
8127get_attr_entry(table, aep)
8128 garray_T *table;
8129 attrentry_T *aep;
8130{
8131 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008132 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008133 static int recursive = FALSE;
8134
8135 /*
8136 * Init the table, in case it wasn't done yet.
8137 */
8138 table->ga_itemsize = sizeof(attrentry_T);
8139 table->ga_growsize = 7;
8140
8141 /*
8142 * Try to find an entry with the same specifications.
8143 */
8144 for (i = 0; i < table->ga_len; ++i)
8145 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008146 taep = &(((attrentry_T *)table->ga_data)[i]);
8147 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008148 && (
8149#ifdef FEAT_GUI
8150 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008151 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8152 && aep->ae_u.gui.bg_color
8153 == taep->ae_u.gui.bg_color
8154 && aep->ae_u.gui.sp_color
8155 == taep->ae_u.gui.sp_color
8156 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008157# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008158 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008159# endif
8160 ))
8161 ||
8162#endif
8163 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008164 && (aep->ae_u.term.start == NULL)
8165 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008166 && (aep->ae_u.term.start == NULL
8167 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008168 taep->ae_u.term.start) == 0)
8169 && (aep->ae_u.term.stop == NULL)
8170 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008171 && (aep->ae_u.term.stop == NULL
8172 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008173 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008175 && aep->ae_u.cterm.fg_color
8176 == taep->ae_u.cterm.fg_color
8177 && aep->ae_u.cterm.bg_color
8178 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 ))
8180
8181 return i + ATTR_OFF;
8182 }
8183
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008184 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008185 {
8186 /*
8187 * Running out of attribute entries! remove all attributes, and
8188 * compute new ones for all groups.
8189 * When called recursively, we are really out of numbers.
8190 */
8191 if (recursive)
8192 {
8193 EMSG(_("E424: Too many different highlighting attributes in use"));
8194 return 0;
8195 }
8196 recursive = TRUE;
8197
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008198 clear_hl_tables();
8199
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200 must_redraw = CLEAR;
8201
8202 for (i = 0; i < highlight_ga.ga_len; ++i)
8203 set_hl_attr(i);
8204
8205 recursive = FALSE;
8206 }
8207
8208 /*
8209 * This is a new combination of colors and font, add an entry.
8210 */
8211 if (ga_grow(table, 1) == FAIL)
8212 return 0;
8213
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008214 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8215 vim_memset(taep, 0, sizeof(attrentry_T));
8216 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217#ifdef FEAT_GUI
8218 if (table == &gui_attr_table)
8219 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008220 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8221 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8222 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8223 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008224# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008225 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226# endif
8227 }
8228#endif
8229 if (table == &term_attr_table)
8230 {
8231 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008232 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008234 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008235 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008236 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008238 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008239 }
8240 else if (table == &cterm_attr_table)
8241 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008242 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8243 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008244 }
8245 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008246 return (table->ga_len - 1 + ATTR_OFF);
8247}
8248
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008249/*
8250 * Clear all highlight tables.
8251 */
8252 void
8253clear_hl_tables()
8254{
8255 int i;
8256 attrentry_T *taep;
8257
8258#ifdef FEAT_GUI
8259 ga_clear(&gui_attr_table);
8260#endif
8261 for (i = 0; i < term_attr_table.ga_len; ++i)
8262 {
8263 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8264 vim_free(taep->ae_u.term.start);
8265 vim_free(taep->ae_u.term.stop);
8266 }
8267 ga_clear(&term_attr_table);
8268 ga_clear(&cterm_attr_table);
8269}
8270
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008271#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008272/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008273 * Combine special attributes (e.g., for spelling) with other attributes
8274 * (e.g., for syntax highlighting).
8275 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008276 * This creates a new group when required.
8277 * Since we expect there to be few spelling mistakes we don't cache the
8278 * result.
8279 * Return the resulting attributes.
8280 */
8281 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008282hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008283 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008284 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008285{
8286 attrentry_T *char_aep = NULL;
8287 attrentry_T *spell_aep;
8288 attrentry_T new_en;
8289
8290 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008291 return prim_attr;
8292 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8293 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008294#ifdef FEAT_GUI
8295 if (gui.in_use)
8296 {
8297 if (char_attr > HL_ALL)
8298 char_aep = syn_gui_attr2entry(char_attr);
8299 if (char_aep != NULL)
8300 new_en = *char_aep;
8301 else
8302 {
8303 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008304 new_en.ae_u.gui.fg_color = INVALCOLOR;
8305 new_en.ae_u.gui.bg_color = INVALCOLOR;
8306 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008307 if (char_attr <= HL_ALL)
8308 new_en.ae_attr = char_attr;
8309 }
8310
Bram Moolenaar30abd282005-06-22 22:35:10 +00008311 if (prim_attr <= HL_ALL)
8312 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008313 else
8314 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008315 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008316 if (spell_aep != NULL)
8317 {
8318 new_en.ae_attr |= spell_aep->ae_attr;
8319 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8320 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8321 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8322 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8323 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8324 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8325 if (spell_aep->ae_u.gui.font != NOFONT)
8326 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8327# ifdef FEAT_XFONTSET
8328 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8329 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8330# endif
8331 }
8332 }
8333 return get_attr_entry(&gui_attr_table, &new_en);
8334 }
8335#endif
8336
8337 if (t_colors > 1)
8338 {
8339 if (char_attr > HL_ALL)
8340 char_aep = syn_cterm_attr2entry(char_attr);
8341 if (char_aep != NULL)
8342 new_en = *char_aep;
8343 else
8344 {
8345 vim_memset(&new_en, 0, sizeof(new_en));
8346 if (char_attr <= HL_ALL)
8347 new_en.ae_attr = char_attr;
8348 }
8349
Bram Moolenaar30abd282005-06-22 22:35:10 +00008350 if (prim_attr <= HL_ALL)
8351 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008352 else
8353 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008354 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008355 if (spell_aep != NULL)
8356 {
8357 new_en.ae_attr |= spell_aep->ae_attr;
8358 if (spell_aep->ae_u.cterm.fg_color > 0)
8359 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8360 if (spell_aep->ae_u.cterm.bg_color > 0)
8361 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8362 }
8363 }
8364 return get_attr_entry(&cterm_attr_table, &new_en);
8365 }
8366
8367 if (char_attr > HL_ALL)
8368 char_aep = syn_term_attr2entry(char_attr);
8369 if (char_aep != NULL)
8370 new_en = *char_aep;
8371 else
8372 {
8373 vim_memset(&new_en, 0, sizeof(new_en));
8374 if (char_attr <= HL_ALL)
8375 new_en.ae_attr = char_attr;
8376 }
8377
Bram Moolenaar30abd282005-06-22 22:35:10 +00008378 if (prim_attr <= HL_ALL)
8379 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008380 else
8381 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008382 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008383 if (spell_aep != NULL)
8384 {
8385 new_en.ae_attr |= spell_aep->ae_attr;
8386 if (spell_aep->ae_u.term.start != NULL)
8387 {
8388 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8389 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8390 }
8391 }
8392 }
8393 return get_attr_entry(&term_attr_table, &new_en);
8394}
8395#endif
8396
Bram Moolenaar071d4272004-06-13 20:20:40 +00008397#ifdef FEAT_GUI
8398
8399 attrentry_T *
8400syn_gui_attr2entry(attr)
8401 int attr;
8402{
8403 attr -= ATTR_OFF;
8404 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8405 return NULL;
8406 return &(GUI_ATTR_ENTRY(attr));
8407}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008408#endif /* FEAT_GUI */
8409
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008410/*
8411 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8412 * Only to be used when "attr" > HL_ALL.
8413 */
8414 int
8415syn_attr2attr(attr)
8416 int attr;
8417{
8418 attrentry_T *aep;
8419
8420#ifdef FEAT_GUI
8421 if (gui.in_use)
8422 aep = syn_gui_attr2entry(attr);
8423 else
8424#endif
8425 if (t_colors > 1)
8426 aep = syn_cterm_attr2entry(attr);
8427 else
8428 aep = syn_term_attr2entry(attr);
8429
8430 if (aep == NULL) /* highlighting not set */
8431 return 0;
8432 return aep->ae_attr;
8433}
8434
8435
Bram Moolenaar071d4272004-06-13 20:20:40 +00008436 attrentry_T *
8437syn_term_attr2entry(attr)
8438 int attr;
8439{
8440 attr -= ATTR_OFF;
8441 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8442 return NULL;
8443 return &(TERM_ATTR_ENTRY(attr));
8444}
8445
8446 attrentry_T *
8447syn_cterm_attr2entry(attr)
8448 int attr;
8449{
8450 attr -= ATTR_OFF;
8451 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8452 return NULL;
8453 return &(CTERM_ATTR_ENTRY(attr));
8454}
8455
8456#define LIST_ATTR 1
8457#define LIST_STRING 2
8458#define LIST_INT 3
8459
8460 static void
8461highlight_list_one(id)
8462 int id;
8463{
8464 struct hl_group *sgp;
8465 int didh = FALSE;
8466
8467 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8468
8469 didh = highlight_list_arg(id, didh, LIST_ATTR,
8470 sgp->sg_term, NULL, "term");
8471 didh = highlight_list_arg(id, didh, LIST_STRING,
8472 0, sgp->sg_start, "start");
8473 didh = highlight_list_arg(id, didh, LIST_STRING,
8474 0, sgp->sg_stop, "stop");
8475
8476 didh = highlight_list_arg(id, didh, LIST_ATTR,
8477 sgp->sg_cterm, NULL, "cterm");
8478 didh = highlight_list_arg(id, didh, LIST_INT,
8479 sgp->sg_cterm_fg, NULL, "ctermfg");
8480 didh = highlight_list_arg(id, didh, LIST_INT,
8481 sgp->sg_cterm_bg, NULL, "ctermbg");
8482
Bram Moolenaar61623362010-07-14 22:04:22 +02008483#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008484 didh = highlight_list_arg(id, didh, LIST_ATTR,
8485 sgp->sg_gui, NULL, "gui");
8486 didh = highlight_list_arg(id, didh, LIST_STRING,
8487 0, sgp->sg_gui_fg_name, "guifg");
8488 didh = highlight_list_arg(id, didh, LIST_STRING,
8489 0, sgp->sg_gui_bg_name, "guibg");
8490 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008491 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008492#endif
8493#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008494 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008495 0, sgp->sg_font_name, "font");
8496#endif
8497
Bram Moolenaar661b1822005-07-28 22:36:45 +00008498 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008499 {
8500 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008501 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008502 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8503 msg_putchar(' ');
8504 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8505 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008506
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008507 if (!didh)
8508 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008509#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008510 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008511 last_set_msg(sgp->sg_scriptID);
8512#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008513}
8514
8515 static int
8516highlight_list_arg(id, didh, type, iarg, sarg, name)
8517 int id;
8518 int didh;
8519 int type;
8520 int iarg;
8521 char_u *sarg;
8522 char *name;
8523{
8524 char_u buf[100];
8525 char_u *ts;
8526 int i;
8527
Bram Moolenaar661b1822005-07-28 22:36:45 +00008528 if (got_int)
8529 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8531 {
8532 ts = buf;
8533 if (type == LIST_INT)
8534 sprintf((char *)buf, "%d", iarg - 1);
8535 else if (type == LIST_STRING)
8536 ts = sarg;
8537 else /* type == LIST_ATTR */
8538 {
8539 buf[0] = NUL;
8540 for (i = 0; hl_attr_table[i] != 0; ++i)
8541 {
8542 if (iarg & hl_attr_table[i])
8543 {
8544 if (buf[0] != NUL)
8545 STRCAT(buf, ",");
8546 STRCAT(buf, hl_name_table[i]);
8547 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8548 }
8549 }
8550 }
8551
8552 (void)syn_list_header(didh,
8553 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8554 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008555 if (!got_int)
8556 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008557 if (*name != NUL)
8558 {
8559 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8560 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8561 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008562 msg_outtrans(ts);
8563 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008564 }
8565 return didh;
8566}
8567
8568#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8569/*
8570 * Return "1" if highlight group "id" has attribute "flag".
8571 * Return NULL otherwise.
8572 */
8573 char_u *
8574highlight_has_attr(id, flag, modec)
8575 int id;
8576 int flag;
8577 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8578{
8579 int attr;
8580
8581 if (id <= 0 || id > highlight_ga.ga_len)
8582 return NULL;
8583
Bram Moolenaar61623362010-07-14 22:04:22 +02008584#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585 if (modec == 'g')
8586 attr = HL_TABLE()[id - 1].sg_gui;
8587 else
8588#endif
8589 if (modec == 'c')
8590 attr = HL_TABLE()[id - 1].sg_cterm;
8591 else
8592 attr = HL_TABLE()[id - 1].sg_term;
8593
8594 if (attr & flag)
8595 return (char_u *)"1";
8596 return NULL;
8597}
8598#endif
8599
8600#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8601/*
8602 * Return color name of highlight group "id".
8603 */
8604 char_u *
8605highlight_color(id, what, modec)
8606 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008607 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008608 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8609{
8610 static char_u name[20];
8611 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008612 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008613 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008614 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008615
8616 if (id <= 0 || id > highlight_ga.ga_len)
8617 return NULL;
8618
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008619 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008620 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008621 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008622 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008623 font = TRUE;
8624 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008625 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008626 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8627 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628 if (modec == 'g')
8629 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008630# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008631 /* return font name */
8632 if (font)
8633 return HL_TABLE()[id - 1].sg_font_name;
8634
Bram Moolenaar071d4272004-06-13 20:20:40 +00008635 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008636 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008637 {
8638 guicolor_T color;
8639 long_u rgb;
8640 static char_u buf[10];
8641
8642 if (fg)
8643 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008644 else if (sp)
8645 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008646 else
8647 color = HL_TABLE()[id - 1].sg_gui_bg;
8648 if (color == INVALCOLOR)
8649 return NULL;
8650 rgb = gui_mch_get_rgb(color);
8651 sprintf((char *)buf, "#%02x%02x%02x",
8652 (unsigned)(rgb >> 16),
8653 (unsigned)(rgb >> 8) & 255,
8654 (unsigned)rgb & 255);
8655 return buf;
8656 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008657#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008658 if (fg)
8659 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008660 if (sp)
8661 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8663 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008664 if (font || sp)
8665 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008666 if (modec == 'c')
8667 {
8668 if (fg)
8669 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8670 else
8671 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8672 sprintf((char *)name, "%d", n);
8673 return name;
8674 }
8675 /* term doesn't have color */
8676 return NULL;
8677}
8678#endif
8679
8680#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8681 || defined(PROTO)
8682/*
8683 * Return color name of highlight group "id" as RGB value.
8684 */
8685 long_u
8686highlight_gui_color_rgb(id, fg)
8687 int id;
8688 int fg; /* TRUE = fg, FALSE = bg */
8689{
8690 guicolor_T color;
8691
8692 if (id <= 0 || id > highlight_ga.ga_len)
8693 return 0L;
8694
8695 if (fg)
8696 color = HL_TABLE()[id - 1].sg_gui_fg;
8697 else
8698 color = HL_TABLE()[id - 1].sg_gui_bg;
8699
8700 if (color == INVALCOLOR)
8701 return 0L;
8702
8703 return gui_mch_get_rgb(color);
8704}
8705#endif
8706
8707/*
8708 * Output the syntax list header.
8709 * Return TRUE when started a new line.
8710 */
8711 static int
8712syn_list_header(did_header, outlen, id)
8713 int did_header; /* did header already */
8714 int outlen; /* length of string that comes */
8715 int id; /* highlight group id */
8716{
8717 int endcol = 19;
8718 int newline = TRUE;
8719
8720 if (!did_header)
8721 {
8722 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008723 if (got_int)
8724 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008725 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8726 endcol = 15;
8727 }
8728 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008729 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008730 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008731 if (got_int)
8732 return TRUE;
8733 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008734 else
8735 {
8736 if (msg_col >= endcol) /* wrap around is like starting a new line */
8737 newline = FALSE;
8738 }
8739
8740 if (msg_col >= endcol) /* output at least one space */
8741 endcol = msg_col + 1;
8742 if (Columns <= endcol) /* avoid hang for tiny window */
8743 endcol = Columns - 1;
8744
8745 msg_advance(endcol);
8746
8747 /* Show "xxx" with the attributes. */
8748 if (!did_header)
8749 {
8750 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8751 msg_putchar(' ');
8752 }
8753
8754 return newline;
8755}
8756
8757/*
8758 * Set the attribute numbers for a highlight group.
8759 * Called after one of the attributes has changed.
8760 */
8761 static void
8762set_hl_attr(idx)
8763 int idx; /* index in array */
8764{
8765 attrentry_T at_en;
8766 struct hl_group *sgp = HL_TABLE() + idx;
8767
8768 /* The "Normal" group doesn't need an attribute number */
8769 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8770 return;
8771
8772#ifdef FEAT_GUI
8773 /*
8774 * For the GUI mode: If there are other than "normal" highlighting
8775 * attributes, need to allocate an attr number.
8776 */
8777 if (sgp->sg_gui_fg == INVALCOLOR
8778 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008779 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008780 && sgp->sg_font == NOFONT
8781# ifdef FEAT_XFONTSET
8782 && sgp->sg_fontset == NOFONTSET
8783# endif
8784 )
8785 {
8786 sgp->sg_gui_attr = sgp->sg_gui;
8787 }
8788 else
8789 {
8790 at_en.ae_attr = sgp->sg_gui;
8791 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8792 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008793 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008794 at_en.ae_u.gui.font = sgp->sg_font;
8795# ifdef FEAT_XFONTSET
8796 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8797# endif
8798 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8799 }
8800#endif
8801 /*
8802 * For the term mode: If there are other than "normal" highlighting
8803 * attributes, need to allocate an attr number.
8804 */
8805 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8806 sgp->sg_term_attr = sgp->sg_term;
8807 else
8808 {
8809 at_en.ae_attr = sgp->sg_term;
8810 at_en.ae_u.term.start = sgp->sg_start;
8811 at_en.ae_u.term.stop = sgp->sg_stop;
8812 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8813 }
8814
8815 /*
8816 * For the color term mode: If there are other than "normal"
8817 * highlighting attributes, need to allocate an attr number.
8818 */
8819 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8820 sgp->sg_cterm_attr = sgp->sg_cterm;
8821 else
8822 {
8823 at_en.ae_attr = sgp->sg_cterm;
8824 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8825 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8826 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8827 }
8828}
8829
8830/*
8831 * Lookup a highlight group name and return it's ID.
8832 * If it is not found, 0 is returned.
8833 */
8834 int
8835syn_name2id(name)
8836 char_u *name;
8837{
8838 int i;
8839 char_u name_u[200];
8840
8841 /* Avoid using stricmp() too much, it's slow on some systems */
8842 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8843 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008844 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008845 vim_strup(name_u);
8846 for (i = highlight_ga.ga_len; --i >= 0; )
8847 if (HL_TABLE()[i].sg_name_u != NULL
8848 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8849 break;
8850 return i + 1;
8851}
8852
8853#if defined(FEAT_EVAL) || defined(PROTO)
8854/*
8855 * Return TRUE if highlight group "name" exists.
8856 */
8857 int
8858highlight_exists(name)
8859 char_u *name;
8860{
8861 return (syn_name2id(name) > 0);
8862}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008863
8864# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8865/*
8866 * Return the name of highlight group "id".
8867 * When not a valid ID return an empty string.
8868 */
8869 char_u *
8870syn_id2name(id)
8871 int id;
8872{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008873 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008874 return (char_u *)"";
8875 return HL_TABLE()[id - 1].sg_name;
8876}
8877# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008878#endif
8879
8880/*
8881 * Like syn_name2id(), but take a pointer + length argument.
8882 */
8883 int
8884syn_namen2id(linep, len)
8885 char_u *linep;
8886 int len;
8887{
8888 char_u *name;
8889 int id = 0;
8890
8891 name = vim_strnsave(linep, len);
8892 if (name != NULL)
8893 {
8894 id = syn_name2id(name);
8895 vim_free(name);
8896 }
8897 return id;
8898}
8899
8900/*
8901 * Find highlight group name in the table and return it's ID.
8902 * The argument is a pointer to the name and the length of the name.
8903 * If it doesn't exist yet, a new entry is created.
8904 * Return 0 for failure.
8905 */
8906 int
8907syn_check_group(pp, len)
8908 char_u *pp;
8909 int len;
8910{
8911 int id;
8912 char_u *name;
8913
8914 name = vim_strnsave(pp, len);
8915 if (name == NULL)
8916 return 0;
8917
8918 id = syn_name2id(name);
8919 if (id == 0) /* doesn't exist yet */
8920 id = syn_add_group(name);
8921 else
8922 vim_free(name);
8923 return id;
8924}
8925
8926/*
8927 * Add new highlight group and return it's ID.
8928 * "name" must be an allocated string, it will be consumed.
8929 * Return 0 for failure.
8930 */
8931 static int
8932syn_add_group(name)
8933 char_u *name;
8934{
8935 char_u *p;
8936
8937 /* Check that the name is ASCII letters, digits and underscore. */
8938 for (p = name; *p != NUL; ++p)
8939 {
8940 if (!vim_isprintc(*p))
8941 {
8942 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008943 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008944 return 0;
8945 }
8946 else if (!ASCII_ISALNUM(*p) && *p != '_')
8947 {
8948 /* This is an error, but since there previously was no check only
8949 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008950 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008951 MSG(_("W18: Invalid character in group name"));
8952 break;
8953 }
8954 }
8955
8956 /*
8957 * First call for this growarray: init growing array.
8958 */
8959 if (highlight_ga.ga_data == NULL)
8960 {
8961 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8962 highlight_ga.ga_growsize = 10;
8963 }
8964
8965 /*
8966 * Make room for at least one other syntax_highlight entry.
8967 */
8968 if (ga_grow(&highlight_ga, 1) == FAIL)
8969 {
8970 vim_free(name);
8971 return 0;
8972 }
8973
8974 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8975 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8976 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8977#ifdef FEAT_GUI
8978 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8979 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008980 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008981#endif
8982 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008983
8984 return highlight_ga.ga_len; /* ID is index plus one */
8985}
8986
8987/*
8988 * When, just after calling syn_add_group(), an error is discovered, this
8989 * function deletes the new name.
8990 */
8991 static void
8992syn_unadd_group()
8993{
8994 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008995 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8996 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8997}
8998
8999/*
9000 * Translate a group ID to highlight attributes.
9001 */
9002 int
9003syn_id2attr(hl_id)
9004 int hl_id;
9005{
9006 int attr;
9007 struct hl_group *sgp;
9008
9009 hl_id = syn_get_final_id(hl_id);
9010 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9011
9012#ifdef FEAT_GUI
9013 /*
9014 * Only use GUI attr when the GUI is being used.
9015 */
9016 if (gui.in_use)
9017 attr = sgp->sg_gui_attr;
9018 else
9019#endif
9020 if (t_colors > 1)
9021 attr = sgp->sg_cterm_attr;
9022 else
9023 attr = sgp->sg_term_attr;
9024
9025 return attr;
9026}
9027
9028#ifdef FEAT_GUI
9029/*
9030 * Get the GUI colors and attributes for a group ID.
9031 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9032 */
9033 int
9034syn_id2colors(hl_id, fgp, bgp)
9035 int hl_id;
9036 guicolor_T *fgp;
9037 guicolor_T *bgp;
9038{
9039 struct hl_group *sgp;
9040
9041 hl_id = syn_get_final_id(hl_id);
9042 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9043
9044 *fgp = sgp->sg_gui_fg;
9045 *bgp = sgp->sg_gui_bg;
9046 return sgp->sg_gui;
9047}
9048#endif
9049
9050/*
9051 * Translate a group ID to the final group ID (following links).
9052 */
9053 int
9054syn_get_final_id(hl_id)
9055 int hl_id;
9056{
9057 int count;
9058 struct hl_group *sgp;
9059
9060 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9061 return 0; /* Can be called from eval!! */
9062
9063 /*
9064 * Follow links until there is no more.
9065 * Look out for loops! Break after 100 links.
9066 */
9067 for (count = 100; --count >= 0; )
9068 {
9069 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9070 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9071 break;
9072 hl_id = sgp->sg_link;
9073 }
9074
9075 return hl_id;
9076}
9077
9078#ifdef FEAT_GUI
9079/*
9080 * Call this function just after the GUI has started.
9081 * It finds the font and color handles for the highlighting groups.
9082 */
9083 void
9084highlight_gui_started()
9085{
9086 int idx;
9087
9088 /* First get the colors from the "Normal" and "Menu" group, if set */
9089 set_normal_colors();
9090
9091 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9092 gui_do_one_color(idx, FALSE, FALSE);
9093
9094 highlight_changed();
9095}
9096
9097 static void
9098gui_do_one_color(idx, do_menu, do_tooltip)
9099 int idx;
9100 int do_menu; /* TRUE: might set the menu font */
9101 int do_tooltip; /* TRUE: might set the tooltip font */
9102{
9103 int didit = FALSE;
9104
9105 if (HL_TABLE()[idx].sg_font_name != NULL)
9106 {
9107 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9108 do_tooltip);
9109 didit = TRUE;
9110 }
9111 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9112 {
9113 HL_TABLE()[idx].sg_gui_fg =
9114 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9115 didit = TRUE;
9116 }
9117 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9118 {
9119 HL_TABLE()[idx].sg_gui_bg =
9120 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9121 didit = TRUE;
9122 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009123 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9124 {
9125 HL_TABLE()[idx].sg_gui_sp =
9126 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9127 didit = TRUE;
9128 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009129 if (didit) /* need to get a new attr number */
9130 set_hl_attr(idx);
9131}
9132
9133#endif
9134
9135/*
9136 * Translate the 'highlight' option into attributes in highlight_attr[] and
9137 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9138 * corresponding highlights to use on top of HLF_SNC is computed.
9139 * Called only when the 'highlight' option has been changed and upon first
9140 * screen redraw after any :highlight command.
9141 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9142 */
9143 int
9144highlight_changed()
9145{
9146 int hlf;
9147 int i;
9148 char_u *p;
9149 int attr;
9150 char_u *end;
9151 int id;
9152#ifdef USER_HIGHLIGHT
9153 char_u userhl[10];
9154# ifdef FEAT_STL_OPT
9155 int id_SNC = -1;
9156 int id_S = -1;
9157 int hlcnt;
9158# endif
9159#endif
9160 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9161
9162 need_highlight_changed = FALSE;
9163
9164 /*
9165 * Clear all attributes.
9166 */
9167 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9168 highlight_attr[hlf] = 0;
9169
9170 /*
9171 * First set all attributes to their default value.
9172 * Then use the attributes from the 'highlight' option.
9173 */
9174 for (i = 0; i < 2; ++i)
9175 {
9176 if (i)
9177 p = p_hl;
9178 else
9179 p = get_highlight_default();
9180 if (p == NULL) /* just in case */
9181 continue;
9182
9183 while (*p)
9184 {
9185 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9186 if (hl_flags[hlf] == *p)
9187 break;
9188 ++p;
9189 if (hlf == (int)HLF_COUNT || *p == NUL)
9190 return FAIL;
9191
9192 /*
9193 * Allow several hl_flags to be combined, like "bu" for
9194 * bold-underlined.
9195 */
9196 attr = 0;
9197 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9198 {
9199 if (vim_iswhite(*p)) /* ignore white space */
9200 continue;
9201
9202 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9203 return FAIL;
9204
9205 switch (*p)
9206 {
9207 case 'b': attr |= HL_BOLD;
9208 break;
9209 case 'i': attr |= HL_ITALIC;
9210 break;
9211 case '-':
9212 case 'n': /* no highlighting */
9213 break;
9214 case 'r': attr |= HL_INVERSE;
9215 break;
9216 case 's': attr |= HL_STANDOUT;
9217 break;
9218 case 'u': attr |= HL_UNDERLINE;
9219 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009220 case 'c': attr |= HL_UNDERCURL;
9221 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009222 case ':': ++p; /* highlight group name */
9223 if (attr || *p == NUL) /* no combinations */
9224 return FAIL;
9225 end = vim_strchr(p, ',');
9226 if (end == NULL)
9227 end = p + STRLEN(p);
9228 id = syn_check_group(p, (int)(end - p));
9229 if (id == 0)
9230 return FAIL;
9231 attr = syn_id2attr(id);
9232 p = end - 1;
9233#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9234 if (hlf == (int)HLF_SNC)
9235 id_SNC = syn_get_final_id(id);
9236 else if (hlf == (int)HLF_S)
9237 id_S = syn_get_final_id(id);
9238#endif
9239 break;
9240 default: return FAIL;
9241 }
9242 }
9243 highlight_attr[hlf] = attr;
9244
9245 p = skip_to_option_part(p); /* skip comma and spaces */
9246 }
9247 }
9248
9249#ifdef USER_HIGHLIGHT
9250 /* Setup the user highlights
9251 *
9252 * Temporarily utilize 10 more hl entries. Have to be in there
9253 * simultaneously in case of table overflows in get_attr_entry()
9254 */
9255# ifdef FEAT_STL_OPT
9256 if (ga_grow(&highlight_ga, 10) == FAIL)
9257 return FAIL;
9258 hlcnt = highlight_ga.ga_len;
9259 if (id_S == 0)
9260 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009261 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009262 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9263 id_S = hlcnt + 10;
9264 }
9265# endif
9266 for (i = 0; i < 9; i++)
9267 {
9268 sprintf((char *)userhl, "User%d", i + 1);
9269 id = syn_name2id(userhl);
9270 if (id == 0)
9271 {
9272 highlight_user[i] = 0;
9273# ifdef FEAT_STL_OPT
9274 highlight_stlnc[i] = 0;
9275# endif
9276 }
9277 else
9278 {
9279# ifdef FEAT_STL_OPT
9280 struct hl_group *hlt = HL_TABLE();
9281# endif
9282
9283 highlight_user[i] = syn_id2attr(id);
9284# ifdef FEAT_STL_OPT
9285 if (id_SNC == 0)
9286 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009287 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009288 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9289 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009290# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009291 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9292# endif
9293 }
9294 else
9295 mch_memmove(&hlt[hlcnt + i],
9296 &hlt[id_SNC - 1],
9297 sizeof(struct hl_group));
9298 hlt[hlcnt + i].sg_link = 0;
9299
9300 /* Apply difference between UserX and HLF_S to HLF_SNC */
9301 hlt[hlcnt + i].sg_term ^=
9302 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9303 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9304 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9305 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9306 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9307 hlt[hlcnt + i].sg_cterm ^=
9308 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9309 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9310 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9311 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9312 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009313# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009314 hlt[hlcnt + i].sg_gui ^=
9315 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009316# endif
9317# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009318 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9319 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9320 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9321 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009322 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9323 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009324 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9325 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9326# ifdef FEAT_XFONTSET
9327 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9328 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9329# endif
9330# endif
9331 highlight_ga.ga_len = hlcnt + i + 1;
9332 set_hl_attr(hlcnt + i); /* At long last we can apply */
9333 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9334# endif
9335 }
9336 }
9337# ifdef FEAT_STL_OPT
9338 highlight_ga.ga_len = hlcnt;
9339# endif
9340
9341#endif /* USER_HIGHLIGHT */
9342
9343 return OK;
9344}
9345
Bram Moolenaar4f688582007-07-24 12:34:30 +00009346#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009347
9348static void highlight_list __ARGS((void));
9349static void highlight_list_two __ARGS((int cnt, int attr));
9350
9351/*
9352 * Handle command line completion for :highlight command.
9353 */
9354 void
9355set_context_in_highlight_cmd(xp, arg)
9356 expand_T *xp;
9357 char_u *arg;
9358{
9359 char_u *p;
9360
9361 /* Default: expand group names */
9362 xp->xp_context = EXPAND_HIGHLIGHT;
9363 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009364 include_link = 2;
9365 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009366
9367 /* (part of) subcommand already typed */
9368 if (*arg != NUL)
9369 {
9370 p = skiptowhite(arg);
9371 if (*p != NUL) /* past "default" or group name */
9372 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009373 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009374 if (STRNCMP("default", arg, p - arg) == 0)
9375 {
9376 arg = skipwhite(p);
9377 xp->xp_pattern = arg;
9378 p = skiptowhite(arg);
9379 }
9380 if (*p != NUL) /* past group name */
9381 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009382 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009383 if (arg[1] == 'i' && arg[0] == 'N')
9384 highlight_list();
9385 if (STRNCMP("link", arg, p - arg) == 0
9386 || STRNCMP("clear", arg, p - arg) == 0)
9387 {
9388 xp->xp_pattern = skipwhite(p);
9389 p = skiptowhite(xp->xp_pattern);
9390 if (*p != NUL) /* past first group name */
9391 {
9392 xp->xp_pattern = skipwhite(p);
9393 p = skiptowhite(xp->xp_pattern);
9394 }
9395 }
9396 if (*p != NUL) /* past group name(s) */
9397 xp->xp_context = EXPAND_NOTHING;
9398 }
9399 }
9400 }
9401}
9402
9403/*
9404 * List highlighting matches in a nice way.
9405 */
9406 static void
9407highlight_list()
9408{
9409 int i;
9410
9411 for (i = 10; --i >= 0; )
9412 highlight_list_two(i, hl_attr(HLF_D));
9413 for (i = 40; --i >= 0; )
9414 highlight_list_two(99, 0);
9415}
9416
9417 static void
9418highlight_list_two(cnt, attr)
9419 int cnt;
9420 int attr;
9421{
9422 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9423 msg_clr_eos();
9424 out_flush();
9425 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9426}
9427
9428#endif /* FEAT_CMDL_COMPL */
9429
9430#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9431 || defined(FEAT_SIGNS) || defined(PROTO)
9432/*
9433 * Function given to ExpandGeneric() to obtain the list of group names.
9434 * Also used for synIDattr() function.
9435 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009436 char_u *
9437get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009438 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009439 int idx;
9440{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009441#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009442 if (idx == highlight_ga.ga_len && include_none != 0)
9443 return (char_u *)"none";
9444 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009445 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009446 if (idx == highlight_ga.ga_len + include_none + include_default
9447 && include_link != 0)
9448 return (char_u *)"link";
9449 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9450 && include_link != 0)
9451 return (char_u *)"clear";
9452#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009453 if (idx < 0 || idx >= highlight_ga.ga_len)
9454 return NULL;
9455 return HL_TABLE()[idx].sg_name;
9456}
9457#endif
9458
Bram Moolenaar4f688582007-07-24 12:34:30 +00009459#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009460/*
9461 * Free all the highlight group fonts.
9462 * Used when quitting for systems which need it.
9463 */
9464 void
9465free_highlight_fonts()
9466{
9467 int idx;
9468
9469 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9470 {
9471 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9472 HL_TABLE()[idx].sg_font = NOFONT;
9473# ifdef FEAT_XFONTSET
9474 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9475 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9476# endif
9477 }
9478
9479 gui_mch_free_font(gui.norm_font);
9480# ifdef FEAT_XFONTSET
9481 gui_mch_free_fontset(gui.fontset);
9482# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009483# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009484 gui_mch_free_font(gui.bold_font);
9485 gui_mch_free_font(gui.ital_font);
9486 gui_mch_free_font(gui.boldital_font);
9487# endif
9488}
9489#endif
9490
9491/**************************************
9492 * End of Highlighting stuff *
9493 **************************************/