blob: 099e0a7c0d110e9023671d6a8bf9715ef9737e00 [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 Moolenaar071d4272004-06-13 20:20:40 +0000477 /*
478 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000479 * Also do this when a change was made, the current state may be invalid
480 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200482 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 {
484 invalidate_current_state();
485 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000488 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 syn_win = wp;
490
491 /*
492 * Allocate syntax stack when needed.
493 */
494 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200495 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000496 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200497 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498
499 /*
500 * If the state of the end of the previous line is useful, store it.
501 */
502 if (VALID_STATE(&current_state)
503 && current_lnum < lnum
504 && current_lnum < syn_buf->b_ml.ml_line_count)
505 {
506 (void)syn_finish_line(FALSE);
507 if (!current_state_stored)
508 {
509 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000510 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000511 }
512
513 /*
514 * If the current_lnum is now the same as "lnum", keep the current
515 * state (this happens very often!). Otherwise invalidate
516 * current_state and figure it out below.
517 */
518 if (current_lnum != lnum)
519 invalidate_current_state();
520 }
521 else
522 invalidate_current_state();
523
524 /*
525 * Try to synchronize from a saved state in b_sst_array[].
526 * Only do this if lnum is not before and not to far beyond a saved state.
527 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200528 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529 {
530 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200531 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 {
533 if (p->sst_lnum > lnum)
534 break;
535 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
536 {
537 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200538 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539 last_min_valid = p;
540 }
541 }
542 if (last_min_valid != NULL)
543 load_current_state(last_min_valid);
544 }
545
546 /*
547 * If "lnum" is before or far beyond a line with a saved state, need to
548 * re-synchronize.
549 */
550 if (INVALID_STATE(&current_state))
551 {
552 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200553 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000554 }
555 else
556 first_stored = current_lnum;
557
558 /*
559 * Advance from the sync point or saved state until the current line.
560 * Save some entries for syncing with later on.
561 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200562 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000563 dist = 999999;
564 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200565 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566 while (current_lnum < lnum)
567 {
568 syn_start_line();
569 (void)syn_finish_line(FALSE);
570 ++current_lnum;
571
572 /* If we parsed at least "minlines" lines or started at a valid
573 * state, the current state is considered valid. */
574 if (current_lnum >= first_stored)
575 {
576 /* Check if the saved state entry is for the current line and is
577 * equal to the current state. If so, then validate all saved
578 * states that depended on a change before the parsed line. */
579 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000580 prev = syn_stack_find_entry(current_lnum - 1);
581 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200582 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000583 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000584 sp = prev;
585 while (sp != NULL && sp->sst_lnum < current_lnum)
586 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 if (sp != NULL
588 && sp->sst_lnum == current_lnum
589 && syn_stack_equal(sp))
590 {
591 parsed_lnum = current_lnum;
592 prev = sp;
593 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
594 {
595 if (sp->sst_lnum <= lnum)
596 /* valid state before desired line, use this one */
597 prev = sp;
598 else if (sp->sst_change_lnum == 0)
599 /* past saved states depending on change, break here. */
600 break;
601 sp->sst_change_lnum = 0;
602 sp = sp->sst_next;
603 }
604 load_current_state(prev);
605 }
606 /* Store the state at this line when it's the first one, the line
607 * where we start parsing, or some distance from the previously
608 * saved state. But only when parsed at least 'minlines'. */
609 else if (prev == NULL
610 || current_lnum == lnum
611 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000612 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613 }
614
615 /* This can take a long time: break when CTRL-C pressed. The current
616 * state will be wrong then. */
617 line_breakcheck();
618 if (got_int)
619 {
620 current_lnum = lnum;
621 break;
622 }
623 }
624
625 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000626}
627
628/*
629 * We cannot simply discard growarrays full of state_items or buf_states; we
630 * have to manually release their extmatch pointers first.
631 */
632 static void
633clear_syn_state(p)
634 synstate_T *p;
635{
636 int i;
637 garray_T *gap;
638
639 if (p->sst_stacksize > SST_FIX_STATES)
640 {
641 gap = &(p->sst_union.sst_ga);
642 for (i = 0; i < gap->ga_len; i++)
643 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
644 ga_clear(gap);
645 }
646 else
647 {
648 for (i = 0; i < p->sst_stacksize; i++)
649 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
650 }
651}
652
653/*
654 * Cleanup the current_state stack.
655 */
656 static void
657clear_current_state()
658{
659 int i;
660 stateitem_T *sip;
661
662 sip = (stateitem_T *)(current_state.ga_data);
663 for (i = 0; i < current_state.ga_len; i++)
664 unref_extmatch(sip[i].si_extmatch);
665 ga_clear(&current_state);
666}
667
668/*
669 * Try to find a synchronisation point for line "lnum".
670 *
671 * This sets current_lnum and the current state. One of three methods is
672 * used:
673 * 1. Search backwards for the end of a C-comment.
674 * 2. Search backwards for given sync patterns.
675 * 3. Simply start on a given number of lines above "lnum".
676 */
677 static void
678syn_sync(wp, start_lnum, last_valid)
679 win_T *wp;
680 linenr_T start_lnum;
681 synstate_T *last_valid;
682{
683 buf_T *curbuf_save;
684 win_T *curwin_save;
685 pos_T cursor_save;
686 int idx;
687 linenr_T lnum;
688 linenr_T end_lnum;
689 linenr_T break_lnum;
690 int had_sync_point;
691 stateitem_T *cur_si;
692 synpat_T *spp;
693 char_u *line;
694 int found_flags = 0;
695 int found_match_idx = 0;
696 linenr_T found_current_lnum = 0;
697 int found_current_col= 0;
698 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000699 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700
701 /*
702 * Clear any current state that might be hanging around.
703 */
704 invalidate_current_state();
705
706 /*
707 * Start at least "minlines" back. Default starting point for parsing is
708 * there.
709 * Start further back, to avoid that scrolling backwards will result in
710 * resyncing for every line. Now it resyncs only one out of N lines,
711 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
712 * Watch out for overflow when minlines is MAXLNUM.
713 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200714 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 start_lnum = 1;
716 else
717 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200718 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200720 else if (syn_block->b_syn_sync_minlines < 10)
721 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000722 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200723 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
724 if (syn_block->b_syn_sync_maxlines != 0
725 && lnum > syn_block->b_syn_sync_maxlines)
726 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000727 if (lnum >= start_lnum)
728 start_lnum = 1;
729 else
730 start_lnum -= lnum;
731 }
732 current_lnum = start_lnum;
733
734 /*
735 * 1. Search backwards for the end of a C-style comment.
736 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200737 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000738 {
739 /* Need to make syn_buf the current buffer for a moment, to be able to
740 * use find_start_comment(). */
741 curwin_save = curwin;
742 curwin = wp;
743 curbuf_save = curbuf;
744 curbuf = syn_buf;
745
746 /*
747 * Skip lines that end in a backslash.
748 */
749 for ( ; start_lnum > 1; --start_lnum)
750 {
751 line = ml_get(start_lnum - 1);
752 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
753 break;
754 }
755 current_lnum = start_lnum;
756
757 /* set cursor to start of search */
758 cursor_save = wp->w_cursor;
759 wp->w_cursor.lnum = start_lnum;
760 wp->w_cursor.col = 0;
761
762 /*
763 * If the line is inside a comment, need to find the syntax item that
764 * defines the comment.
765 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
766 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200767 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000768 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200769 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
770 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
771 == syn_block->b_syn_sync_id
772 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000773 {
774 validate_current_state();
775 if (push_current_state(idx) == OK)
776 update_si_attr(current_state.ga_len - 1);
777 break;
778 }
779 }
780
781 /* restore cursor and buffer */
782 wp->w_cursor = cursor_save;
783 curwin = curwin_save;
784 curbuf = curbuf_save;
785 }
786
787 /*
788 * 2. Search backwards for given sync patterns.
789 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200790 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000791 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200792 if (syn_block->b_syn_sync_maxlines != 0
793 && start_lnum > syn_block->b_syn_sync_maxlines)
794 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795 else
796 break_lnum = 0;
797
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000798 found_m_endpos.lnum = 0;
799 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 end_lnum = start_lnum;
801 lnum = start_lnum;
802 while (--lnum > break_lnum)
803 {
804 /* This can take a long time: break when CTRL-C pressed. */
805 line_breakcheck();
806 if (got_int)
807 {
808 invalidate_current_state();
809 current_lnum = start_lnum;
810 break;
811 }
812
813 /* Check if we have run into a valid saved state stack now. */
814 if (last_valid != NULL && lnum == last_valid->sst_lnum)
815 {
816 load_current_state(last_valid);
817 break;
818 }
819
820 /*
821 * Check if the previous line has the line-continuation pattern.
822 */
823 if (lnum > 1 && syn_match_linecont(lnum - 1))
824 continue;
825
826 /*
827 * Start with nothing on the state stack
828 */
829 validate_current_state();
830
831 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
832 {
833 syn_start_line();
834 for (;;)
835 {
836 had_sync_point = syn_finish_line(TRUE);
837 /*
838 * When a sync point has been found, remember where, and
839 * continue to look for another one, further on in the line.
840 */
841 if (had_sync_point && current_state.ga_len)
842 {
843 cur_si = &CUR_STATE(current_state.ga_len - 1);
844 if (cur_si->si_m_endpos.lnum > start_lnum)
845 {
846 /* ignore match that goes to after where started */
847 current_lnum = end_lnum;
848 break;
849 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000850 if (cur_si->si_idx < 0)
851 {
852 /* Cannot happen? */
853 found_flags = 0;
854 found_match_idx = KEYWORD_IDX;
855 }
856 else
857 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200858 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000859 found_flags = spp->sp_flags;
860 found_match_idx = spp->sp_sync_idx;
861 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000862 found_current_lnum = current_lnum;
863 found_current_col = current_col;
864 found_m_endpos = cur_si->si_m_endpos;
865 /*
866 * Continue after the match (be aware of a zero-length
867 * match).
868 */
869 if (found_m_endpos.lnum > current_lnum)
870 {
871 current_lnum = found_m_endpos.lnum;
872 current_col = found_m_endpos.col;
873 if (current_lnum >= end_lnum)
874 break;
875 }
876 else if (found_m_endpos.col > current_col)
877 current_col = found_m_endpos.col;
878 else
879 ++current_col;
880
881 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000882 * an item that ends here, need to do that now. Be
883 * careful not to go past the NUL. */
884 prev_current_col = current_col;
885 if (syn_getcurline()[current_col] != NUL)
886 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000887 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000888 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889 }
890 else
891 break;
892 }
893 }
894
895 /*
896 * If a sync point was encountered, break here.
897 */
898 if (found_flags)
899 {
900 /*
901 * Put the item that was specified by the sync point on the
902 * state stack. If there was no item specified, make the
903 * state stack empty.
904 */
905 clear_current_state();
906 if (found_match_idx >= 0
907 && push_current_state(found_match_idx) == OK)
908 update_si_attr(current_state.ga_len - 1);
909
910 /*
911 * When using "grouphere", continue from the sync point
912 * match, until the end of the line. Parsing starts at
913 * the next line.
914 * For "groupthere" the parsing starts at start_lnum.
915 */
916 if (found_flags & HL_SYNC_HERE)
917 {
918 if (current_state.ga_len)
919 {
920 cur_si = &CUR_STATE(current_state.ga_len - 1);
921 cur_si->si_h_startpos.lnum = found_current_lnum;
922 cur_si->si_h_startpos.col = found_current_col;
923 update_si_end(cur_si, (int)current_col, TRUE);
924 check_keepend();
925 }
926 current_col = found_m_endpos.col;
927 current_lnum = found_m_endpos.lnum;
928 (void)syn_finish_line(FALSE);
929 ++current_lnum;
930 }
931 else
932 current_lnum = start_lnum;
933
934 break;
935 }
936
937 end_lnum = lnum;
938 invalidate_current_state();
939 }
940
941 /* Ran into start of the file or exceeded maximum number of lines */
942 if (lnum <= break_lnum)
943 {
944 invalidate_current_state();
945 current_lnum = break_lnum + 1;
946 }
947 }
948
949 validate_current_state();
950}
951
952/*
953 * Return TRUE if the line-continuation pattern matches in line "lnum".
954 */
955 static int
956syn_match_linecont(lnum)
957 linenr_T lnum;
958{
959 regmmatch_T regmatch;
960
Bram Moolenaar860cae12010-06-05 23:22:07 +0200961 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200963 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
964 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000965 return syn_regexec(&regmatch, lnum, (colnr_T)0);
966 }
967 return FALSE;
968}
969
970/*
971 * Prepare the current state for the start of a line.
972 */
973 static void
974syn_start_line()
975{
976 current_finished = FALSE;
977 current_col = 0;
978
979 /*
980 * Need to update the end of a start/skip/end that continues from the
981 * previous line and regions that have "keepend".
982 */
983 if (current_state.ga_len > 0)
984 syn_update_ends(TRUE);
985
986 next_match_idx = -1;
987 ++current_line_id;
988}
989
990/*
991 * Check for items in the stack that need their end updated.
992 * When "startofline" is TRUE the last item is always updated.
993 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
994 */
995 static void
996syn_update_ends(startofline)
997 int startofline;
998{
999 stateitem_T *cur_si;
1000 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001001 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002
1003 if (startofline)
1004 {
1005 /* Check for a match carried over from a previous line with a
1006 * contained region. The match ends as soon as the region ends. */
1007 for (i = 0; i < current_state.ga_len; ++i)
1008 {
1009 cur_si = &CUR_STATE(i);
1010 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001011 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001012 == SPTYPE_MATCH
1013 && cur_si->si_m_endpos.lnum < current_lnum)
1014 {
1015 cur_si->si_flags |= HL_MATCHCONT;
1016 cur_si->si_m_endpos.lnum = 0;
1017 cur_si->si_m_endpos.col = 0;
1018 cur_si->si_h_endpos = cur_si->si_m_endpos;
1019 cur_si->si_ends = TRUE;
1020 }
1021 }
1022 }
1023
1024 /*
1025 * Need to update the end of a start/skip/end that continues from the
1026 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001027 * influence contained items. If we've just removed "extend"
1028 * (startofline == 0) then we should update ends of normal regions
1029 * contained inside "keepend" because "extend" could have extended
1030 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001031 * Then check for items ending in column 0.
1032 */
1033 i = current_state.ga_len - 1;
1034 if (keepend_level >= 0)
1035 for ( ; i > keepend_level; --i)
1036 if (CUR_STATE(i).si_flags & HL_EXTEND)
1037 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001038
1039 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 for ( ; i < current_state.ga_len; ++i)
1041 {
1042 cur_si = &CUR_STATE(i);
1043 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001044 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045 || (i == current_state.ga_len - 1 && startofline))
1046 {
1047 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1048 cur_si->si_h_startpos.lnum = current_lnum;
1049
1050 if (!(cur_si->si_flags & HL_MATCHCONT))
1051 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001052
1053 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1054 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055 }
1056 }
1057 check_keepend();
1058 check_state_ends();
1059}
1060
1061/****************************************
1062 * Handling of the state stack cache.
1063 */
1064
1065/*
1066 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1067 *
1068 * To speed up syntax highlighting, the state stack for the start of some
1069 * lines is cached. These entries can be used to start parsing at that point.
1070 *
1071 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1072 * valid entries. b_sst_first points to the first one, then follow sst_next.
1073 * The entries are sorted on line number. The first entry is often for line 2
1074 * (line 1 always starts with an empty stack).
1075 * There is also a list for free entries. This construction is used to avoid
1076 * having to allocate and free memory blocks too often.
1077 *
1078 * When making changes to the buffer, this is logged in b_mod_*. When calling
1079 * update_screen() to update the display, it will call
1080 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1081 * entries. The entries which are inside the changed area are removed,
1082 * because they must be recomputed. Entries below the changed have their line
1083 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1084 * set to indicate that a check must be made if the changed lines would change
1085 * the cached entry.
1086 *
1087 * When later displaying lines, an entry is stored for each line. Displayed
1088 * lines are likely to be displayed again, in which case the state at the
1089 * start of the line is needed.
1090 * For not displayed lines, an entry is stored for every so many lines. These
1091 * entries will be used e.g., when scrolling backwards. The distance between
1092 * entries depends on the number of lines in the buffer. For small buffers
1093 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1094 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1095 */
1096
Bram Moolenaar860cae12010-06-05 23:22:07 +02001097 static void
1098syn_stack_free_block(block)
1099 synblock_T *block;
1100{
1101 synstate_T *p;
1102
1103 if (block->b_sst_array != NULL)
1104 {
1105 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1106 clear_syn_state(p);
1107 vim_free(block->b_sst_array);
1108 block->b_sst_array = NULL;
1109 block->b_sst_len = 0;
1110 }
1111}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001112/*
1113 * Free b_sst_array[] for buffer "buf".
1114 * Used when syntax items changed to force resyncing everywhere.
1115 */
1116 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001117syn_stack_free_all(block)
1118 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 win_T *wp;
1121
Bram Moolenaar860cae12010-06-05 23:22:07 +02001122 syn_stack_free_block(block);
1123
1124
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125#ifdef FEAT_FOLDING
1126 /* When using "syntax" fold method, must update all folds. */
1127 FOR_ALL_WINDOWS(wp)
1128 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001129 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130 foldUpdateAll(wp);
1131 }
1132#endif
1133}
1134
1135/*
1136 * Allocate the syntax state stack for syn_buf when needed.
1137 * If the number of entries in b_sst_array[] is much too big or a bit too
1138 * small, reallocate it.
1139 * Also used to allocate b_sst_array[] for the first time.
1140 */
1141 static void
1142syn_stack_alloc()
1143{
1144 long len;
1145 synstate_T *to, *from;
1146 synstate_T *sstp;
1147
1148 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1149 if (len < SST_MIN_ENTRIES)
1150 len = SST_MIN_ENTRIES;
1151 else if (len > SST_MAX_ENTRIES)
1152 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001153 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154 {
1155 /* Allocate 50% too much, to avoid reallocating too often. */
1156 len = syn_buf->b_ml.ml_line_count;
1157 len = (len + len / 2) / SST_DIST + Rows * 2;
1158 if (len < SST_MIN_ENTRIES)
1159 len = SST_MIN_ENTRIES;
1160 else if (len > SST_MAX_ENTRIES)
1161 len = SST_MAX_ENTRIES;
1162
Bram Moolenaar860cae12010-06-05 23:22:07 +02001163 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164 {
1165 /* When shrinking the array, cleanup the existing stack.
1166 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 && syn_stack_cleanup())
1169 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001170 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1171 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 }
1173
1174 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1175 if (sstp == NULL) /* out of memory! */
1176 return;
1177
1178 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001179 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001180 {
1181 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001182 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001183 from = from->sst_next)
1184 {
1185 ++to;
1186 *to = *from;
1187 to->sst_next = to + 1;
1188 }
1189 }
1190 if (to != sstp - 1)
1191 {
1192 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001193 syn_block->b_sst_first = sstp;
1194 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 }
1196 else
1197 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001198 syn_block->b_sst_first = NULL;
1199 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001200 }
1201
1202 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001203 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 while (++to < sstp + len)
1205 to->sst_next = to + 1;
1206 (sstp + len - 1)->sst_next = NULL;
1207
Bram Moolenaar860cae12010-06-05 23:22:07 +02001208 vim_free(syn_block->b_sst_array);
1209 syn_block->b_sst_array = sstp;
1210 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211 }
1212}
1213
1214/*
1215 * Check for changes in a buffer to affect stored syntax states. Uses the
1216 * b_mod_* fields.
1217 * Called from update_screen(), before screen is being updated, once for each
1218 * displayed buffer.
1219 */
1220 void
1221syn_stack_apply_changes(buf)
1222 buf_T *buf;
1223{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001224 win_T *wp;
1225
1226 syn_stack_apply_changes_block(&buf->b_s, buf);
1227
1228 FOR_ALL_WINDOWS(wp)
1229 {
1230 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1231 syn_stack_apply_changes_block(wp->w_s, buf);
1232 }
1233}
1234
1235 static void
1236syn_stack_apply_changes_block(block, buf)
1237 synblock_T *block;
1238 buf_T *buf;
1239{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001240 synstate_T *p, *prev, *np;
1241 linenr_T n;
1242
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 return;
1245
1246 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 {
1251 n = p->sst_lnum + buf->b_mod_xlines;
1252 if (n <= buf->b_mod_bot)
1253 {
1254 /* this state is inside the changed area, remove it */
1255 np = p->sst_next;
1256 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001257 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 else
1259 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001260 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 p = np;
1262 continue;
1263 }
1264 /* This state is below the changed area. Remember the line
1265 * that needs to be parsed before this entry can be made valid
1266 * again. */
1267 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1268 {
1269 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1270 p->sst_change_lnum += buf->b_mod_xlines;
1271 else
1272 p->sst_change_lnum = buf->b_mod_top;
1273 }
1274 if (p->sst_change_lnum == 0
1275 || p->sst_change_lnum < buf->b_mod_bot)
1276 p->sst_change_lnum = buf->b_mod_bot;
1277
1278 p->sst_lnum = n;
1279 }
1280 prev = p;
1281 p = p->sst_next;
1282 }
1283}
1284
1285/*
1286 * Reduce the number of entries in the state stack for syn_buf.
1287 * Returns TRUE if at least one entry was freed.
1288 */
1289 static int
1290syn_stack_cleanup()
1291{
1292 synstate_T *p, *prev;
1293 disptick_T tick;
1294 int above;
1295 int dist;
1296 int retval = FALSE;
1297
Bram Moolenaar860cae12010-06-05 23:22:07 +02001298 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 return retval;
1300
1301 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001302 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001303 dist = 999999;
1304 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001305 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306
1307 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001308 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 * be removed. Set "above" when the "tick" for the oldest entry is above
1310 * "b_sst_lasttick" (the display tick wraps around).
1311 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001312 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001314 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1316 {
1317 if (prev->sst_lnum + dist > p->sst_lnum)
1318 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001319 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 {
1321 if (!above || p->sst_tick < tick)
1322 tick = p->sst_tick;
1323 above = TRUE;
1324 }
1325 else if (!above && p->sst_tick < tick)
1326 tick = p->sst_tick;
1327 }
1328 }
1329
1330 /*
1331 * Go through the list to make the entries for the oldest tick at an
1332 * interval of several lines.
1333 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001334 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001335 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1336 {
1337 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1338 {
1339 /* Move this entry from used list to free list */
1340 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001341 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001342 p = prev;
1343 retval = TRUE;
1344 }
1345 }
1346 return retval;
1347}
1348
1349/*
1350 * Free the allocated memory for a syn_state item.
1351 * Move the entry into the free list.
1352 */
1353 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001354syn_stack_free_entry(block, p)
1355 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356 synstate_T *p;
1357{
1358 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001359 p->sst_next = block->b_sst_firstfree;
1360 block->b_sst_firstfree = p;
1361 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362}
1363
1364/*
1365 * Find an entry in the list of state stacks at or before "lnum".
1366 * Returns NULL when there is no entry or the first entry is after "lnum".
1367 */
1368 static synstate_T *
1369syn_stack_find_entry(lnum)
1370 linenr_T lnum;
1371{
1372 synstate_T *p, *prev;
1373
1374 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 {
1377 if (p->sst_lnum == lnum)
1378 return p;
1379 if (p->sst_lnum > lnum)
1380 break;
1381 }
1382 return prev;
1383}
1384
1385/*
1386 * Try saving the current state in b_sst_array[].
1387 * The current state must be valid for the start of the current_lnum line!
1388 */
1389 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001390store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391{
1392 int i;
1393 synstate_T *p;
1394 bufstate_T *bp;
1395 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001396 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397
1398 /*
1399 * If the current state contains a start or end pattern that continues
1400 * from the previous line, we can't use it. Don't store it then.
1401 */
1402 for (i = current_state.ga_len - 1; i >= 0; --i)
1403 {
1404 cur_si = &CUR_STATE(i);
1405 if (cur_si->si_h_startpos.lnum >= current_lnum
1406 || cur_si->si_m_endpos.lnum >= current_lnum
1407 || cur_si->si_h_endpos.lnum >= current_lnum
1408 || (cur_si->si_end_idx
1409 && cur_si->si_eoe_pos.lnum >= current_lnum))
1410 break;
1411 }
1412 if (i >= 0)
1413 {
1414 if (sp != NULL)
1415 {
1416 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001417 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001419 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001420 else
1421 {
1422 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 if (p->sst_next == sp)
1425 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001426 if (p != NULL) /* just in case */
1427 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001429 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430 sp = NULL;
1431 }
1432 }
1433 else if (sp == NULL || sp->sst_lnum != current_lnum)
1434 {
1435 /*
1436 * Add a new entry
1437 */
1438 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001439 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001440 {
1441 (void)syn_stack_cleanup();
1442 /* "sp" may have been moved to the freelist now */
1443 sp = syn_stack_find_entry(current_lnum);
1444 }
1445 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001446 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447 sp = NULL;
1448 else
1449 {
1450 /* Take the first item from the free list and put it in the used
1451 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001452 p = syn_block->b_sst_firstfree;
1453 syn_block->b_sst_firstfree = p->sst_next;
1454 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001455 if (sp == NULL)
1456 {
1457 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001458 p->sst_next = syn_block->b_sst_first;
1459 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001460 }
1461 else
1462 {
1463 /* insert in list after *sp */
1464 p->sst_next = sp->sst_next;
1465 sp->sst_next = p;
1466 }
1467 sp = p;
1468 sp->sst_stacksize = 0;
1469 sp->sst_lnum = current_lnum;
1470 }
1471 }
1472 if (sp != NULL)
1473 {
1474 /* When overwriting an existing state stack, clear it first */
1475 clear_syn_state(sp);
1476 sp->sst_stacksize = current_state.ga_len;
1477 if (current_state.ga_len > SST_FIX_STATES)
1478 {
1479 /* Need to clear it, might be something remaining from when the
1480 * length was less than SST_FIX_STATES. */
1481 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1482 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1483 sp->sst_stacksize = 0;
1484 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001486 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1487 }
1488 else
1489 bp = sp->sst_union.sst_stack;
1490 for (i = 0; i < sp->sst_stacksize; ++i)
1491 {
1492 bp[i].bs_idx = CUR_STATE(i).si_idx;
1493 bp[i].bs_flags = CUR_STATE(i).si_flags;
1494 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1495 }
1496 sp->sst_next_flags = current_next_flags;
1497 sp->sst_next_list = current_next_list;
1498 sp->sst_tick = display_tick;
1499 sp->sst_change_lnum = 0;
1500 }
1501 current_state_stored = TRUE;
1502 return sp;
1503}
1504
1505/*
1506 * Copy a state stack from "from" in b_sst_array[] to current_state;
1507 */
1508 static void
1509load_current_state(from)
1510 synstate_T *from;
1511{
1512 int i;
1513 bufstate_T *bp;
1514
1515 clear_current_state();
1516 validate_current_state();
1517 keepend_level = -1;
1518 if (from->sst_stacksize
1519 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1520 {
1521 if (from->sst_stacksize > SST_FIX_STATES)
1522 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1523 else
1524 bp = from->sst_union.sst_stack;
1525 for (i = 0; i < from->sst_stacksize; ++i)
1526 {
1527 CUR_STATE(i).si_idx = bp[i].bs_idx;
1528 CUR_STATE(i).si_flags = bp[i].bs_flags;
1529 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1530 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1531 keepend_level = i;
1532 CUR_STATE(i).si_ends = FALSE;
1533 CUR_STATE(i).si_m_lnum = 0;
1534 if (CUR_STATE(i).si_idx >= 0)
1535 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001536 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537 else
1538 CUR_STATE(i).si_next_list = NULL;
1539 update_si_attr(i);
1540 }
1541 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001542 }
1543 current_next_list = from->sst_next_list;
1544 current_next_flags = from->sst_next_flags;
1545 current_lnum = from->sst_lnum;
1546}
1547
1548/*
1549 * Compare saved state stack "*sp" with the current state.
1550 * Return TRUE when they are equal.
1551 */
1552 static int
1553syn_stack_equal(sp)
1554 synstate_T *sp;
1555{
1556 int i, j;
1557 bufstate_T *bp;
1558 reg_extmatch_T *six, *bsx;
1559
1560 /* First a quick check if the stacks have the same size end nextlist. */
1561 if (sp->sst_stacksize == current_state.ga_len
1562 && sp->sst_next_list == current_next_list)
1563 {
1564 /* Need to compare all states on both stacks. */
1565 if (sp->sst_stacksize > SST_FIX_STATES)
1566 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1567 else
1568 bp = sp->sst_union.sst_stack;
1569
1570 for (i = current_state.ga_len; --i >= 0; )
1571 {
1572 /* If the item has another index the state is different. */
1573 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1574 break;
1575 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1576 {
1577 /* When the extmatch pointers are different, the strings in
1578 * them can still be the same. Check if the extmatch
1579 * references are equal. */
1580 bsx = bp[i].bs_extmatch;
1581 six = CUR_STATE(i).si_extmatch;
1582 /* If one of the extmatch pointers is NULL the states are
1583 * different. */
1584 if (bsx == NULL || six == NULL)
1585 break;
1586 for (j = 0; j < NSUBEXP; ++j)
1587 {
1588 /* Check each referenced match string. They must all be
1589 * equal. */
1590 if (bsx->matches[j] != six->matches[j])
1591 {
1592 /* If the pointer is different it can still be the
1593 * same text. Compare the strings, ignore case when
1594 * the start item has the sp_ic flag set. */
1595 if (bsx->matches[j] == NULL
1596 || six->matches[j] == NULL)
1597 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001598 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001599 ? MB_STRICMP(bsx->matches[j],
1600 six->matches[j]) != 0
1601 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1602 break;
1603 }
1604 }
1605 if (j != NSUBEXP)
1606 break;
1607 }
1608 }
1609 if (i < 0)
1610 return TRUE;
1611 }
1612 return FALSE;
1613}
1614
1615/*
1616 * We stop parsing syntax above line "lnum". If the stored state at or below
1617 * this line depended on a change before it, it now depends on the line below
1618 * the last parsed line.
1619 * The window looks like this:
1620 * line which changed
1621 * displayed line
1622 * displayed line
1623 * lnum -> line below window
1624 */
1625 void
1626syntax_end_parsing(lnum)
1627 linenr_T lnum;
1628{
1629 synstate_T *sp;
1630
1631 sp = syn_stack_find_entry(lnum);
1632 if (sp != NULL && sp->sst_lnum < lnum)
1633 sp = sp->sst_next;
1634
1635 if (sp != NULL && sp->sst_change_lnum != 0)
1636 sp->sst_change_lnum = lnum;
1637}
1638
1639/*
1640 * End of handling of the state stack.
1641 ****************************************/
1642
1643 static void
1644invalidate_current_state()
1645{
1646 clear_current_state();
1647 current_state.ga_itemsize = 0; /* mark current_state invalid */
1648 current_next_list = NULL;
1649 keepend_level = -1;
1650}
1651
1652 static void
1653validate_current_state()
1654{
1655 current_state.ga_itemsize = sizeof(stateitem_T);
1656 current_state.ga_growsize = 3;
1657}
1658
1659/*
1660 * Return TRUE if the syntax at start of lnum changed since last time.
1661 * This will only be called just after get_syntax_attr() for the previous
1662 * line, to check if the next line needs to be redrawn too.
1663 */
1664 int
1665syntax_check_changed(lnum)
1666 linenr_T lnum;
1667{
1668 int retval = TRUE;
1669 synstate_T *sp;
1670
Bram Moolenaar071d4272004-06-13 20:20:40 +00001671 /*
1672 * Check the state stack when:
1673 * - lnum is just below the previously syntaxed line.
1674 * - lnum is not before the lines with saved states.
1675 * - lnum is not past the lines with saved states.
1676 * - lnum is at or before the last changed line.
1677 */
1678 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1679 {
1680 sp = syn_stack_find_entry(lnum);
1681 if (sp != NULL && sp->sst_lnum == lnum)
1682 {
1683 /*
1684 * finish the previous line (needed when not all of the line was
1685 * drawn)
1686 */
1687 (void)syn_finish_line(FALSE);
1688
1689 /*
1690 * Compare the current state with the previously saved state of
1691 * the line.
1692 */
1693 if (syn_stack_equal(sp))
1694 retval = FALSE;
1695
1696 /*
1697 * Store the current state in b_sst_array[] for later use.
1698 */
1699 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001700 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 }
1702 }
1703
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704 return retval;
1705}
1706
1707/*
1708 * Finish the current line.
1709 * This doesn't return any attributes, it only gets the state at the end of
1710 * the line. It can start anywhere in the line, as long as the current state
1711 * is valid.
1712 */
1713 static int
1714syn_finish_line(syncing)
1715 int syncing; /* called for syncing */
1716{
1717 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001718 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719
1720 if (!current_finished)
1721 {
1722 while (!current_finished)
1723 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001724 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725 /*
1726 * When syncing, and found some item, need to check the item.
1727 */
1728 if (syncing && current_state.ga_len)
1729 {
1730 /*
1731 * Check for match with sync item.
1732 */
1733 cur_si = &CUR_STATE(current_state.ga_len - 1);
1734 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001735 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1737 return TRUE;
1738
1739 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001740 * that ends here, need to do that now. Be careful not to go
1741 * past the NUL. */
1742 prev_current_col = current_col;
1743 if (syn_getcurline()[current_col] != NUL)
1744 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001745 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001746 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 }
1748 ++current_col;
1749 }
1750 }
1751 return FALSE;
1752}
1753
1754/*
1755 * Return highlight attributes for next character.
1756 * Must first call syntax_start() once for the line.
1757 * "col" is normally 0 for the first use in a line, and increments by one each
1758 * time. It's allowed to skip characters and to stop before the end of the
1759 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001760 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1761 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762 */
1763 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001764get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001766 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001767 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768{
1769 int attr = 0;
1770
Bram Moolenaar349955a2007-08-14 21:07:36 +00001771 if (can_spell != NULL)
1772 /* Default: Only do spelling when there is no @Spell cluster or when
1773 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001774 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1775 ? (syn_block->b_spell_cluster_id == 0)
1776 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001777
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001779 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 return 0;
1781
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001782 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001783 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001784 {
1785 clear_current_state();
1786#ifdef FEAT_EVAL
1787 current_id = 0;
1788 current_trans_id = 0;
1789#endif
1790 return 0;
1791 }
1792
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 /* Make sure current_state is valid */
1794 if (INVALID_STATE(&current_state))
1795 validate_current_state();
1796
1797 /*
1798 * Skip from the current column to "col", get the attributes for "col".
1799 */
1800 while (current_col <= col)
1801 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001802 attr = syn_current_attr(FALSE, TRUE, can_spell,
1803 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 ++current_col;
1805 }
1806
Bram Moolenaar071d4272004-06-13 20:20:40 +00001807 return attr;
1808}
1809
1810/*
1811 * Get syntax attributes for current_lnum, current_col.
1812 */
1813 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001814syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815 int syncing; /* When 1: called for syncing */
1816 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001817 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001818 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819{
1820 int syn_id;
1821 lpos_T endpos; /* was: char_u *endp; */
1822 lpos_T hl_startpos; /* was: int hl_startcol; */
1823 lpos_T hl_endpos;
1824 lpos_T eos_pos; /* end-of-start match (start region) */
1825 lpos_T eoe_pos; /* end-of-end pattern */
1826 int end_idx; /* group ID for end pattern */
1827 int idx;
1828 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001829 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 int startcol;
1831 int endcol;
1832 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001833 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834 short *next_list;
1835 int found_match; /* found usable match */
1836 static int try_next_column = FALSE; /* must try in next col */
1837 int do_keywords;
1838 regmmatch_T regmatch;
1839 lpos_T pos;
1840 int lc_col;
1841 reg_extmatch_T *cur_extmatch = NULL;
1842 char_u *line; /* current line. NOTE: becomes invalid after
1843 looking for a pattern match! */
1844
1845 /* variables for zero-width matches that have a "nextgroup" argument */
1846 int keep_next_list;
1847 int zero_width_next_list = FALSE;
1848 garray_T zero_width_next_ga;
1849
1850 /*
1851 * No character, no attributes! Past end of line?
1852 * Do try matching with an empty line (could be the start of a region).
1853 */
1854 line = syn_getcurline();
1855 if (line[current_col] == NUL && current_col != 0)
1856 {
1857 /*
1858 * If we found a match after the last column, use it.
1859 */
1860 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1861 && next_match_col != MAXCOL)
1862 (void)push_next_match(NULL);
1863
1864 current_finished = TRUE;
1865 current_state_stored = FALSE;
1866 return 0;
1867 }
1868
1869 /* if the current or next character is NUL, we will finish the line now */
1870 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1871 {
1872 current_finished = TRUE;
1873 current_state_stored = FALSE;
1874 }
1875
1876 /*
1877 * When in the previous column there was a match but it could not be used
1878 * (empty match or already matched in this column) need to try again in
1879 * the next column.
1880 */
1881 if (try_next_column)
1882 {
1883 next_match_idx = -1;
1884 try_next_column = FALSE;
1885 }
1886
1887 /* Only check for keywords when not syncing and there are some. */
1888 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001889 && (syn_block->b_keywtab.ht_used > 0
1890 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891
1892 /* Init the list of zero-width matches with a nextlist. This is used to
1893 * avoid matching the same item in the same position twice. */
1894 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1895
1896 /*
1897 * Repeat matching keywords and patterns, to find contained items at the
1898 * same column. This stops when there are no extra matches at the current
1899 * column.
1900 */
1901 do
1902 {
1903 found_match = FALSE;
1904 keep_next_list = FALSE;
1905 syn_id = 0;
1906
1907 /*
1908 * 1. Check for a current state.
1909 * Only when there is no current state, or if the current state may
1910 * contain other things, we need to check for keywords and patterns.
1911 * Always need to check for contained items if some item has the
1912 * "containedin" argument (takes extra time!).
1913 */
1914 if (current_state.ga_len)
1915 cur_si = &CUR_STATE(current_state.ga_len - 1);
1916 else
1917 cur_si = NULL;
1918
Bram Moolenaar860cae12010-06-05 23:22:07 +02001919 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001920 || cur_si->si_cont_list != NULL)
1921 {
1922 /*
1923 * 2. Check for keywords, if on a keyword char after a non-keyword
1924 * char. Don't do this when syncing.
1925 */
1926 if (do_keywords)
1927 {
1928 line = syn_getcurline();
1929 if (vim_iswordc_buf(line + current_col, syn_buf)
1930 && (current_col == 0
1931 || !vim_iswordc_buf(line + current_col - 1
1932#ifdef FEAT_MBYTE
1933 - (has_mbyte
1934 ? (*mb_head_off)(line, line + current_col - 1)
1935 : 0)
1936#endif
1937 , syn_buf)))
1938 {
1939 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001940 &endcol, &flags, &next_list, cur_si,
1941 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001942 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943 {
1944 if (push_current_state(KEYWORD_IDX) == OK)
1945 {
1946 cur_si = &CUR_STATE(current_state.ga_len - 1);
1947 cur_si->si_m_startcol = current_col;
1948 cur_si->si_h_startpos.lnum = current_lnum;
1949 cur_si->si_h_startpos.col = 0; /* starts right away */
1950 cur_si->si_m_endpos.lnum = current_lnum;
1951 cur_si->si_m_endpos.col = endcol;
1952 cur_si->si_h_endpos.lnum = current_lnum;
1953 cur_si->si_h_endpos.col = endcol;
1954 cur_si->si_ends = TRUE;
1955 cur_si->si_end_idx = 0;
1956 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001957#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001958 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001959 cur_si->si_char = cchar;
1960 if (current_state.ga_len > 1)
1961 cur_si->si_flags |=
1962 CUR_STATE(current_state.ga_len - 2).si_flags
1963 & HL_CONCEAL;
1964#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965 cur_si->si_id = syn_id;
1966 cur_si->si_trans_id = syn_id;
1967 if (flags & HL_TRANSP)
1968 {
1969 if (current_state.ga_len < 2)
1970 {
1971 cur_si->si_attr = 0;
1972 cur_si->si_trans_id = 0;
1973 }
1974 else
1975 {
1976 cur_si->si_attr = CUR_STATE(
1977 current_state.ga_len - 2).si_attr;
1978 cur_si->si_trans_id = CUR_STATE(
1979 current_state.ga_len - 2).si_trans_id;
1980 }
1981 }
1982 else
1983 cur_si->si_attr = syn_id2attr(syn_id);
1984 cur_si->si_cont_list = NULL;
1985 cur_si->si_next_list = next_list;
1986 check_keepend();
1987 }
1988 else
1989 vim_free(next_list);
1990 }
1991 }
1992 }
1993
1994 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001995 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001997 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 {
1999 /*
2000 * If we didn't check for a match yet, or we are past it, check
2001 * for any match with a pattern.
2002 */
2003 if (next_match_idx < 0 || next_match_col < (int)current_col)
2004 {
2005 /*
2006 * Check all relevant patterns for a match at this
2007 * position. This is complicated, because matching with a
2008 * pattern takes quite a bit of time, thus we want to
2009 * avoid doing it when it's not needed.
2010 */
2011 next_match_idx = 0; /* no match in this line yet */
2012 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002013 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002015 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 if ( spp->sp_syncing == syncing
2017 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2018 && (spp->sp_type == SPTYPE_MATCH
2019 || spp->sp_type == SPTYPE_START)
2020 && (current_next_list != NULL
2021 ? in_id_list(NULL, current_next_list,
2022 &spp->sp_syn, 0)
2023 : (cur_si == NULL
2024 ? !(spp->sp_flags & HL_CONTAINED)
2025 : in_id_list(cur_si,
2026 cur_si->si_cont_list, &spp->sp_syn,
2027 spp->sp_flags & HL_CONTAINED))))
2028 {
2029 /* If we already tried matching in this line, and
2030 * there isn't a match before next_match_col, skip
2031 * this item. */
2032 if (spp->sp_line_id == current_line_id
2033 && spp->sp_startcol >= next_match_col)
2034 continue;
2035 spp->sp_line_id = current_line_id;
2036
2037 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2038 if (lc_col < 0)
2039 lc_col = 0;
2040
2041 regmatch.rmm_ic = spp->sp_ic;
2042 regmatch.regprog = spp->sp_prog;
2043 if (!syn_regexec(&regmatch, current_lnum,
2044 (colnr_T)lc_col))
2045 {
2046 /* no match in this line, try another one */
2047 spp->sp_startcol = MAXCOL;
2048 continue;
2049 }
2050
2051 /*
2052 * Compute the first column of the match.
2053 */
2054 syn_add_start_off(&pos, &regmatch,
2055 spp, SPO_MS_OFF, -1);
2056 if (pos.lnum > current_lnum)
2057 {
2058 /* must have used end of match in a next line,
2059 * we can't handle that */
2060 spp->sp_startcol = MAXCOL;
2061 continue;
2062 }
2063 startcol = pos.col;
2064
2065 /* remember the next column where this pattern
2066 * matches in the current line */
2067 spp->sp_startcol = startcol;
2068
2069 /*
2070 * If a previously found match starts at a lower
2071 * column number, don't use this one.
2072 */
2073 if (startcol >= next_match_col)
2074 continue;
2075
2076 /*
2077 * If we matched this pattern at this position
2078 * before, skip it. Must retry in the next
2079 * column, because it may match from there.
2080 */
2081 if (did_match_already(idx, &zero_width_next_ga))
2082 {
2083 try_next_column = TRUE;
2084 continue;
2085 }
2086
2087 endpos.lnum = regmatch.endpos[0].lnum;
2088 endpos.col = regmatch.endpos[0].col;
2089
2090 /* Compute the highlight start. */
2091 syn_add_start_off(&hl_startpos, &regmatch,
2092 spp, SPO_HS_OFF, -1);
2093
2094 /* Compute the region start. */
2095 /* Default is to use the end of the match. */
2096 syn_add_end_off(&eos_pos, &regmatch,
2097 spp, SPO_RS_OFF, 0);
2098
2099 /*
2100 * Grab the external submatches before they get
2101 * overwritten. Reference count doesn't change.
2102 */
2103 unref_extmatch(cur_extmatch);
2104 cur_extmatch = re_extmatch_out;
2105 re_extmatch_out = NULL;
2106
2107 flags = 0;
2108 eoe_pos.lnum = 0; /* avoid warning */
2109 eoe_pos.col = 0;
2110 end_idx = 0;
2111 hl_endpos.lnum = 0;
2112
2113 /*
2114 * For a "oneline" the end must be found in the
2115 * same line too. Search for it after the end of
2116 * the match with the start pattern. Set the
2117 * resulting end positions at the same time.
2118 */
2119 if (spp->sp_type == SPTYPE_START
2120 && (spp->sp_flags & HL_ONELINE))
2121 {
2122 lpos_T startpos;
2123
2124 startpos = endpos;
2125 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2126 &flags, &eoe_pos, &end_idx, cur_extmatch);
2127 if (endpos.lnum == 0)
2128 continue; /* not found */
2129 }
2130
2131 /*
2132 * For a "match" the size must be > 0 after the
2133 * end offset needs has been added. Except when
2134 * syncing.
2135 */
2136 else if (spp->sp_type == SPTYPE_MATCH)
2137 {
2138 syn_add_end_off(&hl_endpos, &regmatch, spp,
2139 SPO_HE_OFF, 0);
2140 syn_add_end_off(&endpos, &regmatch, spp,
2141 SPO_ME_OFF, 0);
2142 if (endpos.lnum == current_lnum
2143 && (int)endpos.col + syncing < startcol)
2144 {
2145 /*
2146 * If an empty string is matched, may need
2147 * to try matching again at next column.
2148 */
2149 if (regmatch.startpos[0].col
2150 == regmatch.endpos[0].col)
2151 try_next_column = TRUE;
2152 continue;
2153 }
2154 }
2155
2156 /*
2157 * keep the best match so far in next_match_*
2158 */
2159 /* Highlighting must start after startpos and end
2160 * before endpos. */
2161 if (hl_startpos.lnum == current_lnum
2162 && (int)hl_startpos.col < startcol)
2163 hl_startpos.col = startcol;
2164 limit_pos_zero(&hl_endpos, &endpos);
2165
2166 next_match_idx = idx;
2167 next_match_col = startcol;
2168 next_match_m_endpos = endpos;
2169 next_match_h_endpos = hl_endpos;
2170 next_match_h_startpos = hl_startpos;
2171 next_match_flags = flags;
2172 next_match_eos_pos = eos_pos;
2173 next_match_eoe_pos = eoe_pos;
2174 next_match_end_idx = end_idx;
2175 unref_extmatch(next_match_extmatch);
2176 next_match_extmatch = cur_extmatch;
2177 cur_extmatch = NULL;
2178 }
2179 }
2180 }
2181
2182 /*
2183 * If we found a match at the current column, use it.
2184 */
2185 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2186 {
2187 synpat_T *lspp;
2188
2189 /* When a zero-width item matched which has a nextgroup,
2190 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002191 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 if (next_match_m_endpos.lnum == current_lnum
2193 && next_match_m_endpos.col == current_col
2194 && lspp->sp_next_list != NULL)
2195 {
2196 current_next_list = lspp->sp_next_list;
2197 current_next_flags = lspp->sp_flags;
2198 keep_next_list = TRUE;
2199 zero_width_next_list = TRUE;
2200
2201 /* Add the index to a list, so that we can check
2202 * later that we don't match it again (and cause an
2203 * endless loop). */
2204 if (ga_grow(&zero_width_next_ga, 1) == OK)
2205 {
2206 ((int *)(zero_width_next_ga.ga_data))
2207 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 }
2209 next_match_idx = -1;
2210 }
2211 else
2212 cur_si = push_next_match(cur_si);
2213 found_match = TRUE;
2214 }
2215 }
2216 }
2217
2218 /*
2219 * Handle searching for nextgroup match.
2220 */
2221 if (current_next_list != NULL && !keep_next_list)
2222 {
2223 /*
2224 * If a nextgroup was not found, continue looking for one if:
2225 * - this is an empty line and the "skipempty" option was given
2226 * - we are on white space and the "skipwhite" option was given
2227 */
2228 if (!found_match)
2229 {
2230 line = syn_getcurline();
2231 if (((current_next_flags & HL_SKIPWHITE)
2232 && vim_iswhite(line[current_col]))
2233 || ((current_next_flags & HL_SKIPEMPTY)
2234 && *line == NUL))
2235 break;
2236 }
2237
2238 /*
2239 * If a nextgroup was found: Use it, and continue looking for
2240 * contained matches.
2241 * If a nextgroup was not found: Continue looking for a normal
2242 * match.
2243 * When did set current_next_list for a zero-width item and no
2244 * match was found don't loop (would get stuck).
2245 */
2246 current_next_list = NULL;
2247 next_match_idx = -1;
2248 if (!zero_width_next_list)
2249 found_match = TRUE;
2250 }
2251
2252 } while (found_match);
2253
2254 /*
2255 * Use attributes from the current state, if within its highlighting.
2256 * If not, use attributes from the current-but-one state, etc.
2257 */
2258 current_attr = 0;
2259#ifdef FEAT_EVAL
2260 current_id = 0;
2261 current_trans_id = 0;
2262#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002263#ifdef FEAT_CONCEAL
2264 current_flags = 0;
2265#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266 if (cur_si != NULL)
2267 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002268#ifndef FEAT_EVAL
2269 int current_trans_id = 0;
2270#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2272 {
2273 sip = &CUR_STATE(idx);
2274 if ((current_lnum > sip->si_h_startpos.lnum
2275 || (current_lnum == sip->si_h_startpos.lnum
2276 && current_col >= sip->si_h_startpos.col))
2277 && (sip->si_h_endpos.lnum == 0
2278 || current_lnum < sip->si_h_endpos.lnum
2279 || (current_lnum == sip->si_h_endpos.lnum
2280 && current_col < sip->si_h_endpos.col)))
2281 {
2282 current_attr = sip->si_attr;
2283#ifdef FEAT_EVAL
2284 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002285#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002286 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002287#ifdef FEAT_CONCEAL
2288 current_flags = sip->si_flags;
2289 current_sub_char = sip->si_char;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002290 current_seqnr = sip->si_seqnr;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002291#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002292 break;
2293 }
2294 }
2295
Bram Moolenaar217ad922005-03-20 22:37:15 +00002296 if (can_spell != NULL)
2297 {
2298 struct sp_syn sps;
2299
2300 /*
2301 * set "can_spell" to TRUE if spell checking is supposed to be
2302 * done in the current item.
2303 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002304 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002305 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002306 /* There is no @Spell cluster: Do spelling for items without
2307 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002308 if (syn_block->b_nospell_cluster_id == 0
2309 || current_trans_id == 0)
2310 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002311 else
2312 {
2313 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002314 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002315 sps.cont_in_list = NULL;
2316 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2317 }
2318 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002319 else
2320 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002321 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002322 * the @Spell cluster. But not when @NoSpell is also there.
2323 * At the toplevel only spell check when ":syn spell toplevel"
2324 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002325 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002326 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002327 else
2328 {
2329 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002330 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002331 sps.cont_in_list = NULL;
2332 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2333
Bram Moolenaar860cae12010-06-05 23:22:07 +02002334 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002335 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002336 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002337 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2338 *can_spell = FALSE;
2339 }
2340 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002341 }
2342 }
2343
2344
Bram Moolenaar071d4272004-06-13 20:20:40 +00002345 /*
2346 * Check for end of current state (and the states before it) at the
2347 * next column. Don't do this for syncing, because we would miss a
2348 * single character match.
2349 * First check if the current state ends at the current column. It
2350 * may be for an empty match and a containing item might end in the
2351 * current column.
2352 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002353 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002354 {
2355 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002356 if (current_state.ga_len > 0
2357 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002358 {
2359 ++current_col;
2360 check_state_ends();
2361 --current_col;
2362 }
2363 }
2364 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002365 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002366 /* Default: Only do spelling when there is no @Spell cluster or when
2367 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002368 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2369 ? (syn_block->b_spell_cluster_id == 0)
2370 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002371
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002372 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 if (current_next_list != NULL
2374 && syn_getcurline()[current_col + 1] == NUL
2375 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2376 current_next_list = NULL;
2377
2378 if (zero_width_next_ga.ga_len > 0)
2379 ga_clear(&zero_width_next_ga);
2380
2381 /* No longer need external matches. But keep next_match_extmatch. */
2382 unref_extmatch(re_extmatch_out);
2383 re_extmatch_out = NULL;
2384 unref_extmatch(cur_extmatch);
2385
2386 return current_attr;
2387}
2388
2389
2390/*
2391 * Check if we already matched pattern "idx" at the current column.
2392 */
2393 static int
2394did_match_already(idx, gap)
2395 int idx;
2396 garray_T *gap;
2397{
2398 int i;
2399
2400 for (i = current_state.ga_len; --i >= 0; )
2401 if (CUR_STATE(i).si_m_startcol == (int)current_col
2402 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2403 && CUR_STATE(i).si_idx == idx)
2404 return TRUE;
2405
2406 /* Zero-width matches with a nextgroup argument are not put on the syntax
2407 * stack, and can only be matched once anyway. */
2408 for (i = gap->ga_len; --i >= 0; )
2409 if (((int *)(gap->ga_data))[i] == idx)
2410 return TRUE;
2411
2412 return FALSE;
2413}
2414
2415/*
2416 * Push the next match onto the stack.
2417 */
2418 static stateitem_T *
2419push_next_match(cur_si)
2420 stateitem_T *cur_si;
2421{
2422 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002423#ifdef FEAT_CONCEAL
2424 int save_flags;
2425#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426
Bram Moolenaar860cae12010-06-05 23:22:07 +02002427 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002428
2429 /*
2430 * Push the item in current_state stack;
2431 */
2432 if (push_current_state(next_match_idx) == OK)
2433 {
2434 /*
2435 * If it's a start-skip-end type that crosses lines, figure out how
2436 * much it continues in this line. Otherwise just fill in the length.
2437 */
2438 cur_si = &CUR_STATE(current_state.ga_len - 1);
2439 cur_si->si_h_startpos = next_match_h_startpos;
2440 cur_si->si_m_startcol = current_col;
2441 cur_si->si_m_lnum = current_lnum;
2442 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002443#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002444 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002445 cur_si->si_char = spp->sp_char;
2446 if (current_state.ga_len > 1)
2447 cur_si->si_flags |=
2448 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2449#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450 cur_si->si_next_list = spp->sp_next_list;
2451 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2452 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2453 {
2454 /* Try to find the end pattern in the current line */
2455 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2456 check_keepend();
2457 }
2458 else
2459 {
2460 cur_si->si_m_endpos = next_match_m_endpos;
2461 cur_si->si_h_endpos = next_match_h_endpos;
2462 cur_si->si_ends = TRUE;
2463 cur_si->si_flags |= next_match_flags;
2464 cur_si->si_eoe_pos = next_match_eoe_pos;
2465 cur_si->si_end_idx = next_match_end_idx;
2466 }
2467 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2468 keepend_level = current_state.ga_len - 1;
2469 check_keepend();
2470 update_si_attr(current_state.ga_len - 1);
2471
Bram Moolenaar860cae12010-06-05 23:22:07 +02002472#ifdef FEAT_CONCEAL
2473 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2474#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475 /*
2476 * If the start pattern has another highlight group, push another item
2477 * on the stack for the start pattern.
2478 */
2479 if ( spp->sp_type == SPTYPE_START
2480 && spp->sp_syn_match_id != 0
2481 && push_current_state(next_match_idx) == OK)
2482 {
2483 cur_si = &CUR_STATE(current_state.ga_len - 1);
2484 cur_si->si_h_startpos = next_match_h_startpos;
2485 cur_si->si_m_startcol = current_col;
2486 cur_si->si_m_lnum = current_lnum;
2487 cur_si->si_m_endpos = next_match_eos_pos;
2488 cur_si->si_h_endpos = next_match_eos_pos;
2489 cur_si->si_ends = TRUE;
2490 cur_si->si_end_idx = 0;
2491 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002492#ifdef FEAT_CONCEAL
2493 cur_si->si_flags |= save_flags;
2494 if (cur_si->si_flags & HL_CONCEALENDS)
2495 cur_si->si_flags |= HL_CONCEAL;
2496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 cur_si->si_next_list = NULL;
2498 check_keepend();
2499 update_si_attr(current_state.ga_len - 1);
2500 }
2501 }
2502
2503 next_match_idx = -1; /* try other match next time */
2504
2505 return cur_si;
2506}
2507
2508/*
2509 * Check for end of current state (and the states before it).
2510 */
2511 static void
2512check_state_ends()
2513{
2514 stateitem_T *cur_si;
2515 int had_extend = FALSE;
2516
2517 cur_si = &CUR_STATE(current_state.ga_len - 1);
2518 for (;;)
2519 {
2520 if (cur_si->si_ends
2521 && (cur_si->si_m_endpos.lnum < current_lnum
2522 || (cur_si->si_m_endpos.lnum == current_lnum
2523 && cur_si->si_m_endpos.col <= current_col)))
2524 {
2525 /*
2526 * If there is an end pattern group ID, highlight the end pattern
2527 * now. No need to pop the current item from the stack.
2528 * Only do this if the end pattern continues beyond the current
2529 * position.
2530 */
2531 if (cur_si->si_end_idx
2532 && (cur_si->si_eoe_pos.lnum > current_lnum
2533 || (cur_si->si_eoe_pos.lnum == current_lnum
2534 && cur_si->si_eoe_pos.col > current_col)))
2535 {
2536 cur_si->si_idx = cur_si->si_end_idx;
2537 cur_si->si_end_idx = 0;
2538 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2539 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2540 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002541#ifdef FEAT_CONCEAL
2542 if (cur_si->si_flags & HL_CONCEALENDS)
2543 cur_si->si_flags |= HL_CONCEAL;
2544#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002546
2547 /* what matches next may be different now, clear it */
2548 next_match_idx = 0;
2549 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550 break;
2551 }
2552 else
2553 {
2554 /* handle next_list, unless at end of line and no "skipnl" or
2555 * "skipempty" */
2556 current_next_list = cur_si->si_next_list;
2557 current_next_flags = cur_si->si_flags;
2558 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2559 && syn_getcurline()[current_col] == NUL)
2560 current_next_list = NULL;
2561
2562 /* When the ended item has "extend", another item with
2563 * "keepend" now needs to check for its end. */
2564 if (cur_si->si_flags & HL_EXTEND)
2565 had_extend = TRUE;
2566
2567 pop_current_state();
2568
2569 if (current_state.ga_len == 0)
2570 break;
2571
Bram Moolenaar81993f42008-01-11 20:27:45 +00002572 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573 {
2574 syn_update_ends(FALSE);
2575 if (current_state.ga_len == 0)
2576 break;
2577 }
2578
2579 cur_si = &CUR_STATE(current_state.ga_len - 1);
2580
2581 /*
2582 * Only for a region the search for the end continues after
2583 * the end of the contained item. If the contained match
2584 * included the end-of-line, break here, the region continues.
2585 * Don't do this when:
2586 * - "keepend" is used for the contained item
2587 * - not at the end of the line (could be end="x$"me=e-1).
2588 * - "excludenl" is used (HL_HAS_EOL won't be set)
2589 */
2590 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002591 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592 == SPTYPE_START
2593 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2594 {
2595 update_si_end(cur_si, (int)current_col, TRUE);
2596 check_keepend();
2597 if ((current_next_flags & HL_HAS_EOL)
2598 && keepend_level < 0
2599 && syn_getcurline()[current_col] == NUL)
2600 break;
2601 }
2602 }
2603 }
2604 else
2605 break;
2606 }
2607}
2608
2609/*
2610 * Update an entry in the current_state stack for a match or region. This
2611 * fills in si_attr, si_next_list and si_cont_list.
2612 */
2613 static void
2614update_si_attr(idx)
2615 int idx;
2616{
2617 stateitem_T *sip = &CUR_STATE(idx);
2618 synpat_T *spp;
2619
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002620 /* This should not happen... */
2621 if (sip->si_idx < 0)
2622 return;
2623
Bram Moolenaar860cae12010-06-05 23:22:07 +02002624 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002625 if (sip->si_flags & HL_MATCH)
2626 sip->si_id = spp->sp_syn_match_id;
2627 else
2628 sip->si_id = spp->sp_syn.id;
2629 sip->si_attr = syn_id2attr(sip->si_id);
2630 sip->si_trans_id = sip->si_id;
2631 if (sip->si_flags & HL_MATCH)
2632 sip->si_cont_list = NULL;
2633 else
2634 sip->si_cont_list = spp->sp_cont_list;
2635
2636 /*
2637 * For transparent items, take attr from outer item.
2638 * Also take cont_list, if there is none.
2639 * Don't do this for the matchgroup of a start or end pattern.
2640 */
2641 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2642 {
2643 if (idx == 0)
2644 {
2645 sip->si_attr = 0;
2646 sip->si_trans_id = 0;
2647 if (sip->si_cont_list == NULL)
2648 sip->si_cont_list = ID_LIST_ALL;
2649 }
2650 else
2651 {
2652 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2653 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002654 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2655 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002656 if (sip->si_cont_list == NULL)
2657 {
2658 sip->si_flags |= HL_TRANS_CONT;
2659 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2660 }
2661 }
2662 }
2663}
2664
2665/*
2666 * Check the current stack for patterns with "keepend" flag.
2667 * Propagate the match-end to contained items, until a "skipend" item is found.
2668 */
2669 static void
2670check_keepend()
2671{
2672 int i;
2673 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002674 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002675 stateitem_T *sip;
2676
2677 /*
2678 * This check can consume a lot of time; only do it from the level where
2679 * there really is a keepend.
2680 */
2681 if (keepend_level < 0)
2682 return;
2683
2684 /*
2685 * Find the last index of an "extend" item. "keepend" items before that
2686 * won't do anything. If there is no "extend" item "i" will be
2687 * "keepend_level" and all "keepend" items will work normally.
2688 */
2689 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2690 if (CUR_STATE(i).si_flags & HL_EXTEND)
2691 break;
2692
2693 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002694 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002695 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002696 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 for ( ; i < current_state.ga_len; ++i)
2698 {
2699 sip = &CUR_STATE(i);
2700 if (maxpos.lnum != 0)
2701 {
2702 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002703 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2705 sip->si_ends = TRUE;
2706 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002707 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2708 {
2709 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002710 || maxpos.lnum > sip->si_m_endpos.lnum
2711 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002712 && maxpos.col > sip->si_m_endpos.col))
2713 maxpos = sip->si_m_endpos;
2714 if (maxpos_h.lnum == 0
2715 || maxpos_h.lnum > sip->si_h_endpos.lnum
2716 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2717 && maxpos_h.col > sip->si_h_endpos.col))
2718 maxpos_h = sip->si_h_endpos;
2719 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720 }
2721}
2722
2723/*
2724 * Update an entry in the current_state stack for a start-skip-end pattern.
2725 * This finds the end of the current item, if it's in the current line.
2726 *
2727 * Return the flags for the matched END.
2728 */
2729 static void
2730update_si_end(sip, startcol, force)
2731 stateitem_T *sip;
2732 int startcol; /* where to start searching for the end */
2733 int force; /* when TRUE overrule a previous end */
2734{
2735 lpos_T startpos;
2736 lpos_T endpos;
2737 lpos_T hl_endpos;
2738 lpos_T end_endpos;
2739 int end_idx;
2740
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002741 /* return quickly for a keyword */
2742 if (sip->si_idx < 0)
2743 return;
2744
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 /* Don't update when it's already done. Can be a match of an end pattern
2746 * that started in a previous line. Watch out: can also be a "keepend"
2747 * from a containing item. */
2748 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2749 return;
2750
2751 /*
2752 * We need to find the end of the region. It may continue in the next
2753 * line.
2754 */
2755 end_idx = 0;
2756 startpos.lnum = current_lnum;
2757 startpos.col = startcol;
2758 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2759 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2760
2761 if (endpos.lnum == 0)
2762 {
2763 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002764 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 {
2766 /* a "oneline" never continues in the next line */
2767 sip->si_ends = TRUE;
2768 sip->si_m_endpos.lnum = current_lnum;
2769 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2770 }
2771 else
2772 {
2773 /* continues in the next line */
2774 sip->si_ends = FALSE;
2775 sip->si_m_endpos.lnum = 0;
2776 }
2777 sip->si_h_endpos = sip->si_m_endpos;
2778 }
2779 else
2780 {
2781 /* match within this line */
2782 sip->si_m_endpos = endpos;
2783 sip->si_h_endpos = hl_endpos;
2784 sip->si_eoe_pos = end_endpos;
2785 sip->si_ends = TRUE;
2786 sip->si_end_idx = end_idx;
2787 }
2788}
2789
2790/*
2791 * Add a new state to the current state stack.
2792 * It is cleared and the index set to "idx".
2793 * Return FAIL if it's not possible (out of memory).
2794 */
2795 static int
2796push_current_state(idx)
2797 int idx;
2798{
2799 if (ga_grow(&current_state, 1) == FAIL)
2800 return FAIL;
2801 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2802 CUR_STATE(current_state.ga_len).si_idx = idx;
2803 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804 return OK;
2805}
2806
2807/*
2808 * Remove a state from the current_state stack.
2809 */
2810 static void
2811pop_current_state()
2812{
2813 if (current_state.ga_len)
2814 {
2815 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2816 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002817 }
2818 /* after the end of a pattern, try matching a keyword or pattern */
2819 next_match_idx = -1;
2820
2821 /* if first state with "keepend" is popped, reset keepend_level */
2822 if (keepend_level >= current_state.ga_len)
2823 keepend_level = -1;
2824}
2825
2826/*
2827 * Find the end of a start/skip/end syntax region after "startpos".
2828 * Only checks one line.
2829 * Also handles a match item that continued from a previous line.
2830 * If not found, the syntax item continues in the next line. m_endpos->lnum
2831 * will be 0.
2832 * If found, the end of the region and the end of the highlighting is
2833 * computed.
2834 */
2835 static void
2836find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2837 end_idx, start_ext)
2838 int idx; /* index of the pattern */
2839 lpos_T *startpos; /* where to start looking for an END match */
2840 lpos_T *m_endpos; /* return: end of match */
2841 lpos_T *hl_endpos; /* return: end of highlighting */
2842 long *flagsp; /* return: flags of matching END */
2843 lpos_T *end_endpos; /* return: end of end pattern match */
2844 int *end_idx; /* return: group ID for end pat. match, or 0 */
2845 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2846{
2847 colnr_T matchcol;
2848 synpat_T *spp, *spp_skip;
2849 int start_idx;
2850 int best_idx;
2851 regmmatch_T regmatch;
2852 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2853 lpos_T pos;
2854 char_u *line;
2855 int had_match = FALSE;
2856
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002857 /* just in case we are invoked for a keyword */
2858 if (idx < 0)
2859 return;
2860
Bram Moolenaar071d4272004-06-13 20:20:40 +00002861 /*
2862 * Check for being called with a START pattern.
2863 * Can happen with a match that continues to the next line, because it
2864 * contained a region.
2865 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002866 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002867 if (spp->sp_type != SPTYPE_START)
2868 {
2869 *hl_endpos = *startpos;
2870 return;
2871 }
2872
2873 /*
2874 * Find the SKIP or first END pattern after the last START pattern.
2875 */
2876 for (;;)
2877 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002878 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879 if (spp->sp_type != SPTYPE_START)
2880 break;
2881 ++idx;
2882 }
2883
2884 /*
2885 * Lookup the SKIP pattern (if present)
2886 */
2887 if (spp->sp_type == SPTYPE_SKIP)
2888 {
2889 spp_skip = spp;
2890 ++idx;
2891 }
2892 else
2893 spp_skip = NULL;
2894
2895 /* Setup external matches for syn_regexec(). */
2896 unref_extmatch(re_extmatch_in);
2897 re_extmatch_in = ref_extmatch(start_ext);
2898
2899 matchcol = startpos->col; /* start looking for a match at sstart */
2900 start_idx = idx; /* remember the first END pattern. */
2901 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2902 for (;;)
2903 {
2904 /*
2905 * Find end pattern that matches first after "matchcol".
2906 */
2907 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002908 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002909 {
2910 int lc_col = matchcol;
2911
Bram Moolenaar860cae12010-06-05 23:22:07 +02002912 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2914 break;
2915 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2916 if (lc_col < 0)
2917 lc_col = 0;
2918
2919 regmatch.rmm_ic = spp->sp_ic;
2920 regmatch.regprog = spp->sp_prog;
2921 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2922 {
2923 if (best_idx == -1 || regmatch.startpos[0].col
2924 < best_regmatch.startpos[0].col)
2925 {
2926 best_idx = idx;
2927 best_regmatch.startpos[0] = regmatch.startpos[0];
2928 best_regmatch.endpos[0] = regmatch.endpos[0];
2929 }
2930 }
2931 }
2932
2933 /*
2934 * If all end patterns have been tried, and there is no match, the
2935 * item continues until end-of-line.
2936 */
2937 if (best_idx == -1)
2938 break;
2939
2940 /*
2941 * If the skip pattern matches before the end pattern,
2942 * continue searching after the skip pattern.
2943 */
2944 if (spp_skip != NULL)
2945 {
2946 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2947
2948 if (lc_col < 0)
2949 lc_col = 0;
2950 regmatch.rmm_ic = spp_skip->sp_ic;
2951 regmatch.regprog = spp_skip->sp_prog;
2952 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2953 && regmatch.startpos[0].col
2954 <= best_regmatch.startpos[0].col)
2955 {
2956 /* Add offset to skip pattern match */
2957 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2958
2959 /* If the skip pattern goes on to the next line, there is no
2960 * match with an end pattern in this line. */
2961 if (pos.lnum > startpos->lnum)
2962 break;
2963
2964 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2965
2966 /* take care of an empty match or negative offset */
2967 if (pos.col <= matchcol)
2968 ++matchcol;
2969 else if (pos.col <= regmatch.endpos[0].col)
2970 matchcol = pos.col;
2971 else
2972 /* Be careful not to jump over the NUL at the end-of-line */
2973 for (matchcol = regmatch.endpos[0].col;
2974 line[matchcol] != NUL && matchcol < pos.col;
2975 ++matchcol)
2976 ;
2977
2978 /* if the skip pattern includes end-of-line, break here */
2979 if (line[matchcol] == NUL)
2980 break;
2981
2982 continue; /* start with first end pattern again */
2983 }
2984 }
2985
2986 /*
2987 * Match from start pattern to end pattern.
2988 * Correct for match and highlight offset of end pattern.
2989 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002990 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2992 /* can't end before the start */
2993 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2994 m_endpos->col = startpos->col;
2995
2996 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2997 /* can't end before the start */
2998 if (end_endpos->lnum == startpos->lnum
2999 && end_endpos->col < startpos->col)
3000 end_endpos->col = startpos->col;
3001 /* can't end after the match */
3002 limit_pos(end_endpos, m_endpos);
3003
3004 /*
3005 * If the end group is highlighted differently, adjust the pointers.
3006 */
3007 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3008 {
3009 *end_idx = best_idx;
3010 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3011 {
3012 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3013 hl_endpos->col = best_regmatch.endpos[0].col;
3014 }
3015 else
3016 {
3017 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3018 hl_endpos->col = best_regmatch.startpos[0].col;
3019 }
3020 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3021
3022 /* can't end before the start */
3023 if (hl_endpos->lnum == startpos->lnum
3024 && hl_endpos->col < startpos->col)
3025 hl_endpos->col = startpos->col;
3026 limit_pos(hl_endpos, m_endpos);
3027
3028 /* now the match ends where the highlighting ends, it is turned
3029 * into the matchgroup for the end */
3030 *m_endpos = *hl_endpos;
3031 }
3032 else
3033 {
3034 *end_idx = 0;
3035 *hl_endpos = *end_endpos;
3036 }
3037
3038 *flagsp = spp->sp_flags;
3039
3040 had_match = TRUE;
3041 break;
3042 }
3043
3044 /* no match for an END pattern in this line */
3045 if (!had_match)
3046 m_endpos->lnum = 0;
3047
3048 /* Remove external matches. */
3049 unref_extmatch(re_extmatch_in);
3050 re_extmatch_in = NULL;
3051}
3052
3053/*
3054 * Limit "pos" not to be after "limit".
3055 */
3056 static void
3057limit_pos(pos, limit)
3058 lpos_T *pos;
3059 lpos_T *limit;
3060{
3061 if (pos->lnum > limit->lnum)
3062 *pos = *limit;
3063 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3064 pos->col = limit->col;
3065}
3066
3067/*
3068 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3069 */
3070 static void
3071limit_pos_zero(pos, limit)
3072 lpos_T *pos;
3073 lpos_T *limit;
3074{
3075 if (pos->lnum == 0)
3076 *pos = *limit;
3077 else
3078 limit_pos(pos, limit);
3079}
3080
3081/*
3082 * Add offset to matched text for end of match or highlight.
3083 */
3084 static void
3085syn_add_end_off(result, regmatch, spp, idx, extra)
3086 lpos_T *result; /* returned position */
3087 regmmatch_T *regmatch; /* start/end of match */
3088 synpat_T *spp; /* matched pattern */
3089 int idx; /* index of offset */
3090 int extra; /* extra chars for offset to start */
3091{
3092 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003093 int off;
3094 char_u *base;
3095 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003096
3097 if (spp->sp_off_flags & (1 << idx))
3098 {
3099 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003100 col = regmatch->startpos[0].col;
3101 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003102 }
3103 else
3104 {
3105 result->lnum = regmatch->endpos[0].lnum;
3106 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003107 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003108 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003109 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3110 * is a matchgroup. Watch out for match with last NL in the buffer. */
3111 if (result->lnum > syn_buf->b_ml.ml_line_count)
3112 col = 0;
3113 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003114 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003115 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3116 p = base + col;
3117 if (off > 0)
3118 {
3119 while (off-- > 0 && *p != NUL)
3120 mb_ptr_adv(p);
3121 }
3122 else if (off < 0)
3123 {
3124 while (off++ < 0 && base < p)
3125 mb_ptr_back(base, p);
3126 }
3127 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003128 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003129 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003130}
3131
3132/*
3133 * Add offset to matched text for start of match or highlight.
3134 * Avoid resulting column to become negative.
3135 */
3136 static void
3137syn_add_start_off(result, regmatch, spp, idx, extra)
3138 lpos_T *result; /* returned position */
3139 regmmatch_T *regmatch; /* start/end of match */
3140 synpat_T *spp;
3141 int idx;
3142 int extra; /* extra chars for offset to end */
3143{
3144 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003145 int off;
3146 char_u *base;
3147 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003148
3149 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3150 {
3151 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003152 col = regmatch->endpos[0].col;
3153 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003154 }
3155 else
3156 {
3157 result->lnum = regmatch->startpos[0].lnum;
3158 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003159 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003160 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003161 if (result->lnum > syn_buf->b_ml.ml_line_count)
3162 {
3163 /* a "\n" at the end of the pattern may take us below the last line */
3164 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003165 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003166 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003167 if (off != 0)
3168 {
3169 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3170 p = base + col;
3171 if (off > 0)
3172 {
3173 while (off-- && *p != NUL)
3174 mb_ptr_adv(p);
3175 }
3176 else if (off < 0)
3177 {
3178 while (off++ && base < p)
3179 mb_ptr_back(base, p);
3180 }
3181 col = (int)(p - base);
3182 }
3183 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003184}
3185
3186/*
3187 * Get current line in syntax buffer.
3188 */
3189 static char_u *
3190syn_getcurline()
3191{
3192 return ml_get_buf(syn_buf, current_lnum, FALSE);
3193}
3194
3195/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003196 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003197 * Returns TRUE when there is a match.
3198 */
3199 static int
3200syn_regexec(rmp, lnum, col)
3201 regmmatch_T *rmp;
3202 linenr_T lnum;
3203 colnr_T col;
3204{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003205 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003206 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003207 {
3208 rmp->startpos[0].lnum += lnum;
3209 rmp->endpos[0].lnum += lnum;
3210 return TRUE;
3211 }
3212 return FALSE;
3213}
3214
3215/*
3216 * Check one position in a line for a matching keyword.
3217 * The caller must check if a keyword can start at startcol.
3218 * Return it's ID if found, 0 otherwise.
3219 */
3220 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003221check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222 char_u *line;
3223 int startcol; /* position in line to check for keyword */
3224 int *endcolp; /* return: character after found keyword */
3225 long *flagsp; /* return: flags of matching keyword */
3226 short **next_listp; /* return: next_list of matching keyword */
3227 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003228 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003230 keyentry_T *kp;
3231 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003233 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003234 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003235 hashtab_T *ht;
3236 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003237
3238 /* Find first character after the keyword. First character was already
3239 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003240 kwp = line + startcol;
3241 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003242 do
3243 {
3244#ifdef FEAT_MBYTE
3245 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003246 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 else
3248#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003249 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003250 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003251 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252
Bram Moolenaardad6b692005-01-25 22:14:34 +00003253 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 return 0;
3255
3256 /*
3257 * Must make a copy of the keyword, so we can add a NUL and make it
3258 * lowercase.
3259 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003260 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261
3262 /*
3263 * Try twice:
3264 * 1. matching case
3265 * 2. ignoring case
3266 */
3267 for (round = 1; round <= 2; ++round)
3268 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003269 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003270 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003272 if (round == 2) /* ignore case */
3273 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274
3275 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003276 * Find keywords that match. There can be several with different
3277 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278 * When current_next_list is non-zero accept only that group, otherwise:
3279 * Accept a not-contained keyword at toplevel.
3280 * Accept a keyword at other levels only if it is in the contains list.
3281 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003282 hi = hash_find(ht, keyword);
3283 if (!HASHITEM_EMPTY(hi))
3284 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003285 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003286 if (current_next_list != 0
3287 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3288 : (cur_si == NULL
3289 ? !(kp->flags & HL_CONTAINED)
3290 : in_id_list(cur_si, cur_si->si_cont_list,
3291 &kp->k_syn, kp->flags & HL_CONTAINED)))
3292 {
3293 *endcolp = startcol + kwlen;
3294 *flagsp = kp->flags;
3295 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003296#ifdef FEAT_CONCEAL
3297 *ccharp = kp->k_char;
3298#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003299 return kp->k_syn.id;
3300 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003301 }
3302 }
3303 return 0;
3304}
3305
3306/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003307 * Handle ":syntax conceal" command.
3308 */
3309 static void
3310syn_cmd_conceal(eap, syncing)
3311 exarg_T *eap UNUSED;
3312 int syncing UNUSED;
3313{
3314#ifdef FEAT_CONCEAL
3315 char_u *arg = eap->arg;
3316 char_u *next;
3317
3318 eap->nextcmd = find_nextcmd(arg);
3319 if (eap->skip)
3320 return;
3321
3322 next = skiptowhite(arg);
3323 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3324 curwin->w_s->b_syn_conceal = TRUE;
3325 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3326 curwin->w_s->b_syn_conceal = FALSE;
3327 else
3328 EMSG2(_("E390: Illegal argument: %s"), arg);
3329#endif
3330}
3331
3332/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333 * Handle ":syntax case" command.
3334 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 static void
3336syn_cmd_case(eap, syncing)
3337 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003338 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003339{
3340 char_u *arg = eap->arg;
3341 char_u *next;
3342
3343 eap->nextcmd = find_nextcmd(arg);
3344 if (eap->skip)
3345 return;
3346
3347 next = skiptowhite(arg);
3348 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003349 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003351 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 else
3353 EMSG2(_("E390: Illegal argument: %s"), arg);
3354}
3355
3356/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003357 * Handle ":syntax spell" command.
3358 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003359 static void
3360syn_cmd_spell(eap, syncing)
3361 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003362 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003363{
3364 char_u *arg = eap->arg;
3365 char_u *next;
3366
3367 eap->nextcmd = find_nextcmd(arg);
3368 if (eap->skip)
3369 return;
3370
3371 next = skiptowhite(arg);
3372 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003373 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003374 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003375 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003376 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003377 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003378 else
3379 EMSG2(_("E390: Illegal argument: %s"), arg);
3380}
3381
3382/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 * Clear all syntax info for one buffer.
3384 */
3385 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003386syntax_clear(block)
3387 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388{
3389 int i;
3390
Bram Moolenaar860cae12010-06-05 23:22:07 +02003391 block->b_syn_error = FALSE; /* clear previous error */
3392 block->b_syn_ic = FALSE; /* Use case, by default */
3393 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3394 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395
3396 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003397 clear_keywtab(&block->b_keywtab);
3398 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399
3400 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003401 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3402 syn_clear_pattern(block, i);
3403 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404
3405 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003406 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3407 syn_clear_cluster(block, i);
3408 ga_clear(&block->b_syn_clusters);
3409 block->b_spell_cluster_id = 0;
3410 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003411
Bram Moolenaar860cae12010-06-05 23:22:07 +02003412 block->b_syn_sync_flags = 0;
3413 block->b_syn_sync_minlines = 0;
3414 block->b_syn_sync_maxlines = 0;
3415 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416
Bram Moolenaar860cae12010-06-05 23:22:07 +02003417 vim_free(block->b_syn_linecont_prog);
3418 block->b_syn_linecont_prog = NULL;
3419 vim_free(block->b_syn_linecont_pat);
3420 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003422 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003423#endif
3424
3425 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003426 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003427 invalidate_current_state();
3428}
3429
3430/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003431 * Get rid of ownsyntax for window "wp".
3432 */
3433 void
3434reset_synblock(wp)
3435 win_T *wp;
3436{
3437 if (wp->w_s != &wp->w_buffer->b_s)
3438 {
3439 syntax_clear(wp->w_s);
3440 vim_free(wp->w_s);
3441 wp->w_s = &wp->w_buffer->b_s;
3442 }
3443}
3444
3445/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003446 * Clear syncing info for one buffer.
3447 */
3448 static void
3449syntax_sync_clear()
3450{
3451 int i;
3452
3453 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003454 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3455 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3456 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003457
Bram Moolenaar860cae12010-06-05 23:22:07 +02003458 curwin->w_s->b_syn_sync_flags = 0;
3459 curwin->w_s->b_syn_sync_minlines = 0;
3460 curwin->w_s->b_syn_sync_maxlines = 0;
3461 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003462
Bram Moolenaar860cae12010-06-05 23:22:07 +02003463 vim_free(curwin->w_s->b_syn_linecont_prog);
3464 curwin->w_s->b_syn_linecont_prog = NULL;
3465 vim_free(curwin->w_s->b_syn_linecont_pat);
3466 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003467
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469}
3470
3471/*
3472 * Remove one pattern from the buffer's pattern list.
3473 */
3474 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475syn_remove_pattern(block, idx)
3476 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 int idx;
3478{
3479 synpat_T *spp;
3480
Bram Moolenaar860cae12010-06-05 23:22:07 +02003481 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482#ifdef FEAT_FOLDING
3483 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003484 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003486 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3489 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490}
3491
3492/*
3493 * Clear and free one syntax pattern. When clearing all, must be called from
3494 * last to first!
3495 */
3496 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003497syn_clear_pattern(block, i)
3498 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499 int i;
3500{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003501 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3502 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003503 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003504 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3507 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3508 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 }
3510}
3511
3512/*
3513 * Clear and free one syntax cluster.
3514 */
3515 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003516syn_clear_cluster(block, i)
3517 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518 int i;
3519{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003520 vim_free(SYN_CLSTR(block)[i].scl_name);
3521 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3522 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003523}
3524
3525/*
3526 * Handle ":syntax clear" command.
3527 */
3528 static void
3529syn_cmd_clear(eap, syncing)
3530 exarg_T *eap;
3531 int syncing;
3532{
3533 char_u *arg = eap->arg;
3534 char_u *arg_end;
3535 int id;
3536
3537 eap->nextcmd = find_nextcmd(arg);
3538 if (eap->skip)
3539 return;
3540
3541 /*
3542 * We have to disable this within ":syn include @group filename",
3543 * because otherwise @group would get deleted.
3544 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3545 * clear".
3546 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003547 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548 return;
3549
3550 if (ends_excmd(*arg))
3551 {
3552 /*
3553 * No argument: Clear all syntax items.
3554 */
3555 if (syncing)
3556 syntax_sync_clear();
3557 else
3558 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 syntax_clear(curwin->w_s);
3560 if (curwin->w_s == &curwin->w_buffer->b_s)
3561 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003562 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563 }
3564 }
3565 else
3566 {
3567 /*
3568 * Clear the group IDs that are in the argument.
3569 */
3570 while (!ends_excmd(*arg))
3571 {
3572 arg_end = skiptowhite(arg);
3573 if (*arg == '@')
3574 {
3575 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3576 if (id == 0)
3577 {
3578 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3579 break;
3580 }
3581 else
3582 {
3583 /*
3584 * We can't physically delete a cluster without changing
3585 * the IDs of other clusters, so we do the next best thing
3586 * and make it empty.
3587 */
3588 short scl_id = id - SYNID_CLUSTER;
3589
Bram Moolenaar860cae12010-06-05 23:22:07 +02003590 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3591 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 }
3593 }
3594 else
3595 {
3596 id = syn_namen2id(arg, (int)(arg_end - arg));
3597 if (id == 0)
3598 {
3599 EMSG2(_(e_nogroup), arg);
3600 break;
3601 }
3602 else
3603 syn_clear_one(id, syncing);
3604 }
3605 arg = skipwhite(arg_end);
3606 }
3607 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003608 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003609 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610}
3611
3612/*
3613 * Clear one syntax group for the current buffer.
3614 */
3615 static void
3616syn_clear_one(id, syncing)
3617 int id;
3618 int syncing;
3619{
3620 synpat_T *spp;
3621 int idx;
3622
3623 /* Clear keywords only when not ":syn sync clear group-name" */
3624 if (!syncing)
3625 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003626 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3627 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628 }
3629
3630 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003631 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003633 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3635 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003636 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637 }
3638}
3639
3640/*
3641 * Handle ":syntax on" command.
3642 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003643 static void
3644syn_cmd_on(eap, syncing)
3645 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003646 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647{
3648 syn_cmd_onoff(eap, "syntax");
3649}
3650
3651/*
3652 * Handle ":syntax enable" command.
3653 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 static void
3655syn_cmd_enable(eap, syncing)
3656 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003657 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658{
3659 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3660 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003661 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662}
3663
3664/*
3665 * Handle ":syntax reset" command.
3666 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667 static void
3668syn_cmd_reset(eap, syncing)
3669 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003670 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671{
3672 eap->nextcmd = check_nextcmd(eap->arg);
3673 if (!eap->skip)
3674 {
3675 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3676 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003677 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678 }
3679}
3680
3681/*
3682 * Handle ":syntax manual" command.
3683 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684 static void
3685syn_cmd_manual(eap, syncing)
3686 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003687 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688{
3689 syn_cmd_onoff(eap, "manual");
3690}
3691
3692/*
3693 * Handle ":syntax off" command.
3694 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 static void
3696syn_cmd_off(eap, syncing)
3697 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003698 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003699{
3700 syn_cmd_onoff(eap, "nosyntax");
3701}
3702
3703 static void
3704syn_cmd_onoff(eap, name)
3705 exarg_T *eap;
3706 char *name;
3707{
3708 char_u buf[100];
3709
3710 eap->nextcmd = check_nextcmd(eap->arg);
3711 if (!eap->skip)
3712 {
3713 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003714 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715 do_cmdline_cmd(buf);
3716 }
3717}
3718
3719/*
3720 * Handle ":syntax [list]" command: list current syntax words.
3721 */
3722 static void
3723syn_cmd_list(eap, syncing)
3724 exarg_T *eap;
3725 int syncing; /* when TRUE: list syncing items */
3726{
3727 char_u *arg = eap->arg;
3728 int id;
3729 char_u *arg_end;
3730
3731 eap->nextcmd = find_nextcmd(arg);
3732 if (eap->skip)
3733 return;
3734
Bram Moolenaar860cae12010-06-05 23:22:07 +02003735 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 {
3737 MSG(_("No Syntax items defined for this buffer"));
3738 return;
3739 }
3740
3741 if (syncing)
3742 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003743 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 {
3745 MSG_PUTS(_("syncing on C-style comments"));
3746 syn_lines_msg();
3747 syn_match_msg();
3748 return;
3749 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003750 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003752 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 MSG_PUTS(_("no syncing"));
3754 else
3755 {
3756 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003757 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 MSG_PUTS(_(" lines before top line"));
3759 syn_match_msg();
3760 }
3761 return;
3762 }
3763 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003764 if (curwin->w_s->b_syn_sync_minlines > 0
3765 || curwin->w_s->b_syn_sync_maxlines > 0
3766 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 {
3768 MSG_PUTS(_("\nsyncing on items"));
3769 syn_lines_msg();
3770 syn_match_msg();
3771 }
3772 }
3773 else
3774 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3775 if (ends_excmd(*arg))
3776 {
3777 /*
3778 * No argument: List all group IDs and all syntax clusters.
3779 */
3780 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3781 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003782 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783 syn_list_cluster(id);
3784 }
3785 else
3786 {
3787 /*
3788 * List the group IDs and syntax clusters that are in the argument.
3789 */
3790 while (!ends_excmd(*arg) && !got_int)
3791 {
3792 arg_end = skiptowhite(arg);
3793 if (*arg == '@')
3794 {
3795 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3796 if (id == 0)
3797 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3798 else
3799 syn_list_cluster(id - SYNID_CLUSTER);
3800 }
3801 else
3802 {
3803 id = syn_namen2id(arg, (int)(arg_end - arg));
3804 if (id == 0)
3805 EMSG2(_(e_nogroup), arg);
3806 else
3807 syn_list_one(id, syncing, TRUE);
3808 }
3809 arg = skipwhite(arg_end);
3810 }
3811 }
3812 eap->nextcmd = check_nextcmd(arg);
3813}
3814
3815 static void
3816syn_lines_msg()
3817{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003818 if (curwin->w_s->b_syn_sync_maxlines > 0
3819 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 {
3821 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003822 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 {
3824 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003825 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3826 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003827 MSG_PUTS(", ");
3828 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003829 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003830 {
3831 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003832 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 }
3834 MSG_PUTS(_(" lines before top line"));
3835 }
3836}
3837
3838 static void
3839syn_match_msg()
3840{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003841 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 {
3843 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003844 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 MSG_PUTS(_(" line breaks"));
3846 }
3847}
3848
3849static int last_matchgroup;
3850
3851struct name_list
3852{
3853 int flag;
3854 char *name;
3855};
3856
3857static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3858
3859/*
3860 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3861 */
3862 static void
3863syn_list_one(id, syncing, link_only)
3864 int id;
3865 int syncing; /* when TRUE: list syncing items */
3866 int link_only; /* when TRUE; list link-only too */
3867{
3868 int attr;
3869 int idx;
3870 int did_header = FALSE;
3871 synpat_T *spp;
3872 static struct name_list namelist1[] =
3873 {
3874 {HL_DISPLAY, "display"},
3875 {HL_CONTAINED, "contained"},
3876 {HL_ONELINE, "oneline"},
3877 {HL_KEEPEND, "keepend"},
3878 {HL_EXTEND, "extend"},
3879 {HL_EXCLUDENL, "excludenl"},
3880 {HL_TRANSP, "transparent"},
3881 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003882#ifdef FEAT_CONCEAL
3883 {HL_CONCEAL, "conceal"},
3884 {HL_CONCEALENDS, "concealends"},
3885#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003886 {0, NULL}
3887 };
3888 static struct name_list namelist2[] =
3889 {
3890 {HL_SKIPWHITE, "skipwhite"},
3891 {HL_SKIPNL, "skipnl"},
3892 {HL_SKIPEMPTY, "skipempty"},
3893 {0, NULL}
3894 };
3895
3896 attr = hl_attr(HLF_D); /* highlight like directories */
3897
3898 /* list the keywords for "id" */
3899 if (!syncing)
3900 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003901 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3902 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903 did_header, attr);
3904 }
3905
3906 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003907 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003908 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003909 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3911 continue;
3912
3913 (void)syn_list_header(did_header, 999, id);
3914 did_header = TRUE;
3915 last_matchgroup = 0;
3916 if (spp->sp_type == SPTYPE_MATCH)
3917 {
3918 put_pattern("match", ' ', spp, attr);
3919 msg_putchar(' ');
3920 }
3921 else if (spp->sp_type == SPTYPE_START)
3922 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003923 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3924 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3925 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3926 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3927 while (idx < curwin->w_s->b_syn_patterns.ga_len
3928 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3929 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003930 --idx;
3931 msg_putchar(' ');
3932 }
3933 syn_list_flags(namelist1, spp->sp_flags, attr);
3934
3935 if (spp->sp_cont_list != NULL)
3936 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3937
3938 if (spp->sp_syn.cont_in_list != NULL)
3939 put_id_list((char_u *)"containedin",
3940 spp->sp_syn.cont_in_list, attr);
3941
3942 if (spp->sp_next_list != NULL)
3943 {
3944 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3945 syn_list_flags(namelist2, spp->sp_flags, attr);
3946 }
3947 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3948 {
3949 if (spp->sp_flags & HL_SYNC_HERE)
3950 msg_puts_attr((char_u *)"grouphere", attr);
3951 else
3952 msg_puts_attr((char_u *)"groupthere", attr);
3953 msg_putchar(' ');
3954 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003955 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3957 else
3958 MSG_PUTS("NONE");
3959 msg_putchar(' ');
3960 }
3961 }
3962
3963 /* list the link, if there is one */
3964 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3965 {
3966 (void)syn_list_header(did_header, 999, id);
3967 msg_puts_attr((char_u *)"links to", attr);
3968 msg_putchar(' ');
3969 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3970 }
3971}
3972
3973 static void
3974syn_list_flags(nl, flags, attr)
3975 struct name_list *nl;
3976 int flags;
3977 int attr;
3978{
3979 int i;
3980
3981 for (i = 0; nl[i].flag != 0; ++i)
3982 if (flags & nl[i].flag)
3983 {
3984 msg_puts_attr((char_u *)nl[i].name, attr);
3985 msg_putchar(' ');
3986 }
3987}
3988
3989/*
3990 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3991 */
3992 static void
3993syn_list_cluster(id)
3994 int id;
3995{
3996 int endcol = 15;
3997
3998 /* slight hack: roughly duplicate the guts of syn_list_header() */
3999 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004000 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001
4002 if (msg_col >= endcol) /* output at least one space */
4003 endcol = msg_col + 1;
4004 if (Columns <= endcol) /* avoid hang for tiny window */
4005 endcol = Columns - 1;
4006
4007 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004008 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004010 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004011 hl_attr(HLF_D));
4012 }
4013 else
4014 {
4015 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4016 msg_puts((char_u *)"=NONE");
4017 }
4018}
4019
4020 static void
4021put_id_list(name, list, attr)
4022 char_u *name;
4023 short *list;
4024 int attr;
4025{
4026 short *p;
4027
4028 msg_puts_attr(name, attr);
4029 msg_putchar('=');
4030 for (p = list; *p; ++p)
4031 {
4032 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4033 {
4034 if (p[1])
4035 MSG_PUTS("ALLBUT");
4036 else
4037 MSG_PUTS("ALL");
4038 }
4039 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4040 {
4041 MSG_PUTS("TOP");
4042 }
4043 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4044 {
4045 MSG_PUTS("CONTAINED");
4046 }
4047 else if (*p >= SYNID_CLUSTER)
4048 {
4049 short scl_id = *p - SYNID_CLUSTER;
4050
4051 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004052 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004053 }
4054 else
4055 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4056 if (p[1])
4057 msg_putchar(',');
4058 }
4059 msg_putchar(' ');
4060}
4061
4062 static void
4063put_pattern(s, c, spp, attr)
4064 char *s;
4065 int c;
4066 synpat_T *spp;
4067 int attr;
4068{
4069 long n;
4070 int mask;
4071 int first;
4072 static char *sepchars = "/+=-#@\"|'^&";
4073 int i;
4074
4075 /* May have to write "matchgroup=group" */
4076 if (last_matchgroup != spp->sp_syn_match_id)
4077 {
4078 last_matchgroup = spp->sp_syn_match_id;
4079 msg_puts_attr((char_u *)"matchgroup", attr);
4080 msg_putchar('=');
4081 if (last_matchgroup == 0)
4082 msg_outtrans((char_u *)"NONE");
4083 else
4084 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4085 msg_putchar(' ');
4086 }
4087
4088 /* output the name of the pattern and an '=' or ' ' */
4089 msg_puts_attr((char_u *)s, attr);
4090 msg_putchar(c);
4091
4092 /* output the pattern, in between a char that is not in the pattern */
4093 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4094 if (sepchars[++i] == NUL)
4095 {
4096 i = 0; /* no good char found, just use the first one */
4097 break;
4098 }
4099 msg_putchar(sepchars[i]);
4100 msg_outtrans(spp->sp_pattern);
4101 msg_putchar(sepchars[i]);
4102
4103 /* output any pattern options */
4104 first = TRUE;
4105 for (i = 0; i < SPO_COUNT; ++i)
4106 {
4107 mask = (1 << i);
4108 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4109 {
4110 if (!first)
4111 msg_putchar(','); /* separate with commas */
4112 msg_puts((char_u *)spo_name_tab[i]);
4113 n = spp->sp_offsets[i];
4114 if (i != SPO_LC_OFF)
4115 {
4116 if (spp->sp_off_flags & mask)
4117 msg_putchar('s');
4118 else
4119 msg_putchar('e');
4120 if (n > 0)
4121 msg_putchar('+');
4122 }
4123 if (n || i == SPO_LC_OFF)
4124 msg_outnum(n);
4125 first = FALSE;
4126 }
4127 }
4128 msg_putchar(' ');
4129}
4130
4131/*
4132 * List or clear the keywords for one syntax group.
4133 * Return TRUE if the header has been printed.
4134 */
4135 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004136syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004138 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 int did_header; /* header has already been printed */
4140 int attr;
4141{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004143 hashitem_T *hi;
4144 keyentry_T *kp;
4145 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 int prev_contained = 0;
4147 short *prev_next_list = NULL;
4148 short *prev_cont_in_list = NULL;
4149 int prev_skipnl = 0;
4150 int prev_skipwhite = 0;
4151 int prev_skipempty = 0;
4152
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 /*
4154 * Unfortunately, this list of keywords is not sorted on alphabet but on
4155 * hash value...
4156 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004157 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004158 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004160 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004161 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004162 --todo;
4163 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004164 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004165 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 if (prev_contained != (kp->flags & HL_CONTAINED)
4168 || prev_skipnl != (kp->flags & HL_SKIPNL)
4169 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4170 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4171 || prev_cont_in_list != kp->k_syn.cont_in_list
4172 || prev_next_list != kp->next_list)
4173 outlen = 9999;
4174 else
4175 outlen = (int)STRLEN(kp->keyword);
4176 /* output "contained" and "nextgroup" on each line */
4177 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004179 prev_contained = 0;
4180 prev_next_list = NULL;
4181 prev_cont_in_list = NULL;
4182 prev_skipnl = 0;
4183 prev_skipwhite = 0;
4184 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004186 did_header = TRUE;
4187 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004189 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004191 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004193 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004195 put_id_list((char_u *)"containedin",
4196 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004197 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004198 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004200 if (kp->next_list != prev_next_list)
4201 {
4202 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4203 msg_putchar(' ');
4204 prev_next_list = kp->next_list;
4205 if (kp->flags & HL_SKIPNL)
4206 {
4207 msg_puts_attr((char_u *)"skipnl", attr);
4208 msg_putchar(' ');
4209 prev_skipnl = (kp->flags & HL_SKIPNL);
4210 }
4211 if (kp->flags & HL_SKIPWHITE)
4212 {
4213 msg_puts_attr((char_u *)"skipwhite", attr);
4214 msg_putchar(' ');
4215 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4216 }
4217 if (kp->flags & HL_SKIPEMPTY)
4218 {
4219 msg_puts_attr((char_u *)"skipempty", attr);
4220 msg_putchar(' ');
4221 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4222 }
4223 }
4224 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 }
4227 }
4228 }
4229
4230 return did_header;
4231}
4232
4233 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004234syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004235 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004236 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004237{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004238 hashitem_T *hi;
4239 keyentry_T *kp;
4240 keyentry_T *kp_prev;
4241 keyentry_T *kp_next;
4242 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243
Bram Moolenaardad6b692005-01-25 22:14:34 +00004244 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004245 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004246 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004248 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004250 --todo;
4251 kp_prev = NULL;
4252 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004254 if (kp->k_syn.id == id)
4255 {
4256 kp_next = kp->ke_next;
4257 if (kp_prev == NULL)
4258 {
4259 if (kp_next == NULL)
4260 hash_remove(ht, hi);
4261 else
4262 hi->hi_key = KE2HIKEY(kp_next);
4263 }
4264 else
4265 kp_prev->ke_next = kp_next;
4266 vim_free(kp->next_list);
4267 vim_free(kp->k_syn.cont_in_list);
4268 vim_free(kp);
4269 kp = kp_next;
4270 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004272 {
4273 kp_prev = kp;
4274 kp = kp->ke_next;
4275 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 }
4277 }
4278 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280}
4281
4282/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004283 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 */
4285 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004286clear_keywtab(ht)
4287 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004289 hashitem_T *hi;
4290 int todo;
4291 keyentry_T *kp;
4292 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004294 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004295 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004296 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004297 if (!HASHITEM_EMPTY(hi))
4298 {
4299 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004302 kp_next = kp->ke_next;
4303 vim_free(kp->next_list);
4304 vim_free(kp->k_syn.cont_in_list);
4305 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004309 hash_clear(ht);
4310 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311}
4312
4313/*
4314 * Add a keyword to the list of keywords.
4315 */
4316 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004317add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318 char_u *name; /* name of keyword */
4319 int id; /* group ID for this keyword */
4320 int flags; /* flags for this keyword */
4321 short *cont_in_list; /* containedin for this keyword */
4322 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004323 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004324{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 keyentry_T *kp;
4326 hashtab_T *ht;
4327 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004328 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004330 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331
Bram Moolenaar860cae12010-06-05 23:22:07 +02004332 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004333 name_ic = str_foldcase(name, (int)STRLEN(name),
4334 name_folded, MAXKEYWLEN + 1);
4335 else
4336 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4338 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004340 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004341 kp->k_syn.id = id;
4342 kp->k_syn.inc_tag = current_syn_inc_tag;
4343 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004344 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004345 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004347 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004348 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349
Bram Moolenaar860cae12010-06-05 23:22:07 +02004350 if (curwin->w_s->b_syn_ic)
4351 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004353 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 hash = hash_hash(kp->keyword);
4356 hi = hash_lookup(ht, kp->keyword, hash);
4357 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 /* new keyword, add to hashtable */
4360 kp->ke_next = NULL;
4361 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 /* keyword already exists, prepend to list */
4366 kp->ke_next = HI2KE(hi);
4367 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369}
4370
4371/*
4372 * Get the start and end of the group name argument.
4373 * Return a pointer to the first argument.
4374 * Return NULL if the end of the command was found instead of further args.
4375 */
4376 static char_u *
4377get_group_name(arg, name_end)
4378 char_u *arg; /* start of the argument */
4379 char_u **name_end; /* pointer to end of the name */
4380{
4381 char_u *rest;
4382
4383 *name_end = skiptowhite(arg);
4384 rest = skipwhite(*name_end);
4385
4386 /*
4387 * Check if there are enough arguments. The first argument may be a
4388 * pattern, where '|' is allowed, so only check for NUL.
4389 */
4390 if (ends_excmd(*arg) || *rest == NUL)
4391 return NULL;
4392 return rest;
4393}
4394
4395/*
4396 * Check for syntax command option arguments.
4397 * This can be called at any place in the list of arguments, and just picks
4398 * out the arguments that are known. Can be called several times in a row to
4399 * collect all options in between other arguments.
4400 * Return a pointer to the next argument (which isn't an option).
4401 * Return NULL for any error;
4402 */
4403 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004404get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004405 char_u *arg; /* next argument to be checked */
4406 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004407 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004409 char_u *gname_start, *gname;
4410 int syn_id;
4411 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004412 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 int i;
4414 int fidx;
4415 static struct flag
4416 {
4417 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004418 int argtype;
4419 int flags;
4420 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4421 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4422 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4423 {"eExXtTeEnNdD", 0, HL_EXTEND},
4424 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4425 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4426 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4427 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4428 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4429 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4430 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4431 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4432 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004433 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4434 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4435 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004436 {"cCoOnNtTaAiInNsS", 1, 0},
4437 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4438 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004440 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441
4442 if (arg == NULL) /* already detected error */
4443 return NULL;
4444
Bram Moolenaar860cae12010-06-05 23:22:07 +02004445#ifdef FEAT_CONCEAL
4446 if (curwin->w_s->b_syn_conceal)
4447 opt->flags |= HL_CONCEAL;
4448#endif
4449
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 for (;;)
4451 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004452 /*
4453 * This is used very often when a large number of keywords is defined.
4454 * Need to skip quickly when no option name is found.
4455 * Also avoid tolower(), it's slow.
4456 */
4457 if (strchr(first_letters, *arg) == NULL)
4458 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459
4460 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4461 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004462 p = flagtab[fidx].name;
4463 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4464 if (arg[len] != p[i] && arg[len] != p[i + 1])
4465 break;
4466 if (p[i] == NUL && (vim_iswhite(arg[len])
4467 || (flagtab[fidx].argtype > 0
4468 ? arg[len] == '='
4469 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004471 if (opt->keyword
4472 && (flagtab[fidx].flags == HL_DISPLAY
4473 || flagtab[fidx].flags == HL_FOLD
4474 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475 /* treat "display", "fold" and "extend" as a keyword */
4476 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 break;
4478 }
4479 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004480 if (fidx < 0) /* no match found */
4481 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004483 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004485 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486 {
4487 EMSG(_("E395: contains argument not accepted here"));
4488 return NULL;
4489 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004490 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004491 return NULL;
4492 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004493 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004495#if 0 /* cannot happen */
4496 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 {
4498 EMSG(_("E396: containedin argument not accepted here"));
4499 return NULL;
4500 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004501#endif
4502 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 return NULL;
4504 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004505 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004507 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004508 return NULL;
4509 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004510 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4511 {
4512#ifdef FEAT_MBYTE
4513 /* cchar=? */
4514 if (has_mbyte)
4515 {
4516# ifdef FEAT_CONCEAL
4517 *conceal_char = mb_ptr2char(arg + 6);
4518# endif
4519 arg += mb_ptr2len(arg + 6) - 1;
4520 }
4521 else
4522#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004523 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004524#ifdef FEAT_CONCEAL
4525 *conceal_char = arg[6];
4526#else
4527 ;
4528#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004529 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004530 arg = skipwhite(arg + 7);
4531 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004533 {
4534 opt->flags |= flagtab[fidx].flags;
4535 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004536
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004537 if (flagtab[fidx].flags == HL_SYNC_HERE
4538 || flagtab[fidx].flags == HL_SYNC_THERE)
4539 {
4540 if (opt->sync_idx == NULL)
4541 {
4542 EMSG(_("E393: group[t]here not accepted here"));
4543 return NULL;
4544 }
4545 gname_start = arg;
4546 arg = skiptowhite(arg);
4547 if (gname_start == arg)
4548 return NULL;
4549 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4550 if (gname == NULL)
4551 return NULL;
4552 if (STRCMP(gname, "NONE") == 0)
4553 *opt->sync_idx = NONE_IDX;
4554 else
4555 {
4556 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004557 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4558 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4559 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004560 {
4561 *opt->sync_idx = i;
4562 break;
4563 }
4564 if (i < 0)
4565 {
4566 EMSG2(_("E394: Didn't find region item for %s"), gname);
4567 vim_free(gname);
4568 return NULL;
4569 }
4570 }
4571
4572 vim_free(gname);
4573 arg = skipwhite(arg);
4574 }
4575#ifdef FEAT_FOLDING
4576 else if (flagtab[fidx].flags == HL_FOLD
4577 && foldmethodIsSyntax(curwin))
4578 /* Need to update folds later. */
4579 foldUpdateAll(curwin);
4580#endif
4581 }
4582 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583
4584 return arg;
4585}
4586
4587/*
4588 * Adjustments to syntax item when declared in a ":syn include"'d file.
4589 * Set the contained flag, and if the item is not already contained, add it
4590 * to the specified top-level group, if any.
4591 */
4592 static void
4593syn_incl_toplevel(id, flagsp)
4594 int id;
4595 int *flagsp;
4596{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004597 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598 return;
4599 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004600 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601 {
4602 /* We have to alloc this, because syn_combine_list() will free it. */
4603 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004604 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605
4606 if (grp_list != NULL)
4607 {
4608 grp_list[0] = id;
4609 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004610 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 CLUSTER_ADD);
4612 }
4613 }
4614}
4615
4616/*
4617 * Handle ":syntax include [@{group-name}] filename" command.
4618 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004619 static void
4620syn_cmd_include(eap, syncing)
4621 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004622 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004623{
4624 char_u *arg = eap->arg;
4625 int sgl_id = 1;
4626 char_u *group_name_end;
4627 char_u *rest;
4628 char_u *errormsg = NULL;
4629 int prev_toplvl_grp;
4630 int prev_syn_inc_tag;
4631 int source = FALSE;
4632
4633 eap->nextcmd = find_nextcmd(arg);
4634 if (eap->skip)
4635 return;
4636
4637 if (arg[0] == '@')
4638 {
4639 ++arg;
4640 rest = get_group_name(arg, &group_name_end);
4641 if (rest == NULL)
4642 {
4643 EMSG((char_u *)_("E397: Filename required"));
4644 return;
4645 }
4646 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4647 /* separate_nextcmd() and expand_filename() depend on this */
4648 eap->arg = rest;
4649 }
4650
4651 /*
4652 * Everything that's left, up to the next command, should be the
4653 * filename to include.
4654 */
4655 eap->argt |= (XFILE | NOSPC);
4656 separate_nextcmd(eap);
4657 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4658 {
4659 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4660 * file. Need to expand the file name first. In other cases
4661 * ":runtime!" is used. */
4662 source = TRUE;
4663 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4664 {
4665 if (errormsg != NULL)
4666 EMSG(errormsg);
4667 return;
4668 }
4669 }
4670
4671 /*
4672 * Save and restore the existing top-level grouplist id and ":syn
4673 * include" tag around the actual inclusion.
4674 */
4675 prev_syn_inc_tag = current_syn_inc_tag;
4676 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004677 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4678 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004679 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4680 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004682 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004683 current_syn_inc_tag = prev_syn_inc_tag;
4684}
4685
4686/*
4687 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4688 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689 static void
4690syn_cmd_keyword(eap, syncing)
4691 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004692 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693{
4694 char_u *arg = eap->arg;
4695 char_u *group_name_end;
4696 int syn_id;
4697 char_u *rest;
4698 char_u *keyword_copy;
4699 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004700 char_u *kw;
4701 syn_opt_arg_T syn_opt_arg;
4702 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004703 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704
4705 rest = get_group_name(arg, &group_name_end);
4706
4707 if (rest != NULL)
4708 {
4709 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4710
4711 /* allocate a buffer, for removing the backslashes in the keyword */
4712 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4713 if (keyword_copy != NULL)
4714 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004715 syn_opt_arg.flags = 0;
4716 syn_opt_arg.keyword = TRUE;
4717 syn_opt_arg.sync_idx = NULL;
4718 syn_opt_arg.has_cont_list = FALSE;
4719 syn_opt_arg.cont_in_list = NULL;
4720 syn_opt_arg.next_list = NULL;
4721
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 /*
4723 * The options given apply to ALL keywords, so all options must be
4724 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004725 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004726 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004727 cnt = 0;
4728 p = keyword_copy;
4729 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004731 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004732 if (rest == NULL || ends_excmd(*rest))
4733 break;
4734 /* Copy the keyword, removing backslashes, and add a NUL. */
4735 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004737 if (*rest == '\\' && rest[1] != NUL)
4738 ++rest;
4739 *p++ = *rest++;
4740 }
4741 *p++ = NUL;
4742 ++cnt;
4743 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004744
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004745 if (!eap->skip)
4746 {
4747 /* Adjust flags for use of ":syn include". */
4748 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4749
4750 /*
4751 * 2: Add an entry for each keyword.
4752 */
4753 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4754 {
4755 for (p = vim_strchr(kw, '['); ; )
4756 {
4757 if (p != NULL)
4758 *p = NUL;
4759 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004760 syn_opt_arg.cont_in_list,
4761 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004762 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004763 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004764 if (p[1] == NUL)
4765 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004766 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004767 kw = p + 2; /* skip over the NUL */
4768 break;
4769 }
4770 if (p[1] == ']')
4771 {
4772 kw = p + 1; /* skip over the "]" */
4773 break;
4774 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004775#ifdef FEAT_MBYTE
4776 if (has_mbyte)
4777 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004778 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004779
4780 mch_memmove(p, p + 1, l);
4781 p += l;
4782 }
4783 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004785 {
4786 p[0] = p[1];
4787 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 }
4789 }
4790 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004791 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004792
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004794 vim_free(syn_opt_arg.cont_in_list);
4795 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 }
4797 }
4798
4799 if (rest != NULL)
4800 eap->nextcmd = check_nextcmd(rest);
4801 else
4802 EMSG2(_(e_invarg2), arg);
4803
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004804 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004805 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806}
4807
4808/*
4809 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4810 *
4811 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4812 */
4813 static void
4814syn_cmd_match(eap, syncing)
4815 exarg_T *eap;
4816 int syncing; /* TRUE for ":syntax sync match .. " */
4817{
4818 char_u *arg = eap->arg;
4819 char_u *group_name_end;
4820 char_u *rest;
4821 synpat_T item; /* the item found in the line */
4822 int syn_id;
4823 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004824 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004826 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827
4828 /* Isolate the group name, check for validity */
4829 rest = get_group_name(arg, &group_name_end);
4830
4831 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004832 syn_opt_arg.flags = 0;
4833 syn_opt_arg.keyword = FALSE;
4834 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4835 syn_opt_arg.has_cont_list = TRUE;
4836 syn_opt_arg.cont_list = NULL;
4837 syn_opt_arg.cont_in_list = NULL;
4838 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004839 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840
4841 /* get the pattern. */
4842 init_syn_patterns();
4843 vim_memset(&item, 0, sizeof(item));
4844 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004845 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4846 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004847
4848 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004849 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850
4851 if (rest != NULL) /* all arguments are valid */
4852 {
4853 /*
4854 * Check for trailing command and illegal trailing arguments.
4855 */
4856 eap->nextcmd = check_nextcmd(rest);
4857 if (!ends_excmd(*rest) || eap->skip)
4858 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004859 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 && (syn_id = syn_check_group(arg,
4861 (int)(group_name_end - arg))) != 0)
4862 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004863 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004864 /*
4865 * Store the pattern in the syn_items list
4866 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004867 idx = curwin->w_s->b_syn_patterns.ga_len;
4868 SYN_ITEMS(curwin->w_s)[idx] = item;
4869 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4870 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4871 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4872 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4873 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4874 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4875 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4876 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004877 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004878#ifdef FEAT_CONCEAL
4879 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4880#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004881 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004882 curwin->w_s->b_syn_containedin = TRUE;
4883 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4884 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885
4886 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004887 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004888 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004890 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892#endif
4893
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004894 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004895 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896 return; /* don't free the progs and patterns now */
4897 }
4898 }
4899
4900 /*
4901 * Something failed, free the allocated memory.
4902 */
4903 vim_free(item.sp_prog);
4904 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004905 vim_free(syn_opt_arg.cont_list);
4906 vim_free(syn_opt_arg.cont_in_list);
4907 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908
4909 if (rest == NULL)
4910 EMSG2(_(e_invarg2), arg);
4911}
4912
4913/*
4914 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4915 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4916 */
4917 static void
4918syn_cmd_region(eap, syncing)
4919 exarg_T *eap;
4920 int syncing; /* TRUE for ":syntax sync region .." */
4921{
4922 char_u *arg = eap->arg;
4923 char_u *group_name_end;
4924 char_u *rest; /* next arg, NULL on error */
4925 char_u *key_end;
4926 char_u *key = NULL;
4927 char_u *p;
4928 int item;
4929#define ITEM_START 0
4930#define ITEM_SKIP 1
4931#define ITEM_END 2
4932#define ITEM_MATCHGROUP 3
4933 struct pat_ptr
4934 {
4935 synpat_T *pp_synp; /* pointer to syn_pattern */
4936 int pp_matchgroup_id; /* matchgroup ID */
4937 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4938 } *(pat_ptrs[3]);
4939 /* patterns found in the line */
4940 struct pat_ptr *ppp;
4941 struct pat_ptr *ppp_next;
4942 int pat_count = 0; /* nr of syn_patterns found */
4943 int syn_id;
4944 int matchgroup_id = 0;
4945 int not_enough = FALSE; /* not enough arguments */
4946 int illegal = FALSE; /* illegal arguments */
4947 int success = FALSE;
4948 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004949 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004950 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004951
4952 /* Isolate the group name, check for validity */
4953 rest = get_group_name(arg, &group_name_end);
4954
4955 pat_ptrs[0] = NULL;
4956 pat_ptrs[1] = NULL;
4957 pat_ptrs[2] = NULL;
4958
4959 init_syn_patterns();
4960
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004961 syn_opt_arg.flags = 0;
4962 syn_opt_arg.keyword = FALSE;
4963 syn_opt_arg.sync_idx = NULL;
4964 syn_opt_arg.has_cont_list = TRUE;
4965 syn_opt_arg.cont_list = NULL;
4966 syn_opt_arg.cont_in_list = NULL;
4967 syn_opt_arg.next_list = NULL;
4968
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 /*
4970 * get the options, patterns and matchgroup.
4971 */
4972 while (rest != NULL && !ends_excmd(*rest))
4973 {
4974 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004975 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004976 if (rest == NULL || ends_excmd(*rest))
4977 break;
4978
4979 /* must be a pattern or matchgroup then */
4980 key_end = rest;
4981 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4982 ++key_end;
4983 vim_free(key);
4984 key = vim_strnsave_up(rest, (int)(key_end - rest));
4985 if (key == NULL) /* out of memory */
4986 {
4987 rest = NULL;
4988 break;
4989 }
4990 if (STRCMP(key, "MATCHGROUP") == 0)
4991 item = ITEM_MATCHGROUP;
4992 else if (STRCMP(key, "START") == 0)
4993 item = ITEM_START;
4994 else if (STRCMP(key, "END") == 0)
4995 item = ITEM_END;
4996 else if (STRCMP(key, "SKIP") == 0)
4997 {
4998 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4999 {
5000 illegal = TRUE;
5001 break;
5002 }
5003 item = ITEM_SKIP;
5004 }
5005 else
5006 break;
5007 rest = skipwhite(key_end);
5008 if (*rest != '=')
5009 {
5010 rest = NULL;
5011 EMSG2(_("E398: Missing '=': %s"), arg);
5012 break;
5013 }
5014 rest = skipwhite(rest + 1);
5015 if (*rest == NUL)
5016 {
5017 not_enough = TRUE;
5018 break;
5019 }
5020
5021 if (item == ITEM_MATCHGROUP)
5022 {
5023 p = skiptowhite(rest);
5024 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5025 matchgroup_id = 0;
5026 else
5027 {
5028 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5029 if (matchgroup_id == 0)
5030 {
5031 illegal = TRUE;
5032 break;
5033 }
5034 }
5035 rest = skipwhite(p);
5036 }
5037 else
5038 {
5039 /*
5040 * Allocate room for a syn_pattern, and link it in the list of
5041 * syn_patterns for this item, at the start (because the list is
5042 * used from end to start).
5043 */
5044 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5045 if (ppp == NULL)
5046 {
5047 rest = NULL;
5048 break;
5049 }
5050 ppp->pp_next = pat_ptrs[item];
5051 pat_ptrs[item] = ppp;
5052 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5053 if (ppp->pp_synp == NULL)
5054 {
5055 rest = NULL;
5056 break;
5057 }
5058
5059 /*
5060 * Get the syntax pattern and the following offset(s).
5061 */
5062 /* Enable the appropriate \z specials. */
5063 if (item == ITEM_START)
5064 reg_do_extmatch = REX_SET;
5065 else if (item == ITEM_SKIP || item == ITEM_END)
5066 reg_do_extmatch = REX_USE;
5067 rest = get_syn_pattern(rest, ppp->pp_synp);
5068 reg_do_extmatch = 0;
5069 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005070 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5072 ppp->pp_matchgroup_id = matchgroup_id;
5073 ++pat_count;
5074 }
5075 }
5076 vim_free(key);
5077 if (illegal || not_enough)
5078 rest = NULL;
5079
5080 /*
5081 * Must have a "start" and "end" pattern.
5082 */
5083 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5084 pat_ptrs[ITEM_END] == NULL))
5085 {
5086 not_enough = TRUE;
5087 rest = NULL;
5088 }
5089
5090 if (rest != NULL)
5091 {
5092 /*
5093 * Check for trailing garbage or command.
5094 * If OK, add the item.
5095 */
5096 eap->nextcmd = check_nextcmd(rest);
5097 if (!ends_excmd(*rest) || eap->skip)
5098 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005099 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005100 && (syn_id = syn_check_group(arg,
5101 (int)(group_name_end - arg))) != 0)
5102 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005103 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005104 /*
5105 * Store the start/skip/end in the syn_items list
5106 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005107 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108 for (item = ITEM_START; item <= ITEM_END; ++item)
5109 {
5110 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5111 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005112 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5113 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5114 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 (item == ITEM_START) ? SPTYPE_START :
5116 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005117 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5118 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5119 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5120 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005122#ifdef FEAT_CONCEAL
5123 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5124#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 if (item == ITEM_START)
5126 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005127 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005128 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005129 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005130 syn_opt_arg.cont_in_list;
5131 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005132 curwin->w_s->b_syn_containedin = TRUE;
5133 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005134 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005136 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005137 ++idx;
5138#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005139 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005140 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005141#endif
5142 }
5143 }
5144
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005145 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005146 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147 success = TRUE; /* don't free the progs and patterns now */
5148 }
5149 }
5150
5151 /*
5152 * Free the allocated memory.
5153 */
5154 for (item = ITEM_START; item <= ITEM_END; ++item)
5155 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5156 {
5157 if (!success)
5158 {
5159 vim_free(ppp->pp_synp->sp_prog);
5160 vim_free(ppp->pp_synp->sp_pattern);
5161 }
5162 vim_free(ppp->pp_synp);
5163 ppp_next = ppp->pp_next;
5164 vim_free(ppp);
5165 }
5166
5167 if (!success)
5168 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005169 vim_free(syn_opt_arg.cont_list);
5170 vim_free(syn_opt_arg.cont_in_list);
5171 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172 if (not_enough)
5173 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5174 else if (illegal || rest == NULL)
5175 EMSG2(_(e_invarg2), arg);
5176 }
5177}
5178
5179/*
5180 * A simple syntax group ID comparison function suitable for use in qsort()
5181 */
5182 static int
5183#ifdef __BORLANDC__
5184_RTLENTRYF
5185#endif
5186syn_compare_stub(v1, v2)
5187 const void *v1;
5188 const void *v2;
5189{
5190 const short *s1 = v1;
5191 const short *s2 = v2;
5192
5193 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5194}
5195
5196/*
5197 * Combines lists of syntax clusters.
5198 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5199 */
5200 static void
5201syn_combine_list(clstr1, clstr2, list_op)
5202 short **clstr1;
5203 short **clstr2;
5204 int list_op;
5205{
5206 int count1 = 0;
5207 int count2 = 0;
5208 short *g1;
5209 short *g2;
5210 short *clstr = NULL;
5211 int count;
5212 int round;
5213
5214 /*
5215 * Handle degenerate cases.
5216 */
5217 if (*clstr2 == NULL)
5218 return;
5219 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5220 {
5221 if (list_op == CLUSTER_REPLACE)
5222 vim_free(*clstr1);
5223 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5224 *clstr1 = *clstr2;
5225 else
5226 vim_free(*clstr2);
5227 return;
5228 }
5229
5230 for (g1 = *clstr1; *g1; g1++)
5231 ++count1;
5232 for (g2 = *clstr2; *g2; g2++)
5233 ++count2;
5234
5235 /*
5236 * For speed purposes, sort both lists.
5237 */
5238 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5239 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5240
5241 /*
5242 * We proceed in two passes; in round 1, we count the elements to place
5243 * in the new list, and in round 2, we allocate and populate the new
5244 * list. For speed, we use a mergesort-like method, adding the smaller
5245 * of the current elements in each list to the new list.
5246 */
5247 for (round = 1; round <= 2; round++)
5248 {
5249 g1 = *clstr1;
5250 g2 = *clstr2;
5251 count = 0;
5252
5253 /*
5254 * First, loop through the lists until one of them is empty.
5255 */
5256 while (*g1 && *g2)
5257 {
5258 /*
5259 * We always want to add from the first list.
5260 */
5261 if (*g1 < *g2)
5262 {
5263 if (round == 2)
5264 clstr[count] = *g1;
5265 count++;
5266 g1++;
5267 continue;
5268 }
5269 /*
5270 * We only want to add from the second list if we're adding the
5271 * lists.
5272 */
5273 if (list_op == CLUSTER_ADD)
5274 {
5275 if (round == 2)
5276 clstr[count] = *g2;
5277 count++;
5278 }
5279 if (*g1 == *g2)
5280 g1++;
5281 g2++;
5282 }
5283
5284 /*
5285 * Now add the leftovers from whichever list didn't get finished
5286 * first. As before, we only want to add from the second list if
5287 * we're adding the lists.
5288 */
5289 for (; *g1; g1++, count++)
5290 if (round == 2)
5291 clstr[count] = *g1;
5292 if (list_op == CLUSTER_ADD)
5293 for (; *g2; g2++, count++)
5294 if (round == 2)
5295 clstr[count] = *g2;
5296
5297 if (round == 1)
5298 {
5299 /*
5300 * If the group ended up empty, we don't need to allocate any
5301 * space for it.
5302 */
5303 if (count == 0)
5304 {
5305 clstr = NULL;
5306 break;
5307 }
5308 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5309 if (clstr == NULL)
5310 break;
5311 clstr[count] = 0;
5312 }
5313 }
5314
5315 /*
5316 * Finally, put the new list in place.
5317 */
5318 vim_free(*clstr1);
5319 vim_free(*clstr2);
5320 *clstr1 = clstr;
5321}
5322
5323/*
5324 * Lookup a syntax cluster name and return it's ID.
5325 * If it is not found, 0 is returned.
5326 */
5327 static int
5328syn_scl_name2id(name)
5329 char_u *name;
5330{
5331 int i;
5332 char_u *name_u;
5333
5334 /* Avoid using stricmp() too much, it's slow on some systems */
5335 name_u = vim_strsave_up(name);
5336 if (name_u == NULL)
5337 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005338 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5339 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5340 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005341 break;
5342 vim_free(name_u);
5343 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5344}
5345
5346/*
5347 * Like syn_scl_name2id(), but take a pointer + length argument.
5348 */
5349 static int
5350syn_scl_namen2id(linep, len)
5351 char_u *linep;
5352 int len;
5353{
5354 char_u *name;
5355 int id = 0;
5356
5357 name = vim_strnsave(linep, len);
5358 if (name != NULL)
5359 {
5360 id = syn_scl_name2id(name);
5361 vim_free(name);
5362 }
5363 return id;
5364}
5365
5366/*
5367 * Find syntax cluster name in the table and return it's ID.
5368 * The argument is a pointer to the name and the length of the name.
5369 * If it doesn't exist yet, a new entry is created.
5370 * Return 0 for failure.
5371 */
5372 static int
5373syn_check_cluster(pp, len)
5374 char_u *pp;
5375 int len;
5376{
5377 int id;
5378 char_u *name;
5379
5380 name = vim_strnsave(pp, len);
5381 if (name == NULL)
5382 return 0;
5383
5384 id = syn_scl_name2id(name);
5385 if (id == 0) /* doesn't exist yet */
5386 id = syn_add_cluster(name);
5387 else
5388 vim_free(name);
5389 return id;
5390}
5391
5392/*
5393 * Add new syntax cluster and return it's ID.
5394 * "name" must be an allocated string, it will be consumed.
5395 * Return 0 for failure.
5396 */
5397 static int
5398syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005399 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005400{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005401 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005402
5403 /*
5404 * First call for this growarray: init growing array.
5405 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005406 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005408 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5409 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410 }
5411
5412 /*
5413 * Make room for at least one other cluster entry.
5414 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005415 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005416 {
5417 vim_free(name);
5418 return 0;
5419 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005420 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005421
Bram Moolenaar860cae12010-06-05 23:22:07 +02005422 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5423 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5424 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5425 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5426 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427
Bram Moolenaar217ad922005-03-20 22:37:15 +00005428 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005429 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005430 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005431 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005432
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 return len + SYNID_CLUSTER;
5434}
5435
5436/*
5437 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5438 * [add={groupname},..] [remove={groupname},..]".
5439 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 static void
5441syn_cmd_cluster(eap, syncing)
5442 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005443 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444{
5445 char_u *arg = eap->arg;
5446 char_u *group_name_end;
5447 char_u *rest;
5448 int scl_id;
5449 short *clstr_list;
5450 int got_clstr = FALSE;
5451 int opt_len;
5452 int list_op;
5453
5454 eap->nextcmd = find_nextcmd(arg);
5455 if (eap->skip)
5456 return;
5457
5458 rest = get_group_name(arg, &group_name_end);
5459
5460 if (rest != NULL)
5461 {
5462 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005463 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464
5465 for (;;)
5466 {
5467 if (STRNICMP(rest, "add", 3) == 0
5468 && (vim_iswhite(rest[3]) || rest[3] == '='))
5469 {
5470 opt_len = 3;
5471 list_op = CLUSTER_ADD;
5472 }
5473 else if (STRNICMP(rest, "remove", 6) == 0
5474 && (vim_iswhite(rest[6]) || rest[6] == '='))
5475 {
5476 opt_len = 6;
5477 list_op = CLUSTER_SUBTRACT;
5478 }
5479 else if (STRNICMP(rest, "contains", 8) == 0
5480 && (vim_iswhite(rest[8]) || rest[8] == '='))
5481 {
5482 opt_len = 8;
5483 list_op = CLUSTER_REPLACE;
5484 }
5485 else
5486 break;
5487
5488 clstr_list = NULL;
5489 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5490 {
5491 EMSG2(_(e_invarg2), rest);
5492 break;
5493 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005494 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005495 &clstr_list, list_op);
5496 got_clstr = TRUE;
5497 }
5498
5499 if (got_clstr)
5500 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005501 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005502 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005503 }
5504 }
5505
5506 if (!got_clstr)
5507 EMSG(_("E400: No cluster specified"));
5508 if (rest == NULL || !ends_excmd(*rest))
5509 EMSG2(_(e_invarg2), arg);
5510}
5511
5512/*
5513 * On first call for current buffer: Init growing array.
5514 */
5515 static void
5516init_syn_patterns()
5517{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005518 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5519 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520}
5521
5522/*
5523 * Get one pattern for a ":syntax match" or ":syntax region" command.
5524 * Stores the pattern and program in a synpat_T.
5525 * Returns a pointer to the next argument, or NULL in case of an error.
5526 */
5527 static char_u *
5528get_syn_pattern(arg, ci)
5529 char_u *arg;
5530 synpat_T *ci;
5531{
5532 char_u *end;
5533 int *p;
5534 int idx;
5535 char_u *cpo_save;
5536
5537 /* need at least three chars */
5538 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5539 return NULL;
5540
5541 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5542 if (*end != *arg) /* end delimiter not found */
5543 {
5544 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5545 return NULL;
5546 }
5547 /* store the pattern and compiled regexp program */
5548 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5549 return NULL;
5550
5551 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5552 cpo_save = p_cpo;
5553 p_cpo = (char_u *)"";
5554 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5555 p_cpo = cpo_save;
5556
5557 if (ci->sp_prog == NULL)
5558 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005559 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560
5561 /*
5562 * Check for a match, highlight or region offset.
5563 */
5564 ++end;
5565 do
5566 {
5567 for (idx = SPO_COUNT; --idx >= 0; )
5568 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5569 break;
5570 if (idx >= 0)
5571 {
5572 p = &(ci->sp_offsets[idx]);
5573 if (idx != SPO_LC_OFF)
5574 switch (end[3])
5575 {
5576 case 's': break;
5577 case 'b': break;
5578 case 'e': idx += SPO_COUNT; break;
5579 default: idx = -1; break;
5580 }
5581 if (idx >= 0)
5582 {
5583 ci->sp_off_flags |= (1 << idx);
5584 if (idx == SPO_LC_OFF) /* lc=99 */
5585 {
5586 end += 3;
5587 *p = getdigits(&end);
5588
5589 /* "lc=" offset automatically sets "ms=" offset */
5590 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5591 {
5592 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5593 ci->sp_offsets[SPO_MS_OFF] = *p;
5594 }
5595 }
5596 else /* yy=x+99 */
5597 {
5598 end += 4;
5599 if (*end == '+')
5600 {
5601 ++end;
5602 *p = getdigits(&end); /* positive offset */
5603 }
5604 else if (*end == '-')
5605 {
5606 ++end;
5607 *p = -getdigits(&end); /* negative offset */
5608 }
5609 }
5610 if (*end != ',')
5611 break;
5612 ++end;
5613 }
5614 }
5615 } while (idx >= 0);
5616
5617 if (!ends_excmd(*end) && !vim_iswhite(*end))
5618 {
5619 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5620 return NULL;
5621 }
5622 return skipwhite(end);
5623}
5624
5625/*
5626 * Handle ":syntax sync .." command.
5627 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005628 static void
5629syn_cmd_sync(eap, syncing)
5630 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005631 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005632{
5633 char_u *arg_start = eap->arg;
5634 char_u *arg_end;
5635 char_u *key = NULL;
5636 char_u *next_arg;
5637 int illegal = FALSE;
5638 int finished = FALSE;
5639 long n;
5640 char_u *cpo_save;
5641
5642 if (ends_excmd(*arg_start))
5643 {
5644 syn_cmd_list(eap, TRUE);
5645 return;
5646 }
5647
5648 while (!ends_excmd(*arg_start))
5649 {
5650 arg_end = skiptowhite(arg_start);
5651 next_arg = skipwhite(arg_end);
5652 vim_free(key);
5653 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5654 if (STRCMP(key, "CCOMMENT") == 0)
5655 {
5656 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005657 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005658 if (!ends_excmd(*next_arg))
5659 {
5660 arg_end = skiptowhite(next_arg);
5661 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005662 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663 (int)(arg_end - next_arg));
5664 next_arg = skipwhite(arg_end);
5665 }
5666 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005667 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 }
5669 else if ( STRNCMP(key, "LINES", 5) == 0
5670 || STRNCMP(key, "MINLINES", 8) == 0
5671 || STRNCMP(key, "MAXLINES", 8) == 0
5672 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5673 {
5674 if (key[4] == 'S')
5675 arg_end = key + 6;
5676 else if (key[0] == 'L')
5677 arg_end = key + 11;
5678 else
5679 arg_end = key + 9;
5680 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5681 {
5682 illegal = TRUE;
5683 break;
5684 }
5685 n = getdigits(&arg_end);
5686 if (!eap->skip)
5687 {
5688 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005689 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005691 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005692 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005693 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005694 }
5695 }
5696 else if (STRCMP(key, "FROMSTART") == 0)
5697 {
5698 if (!eap->skip)
5699 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005700 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5701 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005702 }
5703 }
5704 else if (STRCMP(key, "LINECONT") == 0)
5705 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005706 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707 {
5708 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5709 finished = TRUE;
5710 break;
5711 }
5712 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5713 if (*arg_end != *next_arg) /* end delimiter not found */
5714 {
5715 illegal = TRUE;
5716 break;
5717 }
5718
5719 if (!eap->skip)
5720 {
5721 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005722 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005723 (int)(arg_end - next_arg - 1))) == NULL)
5724 {
5725 finished = TRUE;
5726 break;
5727 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005728 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005729
5730 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5731 cpo_save = p_cpo;
5732 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005733 curwin->w_s->b_syn_linecont_prog =
5734 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005735 p_cpo = cpo_save;
5736
Bram Moolenaar860cae12010-06-05 23:22:07 +02005737 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005739 vim_free(curwin->w_s->b_syn_linecont_pat);
5740 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005741 finished = TRUE;
5742 break;
5743 }
5744 }
5745 next_arg = skipwhite(arg_end + 1);
5746 }
5747 else
5748 {
5749 eap->arg = next_arg;
5750 if (STRCMP(key, "MATCH") == 0)
5751 syn_cmd_match(eap, TRUE);
5752 else if (STRCMP(key, "REGION") == 0)
5753 syn_cmd_region(eap, TRUE);
5754 else if (STRCMP(key, "CLEAR") == 0)
5755 syn_cmd_clear(eap, TRUE);
5756 else
5757 illegal = TRUE;
5758 finished = TRUE;
5759 break;
5760 }
5761 arg_start = next_arg;
5762 }
5763 vim_free(key);
5764 if (illegal)
5765 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5766 else if (!finished)
5767 {
5768 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005769 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005770 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771 }
5772}
5773
5774/*
5775 * Convert a line of highlight group names into a list of group ID numbers.
5776 * "arg" should point to the "contains" or "nextgroup" keyword.
5777 * "arg" is advanced to after the last group name.
5778 * Careful: the argument is modified (NULs added).
5779 * returns FAIL for some error, OK for success.
5780 */
5781 static int
5782get_id_list(arg, keylen, list)
5783 char_u **arg;
5784 int keylen; /* length of keyword */
5785 short **list; /* where to store the resulting list, if not
5786 NULL, the list is silently skipped! */
5787{
5788 char_u *p = NULL;
5789 char_u *end;
5790 int round;
5791 int count;
5792 int total_count = 0;
5793 short *retval = NULL;
5794 char_u *name;
5795 regmatch_T regmatch;
5796 int id;
5797 int i;
5798 int failed = FALSE;
5799
5800 /*
5801 * We parse the list twice:
5802 * round == 1: count the number of items, allocate the array.
5803 * round == 2: fill the array with the items.
5804 * In round 1 new groups may be added, causing the number of items to
5805 * grow when a regexp is used. In that case round 1 is done once again.
5806 */
5807 for (round = 1; round <= 2; ++round)
5808 {
5809 /*
5810 * skip "contains"
5811 */
5812 p = skipwhite(*arg + keylen);
5813 if (*p != '=')
5814 {
5815 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5816 break;
5817 }
5818 p = skipwhite(p + 1);
5819 if (ends_excmd(*p))
5820 {
5821 EMSG2(_("E406: Empty argument: %s"), *arg);
5822 break;
5823 }
5824
5825 /*
5826 * parse the arguments after "contains"
5827 */
5828 count = 0;
5829 while (!ends_excmd(*p))
5830 {
5831 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5832 ;
5833 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5834 if (name == NULL)
5835 {
5836 failed = TRUE;
5837 break;
5838 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005839 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005840 if ( STRCMP(name + 1, "ALLBUT") == 0
5841 || STRCMP(name + 1, "ALL") == 0
5842 || STRCMP(name + 1, "TOP") == 0
5843 || STRCMP(name + 1, "CONTAINED") == 0)
5844 {
5845 if (TOUPPER_ASC(**arg) != 'C')
5846 {
5847 EMSG2(_("E407: %s not allowed here"), name + 1);
5848 failed = TRUE;
5849 vim_free(name);
5850 break;
5851 }
5852 if (count != 0)
5853 {
5854 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5855 failed = TRUE;
5856 vim_free(name);
5857 break;
5858 }
5859 if (name[1] == 'A')
5860 id = SYNID_ALLBUT;
5861 else if (name[1] == 'T')
5862 id = SYNID_TOP;
5863 else
5864 id = SYNID_CONTAINED;
5865 id += current_syn_inc_tag;
5866 }
5867 else if (name[1] == '@')
5868 {
5869 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5870 }
5871 else
5872 {
5873 /*
5874 * Handle full group name.
5875 */
5876 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5877 id = syn_check_group(name + 1, (int)(end - p));
5878 else
5879 {
5880 /*
5881 * Handle match of regexp with group names.
5882 */
5883 *name = '^';
5884 STRCAT(name, "$");
5885 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5886 if (regmatch.regprog == NULL)
5887 {
5888 failed = TRUE;
5889 vim_free(name);
5890 break;
5891 }
5892
5893 regmatch.rm_ic = TRUE;
5894 id = 0;
5895 for (i = highlight_ga.ga_len; --i >= 0; )
5896 {
5897 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5898 (colnr_T)0))
5899 {
5900 if (round == 2)
5901 {
5902 /* Got more items than expected; can happen
5903 * when adding items that match:
5904 * "contains=a.*b,axb".
5905 * Go back to first round */
5906 if (count >= total_count)
5907 {
5908 vim_free(retval);
5909 round = 1;
5910 }
5911 else
5912 retval[count] = i + 1;
5913 }
5914 ++count;
5915 id = -1; /* remember that we found one */
5916 }
5917 }
5918 vim_free(regmatch.regprog);
5919 }
5920 }
5921 vim_free(name);
5922 if (id == 0)
5923 {
5924 EMSG2(_("E409: Unknown group name: %s"), p);
5925 failed = TRUE;
5926 break;
5927 }
5928 if (id > 0)
5929 {
5930 if (round == 2)
5931 {
5932 /* Got more items than expected, go back to first round */
5933 if (count >= total_count)
5934 {
5935 vim_free(retval);
5936 round = 1;
5937 }
5938 else
5939 retval[count] = id;
5940 }
5941 ++count;
5942 }
5943 p = skipwhite(end);
5944 if (*p != ',')
5945 break;
5946 p = skipwhite(p + 1); /* skip comma in between arguments */
5947 }
5948 if (failed)
5949 break;
5950 if (round == 1)
5951 {
5952 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5953 if (retval == NULL)
5954 break;
5955 retval[count] = 0; /* zero means end of the list */
5956 total_count = count;
5957 }
5958 }
5959
5960 *arg = p;
5961 if (failed || retval == NULL)
5962 {
5963 vim_free(retval);
5964 return FAIL;
5965 }
5966
5967 if (*list == NULL)
5968 *list = retval;
5969 else
5970 vim_free(retval); /* list already found, don't overwrite it */
5971
5972 return OK;
5973}
5974
5975/*
5976 * Make a copy of an ID list.
5977 */
5978 static short *
5979copy_id_list(list)
5980 short *list;
5981{
5982 int len;
5983 int count;
5984 short *retval;
5985
5986 if (list == NULL)
5987 return NULL;
5988
5989 for (count = 0; list[count]; ++count)
5990 ;
5991 len = (count + 1) * sizeof(short);
5992 retval = (short *)alloc((unsigned)len);
5993 if (retval != NULL)
5994 mch_memmove(retval, list, (size_t)len);
5995
5996 return retval;
5997}
5998
5999/*
6000 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6001 * "cur_si" can be NULL if not checking the "containedin" list.
6002 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6003 * the current item.
6004 * This function is called very often, keep it fast!!
6005 */
6006 static int
6007in_id_list(cur_si, list, ssp, contained)
6008 stateitem_T *cur_si; /* current item or NULL */
6009 short *list; /* id list */
6010 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6011 int contained; /* group id is contained */
6012{
6013 int retval;
6014 short *scl_list;
6015 short item;
6016 short id = ssp->id;
6017 static int depth = 0;
6018 int r;
6019
6020 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006021 if (cur_si != NULL && ssp->cont_in_list != NULL
6022 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006023 {
6024 /* Ignore transparent items without a contains argument. Double check
6025 * that we don't go back past the first one. */
6026 while ((cur_si->si_flags & HL_TRANS_CONT)
6027 && cur_si > (stateitem_T *)(current_state.ga_data))
6028 --cur_si;
6029 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6030 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006031 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6032 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006033 return TRUE;
6034 }
6035
6036 if (list == NULL)
6037 return FALSE;
6038
6039 /*
6040 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6041 * inside anything. Only allow not-contained groups.
6042 */
6043 if (list == ID_LIST_ALL)
6044 return !contained;
6045
6046 /*
6047 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6048 * contains list. We also require that "id" is at the same ":syn include"
6049 * level as the list.
6050 */
6051 item = *list;
6052 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6053 {
6054 if (item < SYNID_TOP)
6055 {
6056 /* ALL or ALLBUT: accept all groups in the same file */
6057 if (item - SYNID_ALLBUT != ssp->inc_tag)
6058 return FALSE;
6059 }
6060 else if (item < SYNID_CONTAINED)
6061 {
6062 /* TOP: accept all not-contained groups in the same file */
6063 if (item - SYNID_TOP != ssp->inc_tag || contained)
6064 return FALSE;
6065 }
6066 else
6067 {
6068 /* CONTAINED: accept all contained groups in the same file */
6069 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6070 return FALSE;
6071 }
6072 item = *++list;
6073 retval = FALSE;
6074 }
6075 else
6076 retval = TRUE;
6077
6078 /*
6079 * Return "retval" if id is in the contains list.
6080 */
6081 while (item != 0)
6082 {
6083 if (item == id)
6084 return retval;
6085 if (item >= SYNID_CLUSTER)
6086 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006087 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006088 /* restrict recursiveness to 30 to avoid an endless loop for a
6089 * cluster that includes itself (indirectly) */
6090 if (scl_list != NULL && depth < 30)
6091 {
6092 ++depth;
6093 r = in_id_list(NULL, scl_list, ssp, contained);
6094 --depth;
6095 if (r)
6096 return retval;
6097 }
6098 }
6099 item = *++list;
6100 }
6101 return !retval;
6102}
6103
6104struct subcommand
6105{
6106 char *name; /* subcommand name */
6107 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6108};
6109
6110static struct subcommand subcommands[] =
6111{
6112 {"case", syn_cmd_case},
6113 {"clear", syn_cmd_clear},
6114 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006115 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006116 {"enable", syn_cmd_enable},
6117 {"include", syn_cmd_include},
6118 {"keyword", syn_cmd_keyword},
6119 {"list", syn_cmd_list},
6120 {"manual", syn_cmd_manual},
6121 {"match", syn_cmd_match},
6122 {"on", syn_cmd_on},
6123 {"off", syn_cmd_off},
6124 {"region", syn_cmd_region},
6125 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006126 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006127 {"sync", syn_cmd_sync},
6128 {"", syn_cmd_list},
6129 {NULL, NULL}
6130};
6131
6132/*
6133 * ":syntax".
6134 * This searches the subcommands[] table for the subcommand name, and calls a
6135 * syntax_subcommand() function to do the rest.
6136 */
6137 void
6138ex_syntax(eap)
6139 exarg_T *eap;
6140{
6141 char_u *arg = eap->arg;
6142 char_u *subcmd_end;
6143 char_u *subcmd_name;
6144 int i;
6145
6146 syn_cmdlinep = eap->cmdlinep;
6147
6148 /* isolate subcommand name */
6149 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6150 ;
6151 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6152 if (subcmd_name != NULL)
6153 {
6154 if (eap->skip) /* skip error messages for all subcommands */
6155 ++emsg_skip;
6156 for (i = 0; ; ++i)
6157 {
6158 if (subcommands[i].name == NULL)
6159 {
6160 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6161 break;
6162 }
6163 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6164 {
6165 eap->arg = skipwhite(subcmd_end);
6166 (subcommands[i].func)(eap, FALSE);
6167 break;
6168 }
6169 }
6170 vim_free(subcmd_name);
6171 if (eap->skip)
6172 --emsg_skip;
6173 }
6174}
6175
Bram Moolenaar860cae12010-06-05 23:22:07 +02006176 void
6177ex_ownsyntax(eap)
6178 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006179{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006180 char_u *old_value;
6181 char_u *new_value;
6182
Bram Moolenaar860cae12010-06-05 23:22:07 +02006183 if (curwin->w_s == &curwin->w_buffer->b_s)
6184 {
6185 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6186 memset(curwin->w_s, 0, sizeof(synblock_T));
6187#ifdef FEAT_SPELL
6188 curwin->w_p_spell = FALSE; /* No spell checking */
6189 clear_string_option(&curwin->w_s->b_p_spc);
6190 clear_string_option(&curwin->w_s->b_p_spf);
6191 vim_free(curwin->w_s->b_cap_prog);
6192 curwin->w_s->b_cap_prog = NULL;
6193 clear_string_option(&curwin->w_s->b_p_spl);
6194#endif
6195 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006196
6197 /* save value of b:current_syntax */
6198 old_value = get_var_value((char_u *)"b:current_syntax");
6199 if (old_value != NULL)
6200 old_value = vim_strsave(old_value);
6201
6202 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6203 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006204 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006205
6206 /* move value of b:current_syntax to w:current_syntax */
6207 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006208 if (new_value != NULL)
6209 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006210
6211 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006212 if (old_value == NULL)
6213 do_unlet((char_u *)"b:current_syntax", TRUE);
6214 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006215 {
6216 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6217 vim_free(old_value);
6218 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006219}
6220
6221 int
6222syntax_present(win)
6223 win_T *win;
6224{
6225 return (win->w_s->b_syn_patterns.ga_len != 0
6226 || win->w_s->b_syn_clusters.ga_len != 0
6227 || win->w_s->b_keywtab.ht_used > 0
6228 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006229}
6230
6231#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6232
6233static enum
6234{
6235 EXP_SUBCMD, /* expand ":syn" sub-commands */
6236 EXP_CASE /* expand ":syn case" arguments */
6237} expand_what;
6238
Bram Moolenaar4f688582007-07-24 12:34:30 +00006239/*
6240 * Reset include_link, include_default, include_none to 0.
6241 * Called when we are done expanding.
6242 */
6243 void
6244reset_expand_highlight()
6245{
6246 include_link = include_default = include_none = 0;
6247}
6248
6249/*
6250 * Handle command line completion for :match and :echohl command: Add "None"
6251 * as highlight group.
6252 */
6253 void
6254set_context_in_echohl_cmd(xp, arg)
6255 expand_T *xp;
6256 char_u *arg;
6257{
6258 xp->xp_context = EXPAND_HIGHLIGHT;
6259 xp->xp_pattern = arg;
6260 include_none = 1;
6261}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262
6263/*
6264 * Handle command line completion for :syntax command.
6265 */
6266 void
6267set_context_in_syntax_cmd(xp, arg)
6268 expand_T *xp;
6269 char_u *arg;
6270{
6271 char_u *p;
6272
6273 /* Default: expand subcommands */
6274 xp->xp_context = EXPAND_SYNTAX;
6275 expand_what = EXP_SUBCMD;
6276 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006277 include_link = 0;
6278 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006279
6280 /* (part of) subcommand already typed */
6281 if (*arg != NUL)
6282 {
6283 p = skiptowhite(arg);
6284 if (*p != NUL) /* past first word */
6285 {
6286 xp->xp_pattern = skipwhite(p);
6287 if (*skiptowhite(xp->xp_pattern) != NUL)
6288 xp->xp_context = EXPAND_NOTHING;
6289 else if (STRNICMP(arg, "case", p - arg) == 0)
6290 expand_what = EXP_CASE;
6291 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6292 || STRNICMP(arg, "region", p - arg) == 0
6293 || STRNICMP(arg, "match", p - arg) == 0
6294 || STRNICMP(arg, "list", p - arg) == 0)
6295 xp->xp_context = EXPAND_HIGHLIGHT;
6296 else
6297 xp->xp_context = EXPAND_NOTHING;
6298 }
6299 }
6300}
6301
6302static char *(case_args[]) = {"match", "ignore", NULL};
6303
6304/*
6305 * Function given to ExpandGeneric() to obtain the list syntax names for
6306 * expansion.
6307 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006308 char_u *
6309get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006310 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006311 int idx;
6312{
6313 if (expand_what == EXP_SUBCMD)
6314 return (char_u *)subcommands[idx].name;
6315 return (char_u *)case_args[idx];
6316}
6317
6318#endif /* FEAT_CMDL_COMPL */
6319
Bram Moolenaar071d4272004-06-13 20:20:40 +00006320/*
6321 * Function called for expression evaluation: get syntax ID at file position.
6322 */
6323 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006324syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006325 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006327 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006328 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006329 int *spellp; /* return: can do spell checking */
6330 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331{
6332 /* When the position is not after the current position and in the same
6333 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006334 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006335 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006336 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006337 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006338
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006339 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006340
6341 return (trans ? current_trans_id : current_id);
6342}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006343
Bram Moolenaar860cae12010-06-05 23:22:07 +02006344#if defined(FEAT_CONCEAL) || defined(PROTO)
6345/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006346 * Get extra information about the syntax item. Must be called right after
6347 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006348 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006349 * Returns the current flags.
6350 */
6351 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006352get_syntax_info(seqnrp)
6353 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006354{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006355 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006356 return current_flags;
6357}
6358
6359/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006360 * Return conceal substitution character
6361 */
6362 int
6363syn_get_sub_char()
6364{
6365 return current_sub_char;
6366}
6367#endif
6368
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006369#if defined(FEAT_EVAL) || defined(PROTO)
6370/*
6371 * Return the syntax ID at position "i" in the current stack.
6372 * The caller must have called syn_get_id() before to fill the stack.
6373 * Returns -1 when "i" is out of range.
6374 */
6375 int
6376syn_get_stack_item(i)
6377 int i;
6378{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006379 if (i >= current_state.ga_len)
6380 {
6381 /* Need to invalidate the state, because we didn't properly finish it
6382 * for the last character, "keep_state" was TRUE. */
6383 invalidate_current_state();
6384 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006385 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006386 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006387 return CUR_STATE(i).si_id;
6388}
6389#endif
6390
Bram Moolenaar071d4272004-06-13 20:20:40 +00006391#if defined(FEAT_FOLDING) || defined(PROTO)
6392/*
6393 * Function called to get folding level for line "lnum" in window "wp".
6394 */
6395 int
6396syn_get_foldlevel(wp, lnum)
6397 win_T *wp;
6398 long lnum;
6399{
6400 int level = 0;
6401 int i;
6402
6403 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006404 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006405 {
6406 syntax_start(wp, lnum);
6407
6408 for (i = 0; i < current_state.ga_len; ++i)
6409 if (CUR_STATE(i).si_flags & HL_FOLD)
6410 ++level;
6411 }
6412 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006413 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006415 if (level < 0)
6416 level = 0;
6417 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418 return level;
6419}
6420#endif
6421
6422#endif /* FEAT_SYN_HL */
6423
6424
6425/**************************************
6426 * Highlighting stuff *
6427 **************************************/
6428
6429/*
6430 * The default highlight groups. These are compiled-in for fast startup and
6431 * they still work when the runtime files can't be found.
6432 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006433 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6434 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006435 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006436#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006437# define CENT(a, b) b
6438#else
6439# define CENT(a, b) a
6440#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006441static char *(highlight_init_both[]) =
6442 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006443 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6444 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6445 CENT("IncSearch term=reverse cterm=reverse",
6446 "IncSearch term=reverse cterm=reverse gui=reverse"),
6447 CENT("ModeMsg term=bold cterm=bold",
6448 "ModeMsg term=bold cterm=bold gui=bold"),
6449 CENT("NonText term=bold ctermfg=Blue",
6450 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6451 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6452 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6453 CENT("StatusLineNC term=reverse cterm=reverse",
6454 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006455#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006456 CENT("VertSplit term=reverse cterm=reverse",
6457 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006458#endif
6459#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006460 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6461 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006462#endif
6463#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006464 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6465 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006466#endif
6467#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006468 CENT("PmenuThumb cterm=reverse",
6469 "PmenuThumb cterm=reverse gui=reverse"),
6470 CENT("PmenuSbar ctermbg=Grey",
6471 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006472#endif
6473#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006474 CENT("TabLineSel term=bold cterm=bold",
6475 "TabLineSel term=bold cterm=bold gui=bold"),
6476 CENT("TabLineFill term=reverse cterm=reverse",
6477 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006478#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006479#ifdef FEAT_GUI
6480 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006481 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006483 NULL
6484 };
6485
6486static char *(highlight_init_light[]) =
6487 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006488 CENT("Directory term=bold ctermfg=DarkBlue",
6489 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6490 CENT("LineNr term=underline ctermfg=Brown",
6491 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6492 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6493 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6494 CENT("Question term=standout ctermfg=DarkGreen",
6495 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6496 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6497 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006498#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006499 CENT("SpellBad term=reverse ctermbg=LightRed",
6500 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6501 CENT("SpellCap term=reverse ctermbg=LightBlue",
6502 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6503 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6504 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6505 CENT("SpellLocal term=underline ctermbg=Cyan",
6506 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006507#endif
6508#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006509 CENT("Pmenu ctermbg=LightMagenta",
6510 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6511 CENT("PmenuSel ctermbg=LightGrey",
6512 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006513#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006514 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6515 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6516 CENT("Title term=bold ctermfg=DarkMagenta",
6517 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6518 CENT("WarningMsg term=standout ctermfg=DarkRed",
6519 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006520#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006521 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6522 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006523#endif
6524#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006525 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6526 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6527 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6528 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006529#endif
6530#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006531 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6532 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006533#endif
6534#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006535 CENT("Visual term=reverse",
6536 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006537#endif
6538#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006539 CENT("DiffAdd term=bold ctermbg=LightBlue",
6540 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6541 CENT("DiffChange term=bold ctermbg=LightMagenta",
6542 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6543 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6544 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006545#endif
6546#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006547 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6548 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006549#endif
6550#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006551 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006552 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006553 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006554 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006555 CENT("ColorColumn term=reverse ctermbg=LightRed",
6556 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006557#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006558#ifdef FEAT_CONCEAL
6559 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6560 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6561#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006562#ifdef FEAT_AUTOCMD
6563 CENT("MatchParen term=reverse ctermbg=Cyan",
6564 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6565#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006566#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006567 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006568#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006569 NULL
6570 };
6571
6572static char *(highlight_init_dark[]) =
6573 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006574 CENT("Directory term=bold ctermfg=LightCyan",
6575 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6576 CENT("LineNr term=underline ctermfg=Yellow",
6577 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6578 CENT("MoreMsg term=bold ctermfg=LightGreen",
6579 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6580 CENT("Question term=standout ctermfg=LightGreen",
6581 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6582 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6583 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6584 CENT("SpecialKey term=bold ctermfg=LightBlue",
6585 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006586#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006587 CENT("SpellBad term=reverse ctermbg=Red",
6588 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6589 CENT("SpellCap term=reverse ctermbg=Blue",
6590 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6591 CENT("SpellRare term=reverse ctermbg=Magenta",
6592 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6593 CENT("SpellLocal term=underline ctermbg=Cyan",
6594 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006595#endif
6596#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006597 CENT("Pmenu ctermbg=Magenta",
6598 "Pmenu ctermbg=Magenta guibg=Magenta"),
6599 CENT("PmenuSel ctermbg=DarkGrey",
6600 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006601#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006602 CENT("Title term=bold ctermfg=LightMagenta",
6603 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6604 CENT("WarningMsg term=standout ctermfg=LightRed",
6605 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006606#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006607 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6608 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006609#endif
6610#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006611 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6612 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6613 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6614 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006615#endif
6616#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006617 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6618 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006619#endif
6620#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006621 CENT("Visual term=reverse",
6622 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006623#endif
6624#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006625 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6626 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6627 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6628 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6629 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6630 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006631#endif
6632#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006633 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6634 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006635#endif
6636#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006637 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006638 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006639 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006640 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006641 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6642 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006643#endif
6644#ifdef FEAT_AUTOCMD
6645 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6646 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006647#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006648#ifdef FEAT_CONCEAL
6649 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6650 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6651#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006652#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006654#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006655 NULL
6656 };
6657
6658 void
6659init_highlight(both, reset)
6660 int both; /* include groups where 'bg' doesn't matter */
6661 int reset; /* clear group first */
6662{
6663 int i;
6664 char **pp;
6665 static int had_both = FALSE;
6666#ifdef FEAT_EVAL
6667 char_u *p;
6668
6669 /*
6670 * Try finding the color scheme file. Used when a color file was loaded
6671 * and 'background' or 't_Co' is changed.
6672 */
6673 p = get_var_value((char_u *)"g:colors_name");
6674 if (p != NULL && load_colors(p) == OK)
6675 return;
6676#endif
6677
6678 /*
6679 * Didn't use a color file, use the compiled-in colors.
6680 */
6681 if (both)
6682 {
6683 had_both = TRUE;
6684 pp = highlight_init_both;
6685 for (i = 0; pp[i] != NULL; ++i)
6686 do_highlight((char_u *)pp[i], reset, TRUE);
6687 }
6688 else if (!had_both)
6689 /* Don't do anything before the call with both == TRUE from main().
6690 * Not everything has been setup then, and that call will overrule
6691 * everything anyway. */
6692 return;
6693
6694 if (*p_bg == 'l')
6695 pp = highlight_init_light;
6696 else
6697 pp = highlight_init_dark;
6698 for (i = 0; pp[i] != NULL; ++i)
6699 do_highlight((char_u *)pp[i], reset, TRUE);
6700
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006701 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006702 * depend on the number of colors available.
6703 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006704 * to avoid Statement highlighted text disappears.
6705 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006706 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006707 do_highlight((char_u *)(*p_bg == 'l'
6708 ? "Visual cterm=NONE ctermbg=LightGrey"
6709 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006710 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006711 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006712 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6713 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006714 if (*p_bg == 'l')
6715 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6716 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006717
Bram Moolenaar071d4272004-06-13 20:20:40 +00006718#ifdef FEAT_SYN_HL
6719 /*
6720 * If syntax highlighting is enabled load the highlighting for it.
6721 */
6722 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006723 {
6724 static int recursive = 0;
6725
6726 if (recursive >= 5)
6727 EMSG(_("E679: recursive loop loading syncolor.vim"));
6728 else
6729 {
6730 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006731 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006732 --recursive;
6733 }
6734 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006735#endif
6736}
6737
6738/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006739 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006740 * Return OK for success, FAIL for failure.
6741 */
6742 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006743load_colors(name)
6744 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745{
6746 char_u *buf;
6747 int retval = FAIL;
6748 static int recursive = FALSE;
6749
6750 /* When being called recursively, this is probably because setting
6751 * 'background' caused the highlighting to be reloaded. This means it is
6752 * working, thus we should return OK. */
6753 if (recursive)
6754 return OK;
6755
6756 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006757 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006758 if (buf != NULL)
6759 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006760 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006761 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006762 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006763#ifdef FEAT_AUTOCMD
6764 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6765#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006766 }
6767 recursive = FALSE;
6768
6769 return retval;
6770}
6771
6772/*
6773 * Handle the ":highlight .." command.
6774 * When using ":hi clear" this is called recursively for each group with
6775 * "forceit" and "init" both TRUE.
6776 */
6777 void
6778do_highlight(line, forceit, init)
6779 char_u *line;
6780 int forceit;
6781 int init; /* TRUE when called for initializing */
6782{
6783 char_u *name_end;
6784 char_u *p;
6785 char_u *linep;
6786 char_u *key_start;
6787 char_u *arg_start;
6788 char_u *key = NULL, *arg = NULL;
6789 long i;
6790 int off;
6791 int len;
6792 int attr;
6793 int id;
6794 int idx;
6795 int dodefault = FALSE;
6796 int doclear = FALSE;
6797 int dolink = FALSE;
6798 int error = FALSE;
6799 int color;
6800 int is_normal_group = FALSE; /* "Normal" group */
6801#ifdef FEAT_GUI_X11
6802 int is_menu_group = FALSE; /* "Menu" group */
6803 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6804 int is_tooltip_group = FALSE; /* "Tooltip" group */
6805 int do_colors = FALSE; /* need to update colors? */
6806#else
6807# define is_menu_group 0
6808# define is_tooltip_group 0
6809#endif
6810
6811 /*
6812 * If no argument, list current highlighting.
6813 */
6814 if (ends_excmd(*line))
6815 {
6816 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6817 /* TODO: only call when the group has attributes set */
6818 highlight_list_one((int)i);
6819 return;
6820 }
6821
6822 /*
6823 * Isolate the name.
6824 */
6825 name_end = skiptowhite(line);
6826 linep = skipwhite(name_end);
6827
6828 /*
6829 * Check for "default" argument.
6830 */
6831 if (STRNCMP(line, "default", name_end - line) == 0)
6832 {
6833 dodefault = TRUE;
6834 line = linep;
6835 name_end = skiptowhite(line);
6836 linep = skipwhite(name_end);
6837 }
6838
6839 /*
6840 * Check for "clear" or "link" argument.
6841 */
6842 if (STRNCMP(line, "clear", name_end - line) == 0)
6843 doclear = TRUE;
6844 if (STRNCMP(line, "link", name_end - line) == 0)
6845 dolink = TRUE;
6846
6847 /*
6848 * ":highlight {group-name}": list highlighting for one group.
6849 */
6850 if (!doclear && !dolink && ends_excmd(*linep))
6851 {
6852 id = syn_namen2id(line, (int)(name_end - line));
6853 if (id == 0)
6854 EMSG2(_("E411: highlight group not found: %s"), line);
6855 else
6856 highlight_list_one(id);
6857 return;
6858 }
6859
6860 /*
6861 * Handle ":highlight link {from} {to}" command.
6862 */
6863 if (dolink)
6864 {
6865 char_u *from_start = linep;
6866 char_u *from_end;
6867 char_u *to_start;
6868 char_u *to_end;
6869 int from_id;
6870 int to_id;
6871
6872 from_end = skiptowhite(from_start);
6873 to_start = skipwhite(from_end);
6874 to_end = skiptowhite(to_start);
6875
6876 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6877 {
6878 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6879 from_start);
6880 return;
6881 }
6882
6883 if (!ends_excmd(*skipwhite(to_end)))
6884 {
6885 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6886 return;
6887 }
6888
6889 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6890 if (STRNCMP(to_start, "NONE", 4) == 0)
6891 to_id = 0;
6892 else
6893 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6894
6895 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6896 {
6897 /*
6898 * Don't allow a link when there already is some highlighting
6899 * for the group, unless '!' is used
6900 */
6901 if (to_id > 0 && !forceit && !init
6902 && hl_has_settings(from_id - 1, dodefault))
6903 {
6904 if (sourcing_name == NULL && !dodefault)
6905 EMSG(_("E414: group has settings, highlight link ignored"));
6906 }
6907 else
6908 {
6909 if (!init)
6910 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6911 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006912#ifdef FEAT_EVAL
6913 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6914#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006915 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006916 }
6917 }
6918
6919 /* Only call highlight_changed() once, after sourcing a syntax file */
6920 need_highlight_changed = TRUE;
6921
6922 return;
6923 }
6924
6925 if (doclear)
6926 {
6927 /*
6928 * ":highlight clear [group]" command.
6929 */
6930 line = linep;
6931 if (ends_excmd(*line))
6932 {
6933#ifdef FEAT_GUI
6934 /* First, we do not destroy the old values, but allocate the new
6935 * ones and update the display. THEN we destroy the old values.
6936 * If we destroy the old values first, then the old values
6937 * (such as GuiFont's or GuiFontset's) will still be displayed but
6938 * invalid because they were free'd.
6939 */
6940 if (gui.in_use)
6941 {
6942# ifdef FEAT_BEVAL_TIP
6943 gui_init_tooltip_font();
6944# endif
6945# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6946 gui_init_menu_font();
6947# endif
6948 }
6949# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6950 gui_mch_def_colors();
6951# endif
6952# ifdef FEAT_GUI_X11
6953# ifdef FEAT_MENU
6954
6955 /* This only needs to be done when there is no Menu highlight
6956 * group defined by default, which IS currently the case.
6957 */
6958 gui_mch_new_menu_colors();
6959# endif
6960 if (gui.in_use)
6961 {
6962 gui_new_scrollbar_colors();
6963# ifdef FEAT_BEVAL
6964 gui_mch_new_tooltip_colors();
6965# endif
6966# ifdef FEAT_MENU
6967 gui_mch_new_menu_font();
6968# endif
6969 }
6970# endif
6971
6972 /* Ok, we're done allocating the new default graphics items.
6973 * The screen should already be refreshed at this point.
6974 * It is now Ok to clear out the old data.
6975 */
6976#endif
6977#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006978 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006979#endif
6980 restore_cterm_colors();
6981
6982 /*
6983 * Clear all default highlight groups and load the defaults.
6984 */
6985 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6986 highlight_clear(idx);
6987 init_highlight(TRUE, TRUE);
6988#ifdef FEAT_GUI
6989 if (gui.in_use)
6990 highlight_gui_started();
6991#endif
6992 highlight_changed();
6993 redraw_later_clear();
6994 return;
6995 }
6996 name_end = skiptowhite(line);
6997 linep = skipwhite(name_end);
6998 }
6999
7000 /*
7001 * Find the group name in the table. If it does not exist yet, add it.
7002 */
7003 id = syn_check_group(line, (int)(name_end - line));
7004 if (id == 0) /* failed (out of memory) */
7005 return;
7006 idx = id - 1; /* index is ID minus one */
7007
7008 /* Return if "default" was used and the group already has settings. */
7009 if (dodefault && hl_has_settings(idx, TRUE))
7010 return;
7011
7012 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7013 is_normal_group = TRUE;
7014#ifdef FEAT_GUI_X11
7015 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7016 is_menu_group = TRUE;
7017 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7018 is_scrollbar_group = TRUE;
7019 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7020 is_tooltip_group = TRUE;
7021#endif
7022
7023 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7024 if (doclear || (forceit && init))
7025 {
7026 highlight_clear(idx);
7027 if (!doclear)
7028 HL_TABLE()[idx].sg_set = 0;
7029 }
7030
7031 if (!doclear)
7032 while (!ends_excmd(*linep))
7033 {
7034 key_start = linep;
7035 if (*linep == '=')
7036 {
7037 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7038 error = TRUE;
7039 break;
7040 }
7041
7042 /*
7043 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7044 * "guibg").
7045 */
7046 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7047 ++linep;
7048 vim_free(key);
7049 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7050 if (key == NULL)
7051 {
7052 error = TRUE;
7053 break;
7054 }
7055 linep = skipwhite(linep);
7056
7057 if (STRCMP(key, "NONE") == 0)
7058 {
7059 if (!init || HL_TABLE()[idx].sg_set == 0)
7060 {
7061 if (!init)
7062 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7063 highlight_clear(idx);
7064 }
7065 continue;
7066 }
7067
7068 /*
7069 * Check for the equal sign.
7070 */
7071 if (*linep != '=')
7072 {
7073 EMSG2(_("E416: missing equal sign: %s"), key_start);
7074 error = TRUE;
7075 break;
7076 }
7077 ++linep;
7078
7079 /*
7080 * Isolate the argument.
7081 */
7082 linep = skipwhite(linep);
7083 if (*linep == '\'') /* guifg='color name' */
7084 {
7085 arg_start = ++linep;
7086 linep = vim_strchr(linep, '\'');
7087 if (linep == NULL)
7088 {
7089 EMSG2(_(e_invarg2), key_start);
7090 error = TRUE;
7091 break;
7092 }
7093 }
7094 else
7095 {
7096 arg_start = linep;
7097 linep = skiptowhite(linep);
7098 }
7099 if (linep == arg_start)
7100 {
7101 EMSG2(_("E417: missing argument: %s"), key_start);
7102 error = TRUE;
7103 break;
7104 }
7105 vim_free(arg);
7106 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7107 if (arg == NULL)
7108 {
7109 error = TRUE;
7110 break;
7111 }
7112 if (*linep == '\'')
7113 ++linep;
7114
7115 /*
7116 * Store the argument.
7117 */
7118 if ( STRCMP(key, "TERM") == 0
7119 || STRCMP(key, "CTERM") == 0
7120 || STRCMP(key, "GUI") == 0)
7121 {
7122 attr = 0;
7123 off = 0;
7124 while (arg[off] != NUL)
7125 {
7126 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7127 {
7128 len = (int)STRLEN(hl_name_table[i]);
7129 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7130 {
7131 attr |= hl_attr_table[i];
7132 off += len;
7133 break;
7134 }
7135 }
7136 if (i < 0)
7137 {
7138 EMSG2(_("E418: Illegal value: %s"), arg);
7139 error = TRUE;
7140 break;
7141 }
7142 if (arg[off] == ',') /* another one follows */
7143 ++off;
7144 }
7145 if (error)
7146 break;
7147 if (*key == 'T')
7148 {
7149 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7150 {
7151 if (!init)
7152 HL_TABLE()[idx].sg_set |= SG_TERM;
7153 HL_TABLE()[idx].sg_term = attr;
7154 }
7155 }
7156 else if (*key == 'C')
7157 {
7158 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7159 {
7160 if (!init)
7161 HL_TABLE()[idx].sg_set |= SG_CTERM;
7162 HL_TABLE()[idx].sg_cterm = attr;
7163 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7164 }
7165 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007166#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007167 else
7168 {
7169 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7170 {
7171 if (!init)
7172 HL_TABLE()[idx].sg_set |= SG_GUI;
7173 HL_TABLE()[idx].sg_gui = attr;
7174 }
7175 }
7176#endif
7177 }
7178 else if (STRCMP(key, "FONT") == 0)
7179 {
7180 /* in non-GUI fonts are simply ignored */
7181#ifdef FEAT_GUI
7182 if (!gui.shell_created)
7183 {
7184 /* GUI not started yet, always accept the name. */
7185 vim_free(HL_TABLE()[idx].sg_font_name);
7186 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7187 }
7188 else
7189 {
7190 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7191# ifdef FEAT_XFONTSET
7192 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7193# endif
7194 /* First, save the current font/fontset.
7195 * Then try to allocate the font/fontset.
7196 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7197 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7198 */
7199
7200 HL_TABLE()[idx].sg_font = NOFONT;
7201# ifdef FEAT_XFONTSET
7202 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7203# endif
7204 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7205 is_tooltip_group);
7206
7207# ifdef FEAT_XFONTSET
7208 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7209 {
7210 /* New fontset was accepted. Free the old one, if there was
7211 * one.
7212 */
7213 gui_mch_free_fontset(temp_sg_fontset);
7214 vim_free(HL_TABLE()[idx].sg_font_name);
7215 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7216 }
7217 else
7218 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7219# endif
7220 if (HL_TABLE()[idx].sg_font != NOFONT)
7221 {
7222 /* New font was accepted. Free the old one, if there was
7223 * one.
7224 */
7225 gui_mch_free_font(temp_sg_font);
7226 vim_free(HL_TABLE()[idx].sg_font_name);
7227 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7228 }
7229 else
7230 HL_TABLE()[idx].sg_font = temp_sg_font;
7231 }
7232#endif
7233 }
7234 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7235 {
7236 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7237 {
7238 if (!init)
7239 HL_TABLE()[idx].sg_set |= SG_CTERM;
7240
7241 /* When setting the foreground color, and previously the "bold"
7242 * flag was set for a light color, reset it now */
7243 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7244 {
7245 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7246 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7247 }
7248
7249 if (VIM_ISDIGIT(*arg))
7250 color = atoi((char *)arg);
7251 else if (STRICMP(arg, "fg") == 0)
7252 {
7253 if (cterm_normal_fg_color)
7254 color = cterm_normal_fg_color - 1;
7255 else
7256 {
7257 EMSG(_("E419: FG color unknown"));
7258 error = TRUE;
7259 break;
7260 }
7261 }
7262 else if (STRICMP(arg, "bg") == 0)
7263 {
7264 if (cterm_normal_bg_color > 0)
7265 color = cterm_normal_bg_color - 1;
7266 else
7267 {
7268 EMSG(_("E420: BG color unknown"));
7269 error = TRUE;
7270 break;
7271 }
7272 }
7273 else
7274 {
7275 static char *(color_names[28]) = {
7276 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7277 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7278 "Gray", "Grey",
7279 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7280 "Blue", "LightBlue", "Green", "LightGreen",
7281 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7282 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7283 static int color_numbers_16[28] = {0, 1, 2, 3,
7284 4, 5, 6, 6,
7285 7, 7,
7286 7, 7, 8, 8,
7287 9, 9, 10, 10,
7288 11, 11, 12, 12, 13,
7289 13, 14, 14, 15, -1};
7290 /* for xterm with 88 colors... */
7291 static int color_numbers_88[28] = {0, 4, 2, 6,
7292 1, 5, 32, 72,
7293 84, 84,
7294 7, 7, 82, 82,
7295 12, 43, 10, 61,
7296 14, 63, 9, 74, 13,
7297 75, 11, 78, 15, -1};
7298 /* for xterm with 256 colors... */
7299 static int color_numbers_256[28] = {0, 4, 2, 6,
7300 1, 5, 130, 130,
7301 248, 248,
7302 7, 7, 242, 242,
7303 12, 81, 10, 121,
7304 14, 159, 9, 224, 13,
7305 225, 11, 229, 15, -1};
7306 /* for terminals with less than 16 colors... */
7307 static int color_numbers_8[28] = {0, 4, 2, 6,
7308 1, 5, 3, 3,
7309 7, 7,
7310 7, 7, 0+8, 0+8,
7311 4+8, 4+8, 2+8, 2+8,
7312 6+8, 6+8, 1+8, 1+8, 5+8,
7313 5+8, 3+8, 3+8, 7+8, -1};
7314#if defined(__QNXNTO__)
7315 static int *color_numbers_8_qansi = color_numbers_8;
7316 /* On qnx, the 8 & 16 color arrays are the same */
7317 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7318 color_numbers_8_qansi = color_numbers_16;
7319#endif
7320
7321 /* reduce calls to STRICMP a bit, it can be slow */
7322 off = TOUPPER_ASC(*arg);
7323 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7324 if (off == color_names[i][0]
7325 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7326 break;
7327 if (i < 0)
7328 {
7329 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7330 error = TRUE;
7331 break;
7332 }
7333
7334 /* Use the _16 table to check if its a valid color name. */
7335 color = color_numbers_16[i];
7336 if (color >= 0)
7337 {
7338 if (t_colors == 8)
7339 {
7340 /* t_Co is 8: use the 8 colors table */
7341#if defined(__QNXNTO__)
7342 color = color_numbers_8_qansi[i];
7343#else
7344 color = color_numbers_8[i];
7345#endif
7346 if (key[5] == 'F')
7347 {
7348 /* set/reset bold attribute to get light foreground
7349 * colors (on some terminals, e.g. "linux") */
7350 if (color & 8)
7351 {
7352 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7353 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7354 }
7355 else
7356 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7357 }
7358 color &= 7; /* truncate to 8 colors */
7359 }
7360 else if (t_colors == 16 || t_colors == 88
7361 || t_colors == 256)
7362 {
7363 /*
7364 * Guess: if the termcap entry ends in 'm', it is
7365 * probably an xterm-like terminal. Use the changed
7366 * order for colors.
7367 */
7368 if (*T_CAF != NUL)
7369 p = T_CAF;
7370 else
7371 p = T_CSF;
7372 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7373 switch (t_colors)
7374 {
7375 case 16:
7376 color = color_numbers_8[i];
7377 break;
7378 case 88:
7379 color = color_numbers_88[i];
7380 break;
7381 case 256:
7382 color = color_numbers_256[i];
7383 break;
7384 }
7385 }
7386 }
7387 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007388 /* Add one to the argument, to avoid zero. Zero is used for
7389 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007390 if (key[5] == 'F')
7391 {
7392 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7393 if (is_normal_group)
7394 {
7395 cterm_normal_fg_color = color + 1;
7396 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7397#ifdef FEAT_GUI
7398 /* Don't do this if the GUI is used. */
7399 if (!gui.in_use && !gui.starting)
7400#endif
7401 {
7402 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007403 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007404 term_fg_color(color);
7405 }
7406 }
7407 }
7408 else
7409 {
7410 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7411 if (is_normal_group)
7412 {
7413 cterm_normal_bg_color = color + 1;
7414#ifdef FEAT_GUI
7415 /* Don't mess with 'background' if the GUI is used. */
7416 if (!gui.in_use && !gui.starting)
7417#endif
7418 {
7419 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007420 if (color >= 0)
7421 {
7422 if (termcap_active)
7423 term_bg_color(color);
7424 if (t_colors < 16)
7425 i = (color == 0 || color == 4);
7426 else
7427 i = (color < 7 || color == 8);
7428 /* Set the 'background' option if the value is
7429 * wrong. */
7430 if (i != (*p_bg == 'd'))
7431 set_option_value((char_u *)"bg", 0L,
7432 i ? (char_u *)"dark"
7433 : (char_u *)"light", 0);
7434 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007435 }
7436 }
7437 }
7438 }
7439 }
7440 else if (STRCMP(key, "GUIFG") == 0)
7441 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007442#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007443 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007444 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007445 if (!init)
7446 HL_TABLE()[idx].sg_set |= SG_GUI;
7447
Bram Moolenaar61623362010-07-14 22:04:22 +02007448# ifdef FEAT_GUI
7449 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007450 i = color_name2handle(arg);
7451 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7452 {
7453 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007454# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007455 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7456 if (STRCMP(arg, "NONE"))
7457 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7458 else
7459 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007460# ifdef FEAT_GUI
7461# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007462 if (is_menu_group)
7463 gui.menu_fg_pixel = i;
7464 if (is_scrollbar_group)
7465 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007466# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007467 if (is_tooltip_group)
7468 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007469# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007470 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007471# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007472 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007473# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007474 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007475#endif
7476 }
7477 else if (STRCMP(key, "GUIBG") == 0)
7478 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007479#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007480 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007481 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007482 if (!init)
7483 HL_TABLE()[idx].sg_set |= SG_GUI;
7484
Bram Moolenaar61623362010-07-14 22:04:22 +02007485# ifdef FEAT_GUI
7486 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007487 i = color_name2handle(arg);
7488 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7489 {
7490 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007491# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007492 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7493 if (STRCMP(arg, "NONE") != 0)
7494 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7495 else
7496 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007497# ifdef FEAT_GUI
7498# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007499 if (is_menu_group)
7500 gui.menu_bg_pixel = i;
7501 if (is_scrollbar_group)
7502 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007503# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007504 if (is_tooltip_group)
7505 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007506# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007507 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007508# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007509 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007510# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007511 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007512#endif
7513 }
7514 else if (STRCMP(key, "GUISP") == 0)
7515 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007516#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007517 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7518 {
7519 if (!init)
7520 HL_TABLE()[idx].sg_set |= SG_GUI;
7521
Bram Moolenaar61623362010-07-14 22:04:22 +02007522# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007523 i = color_name2handle(arg);
7524 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7525 {
7526 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007527# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007528 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7529 if (STRCMP(arg, "NONE") != 0)
7530 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7531 else
7532 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007533# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007534 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007535# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007536 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007537#endif
7538 }
7539 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7540 {
7541 char_u buf[100];
7542 char_u *tname;
7543
7544 if (!init)
7545 HL_TABLE()[idx].sg_set |= SG_TERM;
7546
7547 /*
7548 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007549 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007550 */
7551 if (STRNCMP(arg, "t_", 2) == 0)
7552 {
7553 off = 0;
7554 buf[0] = 0;
7555 while (arg[off] != NUL)
7556 {
7557 /* Isolate one termcap name */
7558 for (len = 0; arg[off + len] &&
7559 arg[off + len] != ','; ++len)
7560 ;
7561 tname = vim_strnsave(arg + off, len);
7562 if (tname == NULL) /* out of memory */
7563 {
7564 error = TRUE;
7565 break;
7566 }
7567 /* lookup the escape sequence for the item */
7568 p = get_term_code(tname);
7569 vim_free(tname);
7570 if (p == NULL) /* ignore non-existing things */
7571 p = (char_u *)"";
7572
7573 /* Append it to the already found stuff */
7574 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7575 {
7576 EMSG2(_("E422: terminal code too long: %s"), arg);
7577 error = TRUE;
7578 break;
7579 }
7580 STRCAT(buf, p);
7581
7582 /* Advance to the next item */
7583 off += len;
7584 if (arg[off] == ',') /* another one follows */
7585 ++off;
7586 }
7587 }
7588 else
7589 {
7590 /*
7591 * Copy characters from arg[] to buf[], translating <> codes.
7592 */
7593 for (p = arg, off = 0; off < 100 && *p; )
7594 {
7595 len = trans_special(&p, buf + off, FALSE);
7596 if (len) /* recognized special char */
7597 off += len;
7598 else /* copy as normal char */
7599 buf[off++] = *p++;
7600 }
7601 buf[off] = NUL;
7602 }
7603 if (error)
7604 break;
7605
7606 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7607 p = NULL;
7608 else
7609 p = vim_strsave(buf);
7610 if (key[2] == 'A')
7611 {
7612 vim_free(HL_TABLE()[idx].sg_start);
7613 HL_TABLE()[idx].sg_start = p;
7614 }
7615 else
7616 {
7617 vim_free(HL_TABLE()[idx].sg_stop);
7618 HL_TABLE()[idx].sg_stop = p;
7619 }
7620 }
7621 else
7622 {
7623 EMSG2(_("E423: Illegal argument: %s"), key_start);
7624 error = TRUE;
7625 break;
7626 }
7627
7628 /*
7629 * When highlighting has been given for a group, don't link it.
7630 */
7631 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7632 HL_TABLE()[idx].sg_link = 0;
7633
7634 /*
7635 * Continue with next argument.
7636 */
7637 linep = skipwhite(linep);
7638 }
7639
7640 /*
7641 * If there is an error, and it's a new entry, remove it from the table.
7642 */
7643 if (error && idx == highlight_ga.ga_len)
7644 syn_unadd_group();
7645 else
7646 {
7647 if (is_normal_group)
7648 {
7649 HL_TABLE()[idx].sg_term_attr = 0;
7650 HL_TABLE()[idx].sg_cterm_attr = 0;
7651#ifdef FEAT_GUI
7652 HL_TABLE()[idx].sg_gui_attr = 0;
7653 /*
7654 * Need to update all groups, because they might be using "bg"
7655 * and/or "fg", which have been changed now.
7656 */
7657 if (gui.in_use)
7658 highlight_gui_started();
7659#endif
7660 }
7661#ifdef FEAT_GUI_X11
7662# ifdef FEAT_MENU
7663 else if (is_menu_group)
7664 {
7665 if (gui.in_use && do_colors)
7666 gui_mch_new_menu_colors();
7667 }
7668# endif
7669 else if (is_scrollbar_group)
7670 {
7671 if (gui.in_use && do_colors)
7672 gui_new_scrollbar_colors();
7673 }
7674# ifdef FEAT_BEVAL
7675 else if (is_tooltip_group)
7676 {
7677 if (gui.in_use && do_colors)
7678 gui_mch_new_tooltip_colors();
7679 }
7680# endif
7681#endif
7682 else
7683 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007684#ifdef FEAT_EVAL
7685 HL_TABLE()[idx].sg_scriptID = current_SID;
7686#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007687 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007688 }
7689 vim_free(key);
7690 vim_free(arg);
7691
7692 /* Only call highlight_changed() once, after sourcing a syntax file */
7693 need_highlight_changed = TRUE;
7694}
7695
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007696#if defined(EXITFREE) || defined(PROTO)
7697 void
7698free_highlight()
7699{
7700 int i;
7701
7702 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007703 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007704 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007705 vim_free(HL_TABLE()[i].sg_name);
7706 vim_free(HL_TABLE()[i].sg_name_u);
7707 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007708 ga_clear(&highlight_ga);
7709}
7710#endif
7711
Bram Moolenaar071d4272004-06-13 20:20:40 +00007712/*
7713 * Reset the cterm colors to what they were before Vim was started, if
7714 * possible. Otherwise reset them to zero.
7715 */
7716 void
7717restore_cterm_colors()
7718{
7719#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7720 /* Since t_me has been set, this probably means that the user
7721 * wants to use this as default colors. Need to reset default
7722 * background/foreground colors. */
7723 mch_set_normal_colors();
7724#else
7725 cterm_normal_fg_color = 0;
7726 cterm_normal_fg_bold = 0;
7727 cterm_normal_bg_color = 0;
7728#endif
7729}
7730
7731/*
7732 * Return TRUE if highlight group "idx" has any settings.
7733 * When "check_link" is TRUE also check for an existing link.
7734 */
7735 static int
7736hl_has_settings(idx, check_link)
7737 int idx;
7738 int check_link;
7739{
7740 return ( HL_TABLE()[idx].sg_term_attr != 0
7741 || HL_TABLE()[idx].sg_cterm_attr != 0
7742#ifdef FEAT_GUI
7743 || HL_TABLE()[idx].sg_gui_attr != 0
7744#endif
7745 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7746}
7747
7748/*
7749 * Clear highlighting for one group.
7750 */
7751 static void
7752highlight_clear(idx)
7753 int idx;
7754{
7755 HL_TABLE()[idx].sg_term = 0;
7756 vim_free(HL_TABLE()[idx].sg_start);
7757 HL_TABLE()[idx].sg_start = NULL;
7758 vim_free(HL_TABLE()[idx].sg_stop);
7759 HL_TABLE()[idx].sg_stop = NULL;
7760 HL_TABLE()[idx].sg_term_attr = 0;
7761 HL_TABLE()[idx].sg_cterm = 0;
7762 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7763 HL_TABLE()[idx].sg_cterm_fg = 0;
7764 HL_TABLE()[idx].sg_cterm_bg = 0;
7765 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007766#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007767 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007768 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7769 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007770 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7771 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007772 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7773 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007774#endif
7775#ifdef FEAT_GUI
7776 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7777 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7778 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7780 HL_TABLE()[idx].sg_font = NOFONT;
7781# ifdef FEAT_XFONTSET
7782 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7783 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7784# endif
7785 vim_free(HL_TABLE()[idx].sg_font_name);
7786 HL_TABLE()[idx].sg_font_name = NULL;
7787 HL_TABLE()[idx].sg_gui_attr = 0;
7788#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007789#ifdef FEAT_EVAL
7790 /* Clear the script ID only when there is no link, since that is not
7791 * cleared. */
7792 if (HL_TABLE()[idx].sg_link == 0)
7793 HL_TABLE()[idx].sg_scriptID = 0;
7794#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007795}
7796
7797#if defined(FEAT_GUI) || defined(PROTO)
7798/*
7799 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007800 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007801 * "Tooltip" colors.
7802 */
7803 void
7804set_normal_colors()
7805{
7806 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007807 &gui.norm_pixel, &gui.back_pixel,
7808 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809 {
7810 gui_mch_new_colors();
7811 must_redraw = CLEAR;
7812 }
7813#ifdef FEAT_GUI_X11
7814 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007815 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7816 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007817 {
7818# ifdef FEAT_MENU
7819 gui_mch_new_menu_colors();
7820# endif
7821 must_redraw = CLEAR;
7822 }
7823# ifdef FEAT_BEVAL
7824 if (set_group_colors((char_u *)"Tooltip",
7825 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7826 FALSE, FALSE, TRUE))
7827 {
7828# ifdef FEAT_TOOLBAR
7829 gui_mch_new_tooltip_colors();
7830# endif
7831 must_redraw = CLEAR;
7832 }
7833#endif
7834 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007835 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7836 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007837 {
7838 gui_new_scrollbar_colors();
7839 must_redraw = CLEAR;
7840 }
7841#endif
7842}
7843
7844/*
7845 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7846 */
7847 static int
7848set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7849 char_u *name;
7850 guicolor_T *fgp;
7851 guicolor_T *bgp;
7852 int do_menu;
7853 int use_norm;
7854 int do_tooltip;
7855{
7856 int idx;
7857
7858 idx = syn_name2id(name) - 1;
7859 if (idx >= 0)
7860 {
7861 gui_do_one_color(idx, do_menu, do_tooltip);
7862
7863 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7864 *fgp = HL_TABLE()[idx].sg_gui_fg;
7865 else if (use_norm)
7866 *fgp = gui.def_norm_pixel;
7867 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7868 *bgp = HL_TABLE()[idx].sg_gui_bg;
7869 else if (use_norm)
7870 *bgp = gui.def_back_pixel;
7871 return TRUE;
7872 }
7873 return FALSE;
7874}
7875
7876/*
7877 * Get the font of the "Normal" group.
7878 * Returns "" when it's not found or not set.
7879 */
7880 char_u *
7881hl_get_font_name()
7882{
7883 int id;
7884 char_u *s;
7885
7886 id = syn_name2id((char_u *)"Normal");
7887 if (id > 0)
7888 {
7889 s = HL_TABLE()[id - 1].sg_font_name;
7890 if (s != NULL)
7891 return s;
7892 }
7893 return (char_u *)"";
7894}
7895
7896/*
7897 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7898 * actually chosen to be used.
7899 */
7900 void
7901hl_set_font_name(font_name)
7902 char_u *font_name;
7903{
7904 int id;
7905
7906 id = syn_name2id((char_u *)"Normal");
7907 if (id > 0)
7908 {
7909 vim_free(HL_TABLE()[id - 1].sg_font_name);
7910 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7911 }
7912}
7913
7914/*
7915 * Set background color for "Normal" group. Called by gui_set_bg_color()
7916 * when the color is known.
7917 */
7918 void
7919hl_set_bg_color_name(name)
7920 char_u *name; /* must have been allocated */
7921{
7922 int id;
7923
7924 if (name != NULL)
7925 {
7926 id = syn_name2id((char_u *)"Normal");
7927 if (id > 0)
7928 {
7929 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7930 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7931 }
7932 }
7933}
7934
7935/*
7936 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7937 * when the color is known.
7938 */
7939 void
7940hl_set_fg_color_name(name)
7941 char_u *name; /* must have been allocated */
7942{
7943 int id;
7944
7945 if (name != NULL)
7946 {
7947 id = syn_name2id((char_u *)"Normal");
7948 if (id > 0)
7949 {
7950 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7951 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7952 }
7953 }
7954}
7955
7956/*
7957 * Return the handle for a color name.
7958 * Returns INVALCOLOR when failed.
7959 */
7960 static guicolor_T
7961color_name2handle(name)
7962 char_u *name;
7963{
7964 if (STRCMP(name, "NONE") == 0)
7965 return INVALCOLOR;
7966
7967 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7968 return gui.norm_pixel;
7969 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7970 return gui.back_pixel;
7971
7972 return gui_get_color(name);
7973}
7974
7975/*
7976 * Return the handle for a font name.
7977 * Returns NOFONT when failed.
7978 */
7979 static GuiFont
7980font_name2handle(name)
7981 char_u *name;
7982{
7983 if (STRCMP(name, "NONE") == 0)
7984 return NOFONT;
7985
7986 return gui_mch_get_font(name, TRUE);
7987}
7988
7989# ifdef FEAT_XFONTSET
7990/*
7991 * Return the handle for a fontset name.
7992 * Returns NOFONTSET when failed.
7993 */
7994 static GuiFontset
7995fontset_name2handle(name, fixed_width)
7996 char_u *name;
7997 int fixed_width;
7998{
7999 if (STRCMP(name, "NONE") == 0)
8000 return NOFONTSET;
8001
8002 return gui_mch_get_fontset(name, TRUE, fixed_width);
8003}
8004# endif
8005
8006/*
8007 * Get the font or fontset for one highlight group.
8008 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008009 static void
8010hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8011 int idx;
8012 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008013 int do_normal; /* set normal font */
8014 int do_menu UNUSED; /* set menu font */
8015 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008016{
8017# ifdef FEAT_XFONTSET
8018 /* If 'guifontset' is not empty, first try using the name as a
8019 * fontset. If that doesn't work, use it as a font name. */
8020 if (*p_guifontset != NUL
8021# ifdef FONTSET_ALWAYS
8022 || do_menu
8023# endif
8024# ifdef FEAT_BEVAL_TIP
8025 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8026 || do_tooltip
8027# endif
8028 )
8029 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8030# ifdef FONTSET_ALWAYS
8031 || do_menu
8032# endif
8033# ifdef FEAT_BEVAL_TIP
8034 || do_tooltip
8035# endif
8036 );
8037 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8038 {
8039 /* If it worked and it's the Normal group, use it as the
8040 * normal fontset. Same for the Menu group. */
8041 if (do_normal)
8042 gui_init_font(arg, TRUE);
8043# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8044 if (do_menu)
8045 {
8046# ifdef FONTSET_ALWAYS
8047 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8048# else
8049 /* YIKES! This is a bug waiting to crash the program */
8050 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8051# endif
8052 gui_mch_new_menu_font();
8053 }
8054# ifdef FEAT_BEVAL
8055 if (do_tooltip)
8056 {
8057 /* The Athena widget set cannot currently handle switching between
8058 * displaying a single font and a fontset.
8059 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008060 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008061 * XFontStruct is used.
8062 */
8063 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8064 gui_mch_new_tooltip_font();
8065 }
8066# endif
8067# endif
8068 }
8069 else
8070# endif
8071 {
8072 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8073 /* If it worked and it's the Normal group, use it as the
8074 * normal font. Same for the Menu group. */
8075 if (HL_TABLE()[idx].sg_font != NOFONT)
8076 {
8077 if (do_normal)
8078 gui_init_font(arg, FALSE);
8079#ifndef FONTSET_ALWAYS
8080# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8081 if (do_menu)
8082 {
8083 gui.menu_font = HL_TABLE()[idx].sg_font;
8084 gui_mch_new_menu_font();
8085 }
8086# endif
8087#endif
8088 }
8089 }
8090}
8091
8092#endif /* FEAT_GUI */
8093
8094/*
8095 * Table with the specifications for an attribute number.
8096 * Note that this table is used by ALL buffers. This is required because the
8097 * GUI can redraw at any time for any buffer.
8098 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008099static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008100
8101#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8102
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008103static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008104
8105#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8106
8107#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008108static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008109
8110#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8111#endif
8112
8113/*
8114 * Return the attr number for a set of colors and font.
8115 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8116 * if the combination is new.
8117 * Return 0 for error (no more room).
8118 */
8119 static int
8120get_attr_entry(table, aep)
8121 garray_T *table;
8122 attrentry_T *aep;
8123{
8124 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008125 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008126 static int recursive = FALSE;
8127
8128 /*
8129 * Init the table, in case it wasn't done yet.
8130 */
8131 table->ga_itemsize = sizeof(attrentry_T);
8132 table->ga_growsize = 7;
8133
8134 /*
8135 * Try to find an entry with the same specifications.
8136 */
8137 for (i = 0; i < table->ga_len; ++i)
8138 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008139 taep = &(((attrentry_T *)table->ga_data)[i]);
8140 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 && (
8142#ifdef FEAT_GUI
8143 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008144 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8145 && aep->ae_u.gui.bg_color
8146 == taep->ae_u.gui.bg_color
8147 && aep->ae_u.gui.sp_color
8148 == taep->ae_u.gui.sp_color
8149 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008150# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008151 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008152# endif
8153 ))
8154 ||
8155#endif
8156 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008157 && (aep->ae_u.term.start == NULL)
8158 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008159 && (aep->ae_u.term.start == NULL
8160 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008161 taep->ae_u.term.start) == 0)
8162 && (aep->ae_u.term.stop == NULL)
8163 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008164 && (aep->ae_u.term.stop == NULL
8165 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008166 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008167 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008168 && aep->ae_u.cterm.fg_color
8169 == taep->ae_u.cterm.fg_color
8170 && aep->ae_u.cterm.bg_color
8171 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008172 ))
8173
8174 return i + ATTR_OFF;
8175 }
8176
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008177 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008178 {
8179 /*
8180 * Running out of attribute entries! remove all attributes, and
8181 * compute new ones for all groups.
8182 * When called recursively, we are really out of numbers.
8183 */
8184 if (recursive)
8185 {
8186 EMSG(_("E424: Too many different highlighting attributes in use"));
8187 return 0;
8188 }
8189 recursive = TRUE;
8190
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008191 clear_hl_tables();
8192
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193 must_redraw = CLEAR;
8194
8195 for (i = 0; i < highlight_ga.ga_len; ++i)
8196 set_hl_attr(i);
8197
8198 recursive = FALSE;
8199 }
8200
8201 /*
8202 * This is a new combination of colors and font, add an entry.
8203 */
8204 if (ga_grow(table, 1) == FAIL)
8205 return 0;
8206
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008207 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8208 vim_memset(taep, 0, sizeof(attrentry_T));
8209 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210#ifdef FEAT_GUI
8211 if (table == &gui_attr_table)
8212 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008213 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8214 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8215 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8216 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008218 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219# endif
8220 }
8221#endif
8222 if (table == &term_attr_table)
8223 {
8224 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008225 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008226 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008227 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008228 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008229 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008230 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008231 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008232 }
8233 else if (table == &cterm_attr_table)
8234 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008235 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8236 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237 }
8238 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008239 return (table->ga_len - 1 + ATTR_OFF);
8240}
8241
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008242/*
8243 * Clear all highlight tables.
8244 */
8245 void
8246clear_hl_tables()
8247{
8248 int i;
8249 attrentry_T *taep;
8250
8251#ifdef FEAT_GUI
8252 ga_clear(&gui_attr_table);
8253#endif
8254 for (i = 0; i < term_attr_table.ga_len; ++i)
8255 {
8256 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8257 vim_free(taep->ae_u.term.start);
8258 vim_free(taep->ae_u.term.stop);
8259 }
8260 ga_clear(&term_attr_table);
8261 ga_clear(&cterm_attr_table);
8262}
8263
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008264#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008265/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008266 * Combine special attributes (e.g., for spelling) with other attributes
8267 * (e.g., for syntax highlighting).
8268 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008269 * This creates a new group when required.
8270 * Since we expect there to be few spelling mistakes we don't cache the
8271 * result.
8272 * Return the resulting attributes.
8273 */
8274 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008275hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008276 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008277 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008278{
8279 attrentry_T *char_aep = NULL;
8280 attrentry_T *spell_aep;
8281 attrentry_T new_en;
8282
8283 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008284 return prim_attr;
8285 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8286 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008287#ifdef FEAT_GUI
8288 if (gui.in_use)
8289 {
8290 if (char_attr > HL_ALL)
8291 char_aep = syn_gui_attr2entry(char_attr);
8292 if (char_aep != NULL)
8293 new_en = *char_aep;
8294 else
8295 {
8296 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008297 new_en.ae_u.gui.fg_color = INVALCOLOR;
8298 new_en.ae_u.gui.bg_color = INVALCOLOR;
8299 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008300 if (char_attr <= HL_ALL)
8301 new_en.ae_attr = char_attr;
8302 }
8303
Bram Moolenaar30abd282005-06-22 22:35:10 +00008304 if (prim_attr <= HL_ALL)
8305 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008306 else
8307 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008308 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008309 if (spell_aep != NULL)
8310 {
8311 new_en.ae_attr |= spell_aep->ae_attr;
8312 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8313 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8314 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8315 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8316 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8317 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8318 if (spell_aep->ae_u.gui.font != NOFONT)
8319 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8320# ifdef FEAT_XFONTSET
8321 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8322 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8323# endif
8324 }
8325 }
8326 return get_attr_entry(&gui_attr_table, &new_en);
8327 }
8328#endif
8329
8330 if (t_colors > 1)
8331 {
8332 if (char_attr > HL_ALL)
8333 char_aep = syn_cterm_attr2entry(char_attr);
8334 if (char_aep != NULL)
8335 new_en = *char_aep;
8336 else
8337 {
8338 vim_memset(&new_en, 0, sizeof(new_en));
8339 if (char_attr <= HL_ALL)
8340 new_en.ae_attr = char_attr;
8341 }
8342
Bram Moolenaar30abd282005-06-22 22:35:10 +00008343 if (prim_attr <= HL_ALL)
8344 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008345 else
8346 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008347 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008348 if (spell_aep != NULL)
8349 {
8350 new_en.ae_attr |= spell_aep->ae_attr;
8351 if (spell_aep->ae_u.cterm.fg_color > 0)
8352 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8353 if (spell_aep->ae_u.cterm.bg_color > 0)
8354 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8355 }
8356 }
8357 return get_attr_entry(&cterm_attr_table, &new_en);
8358 }
8359
8360 if (char_attr > HL_ALL)
8361 char_aep = syn_term_attr2entry(char_attr);
8362 if (char_aep != NULL)
8363 new_en = *char_aep;
8364 else
8365 {
8366 vim_memset(&new_en, 0, sizeof(new_en));
8367 if (char_attr <= HL_ALL)
8368 new_en.ae_attr = char_attr;
8369 }
8370
Bram Moolenaar30abd282005-06-22 22:35:10 +00008371 if (prim_attr <= HL_ALL)
8372 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008373 else
8374 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008375 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008376 if (spell_aep != NULL)
8377 {
8378 new_en.ae_attr |= spell_aep->ae_attr;
8379 if (spell_aep->ae_u.term.start != NULL)
8380 {
8381 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8382 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8383 }
8384 }
8385 }
8386 return get_attr_entry(&term_attr_table, &new_en);
8387}
8388#endif
8389
Bram Moolenaar071d4272004-06-13 20:20:40 +00008390#ifdef FEAT_GUI
8391
8392 attrentry_T *
8393syn_gui_attr2entry(attr)
8394 int attr;
8395{
8396 attr -= ATTR_OFF;
8397 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8398 return NULL;
8399 return &(GUI_ATTR_ENTRY(attr));
8400}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008401#endif /* FEAT_GUI */
8402
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008403/*
8404 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8405 * Only to be used when "attr" > HL_ALL.
8406 */
8407 int
8408syn_attr2attr(attr)
8409 int attr;
8410{
8411 attrentry_T *aep;
8412
8413#ifdef FEAT_GUI
8414 if (gui.in_use)
8415 aep = syn_gui_attr2entry(attr);
8416 else
8417#endif
8418 if (t_colors > 1)
8419 aep = syn_cterm_attr2entry(attr);
8420 else
8421 aep = syn_term_attr2entry(attr);
8422
8423 if (aep == NULL) /* highlighting not set */
8424 return 0;
8425 return aep->ae_attr;
8426}
8427
8428
Bram Moolenaar071d4272004-06-13 20:20:40 +00008429 attrentry_T *
8430syn_term_attr2entry(attr)
8431 int attr;
8432{
8433 attr -= ATTR_OFF;
8434 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8435 return NULL;
8436 return &(TERM_ATTR_ENTRY(attr));
8437}
8438
8439 attrentry_T *
8440syn_cterm_attr2entry(attr)
8441 int attr;
8442{
8443 attr -= ATTR_OFF;
8444 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8445 return NULL;
8446 return &(CTERM_ATTR_ENTRY(attr));
8447}
8448
8449#define LIST_ATTR 1
8450#define LIST_STRING 2
8451#define LIST_INT 3
8452
8453 static void
8454highlight_list_one(id)
8455 int id;
8456{
8457 struct hl_group *sgp;
8458 int didh = FALSE;
8459
8460 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8461
8462 didh = highlight_list_arg(id, didh, LIST_ATTR,
8463 sgp->sg_term, NULL, "term");
8464 didh = highlight_list_arg(id, didh, LIST_STRING,
8465 0, sgp->sg_start, "start");
8466 didh = highlight_list_arg(id, didh, LIST_STRING,
8467 0, sgp->sg_stop, "stop");
8468
8469 didh = highlight_list_arg(id, didh, LIST_ATTR,
8470 sgp->sg_cterm, NULL, "cterm");
8471 didh = highlight_list_arg(id, didh, LIST_INT,
8472 sgp->sg_cterm_fg, NULL, "ctermfg");
8473 didh = highlight_list_arg(id, didh, LIST_INT,
8474 sgp->sg_cterm_bg, NULL, "ctermbg");
8475
Bram Moolenaar61623362010-07-14 22:04:22 +02008476#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008477 didh = highlight_list_arg(id, didh, LIST_ATTR,
8478 sgp->sg_gui, NULL, "gui");
8479 didh = highlight_list_arg(id, didh, LIST_STRING,
8480 0, sgp->sg_gui_fg_name, "guifg");
8481 didh = highlight_list_arg(id, didh, LIST_STRING,
8482 0, sgp->sg_gui_bg_name, "guibg");
8483 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008484 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008485#endif
8486#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008487 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008488 0, sgp->sg_font_name, "font");
8489#endif
8490
Bram Moolenaar661b1822005-07-28 22:36:45 +00008491 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008492 {
8493 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008494 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008495 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8496 msg_putchar(' ');
8497 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8498 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008499
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008500 if (!didh)
8501 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008502#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008503 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008504 last_set_msg(sgp->sg_scriptID);
8505#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008506}
8507
8508 static int
8509highlight_list_arg(id, didh, type, iarg, sarg, name)
8510 int id;
8511 int didh;
8512 int type;
8513 int iarg;
8514 char_u *sarg;
8515 char *name;
8516{
8517 char_u buf[100];
8518 char_u *ts;
8519 int i;
8520
Bram Moolenaar661b1822005-07-28 22:36:45 +00008521 if (got_int)
8522 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008523 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8524 {
8525 ts = buf;
8526 if (type == LIST_INT)
8527 sprintf((char *)buf, "%d", iarg - 1);
8528 else if (type == LIST_STRING)
8529 ts = sarg;
8530 else /* type == LIST_ATTR */
8531 {
8532 buf[0] = NUL;
8533 for (i = 0; hl_attr_table[i] != 0; ++i)
8534 {
8535 if (iarg & hl_attr_table[i])
8536 {
8537 if (buf[0] != NUL)
8538 STRCAT(buf, ",");
8539 STRCAT(buf, hl_name_table[i]);
8540 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8541 }
8542 }
8543 }
8544
8545 (void)syn_list_header(didh,
8546 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8547 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008548 if (!got_int)
8549 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008550 if (*name != NUL)
8551 {
8552 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8553 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8554 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008555 msg_outtrans(ts);
8556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008557 }
8558 return didh;
8559}
8560
8561#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8562/*
8563 * Return "1" if highlight group "id" has attribute "flag".
8564 * Return NULL otherwise.
8565 */
8566 char_u *
8567highlight_has_attr(id, flag, modec)
8568 int id;
8569 int flag;
8570 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8571{
8572 int attr;
8573
8574 if (id <= 0 || id > highlight_ga.ga_len)
8575 return NULL;
8576
Bram Moolenaar61623362010-07-14 22:04:22 +02008577#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008578 if (modec == 'g')
8579 attr = HL_TABLE()[id - 1].sg_gui;
8580 else
8581#endif
8582 if (modec == 'c')
8583 attr = HL_TABLE()[id - 1].sg_cterm;
8584 else
8585 attr = HL_TABLE()[id - 1].sg_term;
8586
8587 if (attr & flag)
8588 return (char_u *)"1";
8589 return NULL;
8590}
8591#endif
8592
8593#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8594/*
8595 * Return color name of highlight group "id".
8596 */
8597 char_u *
8598highlight_color(id, what, modec)
8599 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008600 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8602{
8603 static char_u name[20];
8604 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008605 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008606 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008607 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008608
8609 if (id <= 0 || id > highlight_ga.ga_len)
8610 return NULL;
8611
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008612 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008613 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008614 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008615 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008616 font = TRUE;
8617 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008618 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008619 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8620 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008621 if (modec == 'g')
8622 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008623# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008624 /* return font name */
8625 if (font)
8626 return HL_TABLE()[id - 1].sg_font_name;
8627
Bram Moolenaar071d4272004-06-13 20:20:40 +00008628 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008629 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008630 {
8631 guicolor_T color;
8632 long_u rgb;
8633 static char_u buf[10];
8634
8635 if (fg)
8636 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008637 else if (sp)
8638 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008639 else
8640 color = HL_TABLE()[id - 1].sg_gui_bg;
8641 if (color == INVALCOLOR)
8642 return NULL;
8643 rgb = gui_mch_get_rgb(color);
8644 sprintf((char *)buf, "#%02x%02x%02x",
8645 (unsigned)(rgb >> 16),
8646 (unsigned)(rgb >> 8) & 255,
8647 (unsigned)rgb & 255);
8648 return buf;
8649 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008650#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008651 if (fg)
8652 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008653 if (sp)
8654 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008655 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8656 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008657 if (font || sp)
8658 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008659 if (modec == 'c')
8660 {
8661 if (fg)
8662 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8663 else
8664 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8665 sprintf((char *)name, "%d", n);
8666 return name;
8667 }
8668 /* term doesn't have color */
8669 return NULL;
8670}
8671#endif
8672
8673#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8674 || defined(PROTO)
8675/*
8676 * Return color name of highlight group "id" as RGB value.
8677 */
8678 long_u
8679highlight_gui_color_rgb(id, fg)
8680 int id;
8681 int fg; /* TRUE = fg, FALSE = bg */
8682{
8683 guicolor_T color;
8684
8685 if (id <= 0 || id > highlight_ga.ga_len)
8686 return 0L;
8687
8688 if (fg)
8689 color = HL_TABLE()[id - 1].sg_gui_fg;
8690 else
8691 color = HL_TABLE()[id - 1].sg_gui_bg;
8692
8693 if (color == INVALCOLOR)
8694 return 0L;
8695
8696 return gui_mch_get_rgb(color);
8697}
8698#endif
8699
8700/*
8701 * Output the syntax list header.
8702 * Return TRUE when started a new line.
8703 */
8704 static int
8705syn_list_header(did_header, outlen, id)
8706 int did_header; /* did header already */
8707 int outlen; /* length of string that comes */
8708 int id; /* highlight group id */
8709{
8710 int endcol = 19;
8711 int newline = TRUE;
8712
8713 if (!did_header)
8714 {
8715 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008716 if (got_int)
8717 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008718 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8719 endcol = 15;
8720 }
8721 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008722 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008723 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008724 if (got_int)
8725 return TRUE;
8726 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008727 else
8728 {
8729 if (msg_col >= endcol) /* wrap around is like starting a new line */
8730 newline = FALSE;
8731 }
8732
8733 if (msg_col >= endcol) /* output at least one space */
8734 endcol = msg_col + 1;
8735 if (Columns <= endcol) /* avoid hang for tiny window */
8736 endcol = Columns - 1;
8737
8738 msg_advance(endcol);
8739
8740 /* Show "xxx" with the attributes. */
8741 if (!did_header)
8742 {
8743 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8744 msg_putchar(' ');
8745 }
8746
8747 return newline;
8748}
8749
8750/*
8751 * Set the attribute numbers for a highlight group.
8752 * Called after one of the attributes has changed.
8753 */
8754 static void
8755set_hl_attr(idx)
8756 int idx; /* index in array */
8757{
8758 attrentry_T at_en;
8759 struct hl_group *sgp = HL_TABLE() + idx;
8760
8761 /* The "Normal" group doesn't need an attribute number */
8762 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8763 return;
8764
8765#ifdef FEAT_GUI
8766 /*
8767 * For the GUI mode: If there are other than "normal" highlighting
8768 * attributes, need to allocate an attr number.
8769 */
8770 if (sgp->sg_gui_fg == INVALCOLOR
8771 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008772 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008773 && sgp->sg_font == NOFONT
8774# ifdef FEAT_XFONTSET
8775 && sgp->sg_fontset == NOFONTSET
8776# endif
8777 )
8778 {
8779 sgp->sg_gui_attr = sgp->sg_gui;
8780 }
8781 else
8782 {
8783 at_en.ae_attr = sgp->sg_gui;
8784 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8785 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008786 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008787 at_en.ae_u.gui.font = sgp->sg_font;
8788# ifdef FEAT_XFONTSET
8789 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8790# endif
8791 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8792 }
8793#endif
8794 /*
8795 * For the term mode: If there are other than "normal" highlighting
8796 * attributes, need to allocate an attr number.
8797 */
8798 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8799 sgp->sg_term_attr = sgp->sg_term;
8800 else
8801 {
8802 at_en.ae_attr = sgp->sg_term;
8803 at_en.ae_u.term.start = sgp->sg_start;
8804 at_en.ae_u.term.stop = sgp->sg_stop;
8805 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8806 }
8807
8808 /*
8809 * For the color term mode: If there are other than "normal"
8810 * highlighting attributes, need to allocate an attr number.
8811 */
8812 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8813 sgp->sg_cterm_attr = sgp->sg_cterm;
8814 else
8815 {
8816 at_en.ae_attr = sgp->sg_cterm;
8817 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8818 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8819 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8820 }
8821}
8822
8823/*
8824 * Lookup a highlight group name and return it's ID.
8825 * If it is not found, 0 is returned.
8826 */
8827 int
8828syn_name2id(name)
8829 char_u *name;
8830{
8831 int i;
8832 char_u name_u[200];
8833
8834 /* Avoid using stricmp() too much, it's slow on some systems */
8835 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8836 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008837 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008838 vim_strup(name_u);
8839 for (i = highlight_ga.ga_len; --i >= 0; )
8840 if (HL_TABLE()[i].sg_name_u != NULL
8841 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8842 break;
8843 return i + 1;
8844}
8845
8846#if defined(FEAT_EVAL) || defined(PROTO)
8847/*
8848 * Return TRUE if highlight group "name" exists.
8849 */
8850 int
8851highlight_exists(name)
8852 char_u *name;
8853{
8854 return (syn_name2id(name) > 0);
8855}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008856
8857# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8858/*
8859 * Return the name of highlight group "id".
8860 * When not a valid ID return an empty string.
8861 */
8862 char_u *
8863syn_id2name(id)
8864 int id;
8865{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008866 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008867 return (char_u *)"";
8868 return HL_TABLE()[id - 1].sg_name;
8869}
8870# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008871#endif
8872
8873/*
8874 * Like syn_name2id(), but take a pointer + length argument.
8875 */
8876 int
8877syn_namen2id(linep, len)
8878 char_u *linep;
8879 int len;
8880{
8881 char_u *name;
8882 int id = 0;
8883
8884 name = vim_strnsave(linep, len);
8885 if (name != NULL)
8886 {
8887 id = syn_name2id(name);
8888 vim_free(name);
8889 }
8890 return id;
8891}
8892
8893/*
8894 * Find highlight group name in the table and return it's ID.
8895 * The argument is a pointer to the name and the length of the name.
8896 * If it doesn't exist yet, a new entry is created.
8897 * Return 0 for failure.
8898 */
8899 int
8900syn_check_group(pp, len)
8901 char_u *pp;
8902 int len;
8903{
8904 int id;
8905 char_u *name;
8906
8907 name = vim_strnsave(pp, len);
8908 if (name == NULL)
8909 return 0;
8910
8911 id = syn_name2id(name);
8912 if (id == 0) /* doesn't exist yet */
8913 id = syn_add_group(name);
8914 else
8915 vim_free(name);
8916 return id;
8917}
8918
8919/*
8920 * Add new highlight group and return it's ID.
8921 * "name" must be an allocated string, it will be consumed.
8922 * Return 0 for failure.
8923 */
8924 static int
8925syn_add_group(name)
8926 char_u *name;
8927{
8928 char_u *p;
8929
8930 /* Check that the name is ASCII letters, digits and underscore. */
8931 for (p = name; *p != NUL; ++p)
8932 {
8933 if (!vim_isprintc(*p))
8934 {
8935 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008936 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008937 return 0;
8938 }
8939 else if (!ASCII_ISALNUM(*p) && *p != '_')
8940 {
8941 /* This is an error, but since there previously was no check only
8942 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008943 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008944 MSG(_("W18: Invalid character in group name"));
8945 break;
8946 }
8947 }
8948
8949 /*
8950 * First call for this growarray: init growing array.
8951 */
8952 if (highlight_ga.ga_data == NULL)
8953 {
8954 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8955 highlight_ga.ga_growsize = 10;
8956 }
8957
8958 /*
8959 * Make room for at least one other syntax_highlight entry.
8960 */
8961 if (ga_grow(&highlight_ga, 1) == FAIL)
8962 {
8963 vim_free(name);
8964 return 0;
8965 }
8966
8967 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8968 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8969 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8970#ifdef FEAT_GUI
8971 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8972 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008973 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008974#endif
8975 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008976
8977 return highlight_ga.ga_len; /* ID is index plus one */
8978}
8979
8980/*
8981 * When, just after calling syn_add_group(), an error is discovered, this
8982 * function deletes the new name.
8983 */
8984 static void
8985syn_unadd_group()
8986{
8987 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008988 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8989 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8990}
8991
8992/*
8993 * Translate a group ID to highlight attributes.
8994 */
8995 int
8996syn_id2attr(hl_id)
8997 int hl_id;
8998{
8999 int attr;
9000 struct hl_group *sgp;
9001
9002 hl_id = syn_get_final_id(hl_id);
9003 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9004
9005#ifdef FEAT_GUI
9006 /*
9007 * Only use GUI attr when the GUI is being used.
9008 */
9009 if (gui.in_use)
9010 attr = sgp->sg_gui_attr;
9011 else
9012#endif
9013 if (t_colors > 1)
9014 attr = sgp->sg_cterm_attr;
9015 else
9016 attr = sgp->sg_term_attr;
9017
9018 return attr;
9019}
9020
9021#ifdef FEAT_GUI
9022/*
9023 * Get the GUI colors and attributes for a group ID.
9024 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9025 */
9026 int
9027syn_id2colors(hl_id, fgp, bgp)
9028 int hl_id;
9029 guicolor_T *fgp;
9030 guicolor_T *bgp;
9031{
9032 struct hl_group *sgp;
9033
9034 hl_id = syn_get_final_id(hl_id);
9035 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9036
9037 *fgp = sgp->sg_gui_fg;
9038 *bgp = sgp->sg_gui_bg;
9039 return sgp->sg_gui;
9040}
9041#endif
9042
9043/*
9044 * Translate a group ID to the final group ID (following links).
9045 */
9046 int
9047syn_get_final_id(hl_id)
9048 int hl_id;
9049{
9050 int count;
9051 struct hl_group *sgp;
9052
9053 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9054 return 0; /* Can be called from eval!! */
9055
9056 /*
9057 * Follow links until there is no more.
9058 * Look out for loops! Break after 100 links.
9059 */
9060 for (count = 100; --count >= 0; )
9061 {
9062 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9063 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9064 break;
9065 hl_id = sgp->sg_link;
9066 }
9067
9068 return hl_id;
9069}
9070
9071#ifdef FEAT_GUI
9072/*
9073 * Call this function just after the GUI has started.
9074 * It finds the font and color handles for the highlighting groups.
9075 */
9076 void
9077highlight_gui_started()
9078{
9079 int idx;
9080
9081 /* First get the colors from the "Normal" and "Menu" group, if set */
9082 set_normal_colors();
9083
9084 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9085 gui_do_one_color(idx, FALSE, FALSE);
9086
9087 highlight_changed();
9088}
9089
9090 static void
9091gui_do_one_color(idx, do_menu, do_tooltip)
9092 int idx;
9093 int do_menu; /* TRUE: might set the menu font */
9094 int do_tooltip; /* TRUE: might set the tooltip font */
9095{
9096 int didit = FALSE;
9097
9098 if (HL_TABLE()[idx].sg_font_name != NULL)
9099 {
9100 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9101 do_tooltip);
9102 didit = TRUE;
9103 }
9104 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9105 {
9106 HL_TABLE()[idx].sg_gui_fg =
9107 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9108 didit = TRUE;
9109 }
9110 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9111 {
9112 HL_TABLE()[idx].sg_gui_bg =
9113 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9114 didit = TRUE;
9115 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009116 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9117 {
9118 HL_TABLE()[idx].sg_gui_sp =
9119 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9120 didit = TRUE;
9121 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009122 if (didit) /* need to get a new attr number */
9123 set_hl_attr(idx);
9124}
9125
9126#endif
9127
9128/*
9129 * Translate the 'highlight' option into attributes in highlight_attr[] and
9130 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9131 * corresponding highlights to use on top of HLF_SNC is computed.
9132 * Called only when the 'highlight' option has been changed and upon first
9133 * screen redraw after any :highlight command.
9134 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9135 */
9136 int
9137highlight_changed()
9138{
9139 int hlf;
9140 int i;
9141 char_u *p;
9142 int attr;
9143 char_u *end;
9144 int id;
9145#ifdef USER_HIGHLIGHT
9146 char_u userhl[10];
9147# ifdef FEAT_STL_OPT
9148 int id_SNC = -1;
9149 int id_S = -1;
9150 int hlcnt;
9151# endif
9152#endif
9153 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9154
9155 need_highlight_changed = FALSE;
9156
9157 /*
9158 * Clear all attributes.
9159 */
9160 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9161 highlight_attr[hlf] = 0;
9162
9163 /*
9164 * First set all attributes to their default value.
9165 * Then use the attributes from the 'highlight' option.
9166 */
9167 for (i = 0; i < 2; ++i)
9168 {
9169 if (i)
9170 p = p_hl;
9171 else
9172 p = get_highlight_default();
9173 if (p == NULL) /* just in case */
9174 continue;
9175
9176 while (*p)
9177 {
9178 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9179 if (hl_flags[hlf] == *p)
9180 break;
9181 ++p;
9182 if (hlf == (int)HLF_COUNT || *p == NUL)
9183 return FAIL;
9184
9185 /*
9186 * Allow several hl_flags to be combined, like "bu" for
9187 * bold-underlined.
9188 */
9189 attr = 0;
9190 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9191 {
9192 if (vim_iswhite(*p)) /* ignore white space */
9193 continue;
9194
9195 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9196 return FAIL;
9197
9198 switch (*p)
9199 {
9200 case 'b': attr |= HL_BOLD;
9201 break;
9202 case 'i': attr |= HL_ITALIC;
9203 break;
9204 case '-':
9205 case 'n': /* no highlighting */
9206 break;
9207 case 'r': attr |= HL_INVERSE;
9208 break;
9209 case 's': attr |= HL_STANDOUT;
9210 break;
9211 case 'u': attr |= HL_UNDERLINE;
9212 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009213 case 'c': attr |= HL_UNDERCURL;
9214 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009215 case ':': ++p; /* highlight group name */
9216 if (attr || *p == NUL) /* no combinations */
9217 return FAIL;
9218 end = vim_strchr(p, ',');
9219 if (end == NULL)
9220 end = p + STRLEN(p);
9221 id = syn_check_group(p, (int)(end - p));
9222 if (id == 0)
9223 return FAIL;
9224 attr = syn_id2attr(id);
9225 p = end - 1;
9226#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9227 if (hlf == (int)HLF_SNC)
9228 id_SNC = syn_get_final_id(id);
9229 else if (hlf == (int)HLF_S)
9230 id_S = syn_get_final_id(id);
9231#endif
9232 break;
9233 default: return FAIL;
9234 }
9235 }
9236 highlight_attr[hlf] = attr;
9237
9238 p = skip_to_option_part(p); /* skip comma and spaces */
9239 }
9240 }
9241
9242#ifdef USER_HIGHLIGHT
9243 /* Setup the user highlights
9244 *
9245 * Temporarily utilize 10 more hl entries. Have to be in there
9246 * simultaneously in case of table overflows in get_attr_entry()
9247 */
9248# ifdef FEAT_STL_OPT
9249 if (ga_grow(&highlight_ga, 10) == FAIL)
9250 return FAIL;
9251 hlcnt = highlight_ga.ga_len;
9252 if (id_S == 0)
9253 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009254 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009255 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9256 id_S = hlcnt + 10;
9257 }
9258# endif
9259 for (i = 0; i < 9; i++)
9260 {
9261 sprintf((char *)userhl, "User%d", i + 1);
9262 id = syn_name2id(userhl);
9263 if (id == 0)
9264 {
9265 highlight_user[i] = 0;
9266# ifdef FEAT_STL_OPT
9267 highlight_stlnc[i] = 0;
9268# endif
9269 }
9270 else
9271 {
9272# ifdef FEAT_STL_OPT
9273 struct hl_group *hlt = HL_TABLE();
9274# endif
9275
9276 highlight_user[i] = syn_id2attr(id);
9277# ifdef FEAT_STL_OPT
9278 if (id_SNC == 0)
9279 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009280 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009281 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9282 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009283# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009284 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9285# endif
9286 }
9287 else
9288 mch_memmove(&hlt[hlcnt + i],
9289 &hlt[id_SNC - 1],
9290 sizeof(struct hl_group));
9291 hlt[hlcnt + i].sg_link = 0;
9292
9293 /* Apply difference between UserX and HLF_S to HLF_SNC */
9294 hlt[hlcnt + i].sg_term ^=
9295 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9296 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9297 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9298 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9299 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9300 hlt[hlcnt + i].sg_cterm ^=
9301 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9302 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9303 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9304 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9305 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009306# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009307 hlt[hlcnt + i].sg_gui ^=
9308 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009309# endif
9310# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009311 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9312 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9313 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9314 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009315 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9316 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9318 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9319# ifdef FEAT_XFONTSET
9320 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9321 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9322# endif
9323# endif
9324 highlight_ga.ga_len = hlcnt + i + 1;
9325 set_hl_attr(hlcnt + i); /* At long last we can apply */
9326 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9327# endif
9328 }
9329 }
9330# ifdef FEAT_STL_OPT
9331 highlight_ga.ga_len = hlcnt;
9332# endif
9333
9334#endif /* USER_HIGHLIGHT */
9335
9336 return OK;
9337}
9338
Bram Moolenaar4f688582007-07-24 12:34:30 +00009339#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009340
9341static void highlight_list __ARGS((void));
9342static void highlight_list_two __ARGS((int cnt, int attr));
9343
9344/*
9345 * Handle command line completion for :highlight command.
9346 */
9347 void
9348set_context_in_highlight_cmd(xp, arg)
9349 expand_T *xp;
9350 char_u *arg;
9351{
9352 char_u *p;
9353
9354 /* Default: expand group names */
9355 xp->xp_context = EXPAND_HIGHLIGHT;
9356 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009357 include_link = 2;
9358 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009359
9360 /* (part of) subcommand already typed */
9361 if (*arg != NUL)
9362 {
9363 p = skiptowhite(arg);
9364 if (*p != NUL) /* past "default" or group name */
9365 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009366 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009367 if (STRNCMP("default", arg, p - arg) == 0)
9368 {
9369 arg = skipwhite(p);
9370 xp->xp_pattern = arg;
9371 p = skiptowhite(arg);
9372 }
9373 if (*p != NUL) /* past group name */
9374 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009375 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009376 if (arg[1] == 'i' && arg[0] == 'N')
9377 highlight_list();
9378 if (STRNCMP("link", arg, p - arg) == 0
9379 || STRNCMP("clear", arg, p - arg) == 0)
9380 {
9381 xp->xp_pattern = skipwhite(p);
9382 p = skiptowhite(xp->xp_pattern);
9383 if (*p != NUL) /* past first group name */
9384 {
9385 xp->xp_pattern = skipwhite(p);
9386 p = skiptowhite(xp->xp_pattern);
9387 }
9388 }
9389 if (*p != NUL) /* past group name(s) */
9390 xp->xp_context = EXPAND_NOTHING;
9391 }
9392 }
9393 }
9394}
9395
9396/*
9397 * List highlighting matches in a nice way.
9398 */
9399 static void
9400highlight_list()
9401{
9402 int i;
9403
9404 for (i = 10; --i >= 0; )
9405 highlight_list_two(i, hl_attr(HLF_D));
9406 for (i = 40; --i >= 0; )
9407 highlight_list_two(99, 0);
9408}
9409
9410 static void
9411highlight_list_two(cnt, attr)
9412 int cnt;
9413 int attr;
9414{
9415 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9416 msg_clr_eos();
9417 out_flush();
9418 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9419}
9420
9421#endif /* FEAT_CMDL_COMPL */
9422
9423#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9424 || defined(FEAT_SIGNS) || defined(PROTO)
9425/*
9426 * Function given to ExpandGeneric() to obtain the list of group names.
9427 * Also used for synIDattr() function.
9428 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009429 char_u *
9430get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009431 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009432 int idx;
9433{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009434#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009435 if (idx == highlight_ga.ga_len && include_none != 0)
9436 return (char_u *)"none";
9437 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009438 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009439 if (idx == highlight_ga.ga_len + include_none + include_default
9440 && include_link != 0)
9441 return (char_u *)"link";
9442 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9443 && include_link != 0)
9444 return (char_u *)"clear";
9445#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009446 if (idx < 0 || idx >= highlight_ga.ga_len)
9447 return NULL;
9448 return HL_TABLE()[idx].sg_name;
9449}
9450#endif
9451
Bram Moolenaar4f688582007-07-24 12:34:30 +00009452#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009453/*
9454 * Free all the highlight group fonts.
9455 * Used when quitting for systems which need it.
9456 */
9457 void
9458free_highlight_fonts()
9459{
9460 int idx;
9461
9462 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9463 {
9464 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9465 HL_TABLE()[idx].sg_font = NOFONT;
9466# ifdef FEAT_XFONTSET
9467 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9468 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9469# endif
9470 }
9471
9472 gui_mch_free_font(gui.norm_font);
9473# ifdef FEAT_XFONTSET
9474 gui_mch_free_fontset(gui.fontset);
9475# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009476# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009477 gui_mch_free_font(gui.bold_font);
9478 gui_mch_free_font(gui.ital_font);
9479 gui_mch_free_font(gui.boldital_font);
9480# endif
9481}
9482#endif
9483
9484/**************************************
9485 * End of Highlighting stuff *
9486 **************************************/