blob: 369311fb74bad22eb4dc742ded3b747e1f5e8a04 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
36#ifdef FEAT_GUI
37/* for when using the GUI */
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
71#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000072/* Flags to indicate an additional string for highlight name completion. */
73static int include_none = 0; /* when 1 include "None" */
74static int include_default = 0; /* when 1 include "default" */
75static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#endif
77
78/*
79 * The "term", "cterm" and "gui" arguments can be any combination of the
80 * following names, separated by commas (but no spaces!).
81 */
82static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000083 {"bold", "standout", "underline", "undercurl",
84 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000085static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000086 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087
88static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
89static void syn_unadd_group __ARGS((void));
90static void set_hl_attr __ARGS((int idx));
91static void highlight_list_one __ARGS((int id));
92static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
93static int syn_add_group __ARGS((char_u *name));
94static int syn_list_header __ARGS((int did_header, int outlen, int id));
95static int hl_has_settings __ARGS((int idx, int check_link));
96static void highlight_clear __ARGS((int idx));
97
98#ifdef FEAT_GUI
99static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
100static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
101static guicolor_T color_name2handle __ARGS((char_u *name));
102static GuiFont font_name2handle __ARGS((char_u *name));
103# ifdef FEAT_XFONTSET
104static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
105# endif
106static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
107#endif
108
109/*
110 * An attribute number is the index in attr_table plus ATTR_OFF.
111 */
112#define ATTR_OFF (HL_ALL + 1)
113
114#if defined(FEAT_SYN_HL) || defined(PROTO)
115
116#define SYN_NAMELEN 50 /* maximum length of a syntax name */
117
118/* different types of offsets that are possible */
119#define SPO_MS_OFF 0 /* match start offset */
120#define SPO_ME_OFF 1 /* match end offset */
121#define SPO_HS_OFF 2 /* highl. start offset */
122#define SPO_HE_OFF 3 /* highl. end offset */
123#define SPO_RS_OFF 4 /* region start offset */
124#define SPO_RE_OFF 5 /* region end offset */
125#define SPO_LC_OFF 6 /* leading context offset */
126#define SPO_COUNT 7
127
128static char *(spo_name_tab[SPO_COUNT]) =
129 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
130
131/*
132 * The patterns that are being searched for are stored in a syn_pattern.
133 * A match item consists of one pattern.
134 * A start/end item consists of n start patterns and m end patterns.
135 * A start/skip/end item consists of n start patterns, one skip pattern and m
136 * end patterns.
137 * For the latter two, the patterns are always consecutive: start-skip-end.
138 *
139 * A character offset can be given for the matched text (_m_start and _m_end)
140 * and for the actually highlighted text (_h_start and _h_end).
141 */
142typedef struct syn_pattern
143{
144 char sp_type; /* see SPTYPE_ defines below */
145 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200146 int sp_flags; /* see HL_ defines below */
147#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200148 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200149#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 struct sp_syn sp_syn; /* struct passed to in_id_list() */
151 short sp_syn_match_id; /* highlight group ID of pattern */
152 char_u *sp_pattern; /* regexp to match, pattern */
153 regprog_T *sp_prog; /* regexp to match, program */
154 int sp_ic; /* ignore-case flag for sp_prog */
155 short sp_off_flags; /* see below */
156 int sp_offsets[SPO_COUNT]; /* offsets */
157 short *sp_cont_list; /* cont. group IDs, if non-zero */
158 short *sp_next_list; /* next group IDs, if non-zero */
159 int sp_sync_idx; /* sync item index (syncing only) */
160 int sp_line_id; /* ID of last line where tried */
161 int sp_startcol; /* next match in sp_line_id line */
162} synpat_T;
163
164/* The sp_off_flags are computed like this:
165 * offset from the start of the matched text: (1 << SPO_XX_OFF)
166 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
167 * When both are present, only one is used.
168 */
169
170#define SPTYPE_MATCH 1 /* match keyword with this group ID */
171#define SPTYPE_START 2 /* match a regexp, start of item */
172#define SPTYPE_END 3 /* match a regexp, end of item */
173#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
174
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175
176#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
177
178#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
179
180/*
181 * Flags for b_syn_sync_flags:
182 */
183#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
184#define SF_MATCH 0x02 /* sync by matching a pattern */
185
186#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
187
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188#define MAXKEYWLEN 80 /* maximum length of a keyword */
189
190/*
191 * The attributes of the syntax item that has been recognized.
192 */
193static int current_attr = 0; /* attr of current syntax word */
194#ifdef FEAT_EVAL
195static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000196static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200198#ifdef FEAT_CONCEAL
199static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200200static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200201static int current_sub_char = 0;
202#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000203
Bram Moolenaar217ad922005-03-20 22:37:15 +0000204typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205{
206 char_u *scl_name; /* syntax cluster name */
207 char_u *scl_name_u; /* uppercase of scl_name */
208 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000209} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210
211/*
212 * Methods of combining two clusters
213 */
214#define CLUSTER_REPLACE 1 /* replace first list with second */
215#define CLUSTER_ADD 2 /* add second list to first */
216#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
217
Bram Moolenaar217ad922005-03-20 22:37:15 +0000218#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219
220/*
221 * Syntax group IDs have different types:
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
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200290 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200291 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200292#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000293 short *si_cont_list; /* list of contained groups */
294 short *si_next_list; /* nextgroup IDs after this item ends */
295 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
296 * pattern */
297} stateitem_T;
298
299#define KEYWORD_IDX -1 /* value of si_idx for keywords */
300#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
301 but contained groups */
302
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200303#ifdef FEAT_CONCEAL
304static int next_seqnr = 0; /* value to use for si_seqnr */
305#endif
306
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000308 * Struct to reduce the number of arguments to get_syn_options(), it's used
309 * very often.
310 */
311typedef struct
312{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000313 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000314 int keyword; /* TRUE for ":syn keyword" */
315 int *sync_idx; /* syntax item for "grouphere" argument, NULL
316 if not allowed */
317 char has_cont_list; /* TRUE if "cont_list" can be used */
318 short *cont_list; /* group IDs for "contains" argument */
319 short *cont_in_list; /* group IDs for "containedin" argument */
320 short *next_list; /* group IDs for "nextgroup" argument */
321} syn_opt_arg_T;
322
323/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 * The next possible match in the current line for any pattern is remembered,
325 * to avoid having to try for a match in each column.
326 * If next_match_idx == -1, not tried (in this line) yet.
327 * If next_match_col == MAXCOL, no match found in this line.
328 * (All end positions have the column of the char after the end)
329 */
330static int next_match_col; /* column for start of next match */
331static lpos_T next_match_m_endpos; /* position for end of next match */
332static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
333static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
334static int next_match_idx; /* index of matched item */
335static long next_match_flags; /* flags for next match */
336static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
337static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
338static int next_match_end_idx; /* ID of group for end pattn or zero */
339static reg_extmatch_T *next_match_extmatch = NULL;
340
341/*
342 * A state stack is an array of integers or stateitem_T, stored in a
343 * garray_T. A state stack is invalid if it's itemsize entry is zero.
344 */
345#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
346#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
347
348/*
349 * The current state (within the line) of the recognition engine.
350 * When current_state.ga_itemsize is 0 the current state is invalid.
351 */
352static win_T *syn_win; /* current window for highlighting */
353static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200354static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355static linenr_T current_lnum = 0; /* lnum of current state */
356static colnr_T current_col = 0; /* column of current state */
357static int current_state_stored = 0; /* TRUE if stored current state
358 * after setting current_finished */
359static int current_finished = 0; /* current line has been finished */
360static garray_T current_state /* current stack of state_items */
361 = {0, 0, 0, 0, NULL};
362static short *current_next_list = NULL; /* when non-zero, nextgroup list */
363static int current_next_flags = 0; /* flags for current_next_list */
364static int current_line_id = 0; /* unique number for current line */
365
366#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
367
368static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
369static int syn_match_linecont __ARGS((linenr_T lnum));
370static void syn_start_line __ARGS((void));
371static void syn_update_ends __ARGS((int startofline));
372static void syn_stack_alloc __ARGS((void));
373static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200374static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000376static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377static void load_current_state __ARGS((synstate_T *from));
378static void invalidate_current_state __ARGS((void));
379static int syn_stack_equal __ARGS((synstate_T *sp));
380static void validate_current_state __ARGS((void));
381static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000382static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383static int did_match_already __ARGS((int idx, garray_T *gap));
384static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
385static void check_state_ends __ARGS((void));
386static void update_si_attr __ARGS((int idx));
387static void check_keepend __ARGS((void));
388static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
389static short *copy_id_list __ARGS((short *list));
390static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
391static int push_current_state __ARGS((int idx));
392static void pop_current_state __ARGS((void));
393
Bram Moolenaar860cae12010-06-05 23:22:07 +0200394static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395static void find_endpos __ARGS((int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext));
396static void clear_syn_state __ARGS((synstate_T *p));
397static void clear_current_state __ARGS((void));
398
399static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
400static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
401static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
402static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
403static char_u *syn_getcurline __ARGS((void));
404static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200405static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000407static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200409static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
410static void syn_clear_pattern __ARGS((synblock_T *block, int i));
411static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200413static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414static void syn_clear_one __ARGS((int id, int syncing));
415static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
416static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
417static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
418static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
419static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
420static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
421static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
422static void syn_lines_msg __ARGS((void));
423static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200424static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425static void syn_list_one __ARGS((int id, int syncing, int link_only));
426static void syn_list_cluster __ARGS((int id));
427static void put_id_list __ARGS((char_u *name, short *list, int attr));
428static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000429static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
430static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
431static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200432static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000433static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200434static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000435static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
436static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
437static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
438static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
439#ifdef __BORLANDC__
440static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
441#else
442static int syn_compare_stub __ARGS((const void *v1, const void *v2));
443#endif
444static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
445static int syn_scl_name2id __ARGS((char_u *name));
446static int syn_scl_namen2id __ARGS((char_u *linep, int len));
447static int syn_check_cluster __ARGS((char_u *pp, int len));
448static int syn_add_cluster __ARGS((char_u *name));
449static void init_syn_patterns __ARGS((void));
450static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
451static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
452static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
453static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
454static void syn_incl_toplevel __ARGS((int id, int *flagsp));
455
456/*
457 * Start the syntax recognition for a line. This function is normally called
458 * from the screen updating, once for each displayed line.
459 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
460 * it. Careful: curbuf and curwin are likely to point to another buffer and
461 * window.
462 */
463 void
464syntax_start(wp, lnum)
465 win_T *wp;
466 linenr_T lnum;
467{
468 synstate_T *p;
469 synstate_T *last_valid = NULL;
470 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000471 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 linenr_T parsed_lnum;
473 linenr_T first_stored;
474 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000475 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200477#ifdef FEAT_CONCEAL
478 current_sub_char = NUL;
479#endif
480
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 /*
482 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000483 * Also do this when a change was made, the current state may be invalid
484 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000487 {
488 invalidate_current_state();
489 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200490 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000491 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000492 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 syn_win = wp;
494
495 /*
496 * Allocate syntax stack when needed.
497 */
498 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200499 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000500 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200501 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502
503 /*
504 * If the state of the end of the previous line is useful, store it.
505 */
506 if (VALID_STATE(&current_state)
507 && current_lnum < lnum
508 && current_lnum < syn_buf->b_ml.ml_line_count)
509 {
510 (void)syn_finish_line(FALSE);
511 if (!current_state_stored)
512 {
513 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000514 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000515 }
516
517 /*
518 * If the current_lnum is now the same as "lnum", keep the current
519 * state (this happens very often!). Otherwise invalidate
520 * current_state and figure it out below.
521 */
522 if (current_lnum != lnum)
523 invalidate_current_state();
524 }
525 else
526 invalidate_current_state();
527
528 /*
529 * Try to synchronize from a saved state in b_sst_array[].
530 * Only do this if lnum is not before and not to far beyond a saved state.
531 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 {
534 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200535 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000536 {
537 if (p->sst_lnum > lnum)
538 break;
539 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
540 {
541 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200542 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000543 last_min_valid = p;
544 }
545 }
546 if (last_min_valid != NULL)
547 load_current_state(last_min_valid);
548 }
549
550 /*
551 * If "lnum" is before or far beyond a line with a saved state, need to
552 * re-synchronize.
553 */
554 if (INVALID_STATE(&current_state))
555 {
556 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200557 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 }
559 else
560 first_stored = current_lnum;
561
562 /*
563 * Advance from the sync point or saved state until the current line.
564 * Save some entries for syncing with later on.
565 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200566 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000567 dist = 999999;
568 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200569 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570 while (current_lnum < lnum)
571 {
572 syn_start_line();
573 (void)syn_finish_line(FALSE);
574 ++current_lnum;
575
576 /* If we parsed at least "minlines" lines or started at a valid
577 * state, the current state is considered valid. */
578 if (current_lnum >= first_stored)
579 {
580 /* Check if the saved state entry is for the current line and is
581 * equal to the current state. If so, then validate all saved
582 * states that depended on a change before the parsed line. */
583 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000584 prev = syn_stack_find_entry(current_lnum - 1);
585 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200586 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000588 sp = prev;
589 while (sp != NULL && sp->sst_lnum < current_lnum)
590 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000591 if (sp != NULL
592 && sp->sst_lnum == current_lnum
593 && syn_stack_equal(sp))
594 {
595 parsed_lnum = current_lnum;
596 prev = sp;
597 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
598 {
599 if (sp->sst_lnum <= lnum)
600 /* valid state before desired line, use this one */
601 prev = sp;
602 else if (sp->sst_change_lnum == 0)
603 /* past saved states depending on change, break here. */
604 break;
605 sp->sst_change_lnum = 0;
606 sp = sp->sst_next;
607 }
608 load_current_state(prev);
609 }
610 /* Store the state at this line when it's the first one, the line
611 * where we start parsing, or some distance from the previously
612 * saved state. But only when parsed at least 'minlines'. */
613 else if (prev == NULL
614 || current_lnum == lnum
615 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000616 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 }
618
619 /* This can take a long time: break when CTRL-C pressed. The current
620 * state will be wrong then. */
621 line_breakcheck();
622 if (got_int)
623 {
624 current_lnum = lnum;
625 break;
626 }
627 }
628
629 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630}
631
632/*
633 * We cannot simply discard growarrays full of state_items or buf_states; we
634 * have to manually release their extmatch pointers first.
635 */
636 static void
637clear_syn_state(p)
638 synstate_T *p;
639{
640 int i;
641 garray_T *gap;
642
643 if (p->sst_stacksize > SST_FIX_STATES)
644 {
645 gap = &(p->sst_union.sst_ga);
646 for (i = 0; i < gap->ga_len; i++)
647 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
648 ga_clear(gap);
649 }
650 else
651 {
652 for (i = 0; i < p->sst_stacksize; i++)
653 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
654 }
655}
656
657/*
658 * Cleanup the current_state stack.
659 */
660 static void
661clear_current_state()
662{
663 int i;
664 stateitem_T *sip;
665
666 sip = (stateitem_T *)(current_state.ga_data);
667 for (i = 0; i < current_state.ga_len; i++)
668 unref_extmatch(sip[i].si_extmatch);
669 ga_clear(&current_state);
670}
671
672/*
673 * Try to find a synchronisation point for line "lnum".
674 *
675 * This sets current_lnum and the current state. One of three methods is
676 * used:
677 * 1. Search backwards for the end of a C-comment.
678 * 2. Search backwards for given sync patterns.
679 * 3. Simply start on a given number of lines above "lnum".
680 */
681 static void
682syn_sync(wp, start_lnum, last_valid)
683 win_T *wp;
684 linenr_T start_lnum;
685 synstate_T *last_valid;
686{
687 buf_T *curbuf_save;
688 win_T *curwin_save;
689 pos_T cursor_save;
690 int idx;
691 linenr_T lnum;
692 linenr_T end_lnum;
693 linenr_T break_lnum;
694 int had_sync_point;
695 stateitem_T *cur_si;
696 synpat_T *spp;
697 char_u *line;
698 int found_flags = 0;
699 int found_match_idx = 0;
700 linenr_T found_current_lnum = 0;
701 int found_current_col= 0;
702 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000703 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000704
705 /*
706 * Clear any current state that might be hanging around.
707 */
708 invalidate_current_state();
709
710 /*
711 * Start at least "minlines" back. Default starting point for parsing is
712 * there.
713 * Start further back, to avoid that scrolling backwards will result in
714 * resyncing for every line. Now it resyncs only one out of N lines,
715 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
716 * Watch out for overflow when minlines is MAXLNUM.
717 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200718 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 start_lnum = 1;
720 else
721 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200722 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200724 else if (syn_block->b_syn_sync_minlines < 10)
725 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200727 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
728 if (syn_block->b_syn_sync_maxlines != 0
729 && lnum > syn_block->b_syn_sync_maxlines)
730 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 if (lnum >= start_lnum)
732 start_lnum = 1;
733 else
734 start_lnum -= lnum;
735 }
736 current_lnum = start_lnum;
737
738 /*
739 * 1. Search backwards for the end of a C-style comment.
740 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200741 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
743 /* Need to make syn_buf the current buffer for a moment, to be able to
744 * use find_start_comment(). */
745 curwin_save = curwin;
746 curwin = wp;
747 curbuf_save = curbuf;
748 curbuf = syn_buf;
749
750 /*
751 * Skip lines that end in a backslash.
752 */
753 for ( ; start_lnum > 1; --start_lnum)
754 {
755 line = ml_get(start_lnum - 1);
756 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
757 break;
758 }
759 current_lnum = start_lnum;
760
761 /* set cursor to start of search */
762 cursor_save = wp->w_cursor;
763 wp->w_cursor.lnum = start_lnum;
764 wp->w_cursor.col = 0;
765
766 /*
767 * If the line is inside a comment, need to find the syntax item that
768 * defines the comment.
769 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
770 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200771 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000772 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200773 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
774 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
775 == syn_block->b_syn_sync_id
776 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 {
778 validate_current_state();
779 if (push_current_state(idx) == OK)
780 update_si_attr(current_state.ga_len - 1);
781 break;
782 }
783 }
784
785 /* restore cursor and buffer */
786 wp->w_cursor = cursor_save;
787 curwin = curwin_save;
788 curbuf = curbuf_save;
789 }
790
791 /*
792 * 2. Search backwards for given sync patterns.
793 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200794 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000795 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200796 if (syn_block->b_syn_sync_maxlines != 0
797 && start_lnum > syn_block->b_syn_sync_maxlines)
798 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 else
800 break_lnum = 0;
801
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000802 found_m_endpos.lnum = 0;
803 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 end_lnum = start_lnum;
805 lnum = start_lnum;
806 while (--lnum > break_lnum)
807 {
808 /* This can take a long time: break when CTRL-C pressed. */
809 line_breakcheck();
810 if (got_int)
811 {
812 invalidate_current_state();
813 current_lnum = start_lnum;
814 break;
815 }
816
817 /* Check if we have run into a valid saved state stack now. */
818 if (last_valid != NULL && lnum == last_valid->sst_lnum)
819 {
820 load_current_state(last_valid);
821 break;
822 }
823
824 /*
825 * Check if the previous line has the line-continuation pattern.
826 */
827 if (lnum > 1 && syn_match_linecont(lnum - 1))
828 continue;
829
830 /*
831 * Start with nothing on the state stack
832 */
833 validate_current_state();
834
835 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
836 {
837 syn_start_line();
838 for (;;)
839 {
840 had_sync_point = syn_finish_line(TRUE);
841 /*
842 * When a sync point has been found, remember where, and
843 * continue to look for another one, further on in the line.
844 */
845 if (had_sync_point && current_state.ga_len)
846 {
847 cur_si = &CUR_STATE(current_state.ga_len - 1);
848 if (cur_si->si_m_endpos.lnum > start_lnum)
849 {
850 /* ignore match that goes to after where started */
851 current_lnum = end_lnum;
852 break;
853 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000854 if (cur_si->si_idx < 0)
855 {
856 /* Cannot happen? */
857 found_flags = 0;
858 found_match_idx = KEYWORD_IDX;
859 }
860 else
861 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200862 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000863 found_flags = spp->sp_flags;
864 found_match_idx = spp->sp_sync_idx;
865 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 found_current_lnum = current_lnum;
867 found_current_col = current_col;
868 found_m_endpos = cur_si->si_m_endpos;
869 /*
870 * Continue after the match (be aware of a zero-length
871 * match).
872 */
873 if (found_m_endpos.lnum > current_lnum)
874 {
875 current_lnum = found_m_endpos.lnum;
876 current_col = found_m_endpos.col;
877 if (current_lnum >= end_lnum)
878 break;
879 }
880 else if (found_m_endpos.col > current_col)
881 current_col = found_m_endpos.col;
882 else
883 ++current_col;
884
885 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000886 * an item that ends here, need to do that now. Be
887 * careful not to go past the NUL. */
888 prev_current_col = current_col;
889 if (syn_getcurline()[current_col] != NUL)
890 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000892 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000893 }
894 else
895 break;
896 }
897 }
898
899 /*
900 * If a sync point was encountered, break here.
901 */
902 if (found_flags)
903 {
904 /*
905 * Put the item that was specified by the sync point on the
906 * state stack. If there was no item specified, make the
907 * state stack empty.
908 */
909 clear_current_state();
910 if (found_match_idx >= 0
911 && push_current_state(found_match_idx) == OK)
912 update_si_attr(current_state.ga_len - 1);
913
914 /*
915 * When using "grouphere", continue from the sync point
916 * match, until the end of the line. Parsing starts at
917 * the next line.
918 * For "groupthere" the parsing starts at start_lnum.
919 */
920 if (found_flags & HL_SYNC_HERE)
921 {
922 if (current_state.ga_len)
923 {
924 cur_si = &CUR_STATE(current_state.ga_len - 1);
925 cur_si->si_h_startpos.lnum = found_current_lnum;
926 cur_si->si_h_startpos.col = found_current_col;
927 update_si_end(cur_si, (int)current_col, TRUE);
928 check_keepend();
929 }
930 current_col = found_m_endpos.col;
931 current_lnum = found_m_endpos.lnum;
932 (void)syn_finish_line(FALSE);
933 ++current_lnum;
934 }
935 else
936 current_lnum = start_lnum;
937
938 break;
939 }
940
941 end_lnum = lnum;
942 invalidate_current_state();
943 }
944
945 /* Ran into start of the file or exceeded maximum number of lines */
946 if (lnum <= break_lnum)
947 {
948 invalidate_current_state();
949 current_lnum = break_lnum + 1;
950 }
951 }
952
953 validate_current_state();
954}
955
956/*
957 * Return TRUE if the line-continuation pattern matches in line "lnum".
958 */
959 static int
960syn_match_linecont(lnum)
961 linenr_T lnum;
962{
963 regmmatch_T regmatch;
964
Bram Moolenaar860cae12010-06-05 23:22:07 +0200965 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200967 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
968 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 return syn_regexec(&regmatch, lnum, (colnr_T)0);
970 }
971 return FALSE;
972}
973
974/*
975 * Prepare the current state for the start of a line.
976 */
977 static void
978syn_start_line()
979{
980 current_finished = FALSE;
981 current_col = 0;
982
983 /*
984 * Need to update the end of a start/skip/end that continues from the
985 * previous line and regions that have "keepend".
986 */
987 if (current_state.ga_len > 0)
988 syn_update_ends(TRUE);
989
990 next_match_idx = -1;
991 ++current_line_id;
992}
993
994/*
995 * Check for items in the stack that need their end updated.
996 * When "startofline" is TRUE the last item is always updated.
997 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
998 */
999 static void
1000syn_update_ends(startofline)
1001 int startofline;
1002{
1003 stateitem_T *cur_si;
1004 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001005 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006
1007 if (startofline)
1008 {
1009 /* Check for a match carried over from a previous line with a
1010 * contained region. The match ends as soon as the region ends. */
1011 for (i = 0; i < current_state.ga_len; ++i)
1012 {
1013 cur_si = &CUR_STATE(i);
1014 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001015 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016 == SPTYPE_MATCH
1017 && cur_si->si_m_endpos.lnum < current_lnum)
1018 {
1019 cur_si->si_flags |= HL_MATCHCONT;
1020 cur_si->si_m_endpos.lnum = 0;
1021 cur_si->si_m_endpos.col = 0;
1022 cur_si->si_h_endpos = cur_si->si_m_endpos;
1023 cur_si->si_ends = TRUE;
1024 }
1025 }
1026 }
1027
1028 /*
1029 * Need to update the end of a start/skip/end that continues from the
1030 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001031 * influence contained items. If we've just removed "extend"
1032 * (startofline == 0) then we should update ends of normal regions
1033 * contained inside "keepend" because "extend" could have extended
1034 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001035 * Then check for items ending in column 0.
1036 */
1037 i = current_state.ga_len - 1;
1038 if (keepend_level >= 0)
1039 for ( ; i > keepend_level; --i)
1040 if (CUR_STATE(i).si_flags & HL_EXTEND)
1041 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001042
1043 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044 for ( ; i < current_state.ga_len; ++i)
1045 {
1046 cur_si = &CUR_STATE(i);
1047 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001048 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 || (i == current_state.ga_len - 1 && startofline))
1050 {
1051 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1052 cur_si->si_h_startpos.lnum = current_lnum;
1053
1054 if (!(cur_si->si_flags & HL_MATCHCONT))
1055 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001056
1057 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1058 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059 }
1060 }
1061 check_keepend();
1062 check_state_ends();
1063}
1064
1065/****************************************
1066 * Handling of the state stack cache.
1067 */
1068
1069/*
1070 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1071 *
1072 * To speed up syntax highlighting, the state stack for the start of some
1073 * lines is cached. These entries can be used to start parsing at that point.
1074 *
1075 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1076 * valid entries. b_sst_first points to the first one, then follow sst_next.
1077 * The entries are sorted on line number. The first entry is often for line 2
1078 * (line 1 always starts with an empty stack).
1079 * There is also a list for free entries. This construction is used to avoid
1080 * having to allocate and free memory blocks too often.
1081 *
1082 * When making changes to the buffer, this is logged in b_mod_*. When calling
1083 * update_screen() to update the display, it will call
1084 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1085 * entries. The entries which are inside the changed area are removed,
1086 * because they must be recomputed. Entries below the changed have their line
1087 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1088 * set to indicate that a check must be made if the changed lines would change
1089 * the cached entry.
1090 *
1091 * When later displaying lines, an entry is stored for each line. Displayed
1092 * lines are likely to be displayed again, in which case the state at the
1093 * start of the line is needed.
1094 * For not displayed lines, an entry is stored for every so many lines. These
1095 * entries will be used e.g., when scrolling backwards. The distance between
1096 * entries depends on the number of lines in the buffer. For small buffers
1097 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1098 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1099 */
1100
Bram Moolenaar860cae12010-06-05 23:22:07 +02001101 static void
1102syn_stack_free_block(block)
1103 synblock_T *block;
1104{
1105 synstate_T *p;
1106
1107 if (block->b_sst_array != NULL)
1108 {
1109 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1110 clear_syn_state(p);
1111 vim_free(block->b_sst_array);
1112 block->b_sst_array = NULL;
1113 block->b_sst_len = 0;
1114 }
1115}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116/*
1117 * Free b_sst_array[] for buffer "buf".
1118 * Used when syntax items changed to force resyncing everywhere.
1119 */
1120 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001121syn_stack_free_all(block)
1122 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 win_T *wp;
1125
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126 syn_stack_free_block(block);
1127
1128
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129#ifdef FEAT_FOLDING
1130 /* When using "syntax" fold method, must update all folds. */
1131 FOR_ALL_WINDOWS(wp)
1132 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001133 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 foldUpdateAll(wp);
1135 }
1136#endif
1137}
1138
1139/*
1140 * Allocate the syntax state stack for syn_buf when needed.
1141 * If the number of entries in b_sst_array[] is much too big or a bit too
1142 * small, reallocate it.
1143 * Also used to allocate b_sst_array[] for the first time.
1144 */
1145 static void
1146syn_stack_alloc()
1147{
1148 long len;
1149 synstate_T *to, *from;
1150 synstate_T *sstp;
1151
1152 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1153 if (len < SST_MIN_ENTRIES)
1154 len = SST_MIN_ENTRIES;
1155 else if (len > SST_MAX_ENTRIES)
1156 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001157 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 {
1159 /* Allocate 50% too much, to avoid reallocating too often. */
1160 len = syn_buf->b_ml.ml_line_count;
1161 len = (len + len / 2) / SST_DIST + Rows * 2;
1162 if (len < SST_MIN_ENTRIES)
1163 len = SST_MIN_ENTRIES;
1164 else if (len > SST_MAX_ENTRIES)
1165 len = SST_MAX_ENTRIES;
1166
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 {
1169 /* When shrinking the array, cleanup the existing stack.
1170 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001171 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 && syn_stack_cleanup())
1173 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1175 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 }
1177
1178 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1179 if (sstp == NULL) /* out of memory! */
1180 return;
1181
1182 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001183 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001184 {
1185 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001186 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 from = from->sst_next)
1188 {
1189 ++to;
1190 *to = *from;
1191 to->sst_next = to + 1;
1192 }
1193 }
1194 if (to != sstp - 1)
1195 {
1196 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 syn_block->b_sst_first = sstp;
1198 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 }
1200 else
1201 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 syn_block->b_sst_first = NULL;
1203 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 }
1205
1206 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001208 while (++to < sstp + len)
1209 to->sst_next = to + 1;
1210 (sstp + len - 1)->sst_next = NULL;
1211
Bram Moolenaar860cae12010-06-05 23:22:07 +02001212 vim_free(syn_block->b_sst_array);
1213 syn_block->b_sst_array = sstp;
1214 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 }
1216}
1217
1218/*
1219 * Check for changes in a buffer to affect stored syntax states. Uses the
1220 * b_mod_* fields.
1221 * Called from update_screen(), before screen is being updated, once for each
1222 * displayed buffer.
1223 */
1224 void
1225syn_stack_apply_changes(buf)
1226 buf_T *buf;
1227{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001228 win_T *wp;
1229
1230 syn_stack_apply_changes_block(&buf->b_s, buf);
1231
1232 FOR_ALL_WINDOWS(wp)
1233 {
1234 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1235 syn_stack_apply_changes_block(wp->w_s, buf);
1236 }
1237}
1238
1239 static void
1240syn_stack_apply_changes_block(block, buf)
1241 synblock_T *block;
1242 buf_T *buf;
1243{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 synstate_T *p, *prev, *np;
1245 linenr_T n;
1246
Bram Moolenaar860cae12010-06-05 23:22:07 +02001247 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 return;
1249
1250 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001253 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254 {
1255 n = p->sst_lnum + buf->b_mod_xlines;
1256 if (n <= buf->b_mod_bot)
1257 {
1258 /* this state is inside the changed area, remove it */
1259 np = p->sst_next;
1260 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001261 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001262 else
1263 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 p = np;
1266 continue;
1267 }
1268 /* This state is below the changed area. Remember the line
1269 * that needs to be parsed before this entry can be made valid
1270 * again. */
1271 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1272 {
1273 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1274 p->sst_change_lnum += buf->b_mod_xlines;
1275 else
1276 p->sst_change_lnum = buf->b_mod_top;
1277 }
1278 if (p->sst_change_lnum == 0
1279 || p->sst_change_lnum < buf->b_mod_bot)
1280 p->sst_change_lnum = buf->b_mod_bot;
1281
1282 p->sst_lnum = n;
1283 }
1284 prev = p;
1285 p = p->sst_next;
1286 }
1287}
1288
1289/*
1290 * Reduce the number of entries in the state stack for syn_buf.
1291 * Returns TRUE if at least one entry was freed.
1292 */
1293 static int
1294syn_stack_cleanup()
1295{
1296 synstate_T *p, *prev;
1297 disptick_T tick;
1298 int above;
1299 int dist;
1300 int retval = FALSE;
1301
Bram Moolenaar860cae12010-06-05 23:22:07 +02001302 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 return retval;
1304
1305 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001307 dist = 999999;
1308 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310
1311 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001312 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 * be removed. Set "above" when the "tick" for the oldest entry is above
1314 * "b_sst_lasttick" (the display tick wraps around).
1315 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001318 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001319 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1320 {
1321 if (prev->sst_lnum + dist > p->sst_lnum)
1322 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 {
1325 if (!above || p->sst_tick < tick)
1326 tick = p->sst_tick;
1327 above = TRUE;
1328 }
1329 else if (!above && p->sst_tick < tick)
1330 tick = p->sst_tick;
1331 }
1332 }
1333
1334 /*
1335 * Go through the list to make the entries for the oldest tick at an
1336 * interval of several lines.
1337 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001338 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001339 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1340 {
1341 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1342 {
1343 /* Move this entry from used list to free list */
1344 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001345 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 p = prev;
1347 retval = TRUE;
1348 }
1349 }
1350 return retval;
1351}
1352
1353/*
1354 * Free the allocated memory for a syn_state item.
1355 * Move the entry into the free list.
1356 */
1357 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358syn_stack_free_entry(block, p)
1359 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001360 synstate_T *p;
1361{
1362 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001363 p->sst_next = block->b_sst_firstfree;
1364 block->b_sst_firstfree = p;
1365 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366}
1367
1368/*
1369 * Find an entry in the list of state stacks at or before "lnum".
1370 * Returns NULL when there is no entry or the first entry is after "lnum".
1371 */
1372 static synstate_T *
1373syn_stack_find_entry(lnum)
1374 linenr_T lnum;
1375{
1376 synstate_T *p, *prev;
1377
1378 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 {
1381 if (p->sst_lnum == lnum)
1382 return p;
1383 if (p->sst_lnum > lnum)
1384 break;
1385 }
1386 return prev;
1387}
1388
1389/*
1390 * Try saving the current state in b_sst_array[].
1391 * The current state must be valid for the start of the current_lnum line!
1392 */
1393 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001394store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001395{
1396 int i;
1397 synstate_T *p;
1398 bufstate_T *bp;
1399 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001400 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001401
1402 /*
1403 * If the current state contains a start or end pattern that continues
1404 * from the previous line, we can't use it. Don't store it then.
1405 */
1406 for (i = current_state.ga_len - 1; i >= 0; --i)
1407 {
1408 cur_si = &CUR_STATE(i);
1409 if (cur_si->si_h_startpos.lnum >= current_lnum
1410 || cur_si->si_m_endpos.lnum >= current_lnum
1411 || cur_si->si_h_endpos.lnum >= current_lnum
1412 || (cur_si->si_end_idx
1413 && cur_si->si_eoe_pos.lnum >= current_lnum))
1414 break;
1415 }
1416 if (i >= 0)
1417 {
1418 if (sp != NULL)
1419 {
1420 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001421 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 else
1425 {
1426 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001427 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 if (p->sst_next == sp)
1429 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001430 if (p != NULL) /* just in case */
1431 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001433 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 sp = NULL;
1435 }
1436 }
1437 else if (sp == NULL || sp->sst_lnum != current_lnum)
1438 {
1439 /*
1440 * Add a new entry
1441 */
1442 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001443 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 {
1445 (void)syn_stack_cleanup();
1446 /* "sp" may have been moved to the freelist now */
1447 sp = syn_stack_find_entry(current_lnum);
1448 }
1449 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001450 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 sp = NULL;
1452 else
1453 {
1454 /* Take the first item from the free list and put it in the used
1455 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001456 p = syn_block->b_sst_firstfree;
1457 syn_block->b_sst_firstfree = p->sst_next;
1458 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 if (sp == NULL)
1460 {
1461 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001462 p->sst_next = syn_block->b_sst_first;
1463 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 }
1465 else
1466 {
1467 /* insert in list after *sp */
1468 p->sst_next = sp->sst_next;
1469 sp->sst_next = p;
1470 }
1471 sp = p;
1472 sp->sst_stacksize = 0;
1473 sp->sst_lnum = current_lnum;
1474 }
1475 }
1476 if (sp != NULL)
1477 {
1478 /* When overwriting an existing state stack, clear it first */
1479 clear_syn_state(sp);
1480 sp->sst_stacksize = current_state.ga_len;
1481 if (current_state.ga_len > SST_FIX_STATES)
1482 {
1483 /* Need to clear it, might be something remaining from when the
1484 * length was less than SST_FIX_STATES. */
1485 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1486 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1487 sp->sst_stacksize = 0;
1488 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001490 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1491 }
1492 else
1493 bp = sp->sst_union.sst_stack;
1494 for (i = 0; i < sp->sst_stacksize; ++i)
1495 {
1496 bp[i].bs_idx = CUR_STATE(i).si_idx;
1497 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001498#ifdef FEAT_CONCEAL
1499 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1500 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1501#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1503 }
1504 sp->sst_next_flags = current_next_flags;
1505 sp->sst_next_list = current_next_list;
1506 sp->sst_tick = display_tick;
1507 sp->sst_change_lnum = 0;
1508 }
1509 current_state_stored = TRUE;
1510 return sp;
1511}
1512
1513/*
1514 * Copy a state stack from "from" in b_sst_array[] to current_state;
1515 */
1516 static void
1517load_current_state(from)
1518 synstate_T *from;
1519{
1520 int i;
1521 bufstate_T *bp;
1522
1523 clear_current_state();
1524 validate_current_state();
1525 keepend_level = -1;
1526 if (from->sst_stacksize
1527 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1528 {
1529 if (from->sst_stacksize > SST_FIX_STATES)
1530 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1531 else
1532 bp = from->sst_union.sst_stack;
1533 for (i = 0; i < from->sst_stacksize; ++i)
1534 {
1535 CUR_STATE(i).si_idx = bp[i].bs_idx;
1536 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001537#ifdef FEAT_CONCEAL
1538 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1539 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1540#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001541 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1542 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1543 keepend_level = i;
1544 CUR_STATE(i).si_ends = FALSE;
1545 CUR_STATE(i).si_m_lnum = 0;
1546 if (CUR_STATE(i).si_idx >= 0)
1547 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001548 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 else
1550 CUR_STATE(i).si_next_list = NULL;
1551 update_si_attr(i);
1552 }
1553 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 }
1555 current_next_list = from->sst_next_list;
1556 current_next_flags = from->sst_next_flags;
1557 current_lnum = from->sst_lnum;
1558}
1559
1560/*
1561 * Compare saved state stack "*sp" with the current state.
1562 * Return TRUE when they are equal.
1563 */
1564 static int
1565syn_stack_equal(sp)
1566 synstate_T *sp;
1567{
1568 int i, j;
1569 bufstate_T *bp;
1570 reg_extmatch_T *six, *bsx;
1571
1572 /* First a quick check if the stacks have the same size end nextlist. */
1573 if (sp->sst_stacksize == current_state.ga_len
1574 && sp->sst_next_list == current_next_list)
1575 {
1576 /* Need to compare all states on both stacks. */
1577 if (sp->sst_stacksize > SST_FIX_STATES)
1578 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1579 else
1580 bp = sp->sst_union.sst_stack;
1581
1582 for (i = current_state.ga_len; --i >= 0; )
1583 {
1584 /* If the item has another index the state is different. */
1585 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1586 break;
1587 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1588 {
1589 /* When the extmatch pointers are different, the strings in
1590 * them can still be the same. Check if the extmatch
1591 * references are equal. */
1592 bsx = bp[i].bs_extmatch;
1593 six = CUR_STATE(i).si_extmatch;
1594 /* If one of the extmatch pointers is NULL the states are
1595 * different. */
1596 if (bsx == NULL || six == NULL)
1597 break;
1598 for (j = 0; j < NSUBEXP; ++j)
1599 {
1600 /* Check each referenced match string. They must all be
1601 * equal. */
1602 if (bsx->matches[j] != six->matches[j])
1603 {
1604 /* If the pointer is different it can still be the
1605 * same text. Compare the strings, ignore case when
1606 * the start item has the sp_ic flag set. */
1607 if (bsx->matches[j] == NULL
1608 || six->matches[j] == NULL)
1609 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001610 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001611 ? MB_STRICMP(bsx->matches[j],
1612 six->matches[j]) != 0
1613 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1614 break;
1615 }
1616 }
1617 if (j != NSUBEXP)
1618 break;
1619 }
1620 }
1621 if (i < 0)
1622 return TRUE;
1623 }
1624 return FALSE;
1625}
1626
1627/*
1628 * We stop parsing syntax above line "lnum". If the stored state at or below
1629 * this line depended on a change before it, it now depends on the line below
1630 * the last parsed line.
1631 * The window looks like this:
1632 * line which changed
1633 * displayed line
1634 * displayed line
1635 * lnum -> line below window
1636 */
1637 void
1638syntax_end_parsing(lnum)
1639 linenr_T lnum;
1640{
1641 synstate_T *sp;
1642
1643 sp = syn_stack_find_entry(lnum);
1644 if (sp != NULL && sp->sst_lnum < lnum)
1645 sp = sp->sst_next;
1646
1647 if (sp != NULL && sp->sst_change_lnum != 0)
1648 sp->sst_change_lnum = lnum;
1649}
1650
1651/*
1652 * End of handling of the state stack.
1653 ****************************************/
1654
1655 static void
1656invalidate_current_state()
1657{
1658 clear_current_state();
1659 current_state.ga_itemsize = 0; /* mark current_state invalid */
1660 current_next_list = NULL;
1661 keepend_level = -1;
1662}
1663
1664 static void
1665validate_current_state()
1666{
1667 current_state.ga_itemsize = sizeof(stateitem_T);
1668 current_state.ga_growsize = 3;
1669}
1670
1671/*
1672 * Return TRUE if the syntax at start of lnum changed since last time.
1673 * This will only be called just after get_syntax_attr() for the previous
1674 * line, to check if the next line needs to be redrawn too.
1675 */
1676 int
1677syntax_check_changed(lnum)
1678 linenr_T lnum;
1679{
1680 int retval = TRUE;
1681 synstate_T *sp;
1682
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 /*
1684 * Check the state stack when:
1685 * - lnum is just below the previously syntaxed line.
1686 * - lnum is not before the lines with saved states.
1687 * - lnum is not past the lines with saved states.
1688 * - lnum is at or before the last changed line.
1689 */
1690 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1691 {
1692 sp = syn_stack_find_entry(lnum);
1693 if (sp != NULL && sp->sst_lnum == lnum)
1694 {
1695 /*
1696 * finish the previous line (needed when not all of the line was
1697 * drawn)
1698 */
1699 (void)syn_finish_line(FALSE);
1700
1701 /*
1702 * Compare the current state with the previously saved state of
1703 * the line.
1704 */
1705 if (syn_stack_equal(sp))
1706 retval = FALSE;
1707
1708 /*
1709 * Store the current state in b_sst_array[] for later use.
1710 */
1711 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001712 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713 }
1714 }
1715
Bram Moolenaar071d4272004-06-13 20:20:40 +00001716 return retval;
1717}
1718
1719/*
1720 * Finish the current line.
1721 * This doesn't return any attributes, it only gets the state at the end of
1722 * the line. It can start anywhere in the line, as long as the current state
1723 * is valid.
1724 */
1725 static int
1726syn_finish_line(syncing)
1727 int syncing; /* called for syncing */
1728{
1729 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001730 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731
1732 if (!current_finished)
1733 {
1734 while (!current_finished)
1735 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001736 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737 /*
1738 * When syncing, and found some item, need to check the item.
1739 */
1740 if (syncing && current_state.ga_len)
1741 {
1742 /*
1743 * Check for match with sync item.
1744 */
1745 cur_si = &CUR_STATE(current_state.ga_len - 1);
1746 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001747 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1749 return TRUE;
1750
1751 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001752 * that ends here, need to do that now. Be careful not to go
1753 * past the NUL. */
1754 prev_current_col = current_col;
1755 if (syn_getcurline()[current_col] != NUL)
1756 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001757 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001758 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759 }
1760 ++current_col;
1761 }
1762 }
1763 return FALSE;
1764}
1765
1766/*
1767 * Return highlight attributes for next character.
1768 * Must first call syntax_start() once for the line.
1769 * "col" is normally 0 for the first use in a line, and increments by one each
1770 * time. It's allowed to skip characters and to stop before the end of the
1771 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001772 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1773 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774 */
1775 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001776get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001777 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001778 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001779 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780{
1781 int attr = 0;
1782
Bram Moolenaar349955a2007-08-14 21:07:36 +00001783 if (can_spell != NULL)
1784 /* Default: Only do spelling when there is no @Spell cluster or when
1785 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001786 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1787 ? (syn_block->b_spell_cluster_id == 0)
1788 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001789
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001791 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001792 return 0;
1793
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001794 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001795 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001796 {
1797 clear_current_state();
1798#ifdef FEAT_EVAL
1799 current_id = 0;
1800 current_trans_id = 0;
1801#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001802#ifdef FEAT_CONCEAL
1803 current_flags = 0;
1804#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001805 return 0;
1806 }
1807
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808 /* Make sure current_state is valid */
1809 if (INVALID_STATE(&current_state))
1810 validate_current_state();
1811
1812 /*
1813 * Skip from the current column to "col", get the attributes for "col".
1814 */
1815 while (current_col <= col)
1816 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001817 attr = syn_current_attr(FALSE, TRUE, can_spell,
1818 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 ++current_col;
1820 }
1821
Bram Moolenaar071d4272004-06-13 20:20:40 +00001822 return attr;
1823}
1824
1825/*
1826 * Get syntax attributes for current_lnum, current_col.
1827 */
1828 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001829syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 int syncing; /* When 1: called for syncing */
1831 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001832 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001833 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001834{
1835 int syn_id;
1836 lpos_T endpos; /* was: char_u *endp; */
1837 lpos_T hl_startpos; /* was: int hl_startcol; */
1838 lpos_T hl_endpos;
1839 lpos_T eos_pos; /* end-of-start match (start region) */
1840 lpos_T eoe_pos; /* end-of-end pattern */
1841 int end_idx; /* group ID for end pattern */
1842 int idx;
1843 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001844 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845 int startcol;
1846 int endcol;
1847 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001848 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001849 short *next_list;
1850 int found_match; /* found usable match */
1851 static int try_next_column = FALSE; /* must try in next col */
1852 int do_keywords;
1853 regmmatch_T regmatch;
1854 lpos_T pos;
1855 int lc_col;
1856 reg_extmatch_T *cur_extmatch = NULL;
1857 char_u *line; /* current line. NOTE: becomes invalid after
1858 looking for a pattern match! */
1859
1860 /* variables for zero-width matches that have a "nextgroup" argument */
1861 int keep_next_list;
1862 int zero_width_next_list = FALSE;
1863 garray_T zero_width_next_ga;
1864
1865 /*
1866 * No character, no attributes! Past end of line?
1867 * Do try matching with an empty line (could be the start of a region).
1868 */
1869 line = syn_getcurline();
1870 if (line[current_col] == NUL && current_col != 0)
1871 {
1872 /*
1873 * If we found a match after the last column, use it.
1874 */
1875 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1876 && next_match_col != MAXCOL)
1877 (void)push_next_match(NULL);
1878
1879 current_finished = TRUE;
1880 current_state_stored = FALSE;
1881 return 0;
1882 }
1883
1884 /* if the current or next character is NUL, we will finish the line now */
1885 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1886 {
1887 current_finished = TRUE;
1888 current_state_stored = FALSE;
1889 }
1890
1891 /*
1892 * When in the previous column there was a match but it could not be used
1893 * (empty match or already matched in this column) need to try again in
1894 * the next column.
1895 */
1896 if (try_next_column)
1897 {
1898 next_match_idx = -1;
1899 try_next_column = FALSE;
1900 }
1901
1902 /* Only check for keywords when not syncing and there are some. */
1903 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001904 && (syn_block->b_keywtab.ht_used > 0
1905 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001906
1907 /* Init the list of zero-width matches with a nextlist. This is used to
1908 * avoid matching the same item in the same position twice. */
1909 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1910
1911 /*
1912 * Repeat matching keywords and patterns, to find contained items at the
1913 * same column. This stops when there are no extra matches at the current
1914 * column.
1915 */
1916 do
1917 {
1918 found_match = FALSE;
1919 keep_next_list = FALSE;
1920 syn_id = 0;
1921
1922 /*
1923 * 1. Check for a current state.
1924 * Only when there is no current state, or if the current state may
1925 * contain other things, we need to check for keywords and patterns.
1926 * Always need to check for contained items if some item has the
1927 * "containedin" argument (takes extra time!).
1928 */
1929 if (current_state.ga_len)
1930 cur_si = &CUR_STATE(current_state.ga_len - 1);
1931 else
1932 cur_si = NULL;
1933
Bram Moolenaar860cae12010-06-05 23:22:07 +02001934 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935 || cur_si->si_cont_list != NULL)
1936 {
1937 /*
1938 * 2. Check for keywords, if on a keyword char after a non-keyword
1939 * char. Don't do this when syncing.
1940 */
1941 if (do_keywords)
1942 {
1943 line = syn_getcurline();
1944 if (vim_iswordc_buf(line + current_col, syn_buf)
1945 && (current_col == 0
1946 || !vim_iswordc_buf(line + current_col - 1
1947#ifdef FEAT_MBYTE
1948 - (has_mbyte
1949 ? (*mb_head_off)(line, line + current_col - 1)
1950 : 0)
1951#endif
1952 , syn_buf)))
1953 {
1954 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001955 &endcol, &flags, &next_list, cur_si,
1956 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001957 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958 {
1959 if (push_current_state(KEYWORD_IDX) == OK)
1960 {
1961 cur_si = &CUR_STATE(current_state.ga_len - 1);
1962 cur_si->si_m_startcol = current_col;
1963 cur_si->si_h_startpos.lnum = current_lnum;
1964 cur_si->si_h_startpos.col = 0; /* starts right away */
1965 cur_si->si_m_endpos.lnum = current_lnum;
1966 cur_si->si_m_endpos.col = endcol;
1967 cur_si->si_h_endpos.lnum = current_lnum;
1968 cur_si->si_h_endpos.col = endcol;
1969 cur_si->si_ends = TRUE;
1970 cur_si->si_end_idx = 0;
1971 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001972#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001973 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001974 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001975 if (current_state.ga_len > 1)
1976 cur_si->si_flags |=
1977 CUR_STATE(current_state.ga_len - 2).si_flags
1978 & HL_CONCEAL;
1979#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980 cur_si->si_id = syn_id;
1981 cur_si->si_trans_id = syn_id;
1982 if (flags & HL_TRANSP)
1983 {
1984 if (current_state.ga_len < 2)
1985 {
1986 cur_si->si_attr = 0;
1987 cur_si->si_trans_id = 0;
1988 }
1989 else
1990 {
1991 cur_si->si_attr = CUR_STATE(
1992 current_state.ga_len - 2).si_attr;
1993 cur_si->si_trans_id = CUR_STATE(
1994 current_state.ga_len - 2).si_trans_id;
1995 }
1996 }
1997 else
1998 cur_si->si_attr = syn_id2attr(syn_id);
1999 cur_si->si_cont_list = NULL;
2000 cur_si->si_next_list = next_list;
2001 check_keepend();
2002 }
2003 else
2004 vim_free(next_list);
2005 }
2006 }
2007 }
2008
2009 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002010 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002012 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002013 {
2014 /*
2015 * If we didn't check for a match yet, or we are past it, check
2016 * for any match with a pattern.
2017 */
2018 if (next_match_idx < 0 || next_match_col < (int)current_col)
2019 {
2020 /*
2021 * Check all relevant patterns for a match at this
2022 * position. This is complicated, because matching with a
2023 * pattern takes quite a bit of time, thus we want to
2024 * avoid doing it when it's not needed.
2025 */
2026 next_match_idx = 0; /* no match in this line yet */
2027 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002028 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002029 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002030 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002031 if ( spp->sp_syncing == syncing
2032 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2033 && (spp->sp_type == SPTYPE_MATCH
2034 || spp->sp_type == SPTYPE_START)
2035 && (current_next_list != NULL
2036 ? in_id_list(NULL, current_next_list,
2037 &spp->sp_syn, 0)
2038 : (cur_si == NULL
2039 ? !(spp->sp_flags & HL_CONTAINED)
2040 : in_id_list(cur_si,
2041 cur_si->si_cont_list, &spp->sp_syn,
2042 spp->sp_flags & HL_CONTAINED))))
2043 {
2044 /* If we already tried matching in this line, and
2045 * there isn't a match before next_match_col, skip
2046 * this item. */
2047 if (spp->sp_line_id == current_line_id
2048 && spp->sp_startcol >= next_match_col)
2049 continue;
2050 spp->sp_line_id = current_line_id;
2051
2052 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2053 if (lc_col < 0)
2054 lc_col = 0;
2055
2056 regmatch.rmm_ic = spp->sp_ic;
2057 regmatch.regprog = spp->sp_prog;
2058 if (!syn_regexec(&regmatch, current_lnum,
2059 (colnr_T)lc_col))
2060 {
2061 /* no match in this line, try another one */
2062 spp->sp_startcol = MAXCOL;
2063 continue;
2064 }
2065
2066 /*
2067 * Compute the first column of the match.
2068 */
2069 syn_add_start_off(&pos, &regmatch,
2070 spp, SPO_MS_OFF, -1);
2071 if (pos.lnum > current_lnum)
2072 {
2073 /* must have used end of match in a next line,
2074 * we can't handle that */
2075 spp->sp_startcol = MAXCOL;
2076 continue;
2077 }
2078 startcol = pos.col;
2079
2080 /* remember the next column where this pattern
2081 * matches in the current line */
2082 spp->sp_startcol = startcol;
2083
2084 /*
2085 * If a previously found match starts at a lower
2086 * column number, don't use this one.
2087 */
2088 if (startcol >= next_match_col)
2089 continue;
2090
2091 /*
2092 * If we matched this pattern at this position
2093 * before, skip it. Must retry in the next
2094 * column, because it may match from there.
2095 */
2096 if (did_match_already(idx, &zero_width_next_ga))
2097 {
2098 try_next_column = TRUE;
2099 continue;
2100 }
2101
2102 endpos.lnum = regmatch.endpos[0].lnum;
2103 endpos.col = regmatch.endpos[0].col;
2104
2105 /* Compute the highlight start. */
2106 syn_add_start_off(&hl_startpos, &regmatch,
2107 spp, SPO_HS_OFF, -1);
2108
2109 /* Compute the region start. */
2110 /* Default is to use the end of the match. */
2111 syn_add_end_off(&eos_pos, &regmatch,
2112 spp, SPO_RS_OFF, 0);
2113
2114 /*
2115 * Grab the external submatches before they get
2116 * overwritten. Reference count doesn't change.
2117 */
2118 unref_extmatch(cur_extmatch);
2119 cur_extmatch = re_extmatch_out;
2120 re_extmatch_out = NULL;
2121
2122 flags = 0;
2123 eoe_pos.lnum = 0; /* avoid warning */
2124 eoe_pos.col = 0;
2125 end_idx = 0;
2126 hl_endpos.lnum = 0;
2127
2128 /*
2129 * For a "oneline" the end must be found in the
2130 * same line too. Search for it after the end of
2131 * the match with the start pattern. Set the
2132 * resulting end positions at the same time.
2133 */
2134 if (spp->sp_type == SPTYPE_START
2135 && (spp->sp_flags & HL_ONELINE))
2136 {
2137 lpos_T startpos;
2138
2139 startpos = endpos;
2140 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2141 &flags, &eoe_pos, &end_idx, cur_extmatch);
2142 if (endpos.lnum == 0)
2143 continue; /* not found */
2144 }
2145
2146 /*
2147 * For a "match" the size must be > 0 after the
2148 * end offset needs has been added. Except when
2149 * syncing.
2150 */
2151 else if (spp->sp_type == SPTYPE_MATCH)
2152 {
2153 syn_add_end_off(&hl_endpos, &regmatch, spp,
2154 SPO_HE_OFF, 0);
2155 syn_add_end_off(&endpos, &regmatch, spp,
2156 SPO_ME_OFF, 0);
2157 if (endpos.lnum == current_lnum
2158 && (int)endpos.col + syncing < startcol)
2159 {
2160 /*
2161 * If an empty string is matched, may need
2162 * to try matching again at next column.
2163 */
2164 if (regmatch.startpos[0].col
2165 == regmatch.endpos[0].col)
2166 try_next_column = TRUE;
2167 continue;
2168 }
2169 }
2170
2171 /*
2172 * keep the best match so far in next_match_*
2173 */
2174 /* Highlighting must start after startpos and end
2175 * before endpos. */
2176 if (hl_startpos.lnum == current_lnum
2177 && (int)hl_startpos.col < startcol)
2178 hl_startpos.col = startcol;
2179 limit_pos_zero(&hl_endpos, &endpos);
2180
2181 next_match_idx = idx;
2182 next_match_col = startcol;
2183 next_match_m_endpos = endpos;
2184 next_match_h_endpos = hl_endpos;
2185 next_match_h_startpos = hl_startpos;
2186 next_match_flags = flags;
2187 next_match_eos_pos = eos_pos;
2188 next_match_eoe_pos = eoe_pos;
2189 next_match_end_idx = end_idx;
2190 unref_extmatch(next_match_extmatch);
2191 next_match_extmatch = cur_extmatch;
2192 cur_extmatch = NULL;
2193 }
2194 }
2195 }
2196
2197 /*
2198 * If we found a match at the current column, use it.
2199 */
2200 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2201 {
2202 synpat_T *lspp;
2203
2204 /* When a zero-width item matched which has a nextgroup,
2205 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002206 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002207 if (next_match_m_endpos.lnum == current_lnum
2208 && next_match_m_endpos.col == current_col
2209 && lspp->sp_next_list != NULL)
2210 {
2211 current_next_list = lspp->sp_next_list;
2212 current_next_flags = lspp->sp_flags;
2213 keep_next_list = TRUE;
2214 zero_width_next_list = TRUE;
2215
2216 /* Add the index to a list, so that we can check
2217 * later that we don't match it again (and cause an
2218 * endless loop). */
2219 if (ga_grow(&zero_width_next_ga, 1) == OK)
2220 {
2221 ((int *)(zero_width_next_ga.ga_data))
2222 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 }
2224 next_match_idx = -1;
2225 }
2226 else
2227 cur_si = push_next_match(cur_si);
2228 found_match = TRUE;
2229 }
2230 }
2231 }
2232
2233 /*
2234 * Handle searching for nextgroup match.
2235 */
2236 if (current_next_list != NULL && !keep_next_list)
2237 {
2238 /*
2239 * If a nextgroup was not found, continue looking for one if:
2240 * - this is an empty line and the "skipempty" option was given
2241 * - we are on white space and the "skipwhite" option was given
2242 */
2243 if (!found_match)
2244 {
2245 line = syn_getcurline();
2246 if (((current_next_flags & HL_SKIPWHITE)
2247 && vim_iswhite(line[current_col]))
2248 || ((current_next_flags & HL_SKIPEMPTY)
2249 && *line == NUL))
2250 break;
2251 }
2252
2253 /*
2254 * If a nextgroup was found: Use it, and continue looking for
2255 * contained matches.
2256 * If a nextgroup was not found: Continue looking for a normal
2257 * match.
2258 * When did set current_next_list for a zero-width item and no
2259 * match was found don't loop (would get stuck).
2260 */
2261 current_next_list = NULL;
2262 next_match_idx = -1;
2263 if (!zero_width_next_list)
2264 found_match = TRUE;
2265 }
2266
2267 } while (found_match);
2268
2269 /*
2270 * Use attributes from the current state, if within its highlighting.
2271 * If not, use attributes from the current-but-one state, etc.
2272 */
2273 current_attr = 0;
2274#ifdef FEAT_EVAL
2275 current_id = 0;
2276 current_trans_id = 0;
2277#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002278#ifdef FEAT_CONCEAL
2279 current_flags = 0;
2280#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 if (cur_si != NULL)
2282 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002283#ifndef FEAT_EVAL
2284 int current_trans_id = 0;
2285#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2287 {
2288 sip = &CUR_STATE(idx);
2289 if ((current_lnum > sip->si_h_startpos.lnum
2290 || (current_lnum == sip->si_h_startpos.lnum
2291 && current_col >= sip->si_h_startpos.col))
2292 && (sip->si_h_endpos.lnum == 0
2293 || current_lnum < sip->si_h_endpos.lnum
2294 || (current_lnum == sip->si_h_endpos.lnum
2295 && current_col < sip->si_h_endpos.col)))
2296 {
2297 current_attr = sip->si_attr;
2298#ifdef FEAT_EVAL
2299 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002301 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002302#ifdef FEAT_CONCEAL
2303 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002304 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002305 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002306#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002307 break;
2308 }
2309 }
2310
Bram Moolenaar217ad922005-03-20 22:37:15 +00002311 if (can_spell != NULL)
2312 {
2313 struct sp_syn sps;
2314
2315 /*
2316 * set "can_spell" to TRUE if spell checking is supposed to be
2317 * done in the current item.
2318 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002319 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002320 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002321 /* There is no @Spell cluster: Do spelling for items without
2322 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002323 if (syn_block->b_nospell_cluster_id == 0
2324 || current_trans_id == 0)
2325 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002326 else
2327 {
2328 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002329 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002330 sps.cont_in_list = NULL;
2331 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2332 }
2333 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002334 else
2335 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002336 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002337 * the @Spell cluster. But not when @NoSpell is also there.
2338 * At the toplevel only spell check when ":syn spell toplevel"
2339 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002340 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002341 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002342 else
2343 {
2344 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002345 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002346 sps.cont_in_list = NULL;
2347 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2348
Bram Moolenaar860cae12010-06-05 23:22:07 +02002349 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002350 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002351 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002352 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2353 *can_spell = FALSE;
2354 }
2355 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002356 }
2357 }
2358
2359
Bram Moolenaar071d4272004-06-13 20:20:40 +00002360 /*
2361 * Check for end of current state (and the states before it) at the
2362 * next column. Don't do this for syncing, because we would miss a
2363 * single character match.
2364 * First check if the current state ends at the current column. It
2365 * may be for an empty match and a containing item might end in the
2366 * current column.
2367 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002368 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002369 {
2370 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002371 if (current_state.ga_len > 0
2372 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 {
2374 ++current_col;
2375 check_state_ends();
2376 --current_col;
2377 }
2378 }
2379 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002380 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002381 /* Default: Only do spelling when there is no @Spell cluster or when
2382 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002383 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2384 ? (syn_block->b_spell_cluster_id == 0)
2385 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002387 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388 if (current_next_list != NULL
2389 && syn_getcurline()[current_col + 1] == NUL
2390 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2391 current_next_list = NULL;
2392
2393 if (zero_width_next_ga.ga_len > 0)
2394 ga_clear(&zero_width_next_ga);
2395
2396 /* No longer need external matches. But keep next_match_extmatch. */
2397 unref_extmatch(re_extmatch_out);
2398 re_extmatch_out = NULL;
2399 unref_extmatch(cur_extmatch);
2400
2401 return current_attr;
2402}
2403
2404
2405/*
2406 * Check if we already matched pattern "idx" at the current column.
2407 */
2408 static int
2409did_match_already(idx, gap)
2410 int idx;
2411 garray_T *gap;
2412{
2413 int i;
2414
2415 for (i = current_state.ga_len; --i >= 0; )
2416 if (CUR_STATE(i).si_m_startcol == (int)current_col
2417 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2418 && CUR_STATE(i).si_idx == idx)
2419 return TRUE;
2420
2421 /* Zero-width matches with a nextgroup argument are not put on the syntax
2422 * stack, and can only be matched once anyway. */
2423 for (i = gap->ga_len; --i >= 0; )
2424 if (((int *)(gap->ga_data))[i] == idx)
2425 return TRUE;
2426
2427 return FALSE;
2428}
2429
2430/*
2431 * Push the next match onto the stack.
2432 */
2433 static stateitem_T *
2434push_next_match(cur_si)
2435 stateitem_T *cur_si;
2436{
2437 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002438#ifdef FEAT_CONCEAL
2439 int save_flags;
2440#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441
Bram Moolenaar860cae12010-06-05 23:22:07 +02002442 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443
2444 /*
2445 * Push the item in current_state stack;
2446 */
2447 if (push_current_state(next_match_idx) == OK)
2448 {
2449 /*
2450 * If it's a start-skip-end type that crosses lines, figure out how
2451 * much it continues in this line. Otherwise just fill in the length.
2452 */
2453 cur_si = &CUR_STATE(current_state.ga_len - 1);
2454 cur_si->si_h_startpos = next_match_h_startpos;
2455 cur_si->si_m_startcol = current_col;
2456 cur_si->si_m_lnum = current_lnum;
2457 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002458#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002459 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002460 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002461 if (current_state.ga_len > 1)
2462 cur_si->si_flags |=
2463 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2464#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002465 cur_si->si_next_list = spp->sp_next_list;
2466 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2467 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2468 {
2469 /* Try to find the end pattern in the current line */
2470 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2471 check_keepend();
2472 }
2473 else
2474 {
2475 cur_si->si_m_endpos = next_match_m_endpos;
2476 cur_si->si_h_endpos = next_match_h_endpos;
2477 cur_si->si_ends = TRUE;
2478 cur_si->si_flags |= next_match_flags;
2479 cur_si->si_eoe_pos = next_match_eoe_pos;
2480 cur_si->si_end_idx = next_match_end_idx;
2481 }
2482 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2483 keepend_level = current_state.ga_len - 1;
2484 check_keepend();
2485 update_si_attr(current_state.ga_len - 1);
2486
Bram Moolenaar860cae12010-06-05 23:22:07 +02002487#ifdef FEAT_CONCEAL
2488 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2489#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002490 /*
2491 * If the start pattern has another highlight group, push another item
2492 * on the stack for the start pattern.
2493 */
2494 if ( spp->sp_type == SPTYPE_START
2495 && spp->sp_syn_match_id != 0
2496 && push_current_state(next_match_idx) == OK)
2497 {
2498 cur_si = &CUR_STATE(current_state.ga_len - 1);
2499 cur_si->si_h_startpos = next_match_h_startpos;
2500 cur_si->si_m_startcol = current_col;
2501 cur_si->si_m_lnum = current_lnum;
2502 cur_si->si_m_endpos = next_match_eos_pos;
2503 cur_si->si_h_endpos = next_match_eos_pos;
2504 cur_si->si_ends = TRUE;
2505 cur_si->si_end_idx = 0;
2506 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002507#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002508 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002509 cur_si->si_flags |= save_flags;
2510 if (cur_si->si_flags & HL_CONCEALENDS)
2511 cur_si->si_flags |= HL_CONCEAL;
2512#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513 cur_si->si_next_list = NULL;
2514 check_keepend();
2515 update_si_attr(current_state.ga_len - 1);
2516 }
2517 }
2518
2519 next_match_idx = -1; /* try other match next time */
2520
2521 return cur_si;
2522}
2523
2524/*
2525 * Check for end of current state (and the states before it).
2526 */
2527 static void
2528check_state_ends()
2529{
2530 stateitem_T *cur_si;
2531 int had_extend = FALSE;
2532
2533 cur_si = &CUR_STATE(current_state.ga_len - 1);
2534 for (;;)
2535 {
2536 if (cur_si->si_ends
2537 && (cur_si->si_m_endpos.lnum < current_lnum
2538 || (cur_si->si_m_endpos.lnum == current_lnum
2539 && cur_si->si_m_endpos.col <= current_col)))
2540 {
2541 /*
2542 * If there is an end pattern group ID, highlight the end pattern
2543 * now. No need to pop the current item from the stack.
2544 * Only do this if the end pattern continues beyond the current
2545 * position.
2546 */
2547 if (cur_si->si_end_idx
2548 && (cur_si->si_eoe_pos.lnum > current_lnum
2549 || (cur_si->si_eoe_pos.lnum == current_lnum
2550 && cur_si->si_eoe_pos.col > current_col)))
2551 {
2552 cur_si->si_idx = cur_si->si_end_idx;
2553 cur_si->si_end_idx = 0;
2554 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2555 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2556 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002557#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002558 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002559 if (cur_si->si_flags & HL_CONCEALENDS)
2560 cur_si->si_flags |= HL_CONCEAL;
2561#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002562 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002563
2564 /* what matches next may be different now, clear it */
2565 next_match_idx = 0;
2566 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567 break;
2568 }
2569 else
2570 {
2571 /* handle next_list, unless at end of line and no "skipnl" or
2572 * "skipempty" */
2573 current_next_list = cur_si->si_next_list;
2574 current_next_flags = cur_si->si_flags;
2575 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2576 && syn_getcurline()[current_col] == NUL)
2577 current_next_list = NULL;
2578
2579 /* When the ended item has "extend", another item with
2580 * "keepend" now needs to check for its end. */
2581 if (cur_si->si_flags & HL_EXTEND)
2582 had_extend = TRUE;
2583
2584 pop_current_state();
2585
2586 if (current_state.ga_len == 0)
2587 break;
2588
Bram Moolenaar81993f42008-01-11 20:27:45 +00002589 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590 {
2591 syn_update_ends(FALSE);
2592 if (current_state.ga_len == 0)
2593 break;
2594 }
2595
2596 cur_si = &CUR_STATE(current_state.ga_len - 1);
2597
2598 /*
2599 * Only for a region the search for the end continues after
2600 * the end of the contained item. If the contained match
2601 * included the end-of-line, break here, the region continues.
2602 * Don't do this when:
2603 * - "keepend" is used for the contained item
2604 * - not at the end of the line (could be end="x$"me=e-1).
2605 * - "excludenl" is used (HL_HAS_EOL won't be set)
2606 */
2607 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002608 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002609 == SPTYPE_START
2610 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2611 {
2612 update_si_end(cur_si, (int)current_col, TRUE);
2613 check_keepend();
2614 if ((current_next_flags & HL_HAS_EOL)
2615 && keepend_level < 0
2616 && syn_getcurline()[current_col] == NUL)
2617 break;
2618 }
2619 }
2620 }
2621 else
2622 break;
2623 }
2624}
2625
2626/*
2627 * Update an entry in the current_state stack for a match or region. This
2628 * fills in si_attr, si_next_list and si_cont_list.
2629 */
2630 static void
2631update_si_attr(idx)
2632 int idx;
2633{
2634 stateitem_T *sip = &CUR_STATE(idx);
2635 synpat_T *spp;
2636
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002637 /* This should not happen... */
2638 if (sip->si_idx < 0)
2639 return;
2640
Bram Moolenaar860cae12010-06-05 23:22:07 +02002641 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 if (sip->si_flags & HL_MATCH)
2643 sip->si_id = spp->sp_syn_match_id;
2644 else
2645 sip->si_id = spp->sp_syn.id;
2646 sip->si_attr = syn_id2attr(sip->si_id);
2647 sip->si_trans_id = sip->si_id;
2648 if (sip->si_flags & HL_MATCH)
2649 sip->si_cont_list = NULL;
2650 else
2651 sip->si_cont_list = spp->sp_cont_list;
2652
2653 /*
2654 * For transparent items, take attr from outer item.
2655 * Also take cont_list, if there is none.
2656 * Don't do this for the matchgroup of a start or end pattern.
2657 */
2658 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2659 {
2660 if (idx == 0)
2661 {
2662 sip->si_attr = 0;
2663 sip->si_trans_id = 0;
2664 if (sip->si_cont_list == NULL)
2665 sip->si_cont_list = ID_LIST_ALL;
2666 }
2667 else
2668 {
2669 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2670 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002671 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2672 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673 if (sip->si_cont_list == NULL)
2674 {
2675 sip->si_flags |= HL_TRANS_CONT;
2676 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2677 }
2678 }
2679 }
2680}
2681
2682/*
2683 * Check the current stack for patterns with "keepend" flag.
2684 * Propagate the match-end to contained items, until a "skipend" item is found.
2685 */
2686 static void
2687check_keepend()
2688{
2689 int i;
2690 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002691 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 stateitem_T *sip;
2693
2694 /*
2695 * This check can consume a lot of time; only do it from the level where
2696 * there really is a keepend.
2697 */
2698 if (keepend_level < 0)
2699 return;
2700
2701 /*
2702 * Find the last index of an "extend" item. "keepend" items before that
2703 * won't do anything. If there is no "extend" item "i" will be
2704 * "keepend_level" and all "keepend" items will work normally.
2705 */
2706 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2707 if (CUR_STATE(i).si_flags & HL_EXTEND)
2708 break;
2709
2710 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002711 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002712 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002713 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 for ( ; i < current_state.ga_len; ++i)
2715 {
2716 sip = &CUR_STATE(i);
2717 if (maxpos.lnum != 0)
2718 {
2719 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002720 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002721 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2722 sip->si_ends = TRUE;
2723 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002724 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2725 {
2726 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002727 || maxpos.lnum > sip->si_m_endpos.lnum
2728 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002729 && maxpos.col > sip->si_m_endpos.col))
2730 maxpos = sip->si_m_endpos;
2731 if (maxpos_h.lnum == 0
2732 || maxpos_h.lnum > sip->si_h_endpos.lnum
2733 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2734 && maxpos_h.col > sip->si_h_endpos.col))
2735 maxpos_h = sip->si_h_endpos;
2736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002737 }
2738}
2739
2740/*
2741 * Update an entry in the current_state stack for a start-skip-end pattern.
2742 * This finds the end of the current item, if it's in the current line.
2743 *
2744 * Return the flags for the matched END.
2745 */
2746 static void
2747update_si_end(sip, startcol, force)
2748 stateitem_T *sip;
2749 int startcol; /* where to start searching for the end */
2750 int force; /* when TRUE overrule a previous end */
2751{
2752 lpos_T startpos;
2753 lpos_T endpos;
2754 lpos_T hl_endpos;
2755 lpos_T end_endpos;
2756 int end_idx;
2757
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002758 /* return quickly for a keyword */
2759 if (sip->si_idx < 0)
2760 return;
2761
Bram Moolenaar071d4272004-06-13 20:20:40 +00002762 /* Don't update when it's already done. Can be a match of an end pattern
2763 * that started in a previous line. Watch out: can also be a "keepend"
2764 * from a containing item. */
2765 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2766 return;
2767
2768 /*
2769 * We need to find the end of the region. It may continue in the next
2770 * line.
2771 */
2772 end_idx = 0;
2773 startpos.lnum = current_lnum;
2774 startpos.col = startcol;
2775 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2776 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2777
2778 if (endpos.lnum == 0)
2779 {
2780 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002781 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002782 {
2783 /* a "oneline" never continues in the next line */
2784 sip->si_ends = TRUE;
2785 sip->si_m_endpos.lnum = current_lnum;
2786 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2787 }
2788 else
2789 {
2790 /* continues in the next line */
2791 sip->si_ends = FALSE;
2792 sip->si_m_endpos.lnum = 0;
2793 }
2794 sip->si_h_endpos = sip->si_m_endpos;
2795 }
2796 else
2797 {
2798 /* match within this line */
2799 sip->si_m_endpos = endpos;
2800 sip->si_h_endpos = hl_endpos;
2801 sip->si_eoe_pos = end_endpos;
2802 sip->si_ends = TRUE;
2803 sip->si_end_idx = end_idx;
2804 }
2805}
2806
2807/*
2808 * Add a new state to the current state stack.
2809 * It is cleared and the index set to "idx".
2810 * Return FAIL if it's not possible (out of memory).
2811 */
2812 static int
2813push_current_state(idx)
2814 int idx;
2815{
2816 if (ga_grow(&current_state, 1) == FAIL)
2817 return FAIL;
2818 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2819 CUR_STATE(current_state.ga_len).si_idx = idx;
2820 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002821 return OK;
2822}
2823
2824/*
2825 * Remove a state from the current_state stack.
2826 */
2827 static void
2828pop_current_state()
2829{
2830 if (current_state.ga_len)
2831 {
2832 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2833 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 }
2835 /* after the end of a pattern, try matching a keyword or pattern */
2836 next_match_idx = -1;
2837
2838 /* if first state with "keepend" is popped, reset keepend_level */
2839 if (keepend_level >= current_state.ga_len)
2840 keepend_level = -1;
2841}
2842
2843/*
2844 * Find the end of a start/skip/end syntax region after "startpos".
2845 * Only checks one line.
2846 * Also handles a match item that continued from a previous line.
2847 * If not found, the syntax item continues in the next line. m_endpos->lnum
2848 * will be 0.
2849 * If found, the end of the region and the end of the highlighting is
2850 * computed.
2851 */
2852 static void
2853find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2854 end_idx, start_ext)
2855 int idx; /* index of the pattern */
2856 lpos_T *startpos; /* where to start looking for an END match */
2857 lpos_T *m_endpos; /* return: end of match */
2858 lpos_T *hl_endpos; /* return: end of highlighting */
2859 long *flagsp; /* return: flags of matching END */
2860 lpos_T *end_endpos; /* return: end of end pattern match */
2861 int *end_idx; /* return: group ID for end pat. match, or 0 */
2862 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2863{
2864 colnr_T matchcol;
2865 synpat_T *spp, *spp_skip;
2866 int start_idx;
2867 int best_idx;
2868 regmmatch_T regmatch;
2869 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2870 lpos_T pos;
2871 char_u *line;
2872 int had_match = FALSE;
2873
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002874 /* just in case we are invoked for a keyword */
2875 if (idx < 0)
2876 return;
2877
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 /*
2879 * Check for being called with a START pattern.
2880 * Can happen with a match that continues to the next line, because it
2881 * contained a region.
2882 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002883 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002884 if (spp->sp_type != SPTYPE_START)
2885 {
2886 *hl_endpos = *startpos;
2887 return;
2888 }
2889
2890 /*
2891 * Find the SKIP or first END pattern after the last START pattern.
2892 */
2893 for (;;)
2894 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002895 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002896 if (spp->sp_type != SPTYPE_START)
2897 break;
2898 ++idx;
2899 }
2900
2901 /*
2902 * Lookup the SKIP pattern (if present)
2903 */
2904 if (spp->sp_type == SPTYPE_SKIP)
2905 {
2906 spp_skip = spp;
2907 ++idx;
2908 }
2909 else
2910 spp_skip = NULL;
2911
2912 /* Setup external matches for syn_regexec(). */
2913 unref_extmatch(re_extmatch_in);
2914 re_extmatch_in = ref_extmatch(start_ext);
2915
2916 matchcol = startpos->col; /* start looking for a match at sstart */
2917 start_idx = idx; /* remember the first END pattern. */
2918 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2919 for (;;)
2920 {
2921 /*
2922 * Find end pattern that matches first after "matchcol".
2923 */
2924 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002925 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 {
2927 int lc_col = matchcol;
2928
Bram Moolenaar860cae12010-06-05 23:22:07 +02002929 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2931 break;
2932 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2933 if (lc_col < 0)
2934 lc_col = 0;
2935
2936 regmatch.rmm_ic = spp->sp_ic;
2937 regmatch.regprog = spp->sp_prog;
2938 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2939 {
2940 if (best_idx == -1 || regmatch.startpos[0].col
2941 < best_regmatch.startpos[0].col)
2942 {
2943 best_idx = idx;
2944 best_regmatch.startpos[0] = regmatch.startpos[0];
2945 best_regmatch.endpos[0] = regmatch.endpos[0];
2946 }
2947 }
2948 }
2949
2950 /*
2951 * If all end patterns have been tried, and there is no match, the
2952 * item continues until end-of-line.
2953 */
2954 if (best_idx == -1)
2955 break;
2956
2957 /*
2958 * If the skip pattern matches before the end pattern,
2959 * continue searching after the skip pattern.
2960 */
2961 if (spp_skip != NULL)
2962 {
2963 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2964
2965 if (lc_col < 0)
2966 lc_col = 0;
2967 regmatch.rmm_ic = spp_skip->sp_ic;
2968 regmatch.regprog = spp_skip->sp_prog;
2969 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2970 && regmatch.startpos[0].col
2971 <= best_regmatch.startpos[0].col)
2972 {
2973 /* Add offset to skip pattern match */
2974 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2975
2976 /* If the skip pattern goes on to the next line, there is no
2977 * match with an end pattern in this line. */
2978 if (pos.lnum > startpos->lnum)
2979 break;
2980
2981 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2982
2983 /* take care of an empty match or negative offset */
2984 if (pos.col <= matchcol)
2985 ++matchcol;
2986 else if (pos.col <= regmatch.endpos[0].col)
2987 matchcol = pos.col;
2988 else
2989 /* Be careful not to jump over the NUL at the end-of-line */
2990 for (matchcol = regmatch.endpos[0].col;
2991 line[matchcol] != NUL && matchcol < pos.col;
2992 ++matchcol)
2993 ;
2994
2995 /* if the skip pattern includes end-of-line, break here */
2996 if (line[matchcol] == NUL)
2997 break;
2998
2999 continue; /* start with first end pattern again */
3000 }
3001 }
3002
3003 /*
3004 * Match from start pattern to end pattern.
3005 * Correct for match and highlight offset of end pattern.
3006 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003007 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3009 /* can't end before the start */
3010 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3011 m_endpos->col = startpos->col;
3012
3013 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3014 /* can't end before the start */
3015 if (end_endpos->lnum == startpos->lnum
3016 && end_endpos->col < startpos->col)
3017 end_endpos->col = startpos->col;
3018 /* can't end after the match */
3019 limit_pos(end_endpos, m_endpos);
3020
3021 /*
3022 * If the end group is highlighted differently, adjust the pointers.
3023 */
3024 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3025 {
3026 *end_idx = best_idx;
3027 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3028 {
3029 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3030 hl_endpos->col = best_regmatch.endpos[0].col;
3031 }
3032 else
3033 {
3034 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3035 hl_endpos->col = best_regmatch.startpos[0].col;
3036 }
3037 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3038
3039 /* can't end before the start */
3040 if (hl_endpos->lnum == startpos->lnum
3041 && hl_endpos->col < startpos->col)
3042 hl_endpos->col = startpos->col;
3043 limit_pos(hl_endpos, m_endpos);
3044
3045 /* now the match ends where the highlighting ends, it is turned
3046 * into the matchgroup for the end */
3047 *m_endpos = *hl_endpos;
3048 }
3049 else
3050 {
3051 *end_idx = 0;
3052 *hl_endpos = *end_endpos;
3053 }
3054
3055 *flagsp = spp->sp_flags;
3056
3057 had_match = TRUE;
3058 break;
3059 }
3060
3061 /* no match for an END pattern in this line */
3062 if (!had_match)
3063 m_endpos->lnum = 0;
3064
3065 /* Remove external matches. */
3066 unref_extmatch(re_extmatch_in);
3067 re_extmatch_in = NULL;
3068}
3069
3070/*
3071 * Limit "pos" not to be after "limit".
3072 */
3073 static void
3074limit_pos(pos, limit)
3075 lpos_T *pos;
3076 lpos_T *limit;
3077{
3078 if (pos->lnum > limit->lnum)
3079 *pos = *limit;
3080 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3081 pos->col = limit->col;
3082}
3083
3084/*
3085 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3086 */
3087 static void
3088limit_pos_zero(pos, limit)
3089 lpos_T *pos;
3090 lpos_T *limit;
3091{
3092 if (pos->lnum == 0)
3093 *pos = *limit;
3094 else
3095 limit_pos(pos, limit);
3096}
3097
3098/*
3099 * Add offset to matched text for end of match or highlight.
3100 */
3101 static void
3102syn_add_end_off(result, regmatch, spp, idx, extra)
3103 lpos_T *result; /* returned position */
3104 regmmatch_T *regmatch; /* start/end of match */
3105 synpat_T *spp; /* matched pattern */
3106 int idx; /* index of offset */
3107 int extra; /* extra chars for offset to start */
3108{
3109 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003110 int off;
3111 char_u *base;
3112 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003113
3114 if (spp->sp_off_flags & (1 << idx))
3115 {
3116 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003117 col = regmatch->startpos[0].col;
3118 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003119 }
3120 else
3121 {
3122 result->lnum = regmatch->endpos[0].lnum;
3123 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003124 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003125 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003126 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3127 * is a matchgroup. Watch out for match with last NL in the buffer. */
3128 if (result->lnum > syn_buf->b_ml.ml_line_count)
3129 col = 0;
3130 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003131 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003132 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3133 p = base + col;
3134 if (off > 0)
3135 {
3136 while (off-- > 0 && *p != NUL)
3137 mb_ptr_adv(p);
3138 }
3139 else if (off < 0)
3140 {
3141 while (off++ < 0 && base < p)
3142 mb_ptr_back(base, p);
3143 }
3144 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003145 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003146 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003147}
3148
3149/*
3150 * Add offset to matched text for start of match or highlight.
3151 * Avoid resulting column to become negative.
3152 */
3153 static void
3154syn_add_start_off(result, regmatch, spp, idx, extra)
3155 lpos_T *result; /* returned position */
3156 regmmatch_T *regmatch; /* start/end of match */
3157 synpat_T *spp;
3158 int idx;
3159 int extra; /* extra chars for offset to end */
3160{
3161 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003162 int off;
3163 char_u *base;
3164 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165
3166 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3167 {
3168 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003169 col = regmatch->endpos[0].col;
3170 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171 }
3172 else
3173 {
3174 result->lnum = regmatch->startpos[0].lnum;
3175 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003176 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003178 if (result->lnum > syn_buf->b_ml.ml_line_count)
3179 {
3180 /* a "\n" at the end of the pattern may take us below the last line */
3181 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003182 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003183 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003184 if (off != 0)
3185 {
3186 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3187 p = base + col;
3188 if (off > 0)
3189 {
3190 while (off-- && *p != NUL)
3191 mb_ptr_adv(p);
3192 }
3193 else if (off < 0)
3194 {
3195 while (off++ && base < p)
3196 mb_ptr_back(base, p);
3197 }
3198 col = (int)(p - base);
3199 }
3200 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003201}
3202
3203/*
3204 * Get current line in syntax buffer.
3205 */
3206 static char_u *
3207syn_getcurline()
3208{
3209 return ml_get_buf(syn_buf, current_lnum, FALSE);
3210}
3211
3212/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003213 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003214 * Returns TRUE when there is a match.
3215 */
3216 static int
3217syn_regexec(rmp, lnum, col)
3218 regmmatch_T *rmp;
3219 linenr_T lnum;
3220 colnr_T col;
3221{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003222 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003223 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003224 {
3225 rmp->startpos[0].lnum += lnum;
3226 rmp->endpos[0].lnum += lnum;
3227 return TRUE;
3228 }
3229 return FALSE;
3230}
3231
3232/*
3233 * Check one position in a line for a matching keyword.
3234 * The caller must check if a keyword can start at startcol.
3235 * Return it's ID if found, 0 otherwise.
3236 */
3237 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003238check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239 char_u *line;
3240 int startcol; /* position in line to check for keyword */
3241 int *endcolp; /* return: character after found keyword */
3242 long *flagsp; /* return: flags of matching keyword */
3243 short **next_listp; /* return: next_list of matching keyword */
3244 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003245 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003247 keyentry_T *kp;
3248 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003249 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003250 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003252 hashtab_T *ht;
3253 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254
3255 /* Find first character after the keyword. First character was already
3256 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003257 kwp = line + startcol;
3258 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259 do
3260 {
3261#ifdef FEAT_MBYTE
3262 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003263 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 else
3265#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003266 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003268 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269
Bram Moolenaardad6b692005-01-25 22:14:34 +00003270 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 return 0;
3272
3273 /*
3274 * Must make a copy of the keyword, so we can add a NUL and make it
3275 * lowercase.
3276 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003277 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278
3279 /*
3280 * Try twice:
3281 * 1. matching case
3282 * 2. ignoring case
3283 */
3284 for (round = 1; round <= 2; ++round)
3285 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003286 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003287 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003289 if (round == 2) /* ignore case */
3290 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291
3292 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003293 * Find keywords that match. There can be several with different
3294 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295 * When current_next_list is non-zero accept only that group, otherwise:
3296 * Accept a not-contained keyword at toplevel.
3297 * Accept a keyword at other levels only if it is in the contains list.
3298 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003299 hi = hash_find(ht, keyword);
3300 if (!HASHITEM_EMPTY(hi))
3301 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003303 if (current_next_list != 0
3304 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3305 : (cur_si == NULL
3306 ? !(kp->flags & HL_CONTAINED)
3307 : in_id_list(cur_si, cur_si->si_cont_list,
3308 &kp->k_syn, kp->flags & HL_CONTAINED)))
3309 {
3310 *endcolp = startcol + kwlen;
3311 *flagsp = kp->flags;
3312 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003313#ifdef FEAT_CONCEAL
3314 *ccharp = kp->k_char;
3315#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003316 return kp->k_syn.id;
3317 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318 }
3319 }
3320 return 0;
3321}
3322
3323/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003324 * Handle ":syntax conceal" command.
3325 */
3326 static void
3327syn_cmd_conceal(eap, syncing)
3328 exarg_T *eap UNUSED;
3329 int syncing UNUSED;
3330{
3331#ifdef FEAT_CONCEAL
3332 char_u *arg = eap->arg;
3333 char_u *next;
3334
3335 eap->nextcmd = find_nextcmd(arg);
3336 if (eap->skip)
3337 return;
3338
3339 next = skiptowhite(arg);
3340 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3341 curwin->w_s->b_syn_conceal = TRUE;
3342 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3343 curwin->w_s->b_syn_conceal = FALSE;
3344 else
3345 EMSG2(_("E390: Illegal argument: %s"), arg);
3346#endif
3347}
3348
3349/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 * Handle ":syntax case" command.
3351 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 static void
3353syn_cmd_case(eap, syncing)
3354 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003355 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356{
3357 char_u *arg = eap->arg;
3358 char_u *next;
3359
3360 eap->nextcmd = find_nextcmd(arg);
3361 if (eap->skip)
3362 return;
3363
3364 next = skiptowhite(arg);
3365 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003366 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003368 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003369 else
3370 EMSG2(_("E390: Illegal argument: %s"), arg);
3371}
3372
3373/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003374 * Handle ":syntax spell" command.
3375 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003376 static void
3377syn_cmd_spell(eap, syncing)
3378 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003379 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003380{
3381 char_u *arg = eap->arg;
3382 char_u *next;
3383
3384 eap->nextcmd = find_nextcmd(arg);
3385 if (eap->skip)
3386 return;
3387
3388 next = skiptowhite(arg);
3389 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003390 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003391 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003392 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003393 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003394 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003395 else
3396 EMSG2(_("E390: Illegal argument: %s"), arg);
3397}
3398
3399/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003400 * Clear all syntax info for one buffer.
3401 */
3402 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003403syntax_clear(block)
3404 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003405{
3406 int i;
3407
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408 block->b_syn_error = FALSE; /* clear previous error */
3409 block->b_syn_ic = FALSE; /* Use case, by default */
3410 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3411 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412
3413 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003414 clear_keywtab(&block->b_keywtab);
3415 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416
3417 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003418 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3419 syn_clear_pattern(block, i);
3420 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421
3422 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3424 syn_clear_cluster(block, i);
3425 ga_clear(&block->b_syn_clusters);
3426 block->b_spell_cluster_id = 0;
3427 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428
Bram Moolenaar860cae12010-06-05 23:22:07 +02003429 block->b_syn_sync_flags = 0;
3430 block->b_syn_sync_minlines = 0;
3431 block->b_syn_sync_maxlines = 0;
3432 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433
Bram Moolenaar860cae12010-06-05 23:22:07 +02003434 vim_free(block->b_syn_linecont_prog);
3435 block->b_syn_linecont_prog = NULL;
3436 vim_free(block->b_syn_linecont_pat);
3437 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003438#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003439 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440#endif
3441
3442 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003443 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003444 invalidate_current_state();
3445}
3446
3447/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003448 * Get rid of ownsyntax for window "wp".
3449 */
3450 void
3451reset_synblock(wp)
3452 win_T *wp;
3453{
3454 if (wp->w_s != &wp->w_buffer->b_s)
3455 {
3456 syntax_clear(wp->w_s);
3457 vim_free(wp->w_s);
3458 wp->w_s = &wp->w_buffer->b_s;
3459 }
3460}
3461
3462/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003463 * Clear syncing info for one buffer.
3464 */
3465 static void
3466syntax_sync_clear()
3467{
3468 int i;
3469
3470 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003471 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3472 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3473 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475 curwin->w_s->b_syn_sync_flags = 0;
3476 curwin->w_s->b_syn_sync_minlines = 0;
3477 curwin->w_s->b_syn_sync_maxlines = 0;
3478 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479
Bram Moolenaar860cae12010-06-05 23:22:07 +02003480 vim_free(curwin->w_s->b_syn_linecont_prog);
3481 curwin->w_s->b_syn_linecont_prog = NULL;
3482 vim_free(curwin->w_s->b_syn_linecont_pat);
3483 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484
Bram Moolenaar860cae12010-06-05 23:22:07 +02003485 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486}
3487
3488/*
3489 * Remove one pattern from the buffer's pattern list.
3490 */
3491 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003492syn_remove_pattern(block, idx)
3493 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 int idx;
3495{
3496 synpat_T *spp;
3497
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003499#ifdef FEAT_FOLDING
3500 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003501 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003505 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3506 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507}
3508
3509/*
3510 * Clear and free one syntax pattern. When clearing all, must be called from
3511 * last to first!
3512 */
3513 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003514syn_clear_pattern(block, i)
3515 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516 int i;
3517{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003518 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3519 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003520 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003521 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003523 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3524 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3525 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526 }
3527}
3528
3529/*
3530 * Clear and free one syntax cluster.
3531 */
3532 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003533syn_clear_cluster(block, i)
3534 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003535 int i;
3536{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003537 vim_free(SYN_CLSTR(block)[i].scl_name);
3538 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3539 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540}
3541
3542/*
3543 * Handle ":syntax clear" command.
3544 */
3545 static void
3546syn_cmd_clear(eap, syncing)
3547 exarg_T *eap;
3548 int syncing;
3549{
3550 char_u *arg = eap->arg;
3551 char_u *arg_end;
3552 int id;
3553
3554 eap->nextcmd = find_nextcmd(arg);
3555 if (eap->skip)
3556 return;
3557
3558 /*
3559 * We have to disable this within ":syn include @group filename",
3560 * because otherwise @group would get deleted.
3561 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3562 * clear".
3563 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003564 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003565 return;
3566
3567 if (ends_excmd(*arg))
3568 {
3569 /*
3570 * No argument: Clear all syntax items.
3571 */
3572 if (syncing)
3573 syntax_sync_clear();
3574 else
3575 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003576 syntax_clear(curwin->w_s);
3577 if (curwin->w_s == &curwin->w_buffer->b_s)
3578 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003579 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580 }
3581 }
3582 else
3583 {
3584 /*
3585 * Clear the group IDs that are in the argument.
3586 */
3587 while (!ends_excmd(*arg))
3588 {
3589 arg_end = skiptowhite(arg);
3590 if (*arg == '@')
3591 {
3592 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3593 if (id == 0)
3594 {
3595 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3596 break;
3597 }
3598 else
3599 {
3600 /*
3601 * We can't physically delete a cluster without changing
3602 * the IDs of other clusters, so we do the next best thing
3603 * and make it empty.
3604 */
3605 short scl_id = id - SYNID_CLUSTER;
3606
Bram Moolenaar860cae12010-06-05 23:22:07 +02003607 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3608 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609 }
3610 }
3611 else
3612 {
3613 id = syn_namen2id(arg, (int)(arg_end - arg));
3614 if (id == 0)
3615 {
3616 EMSG2(_(e_nogroup), arg);
3617 break;
3618 }
3619 else
3620 syn_clear_one(id, syncing);
3621 }
3622 arg = skipwhite(arg_end);
3623 }
3624 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003625 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003626 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627}
3628
3629/*
3630 * Clear one syntax group for the current buffer.
3631 */
3632 static void
3633syn_clear_one(id, syncing)
3634 int id;
3635 int syncing;
3636{
3637 synpat_T *spp;
3638 int idx;
3639
3640 /* Clear keywords only when not ":syn sync clear group-name" */
3641 if (!syncing)
3642 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3644 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 }
3646
3647 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003648 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003650 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3652 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003653 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 }
3655}
3656
3657/*
3658 * Handle ":syntax on" command.
3659 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660 static void
3661syn_cmd_on(eap, syncing)
3662 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003663 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664{
3665 syn_cmd_onoff(eap, "syntax");
3666}
3667
3668/*
3669 * Handle ":syntax enable" command.
3670 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671 static void
3672syn_cmd_enable(eap, syncing)
3673 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003674 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675{
3676 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3677 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003678 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679}
3680
3681/*
3682 * Handle ":syntax reset" command.
3683 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684 static void
3685syn_cmd_reset(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 eap->nextcmd = check_nextcmd(eap->arg);
3690 if (!eap->skip)
3691 {
3692 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3693 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003694 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 }
3696}
3697
3698/*
3699 * Handle ":syntax manual" command.
3700 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003701 static void
3702syn_cmd_manual(eap, syncing)
3703 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003704 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003705{
3706 syn_cmd_onoff(eap, "manual");
3707}
3708
3709/*
3710 * Handle ":syntax off" command.
3711 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 static void
3713syn_cmd_off(eap, syncing)
3714 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003715 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716{
3717 syn_cmd_onoff(eap, "nosyntax");
3718}
3719
3720 static void
3721syn_cmd_onoff(eap, name)
3722 exarg_T *eap;
3723 char *name;
3724{
3725 char_u buf[100];
3726
3727 eap->nextcmd = check_nextcmd(eap->arg);
3728 if (!eap->skip)
3729 {
3730 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003731 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003732 do_cmdline_cmd(buf);
3733 }
3734}
3735
3736/*
3737 * Handle ":syntax [list]" command: list current syntax words.
3738 */
3739 static void
3740syn_cmd_list(eap, syncing)
3741 exarg_T *eap;
3742 int syncing; /* when TRUE: list syncing items */
3743{
3744 char_u *arg = eap->arg;
3745 int id;
3746 char_u *arg_end;
3747
3748 eap->nextcmd = find_nextcmd(arg);
3749 if (eap->skip)
3750 return;
3751
Bram Moolenaar860cae12010-06-05 23:22:07 +02003752 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003753 {
3754 MSG(_("No Syntax items defined for this buffer"));
3755 return;
3756 }
3757
3758 if (syncing)
3759 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003760 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003761 {
3762 MSG_PUTS(_("syncing on C-style comments"));
3763 syn_lines_msg();
3764 syn_match_msg();
3765 return;
3766 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003767 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003769 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003770 MSG_PUTS(_("no syncing"));
3771 else
3772 {
3773 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003774 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003775 MSG_PUTS(_(" lines before top line"));
3776 syn_match_msg();
3777 }
3778 return;
3779 }
3780 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003781 if (curwin->w_s->b_syn_sync_minlines > 0
3782 || curwin->w_s->b_syn_sync_maxlines > 0
3783 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003784 {
3785 MSG_PUTS(_("\nsyncing on items"));
3786 syn_lines_msg();
3787 syn_match_msg();
3788 }
3789 }
3790 else
3791 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3792 if (ends_excmd(*arg))
3793 {
3794 /*
3795 * No argument: List all group IDs and all syntax clusters.
3796 */
3797 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3798 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003799 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800 syn_list_cluster(id);
3801 }
3802 else
3803 {
3804 /*
3805 * List the group IDs and syntax clusters that are in the argument.
3806 */
3807 while (!ends_excmd(*arg) && !got_int)
3808 {
3809 arg_end = skiptowhite(arg);
3810 if (*arg == '@')
3811 {
3812 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3813 if (id == 0)
3814 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3815 else
3816 syn_list_cluster(id - SYNID_CLUSTER);
3817 }
3818 else
3819 {
3820 id = syn_namen2id(arg, (int)(arg_end - arg));
3821 if (id == 0)
3822 EMSG2(_(e_nogroup), arg);
3823 else
3824 syn_list_one(id, syncing, TRUE);
3825 }
3826 arg = skipwhite(arg_end);
3827 }
3828 }
3829 eap->nextcmd = check_nextcmd(arg);
3830}
3831
3832 static void
3833syn_lines_msg()
3834{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003835 if (curwin->w_s->b_syn_sync_maxlines > 0
3836 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 {
3838 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003839 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 {
3841 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003842 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3843 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 MSG_PUTS(", ");
3845 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847 {
3848 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003849 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 }
3851 MSG_PUTS(_(" lines before top line"));
3852 }
3853}
3854
3855 static void
3856syn_match_msg()
3857{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003858 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 {
3860 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003861 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 MSG_PUTS(_(" line breaks"));
3863 }
3864}
3865
3866static int last_matchgroup;
3867
3868struct name_list
3869{
3870 int flag;
3871 char *name;
3872};
3873
3874static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3875
3876/*
3877 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3878 */
3879 static void
3880syn_list_one(id, syncing, link_only)
3881 int id;
3882 int syncing; /* when TRUE: list syncing items */
3883 int link_only; /* when TRUE; list link-only too */
3884{
3885 int attr;
3886 int idx;
3887 int did_header = FALSE;
3888 synpat_T *spp;
3889 static struct name_list namelist1[] =
3890 {
3891 {HL_DISPLAY, "display"},
3892 {HL_CONTAINED, "contained"},
3893 {HL_ONELINE, "oneline"},
3894 {HL_KEEPEND, "keepend"},
3895 {HL_EXTEND, "extend"},
3896 {HL_EXCLUDENL, "excludenl"},
3897 {HL_TRANSP, "transparent"},
3898 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003899#ifdef FEAT_CONCEAL
3900 {HL_CONCEAL, "conceal"},
3901 {HL_CONCEALENDS, "concealends"},
3902#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903 {0, NULL}
3904 };
3905 static struct name_list namelist2[] =
3906 {
3907 {HL_SKIPWHITE, "skipwhite"},
3908 {HL_SKIPNL, "skipnl"},
3909 {HL_SKIPEMPTY, "skipempty"},
3910 {0, NULL}
3911 };
3912
3913 attr = hl_attr(HLF_D); /* highlight like directories */
3914
3915 /* list the keywords for "id" */
3916 if (!syncing)
3917 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003918 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3919 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003920 did_header, attr);
3921 }
3922
3923 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003924 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003926 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3928 continue;
3929
3930 (void)syn_list_header(did_header, 999, id);
3931 did_header = TRUE;
3932 last_matchgroup = 0;
3933 if (spp->sp_type == SPTYPE_MATCH)
3934 {
3935 put_pattern("match", ' ', spp, attr);
3936 msg_putchar(' ');
3937 }
3938 else if (spp->sp_type == SPTYPE_START)
3939 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003940 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3941 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3942 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3943 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3944 while (idx < curwin->w_s->b_syn_patterns.ga_len
3945 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3946 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 --idx;
3948 msg_putchar(' ');
3949 }
3950 syn_list_flags(namelist1, spp->sp_flags, attr);
3951
3952 if (spp->sp_cont_list != NULL)
3953 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3954
3955 if (spp->sp_syn.cont_in_list != NULL)
3956 put_id_list((char_u *)"containedin",
3957 spp->sp_syn.cont_in_list, attr);
3958
3959 if (spp->sp_next_list != NULL)
3960 {
3961 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3962 syn_list_flags(namelist2, spp->sp_flags, attr);
3963 }
3964 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3965 {
3966 if (spp->sp_flags & HL_SYNC_HERE)
3967 msg_puts_attr((char_u *)"grouphere", attr);
3968 else
3969 msg_puts_attr((char_u *)"groupthere", attr);
3970 msg_putchar(' ');
3971 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003972 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3974 else
3975 MSG_PUTS("NONE");
3976 msg_putchar(' ');
3977 }
3978 }
3979
3980 /* list the link, if there is one */
3981 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3982 {
3983 (void)syn_list_header(did_header, 999, id);
3984 msg_puts_attr((char_u *)"links to", attr);
3985 msg_putchar(' ');
3986 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3987 }
3988}
3989
3990 static void
3991syn_list_flags(nl, flags, attr)
3992 struct name_list *nl;
3993 int flags;
3994 int attr;
3995{
3996 int i;
3997
3998 for (i = 0; nl[i].flag != 0; ++i)
3999 if (flags & nl[i].flag)
4000 {
4001 msg_puts_attr((char_u *)nl[i].name, attr);
4002 msg_putchar(' ');
4003 }
4004}
4005
4006/*
4007 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4008 */
4009 static void
4010syn_list_cluster(id)
4011 int id;
4012{
4013 int endcol = 15;
4014
4015 /* slight hack: roughly duplicate the guts of syn_list_header() */
4016 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004017 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018
4019 if (msg_col >= endcol) /* output at least one space */
4020 endcol = msg_col + 1;
4021 if (Columns <= endcol) /* avoid hang for tiny window */
4022 endcol = Columns - 1;
4023
4024 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004025 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004027 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028 hl_attr(HLF_D));
4029 }
4030 else
4031 {
4032 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4033 msg_puts((char_u *)"=NONE");
4034 }
4035}
4036
4037 static void
4038put_id_list(name, list, attr)
4039 char_u *name;
4040 short *list;
4041 int attr;
4042{
4043 short *p;
4044
4045 msg_puts_attr(name, attr);
4046 msg_putchar('=');
4047 for (p = list; *p; ++p)
4048 {
4049 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4050 {
4051 if (p[1])
4052 MSG_PUTS("ALLBUT");
4053 else
4054 MSG_PUTS("ALL");
4055 }
4056 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4057 {
4058 MSG_PUTS("TOP");
4059 }
4060 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4061 {
4062 MSG_PUTS("CONTAINED");
4063 }
4064 else if (*p >= SYNID_CLUSTER)
4065 {
4066 short scl_id = *p - SYNID_CLUSTER;
4067
4068 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004069 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070 }
4071 else
4072 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4073 if (p[1])
4074 msg_putchar(',');
4075 }
4076 msg_putchar(' ');
4077}
4078
4079 static void
4080put_pattern(s, c, spp, attr)
4081 char *s;
4082 int c;
4083 synpat_T *spp;
4084 int attr;
4085{
4086 long n;
4087 int mask;
4088 int first;
4089 static char *sepchars = "/+=-#@\"|'^&";
4090 int i;
4091
4092 /* May have to write "matchgroup=group" */
4093 if (last_matchgroup != spp->sp_syn_match_id)
4094 {
4095 last_matchgroup = spp->sp_syn_match_id;
4096 msg_puts_attr((char_u *)"matchgroup", attr);
4097 msg_putchar('=');
4098 if (last_matchgroup == 0)
4099 msg_outtrans((char_u *)"NONE");
4100 else
4101 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4102 msg_putchar(' ');
4103 }
4104
4105 /* output the name of the pattern and an '=' or ' ' */
4106 msg_puts_attr((char_u *)s, attr);
4107 msg_putchar(c);
4108
4109 /* output the pattern, in between a char that is not in the pattern */
4110 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4111 if (sepchars[++i] == NUL)
4112 {
4113 i = 0; /* no good char found, just use the first one */
4114 break;
4115 }
4116 msg_putchar(sepchars[i]);
4117 msg_outtrans(spp->sp_pattern);
4118 msg_putchar(sepchars[i]);
4119
4120 /* output any pattern options */
4121 first = TRUE;
4122 for (i = 0; i < SPO_COUNT; ++i)
4123 {
4124 mask = (1 << i);
4125 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4126 {
4127 if (!first)
4128 msg_putchar(','); /* separate with commas */
4129 msg_puts((char_u *)spo_name_tab[i]);
4130 n = spp->sp_offsets[i];
4131 if (i != SPO_LC_OFF)
4132 {
4133 if (spp->sp_off_flags & mask)
4134 msg_putchar('s');
4135 else
4136 msg_putchar('e');
4137 if (n > 0)
4138 msg_putchar('+');
4139 }
4140 if (n || i == SPO_LC_OFF)
4141 msg_outnum(n);
4142 first = FALSE;
4143 }
4144 }
4145 msg_putchar(' ');
4146}
4147
4148/*
4149 * List or clear the keywords for one syntax group.
4150 * Return TRUE if the header has been printed.
4151 */
4152 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004153syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004155 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 int did_header; /* header has already been printed */
4157 int attr;
4158{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004160 hashitem_T *hi;
4161 keyentry_T *kp;
4162 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004163 int prev_contained = 0;
4164 short *prev_next_list = NULL;
4165 short *prev_cont_in_list = NULL;
4166 int prev_skipnl = 0;
4167 int prev_skipwhite = 0;
4168 int prev_skipempty = 0;
4169
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 /*
4171 * Unfortunately, this list of keywords is not sorted on alphabet but on
4172 * hash value...
4173 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004174 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004175 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004177 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004179 --todo;
4180 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004182 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004184 if (prev_contained != (kp->flags & HL_CONTAINED)
4185 || prev_skipnl != (kp->flags & HL_SKIPNL)
4186 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4187 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4188 || prev_cont_in_list != kp->k_syn.cont_in_list
4189 || prev_next_list != kp->next_list)
4190 outlen = 9999;
4191 else
4192 outlen = (int)STRLEN(kp->keyword);
4193 /* output "contained" and "nextgroup" on each line */
4194 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004196 prev_contained = 0;
4197 prev_next_list = NULL;
4198 prev_cont_in_list = NULL;
4199 prev_skipnl = 0;
4200 prev_skipwhite = 0;
4201 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004203 did_header = TRUE;
4204 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004206 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004208 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004210 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004212 put_id_list((char_u *)"containedin",
4213 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004215 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004217 if (kp->next_list != prev_next_list)
4218 {
4219 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4220 msg_putchar(' ');
4221 prev_next_list = kp->next_list;
4222 if (kp->flags & HL_SKIPNL)
4223 {
4224 msg_puts_attr((char_u *)"skipnl", attr);
4225 msg_putchar(' ');
4226 prev_skipnl = (kp->flags & HL_SKIPNL);
4227 }
4228 if (kp->flags & HL_SKIPWHITE)
4229 {
4230 msg_puts_attr((char_u *)"skipwhite", attr);
4231 msg_putchar(' ');
4232 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4233 }
4234 if (kp->flags & HL_SKIPEMPTY)
4235 {
4236 msg_puts_attr((char_u *)"skipempty", attr);
4237 msg_putchar(' ');
4238 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4239 }
4240 }
4241 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 }
4244 }
4245 }
4246
4247 return did_header;
4248}
4249
4250 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004251syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004252 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004253 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004255 hashitem_T *hi;
4256 keyentry_T *kp;
4257 keyentry_T *kp_prev;
4258 keyentry_T *kp_next;
4259 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260
Bram Moolenaardad6b692005-01-25 22:14:34 +00004261 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004262 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004265 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004267 --todo;
4268 kp_prev = NULL;
4269 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004270 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004271 if (kp->k_syn.id == id)
4272 {
4273 kp_next = kp->ke_next;
4274 if (kp_prev == NULL)
4275 {
4276 if (kp_next == NULL)
4277 hash_remove(ht, hi);
4278 else
4279 hi->hi_key = KE2HIKEY(kp_next);
4280 }
4281 else
4282 kp_prev->ke_next = kp_next;
4283 vim_free(kp->next_list);
4284 vim_free(kp->k_syn.cont_in_list);
4285 vim_free(kp);
4286 kp = kp_next;
4287 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004289 {
4290 kp_prev = kp;
4291 kp = kp->ke_next;
4292 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293 }
4294 }
4295 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297}
4298
4299/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 */
4302 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004303clear_keywtab(ht)
4304 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004306 hashitem_T *hi;
4307 int todo;
4308 keyentry_T *kp;
4309 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004311 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004312 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004314 if (!HASHITEM_EMPTY(hi))
4315 {
4316 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004317 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004319 kp_next = kp->ke_next;
4320 vim_free(kp->next_list);
4321 vim_free(kp->k_syn.cont_in_list);
4322 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004324 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 hash_clear(ht);
4327 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328}
4329
4330/*
4331 * Add a keyword to the list of keywords.
4332 */
4333 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004334add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 char_u *name; /* name of keyword */
4336 int id; /* group ID for this keyword */
4337 int flags; /* flags for this keyword */
4338 short *cont_in_list; /* containedin for this keyword */
4339 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004340 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004342 keyentry_T *kp;
4343 hashtab_T *ht;
4344 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004345 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004346 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004347 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348
Bram Moolenaar860cae12010-06-05 23:22:07 +02004349 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004350 name_ic = str_foldcase(name, (int)STRLEN(name),
4351 name_folded, MAXKEYWLEN + 1);
4352 else
4353 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4355 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 kp->k_syn.id = id;
4359 kp->k_syn.inc_tag = current_syn_inc_tag;
4360 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004361 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004364 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366
Bram Moolenaar860cae12010-06-05 23:22:07 +02004367 if (curwin->w_s->b_syn_ic)
4368 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004370 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371
Bram Moolenaardad6b692005-01-25 22:14:34 +00004372 hash = hash_hash(kp->keyword);
4373 hi = hash_lookup(ht, kp->keyword, hash);
4374 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004376 /* new keyword, add to hashtable */
4377 kp->ke_next = NULL;
4378 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004382 /* keyword already exists, prepend to list */
4383 kp->ke_next = HI2KE(hi);
4384 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386}
4387
4388/*
4389 * Get the start and end of the group name argument.
4390 * Return a pointer to the first argument.
4391 * Return NULL if the end of the command was found instead of further args.
4392 */
4393 static char_u *
4394get_group_name(arg, name_end)
4395 char_u *arg; /* start of the argument */
4396 char_u **name_end; /* pointer to end of the name */
4397{
4398 char_u *rest;
4399
4400 *name_end = skiptowhite(arg);
4401 rest = skipwhite(*name_end);
4402
4403 /*
4404 * Check if there are enough arguments. The first argument may be a
4405 * pattern, where '|' is allowed, so only check for NUL.
4406 */
4407 if (ends_excmd(*arg) || *rest == NUL)
4408 return NULL;
4409 return rest;
4410}
4411
4412/*
4413 * Check for syntax command option arguments.
4414 * This can be called at any place in the list of arguments, and just picks
4415 * out the arguments that are known. Can be called several times in a row to
4416 * collect all options in between other arguments.
4417 * Return a pointer to the next argument (which isn't an option).
4418 * Return NULL for any error;
4419 */
4420 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004421get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004422 char_u *arg; /* next argument to be checked */
4423 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004424 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004425{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004426 char_u *gname_start, *gname;
4427 int syn_id;
4428 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004429 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430 int i;
4431 int fidx;
4432 static struct flag
4433 {
4434 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004435 int argtype;
4436 int flags;
4437 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4438 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4439 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4440 {"eExXtTeEnNdD", 0, HL_EXTEND},
4441 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4442 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4443 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4444 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4445 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4446 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4447 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4448 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4449 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004450 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4451 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4452 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004453 {"cCoOnNtTaAiInNsS", 1, 0},
4454 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4455 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004457 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458
4459 if (arg == NULL) /* already detected error */
4460 return NULL;
4461
Bram Moolenaar860cae12010-06-05 23:22:07 +02004462#ifdef FEAT_CONCEAL
4463 if (curwin->w_s->b_syn_conceal)
4464 opt->flags |= HL_CONCEAL;
4465#endif
4466
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 for (;;)
4468 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004469 /*
4470 * This is used very often when a large number of keywords is defined.
4471 * Need to skip quickly when no option name is found.
4472 * Also avoid tolower(), it's slow.
4473 */
4474 if (strchr(first_letters, *arg) == NULL)
4475 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476
4477 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4478 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004479 p = flagtab[fidx].name;
4480 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4481 if (arg[len] != p[i] && arg[len] != p[i + 1])
4482 break;
4483 if (p[i] == NUL && (vim_iswhite(arg[len])
4484 || (flagtab[fidx].argtype > 0
4485 ? arg[len] == '='
4486 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004488 if (opt->keyword
4489 && (flagtab[fidx].flags == HL_DISPLAY
4490 || flagtab[fidx].flags == HL_FOLD
4491 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 /* treat "display", "fold" and "extend" as a keyword */
4493 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 break;
4495 }
4496 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 if (fidx < 0) /* no match found */
4498 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004500 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004502 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 {
4504 EMSG(_("E395: contains argument not accepted here"));
4505 return NULL;
4506 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004507 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004508 return NULL;
4509 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004510 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004512 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513 return NULL;
4514 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 return NULL;
4519 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004520 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4521 {
4522#ifdef FEAT_MBYTE
4523 /* cchar=? */
4524 if (has_mbyte)
4525 {
4526# ifdef FEAT_CONCEAL
4527 *conceal_char = mb_ptr2char(arg + 6);
4528# endif
4529 arg += mb_ptr2len(arg + 6) - 1;
4530 }
4531 else
4532#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004533 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004534#ifdef FEAT_CONCEAL
4535 *conceal_char = arg[6];
4536#else
4537 ;
4538#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004539 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004540#ifdef FEAT_CONCEAL
4541 if (!vim_isprintc_strict(*conceal_char))
4542 {
4543 EMSG(_("E844: invalid cchar value"));
4544 return NULL;
4545 }
4546#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004547 arg = skipwhite(arg + 7);
4548 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004550 {
4551 opt->flags |= flagtab[fidx].flags;
4552 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004554 if (flagtab[fidx].flags == HL_SYNC_HERE
4555 || flagtab[fidx].flags == HL_SYNC_THERE)
4556 {
4557 if (opt->sync_idx == NULL)
4558 {
4559 EMSG(_("E393: group[t]here not accepted here"));
4560 return NULL;
4561 }
4562 gname_start = arg;
4563 arg = skiptowhite(arg);
4564 if (gname_start == arg)
4565 return NULL;
4566 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4567 if (gname == NULL)
4568 return NULL;
4569 if (STRCMP(gname, "NONE") == 0)
4570 *opt->sync_idx = NONE_IDX;
4571 else
4572 {
4573 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004574 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4575 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4576 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004577 {
4578 *opt->sync_idx = i;
4579 break;
4580 }
4581 if (i < 0)
4582 {
4583 EMSG2(_("E394: Didn't find region item for %s"), gname);
4584 vim_free(gname);
4585 return NULL;
4586 }
4587 }
4588
4589 vim_free(gname);
4590 arg = skipwhite(arg);
4591 }
4592#ifdef FEAT_FOLDING
4593 else if (flagtab[fidx].flags == HL_FOLD
4594 && foldmethodIsSyntax(curwin))
4595 /* Need to update folds later. */
4596 foldUpdateAll(curwin);
4597#endif
4598 }
4599 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600
4601 return arg;
4602}
4603
4604/*
4605 * Adjustments to syntax item when declared in a ":syn include"'d file.
4606 * Set the contained flag, and if the item is not already contained, add it
4607 * to the specified top-level group, if any.
4608 */
4609 static void
4610syn_incl_toplevel(id, flagsp)
4611 int id;
4612 int *flagsp;
4613{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004614 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615 return;
4616 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004617 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618 {
4619 /* We have to alloc this, because syn_combine_list() will free it. */
4620 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004621 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004622
4623 if (grp_list != NULL)
4624 {
4625 grp_list[0] = id;
4626 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004627 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004628 CLUSTER_ADD);
4629 }
4630 }
4631}
4632
4633/*
4634 * Handle ":syntax include [@{group-name}] filename" command.
4635 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 static void
4637syn_cmd_include(eap, syncing)
4638 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004639 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640{
4641 char_u *arg = eap->arg;
4642 int sgl_id = 1;
4643 char_u *group_name_end;
4644 char_u *rest;
4645 char_u *errormsg = NULL;
4646 int prev_toplvl_grp;
4647 int prev_syn_inc_tag;
4648 int source = FALSE;
4649
4650 eap->nextcmd = find_nextcmd(arg);
4651 if (eap->skip)
4652 return;
4653
4654 if (arg[0] == '@')
4655 {
4656 ++arg;
4657 rest = get_group_name(arg, &group_name_end);
4658 if (rest == NULL)
4659 {
4660 EMSG((char_u *)_("E397: Filename required"));
4661 return;
4662 }
4663 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4664 /* separate_nextcmd() and expand_filename() depend on this */
4665 eap->arg = rest;
4666 }
4667
4668 /*
4669 * Everything that's left, up to the next command, should be the
4670 * filename to include.
4671 */
4672 eap->argt |= (XFILE | NOSPC);
4673 separate_nextcmd(eap);
4674 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4675 {
4676 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4677 * file. Need to expand the file name first. In other cases
4678 * ":runtime!" is used. */
4679 source = TRUE;
4680 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4681 {
4682 if (errormsg != NULL)
4683 EMSG(errormsg);
4684 return;
4685 }
4686 }
4687
4688 /*
4689 * Save and restore the existing top-level grouplist id and ":syn
4690 * include" tag around the actual inclusion.
4691 */
4692 prev_syn_inc_tag = current_syn_inc_tag;
4693 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004694 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4695 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004696 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4697 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004699 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 current_syn_inc_tag = prev_syn_inc_tag;
4701}
4702
4703/*
4704 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4705 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004706 static void
4707syn_cmd_keyword(eap, syncing)
4708 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004709 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710{
4711 char_u *arg = eap->arg;
4712 char_u *group_name_end;
4713 int syn_id;
4714 char_u *rest;
4715 char_u *keyword_copy;
4716 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004717 char_u *kw;
4718 syn_opt_arg_T syn_opt_arg;
4719 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004720 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721
4722 rest = get_group_name(arg, &group_name_end);
4723
4724 if (rest != NULL)
4725 {
4726 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4727
4728 /* allocate a buffer, for removing the backslashes in the keyword */
4729 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4730 if (keyword_copy != NULL)
4731 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004732 syn_opt_arg.flags = 0;
4733 syn_opt_arg.keyword = TRUE;
4734 syn_opt_arg.sync_idx = NULL;
4735 syn_opt_arg.has_cont_list = FALSE;
4736 syn_opt_arg.cont_in_list = NULL;
4737 syn_opt_arg.next_list = NULL;
4738
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739 /*
4740 * The options given apply to ALL keywords, so all options must be
4741 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004742 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004743 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004744 cnt = 0;
4745 p = keyword_copy;
4746 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004747 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004748 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004749 if (rest == NULL || ends_excmd(*rest))
4750 break;
4751 /* Copy the keyword, removing backslashes, and add a NUL. */
4752 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004753 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004754 if (*rest == '\\' && rest[1] != NUL)
4755 ++rest;
4756 *p++ = *rest++;
4757 }
4758 *p++ = NUL;
4759 ++cnt;
4760 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004762 if (!eap->skip)
4763 {
4764 /* Adjust flags for use of ":syn include". */
4765 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4766
4767 /*
4768 * 2: Add an entry for each keyword.
4769 */
4770 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4771 {
4772 for (p = vim_strchr(kw, '['); ; )
4773 {
4774 if (p != NULL)
4775 *p = NUL;
4776 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004777 syn_opt_arg.cont_in_list,
4778 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004779 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004780 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004781 if (p[1] == NUL)
4782 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004783 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004784 kw = p + 2; /* skip over the NUL */
4785 break;
4786 }
4787 if (p[1] == ']')
4788 {
4789 kw = p + 1; /* skip over the "]" */
4790 break;
4791 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004792#ifdef FEAT_MBYTE
4793 if (has_mbyte)
4794 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004795 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004796
4797 mch_memmove(p, p + 1, l);
4798 p += l;
4799 }
4800 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004801#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004802 {
4803 p[0] = p[1];
4804 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805 }
4806 }
4807 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004809
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004811 vim_free(syn_opt_arg.cont_in_list);
4812 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 }
4814 }
4815
4816 if (rest != NULL)
4817 eap->nextcmd = check_nextcmd(rest);
4818 else
4819 EMSG2(_(e_invarg2), arg);
4820
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004821 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004822 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823}
4824
4825/*
4826 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4827 *
4828 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4829 */
4830 static void
4831syn_cmd_match(eap, syncing)
4832 exarg_T *eap;
4833 int syncing; /* TRUE for ":syntax sync match .. " */
4834{
4835 char_u *arg = eap->arg;
4836 char_u *group_name_end;
4837 char_u *rest;
4838 synpat_T item; /* the item found in the line */
4839 int syn_id;
4840 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004841 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004842 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004843 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004844
4845 /* Isolate the group name, check for validity */
4846 rest = get_group_name(arg, &group_name_end);
4847
4848 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004849 syn_opt_arg.flags = 0;
4850 syn_opt_arg.keyword = FALSE;
4851 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4852 syn_opt_arg.has_cont_list = TRUE;
4853 syn_opt_arg.cont_list = NULL;
4854 syn_opt_arg.cont_in_list = NULL;
4855 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004856 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857
4858 /* get the pattern. */
4859 init_syn_patterns();
4860 vim_memset(&item, 0, sizeof(item));
4861 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004862 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4863 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004864
4865 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004866 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867
4868 if (rest != NULL) /* all arguments are valid */
4869 {
4870 /*
4871 * Check for trailing command and illegal trailing arguments.
4872 */
4873 eap->nextcmd = check_nextcmd(rest);
4874 if (!ends_excmd(*rest) || eap->skip)
4875 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004876 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004877 && (syn_id = syn_check_group(arg,
4878 (int)(group_name_end - arg))) != 0)
4879 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004880 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881 /*
4882 * Store the pattern in the syn_items list
4883 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004884 idx = curwin->w_s->b_syn_patterns.ga_len;
4885 SYN_ITEMS(curwin->w_s)[idx] = item;
4886 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4887 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4888 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4889 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4890 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4891 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4892 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4893 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004894 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004895#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004896 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004897#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004898 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004899 curwin->w_s->b_syn_containedin = TRUE;
4900 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4901 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902
4903 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004904 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004905 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004907 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004908 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004909#endif
4910
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004911 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004912 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 return; /* don't free the progs and patterns now */
4914 }
4915 }
4916
4917 /*
4918 * Something failed, free the allocated memory.
4919 */
4920 vim_free(item.sp_prog);
4921 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004922 vim_free(syn_opt_arg.cont_list);
4923 vim_free(syn_opt_arg.cont_in_list);
4924 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004925
4926 if (rest == NULL)
4927 EMSG2(_(e_invarg2), arg);
4928}
4929
4930/*
4931 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4932 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4933 */
4934 static void
4935syn_cmd_region(eap, syncing)
4936 exarg_T *eap;
4937 int syncing; /* TRUE for ":syntax sync region .." */
4938{
4939 char_u *arg = eap->arg;
4940 char_u *group_name_end;
4941 char_u *rest; /* next arg, NULL on error */
4942 char_u *key_end;
4943 char_u *key = NULL;
4944 char_u *p;
4945 int item;
4946#define ITEM_START 0
4947#define ITEM_SKIP 1
4948#define ITEM_END 2
4949#define ITEM_MATCHGROUP 3
4950 struct pat_ptr
4951 {
4952 synpat_T *pp_synp; /* pointer to syn_pattern */
4953 int pp_matchgroup_id; /* matchgroup ID */
4954 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4955 } *(pat_ptrs[3]);
4956 /* patterns found in the line */
4957 struct pat_ptr *ppp;
4958 struct pat_ptr *ppp_next;
4959 int pat_count = 0; /* nr of syn_patterns found */
4960 int syn_id;
4961 int matchgroup_id = 0;
4962 int not_enough = FALSE; /* not enough arguments */
4963 int illegal = FALSE; /* illegal arguments */
4964 int success = FALSE;
4965 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004966 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004967 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004968
4969 /* Isolate the group name, check for validity */
4970 rest = get_group_name(arg, &group_name_end);
4971
4972 pat_ptrs[0] = NULL;
4973 pat_ptrs[1] = NULL;
4974 pat_ptrs[2] = NULL;
4975
4976 init_syn_patterns();
4977
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004978 syn_opt_arg.flags = 0;
4979 syn_opt_arg.keyword = FALSE;
4980 syn_opt_arg.sync_idx = NULL;
4981 syn_opt_arg.has_cont_list = TRUE;
4982 syn_opt_arg.cont_list = NULL;
4983 syn_opt_arg.cont_in_list = NULL;
4984 syn_opt_arg.next_list = NULL;
4985
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986 /*
4987 * get the options, patterns and matchgroup.
4988 */
4989 while (rest != NULL && !ends_excmd(*rest))
4990 {
4991 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004992 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993 if (rest == NULL || ends_excmd(*rest))
4994 break;
4995
4996 /* must be a pattern or matchgroup then */
4997 key_end = rest;
4998 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4999 ++key_end;
5000 vim_free(key);
5001 key = vim_strnsave_up(rest, (int)(key_end - rest));
5002 if (key == NULL) /* out of memory */
5003 {
5004 rest = NULL;
5005 break;
5006 }
5007 if (STRCMP(key, "MATCHGROUP") == 0)
5008 item = ITEM_MATCHGROUP;
5009 else if (STRCMP(key, "START") == 0)
5010 item = ITEM_START;
5011 else if (STRCMP(key, "END") == 0)
5012 item = ITEM_END;
5013 else if (STRCMP(key, "SKIP") == 0)
5014 {
5015 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5016 {
5017 illegal = TRUE;
5018 break;
5019 }
5020 item = ITEM_SKIP;
5021 }
5022 else
5023 break;
5024 rest = skipwhite(key_end);
5025 if (*rest != '=')
5026 {
5027 rest = NULL;
5028 EMSG2(_("E398: Missing '=': %s"), arg);
5029 break;
5030 }
5031 rest = skipwhite(rest + 1);
5032 if (*rest == NUL)
5033 {
5034 not_enough = TRUE;
5035 break;
5036 }
5037
5038 if (item == ITEM_MATCHGROUP)
5039 {
5040 p = skiptowhite(rest);
5041 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5042 matchgroup_id = 0;
5043 else
5044 {
5045 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5046 if (matchgroup_id == 0)
5047 {
5048 illegal = TRUE;
5049 break;
5050 }
5051 }
5052 rest = skipwhite(p);
5053 }
5054 else
5055 {
5056 /*
5057 * Allocate room for a syn_pattern, and link it in the list of
5058 * syn_patterns for this item, at the start (because the list is
5059 * used from end to start).
5060 */
5061 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5062 if (ppp == NULL)
5063 {
5064 rest = NULL;
5065 break;
5066 }
5067 ppp->pp_next = pat_ptrs[item];
5068 pat_ptrs[item] = ppp;
5069 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5070 if (ppp->pp_synp == NULL)
5071 {
5072 rest = NULL;
5073 break;
5074 }
5075
5076 /*
5077 * Get the syntax pattern and the following offset(s).
5078 */
5079 /* Enable the appropriate \z specials. */
5080 if (item == ITEM_START)
5081 reg_do_extmatch = REX_SET;
5082 else if (item == ITEM_SKIP || item == ITEM_END)
5083 reg_do_extmatch = REX_USE;
5084 rest = get_syn_pattern(rest, ppp->pp_synp);
5085 reg_do_extmatch = 0;
5086 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005087 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005088 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5089 ppp->pp_matchgroup_id = matchgroup_id;
5090 ++pat_count;
5091 }
5092 }
5093 vim_free(key);
5094 if (illegal || not_enough)
5095 rest = NULL;
5096
5097 /*
5098 * Must have a "start" and "end" pattern.
5099 */
5100 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5101 pat_ptrs[ITEM_END] == NULL))
5102 {
5103 not_enough = TRUE;
5104 rest = NULL;
5105 }
5106
5107 if (rest != NULL)
5108 {
5109 /*
5110 * Check for trailing garbage or command.
5111 * If OK, add the item.
5112 */
5113 eap->nextcmd = check_nextcmd(rest);
5114 if (!ends_excmd(*rest) || eap->skip)
5115 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005116 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005117 && (syn_id = syn_check_group(arg,
5118 (int)(group_name_end - arg))) != 0)
5119 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005120 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005121 /*
5122 * Store the start/skip/end in the syn_items list
5123 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005124 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 for (item = ITEM_START; item <= ITEM_END; ++item)
5126 {
5127 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5128 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005129 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5130 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5131 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132 (item == ITEM_START) ? SPTYPE_START :
5133 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005134 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5135 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5136 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5137 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005139#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005140 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005141#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142 if (item == ITEM_START)
5143 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005144 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005145 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005146 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005147 syn_opt_arg.cont_in_list;
5148 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005149 curwin->w_s->b_syn_containedin = TRUE;
5150 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005151 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005153 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 ++idx;
5155#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005156 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005157 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158#endif
5159 }
5160 }
5161
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005162 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005163 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005164 success = TRUE; /* don't free the progs and patterns now */
5165 }
5166 }
5167
5168 /*
5169 * Free the allocated memory.
5170 */
5171 for (item = ITEM_START; item <= ITEM_END; ++item)
5172 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5173 {
5174 if (!success)
5175 {
5176 vim_free(ppp->pp_synp->sp_prog);
5177 vim_free(ppp->pp_synp->sp_pattern);
5178 }
5179 vim_free(ppp->pp_synp);
5180 ppp_next = ppp->pp_next;
5181 vim_free(ppp);
5182 }
5183
5184 if (!success)
5185 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005186 vim_free(syn_opt_arg.cont_list);
5187 vim_free(syn_opt_arg.cont_in_list);
5188 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005189 if (not_enough)
5190 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5191 else if (illegal || rest == NULL)
5192 EMSG2(_(e_invarg2), arg);
5193 }
5194}
5195
5196/*
5197 * A simple syntax group ID comparison function suitable for use in qsort()
5198 */
5199 static int
5200#ifdef __BORLANDC__
5201_RTLENTRYF
5202#endif
5203syn_compare_stub(v1, v2)
5204 const void *v1;
5205 const void *v2;
5206{
5207 const short *s1 = v1;
5208 const short *s2 = v2;
5209
5210 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5211}
5212
5213/*
5214 * Combines lists of syntax clusters.
5215 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5216 */
5217 static void
5218syn_combine_list(clstr1, clstr2, list_op)
5219 short **clstr1;
5220 short **clstr2;
5221 int list_op;
5222{
5223 int count1 = 0;
5224 int count2 = 0;
5225 short *g1;
5226 short *g2;
5227 short *clstr = NULL;
5228 int count;
5229 int round;
5230
5231 /*
5232 * Handle degenerate cases.
5233 */
5234 if (*clstr2 == NULL)
5235 return;
5236 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5237 {
5238 if (list_op == CLUSTER_REPLACE)
5239 vim_free(*clstr1);
5240 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5241 *clstr1 = *clstr2;
5242 else
5243 vim_free(*clstr2);
5244 return;
5245 }
5246
5247 for (g1 = *clstr1; *g1; g1++)
5248 ++count1;
5249 for (g2 = *clstr2; *g2; g2++)
5250 ++count2;
5251
5252 /*
5253 * For speed purposes, sort both lists.
5254 */
5255 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5256 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5257
5258 /*
5259 * We proceed in two passes; in round 1, we count the elements to place
5260 * in the new list, and in round 2, we allocate and populate the new
5261 * list. For speed, we use a mergesort-like method, adding the smaller
5262 * of the current elements in each list to the new list.
5263 */
5264 for (round = 1; round <= 2; round++)
5265 {
5266 g1 = *clstr1;
5267 g2 = *clstr2;
5268 count = 0;
5269
5270 /*
5271 * First, loop through the lists until one of them is empty.
5272 */
5273 while (*g1 && *g2)
5274 {
5275 /*
5276 * We always want to add from the first list.
5277 */
5278 if (*g1 < *g2)
5279 {
5280 if (round == 2)
5281 clstr[count] = *g1;
5282 count++;
5283 g1++;
5284 continue;
5285 }
5286 /*
5287 * We only want to add from the second list if we're adding the
5288 * lists.
5289 */
5290 if (list_op == CLUSTER_ADD)
5291 {
5292 if (round == 2)
5293 clstr[count] = *g2;
5294 count++;
5295 }
5296 if (*g1 == *g2)
5297 g1++;
5298 g2++;
5299 }
5300
5301 /*
5302 * Now add the leftovers from whichever list didn't get finished
5303 * first. As before, we only want to add from the second list if
5304 * we're adding the lists.
5305 */
5306 for (; *g1; g1++, count++)
5307 if (round == 2)
5308 clstr[count] = *g1;
5309 if (list_op == CLUSTER_ADD)
5310 for (; *g2; g2++, count++)
5311 if (round == 2)
5312 clstr[count] = *g2;
5313
5314 if (round == 1)
5315 {
5316 /*
5317 * If the group ended up empty, we don't need to allocate any
5318 * space for it.
5319 */
5320 if (count == 0)
5321 {
5322 clstr = NULL;
5323 break;
5324 }
5325 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5326 if (clstr == NULL)
5327 break;
5328 clstr[count] = 0;
5329 }
5330 }
5331
5332 /*
5333 * Finally, put the new list in place.
5334 */
5335 vim_free(*clstr1);
5336 vim_free(*clstr2);
5337 *clstr1 = clstr;
5338}
5339
5340/*
5341 * Lookup a syntax cluster name and return it's ID.
5342 * If it is not found, 0 is returned.
5343 */
5344 static int
5345syn_scl_name2id(name)
5346 char_u *name;
5347{
5348 int i;
5349 char_u *name_u;
5350
5351 /* Avoid using stricmp() too much, it's slow on some systems */
5352 name_u = vim_strsave_up(name);
5353 if (name_u == NULL)
5354 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005355 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5356 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5357 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005358 break;
5359 vim_free(name_u);
5360 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5361}
5362
5363/*
5364 * Like syn_scl_name2id(), but take a pointer + length argument.
5365 */
5366 static int
5367syn_scl_namen2id(linep, len)
5368 char_u *linep;
5369 int len;
5370{
5371 char_u *name;
5372 int id = 0;
5373
5374 name = vim_strnsave(linep, len);
5375 if (name != NULL)
5376 {
5377 id = syn_scl_name2id(name);
5378 vim_free(name);
5379 }
5380 return id;
5381}
5382
5383/*
5384 * Find syntax cluster name in the table and return it's ID.
5385 * The argument is a pointer to the name and the length of the name.
5386 * If it doesn't exist yet, a new entry is created.
5387 * Return 0 for failure.
5388 */
5389 static int
5390syn_check_cluster(pp, len)
5391 char_u *pp;
5392 int len;
5393{
5394 int id;
5395 char_u *name;
5396
5397 name = vim_strnsave(pp, len);
5398 if (name == NULL)
5399 return 0;
5400
5401 id = syn_scl_name2id(name);
5402 if (id == 0) /* doesn't exist yet */
5403 id = syn_add_cluster(name);
5404 else
5405 vim_free(name);
5406 return id;
5407}
5408
5409/*
5410 * Add new syntax cluster and return it's ID.
5411 * "name" must be an allocated string, it will be consumed.
5412 * Return 0 for failure.
5413 */
5414 static int
5415syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005416 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005418 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005419
5420 /*
5421 * First call for this growarray: init growing array.
5422 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005423 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005424 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005425 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5426 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005427 }
5428
5429 /*
5430 * Make room for at least one other cluster entry.
5431 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005432 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 {
5434 vim_free(name);
5435 return 0;
5436 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005437 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005438
Bram Moolenaar860cae12010-06-05 23:22:07 +02005439 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5440 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5441 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5442 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5443 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444
Bram Moolenaar217ad922005-03-20 22:37:15 +00005445 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005446 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005447 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005448 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005449
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 return len + SYNID_CLUSTER;
5451}
5452
5453/*
5454 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5455 * [add={groupname},..] [remove={groupname},..]".
5456 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457 static void
5458syn_cmd_cluster(eap, syncing)
5459 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005460 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461{
5462 char_u *arg = eap->arg;
5463 char_u *group_name_end;
5464 char_u *rest;
5465 int scl_id;
5466 short *clstr_list;
5467 int got_clstr = FALSE;
5468 int opt_len;
5469 int list_op;
5470
5471 eap->nextcmd = find_nextcmd(arg);
5472 if (eap->skip)
5473 return;
5474
5475 rest = get_group_name(arg, &group_name_end);
5476
5477 if (rest != NULL)
5478 {
5479 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005480 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481
5482 for (;;)
5483 {
5484 if (STRNICMP(rest, "add", 3) == 0
5485 && (vim_iswhite(rest[3]) || rest[3] == '='))
5486 {
5487 opt_len = 3;
5488 list_op = CLUSTER_ADD;
5489 }
5490 else if (STRNICMP(rest, "remove", 6) == 0
5491 && (vim_iswhite(rest[6]) || rest[6] == '='))
5492 {
5493 opt_len = 6;
5494 list_op = CLUSTER_SUBTRACT;
5495 }
5496 else if (STRNICMP(rest, "contains", 8) == 0
5497 && (vim_iswhite(rest[8]) || rest[8] == '='))
5498 {
5499 opt_len = 8;
5500 list_op = CLUSTER_REPLACE;
5501 }
5502 else
5503 break;
5504
5505 clstr_list = NULL;
5506 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5507 {
5508 EMSG2(_(e_invarg2), rest);
5509 break;
5510 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005511 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 &clstr_list, list_op);
5513 got_clstr = TRUE;
5514 }
5515
5516 if (got_clstr)
5517 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005518 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005519 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520 }
5521 }
5522
5523 if (!got_clstr)
5524 EMSG(_("E400: No cluster specified"));
5525 if (rest == NULL || !ends_excmd(*rest))
5526 EMSG2(_(e_invarg2), arg);
5527}
5528
5529/*
5530 * On first call for current buffer: Init growing array.
5531 */
5532 static void
5533init_syn_patterns()
5534{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005535 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5536 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005537}
5538
5539/*
5540 * Get one pattern for a ":syntax match" or ":syntax region" command.
5541 * Stores the pattern and program in a synpat_T.
5542 * Returns a pointer to the next argument, or NULL in case of an error.
5543 */
5544 static char_u *
5545get_syn_pattern(arg, ci)
5546 char_u *arg;
5547 synpat_T *ci;
5548{
5549 char_u *end;
5550 int *p;
5551 int idx;
5552 char_u *cpo_save;
5553
5554 /* need at least three chars */
5555 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5556 return NULL;
5557
5558 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5559 if (*end != *arg) /* end delimiter not found */
5560 {
5561 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5562 return NULL;
5563 }
5564 /* store the pattern and compiled regexp program */
5565 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5566 return NULL;
5567
5568 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5569 cpo_save = p_cpo;
5570 p_cpo = (char_u *)"";
5571 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5572 p_cpo = cpo_save;
5573
5574 if (ci->sp_prog == NULL)
5575 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005576 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005577
5578 /*
5579 * Check for a match, highlight or region offset.
5580 */
5581 ++end;
5582 do
5583 {
5584 for (idx = SPO_COUNT; --idx >= 0; )
5585 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5586 break;
5587 if (idx >= 0)
5588 {
5589 p = &(ci->sp_offsets[idx]);
5590 if (idx != SPO_LC_OFF)
5591 switch (end[3])
5592 {
5593 case 's': break;
5594 case 'b': break;
5595 case 'e': idx += SPO_COUNT; break;
5596 default: idx = -1; break;
5597 }
5598 if (idx >= 0)
5599 {
5600 ci->sp_off_flags |= (1 << idx);
5601 if (idx == SPO_LC_OFF) /* lc=99 */
5602 {
5603 end += 3;
5604 *p = getdigits(&end);
5605
5606 /* "lc=" offset automatically sets "ms=" offset */
5607 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5608 {
5609 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5610 ci->sp_offsets[SPO_MS_OFF] = *p;
5611 }
5612 }
5613 else /* yy=x+99 */
5614 {
5615 end += 4;
5616 if (*end == '+')
5617 {
5618 ++end;
5619 *p = getdigits(&end); /* positive offset */
5620 }
5621 else if (*end == '-')
5622 {
5623 ++end;
5624 *p = -getdigits(&end); /* negative offset */
5625 }
5626 }
5627 if (*end != ',')
5628 break;
5629 ++end;
5630 }
5631 }
5632 } while (idx >= 0);
5633
5634 if (!ends_excmd(*end) && !vim_iswhite(*end))
5635 {
5636 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5637 return NULL;
5638 }
5639 return skipwhite(end);
5640}
5641
5642/*
5643 * Handle ":syntax sync .." command.
5644 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005645 static void
5646syn_cmd_sync(eap, syncing)
5647 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005648 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649{
5650 char_u *arg_start = eap->arg;
5651 char_u *arg_end;
5652 char_u *key = NULL;
5653 char_u *next_arg;
5654 int illegal = FALSE;
5655 int finished = FALSE;
5656 long n;
5657 char_u *cpo_save;
5658
5659 if (ends_excmd(*arg_start))
5660 {
5661 syn_cmd_list(eap, TRUE);
5662 return;
5663 }
5664
5665 while (!ends_excmd(*arg_start))
5666 {
5667 arg_end = skiptowhite(arg_start);
5668 next_arg = skipwhite(arg_end);
5669 vim_free(key);
5670 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5671 if (STRCMP(key, "CCOMMENT") == 0)
5672 {
5673 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005674 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 if (!ends_excmd(*next_arg))
5676 {
5677 arg_end = skiptowhite(next_arg);
5678 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005679 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 (int)(arg_end - next_arg));
5681 next_arg = skipwhite(arg_end);
5682 }
5683 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005684 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 }
5686 else if ( STRNCMP(key, "LINES", 5) == 0
5687 || STRNCMP(key, "MINLINES", 8) == 0
5688 || STRNCMP(key, "MAXLINES", 8) == 0
5689 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5690 {
5691 if (key[4] == 'S')
5692 arg_end = key + 6;
5693 else if (key[0] == 'L')
5694 arg_end = key + 11;
5695 else
5696 arg_end = key + 9;
5697 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5698 {
5699 illegal = TRUE;
5700 break;
5701 }
5702 n = getdigits(&arg_end);
5703 if (!eap->skip)
5704 {
5705 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005706 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005708 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005710 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005711 }
5712 }
5713 else if (STRCMP(key, "FROMSTART") == 0)
5714 {
5715 if (!eap->skip)
5716 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005717 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5718 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005719 }
5720 }
5721 else if (STRCMP(key, "LINECONT") == 0)
5722 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005723 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005724 {
5725 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5726 finished = TRUE;
5727 break;
5728 }
5729 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5730 if (*arg_end != *next_arg) /* end delimiter not found */
5731 {
5732 illegal = TRUE;
5733 break;
5734 }
5735
5736 if (!eap->skip)
5737 {
5738 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005739 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740 (int)(arg_end - next_arg - 1))) == NULL)
5741 {
5742 finished = TRUE;
5743 break;
5744 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005745 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746
5747 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5748 cpo_save = p_cpo;
5749 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005750 curwin->w_s->b_syn_linecont_prog =
5751 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005752 p_cpo = cpo_save;
5753
Bram Moolenaar860cae12010-06-05 23:22:07 +02005754 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005755 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005756 vim_free(curwin->w_s->b_syn_linecont_pat);
5757 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005758 finished = TRUE;
5759 break;
5760 }
5761 }
5762 next_arg = skipwhite(arg_end + 1);
5763 }
5764 else
5765 {
5766 eap->arg = next_arg;
5767 if (STRCMP(key, "MATCH") == 0)
5768 syn_cmd_match(eap, TRUE);
5769 else if (STRCMP(key, "REGION") == 0)
5770 syn_cmd_region(eap, TRUE);
5771 else if (STRCMP(key, "CLEAR") == 0)
5772 syn_cmd_clear(eap, TRUE);
5773 else
5774 illegal = TRUE;
5775 finished = TRUE;
5776 break;
5777 }
5778 arg_start = next_arg;
5779 }
5780 vim_free(key);
5781 if (illegal)
5782 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5783 else if (!finished)
5784 {
5785 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005786 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005787 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005788 }
5789}
5790
5791/*
5792 * Convert a line of highlight group names into a list of group ID numbers.
5793 * "arg" should point to the "contains" or "nextgroup" keyword.
5794 * "arg" is advanced to after the last group name.
5795 * Careful: the argument is modified (NULs added).
5796 * returns FAIL for some error, OK for success.
5797 */
5798 static int
5799get_id_list(arg, keylen, list)
5800 char_u **arg;
5801 int keylen; /* length of keyword */
5802 short **list; /* where to store the resulting list, if not
5803 NULL, the list is silently skipped! */
5804{
5805 char_u *p = NULL;
5806 char_u *end;
5807 int round;
5808 int count;
5809 int total_count = 0;
5810 short *retval = NULL;
5811 char_u *name;
5812 regmatch_T regmatch;
5813 int id;
5814 int i;
5815 int failed = FALSE;
5816
5817 /*
5818 * We parse the list twice:
5819 * round == 1: count the number of items, allocate the array.
5820 * round == 2: fill the array with the items.
5821 * In round 1 new groups may be added, causing the number of items to
5822 * grow when a regexp is used. In that case round 1 is done once again.
5823 */
5824 for (round = 1; round <= 2; ++round)
5825 {
5826 /*
5827 * skip "contains"
5828 */
5829 p = skipwhite(*arg + keylen);
5830 if (*p != '=')
5831 {
5832 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5833 break;
5834 }
5835 p = skipwhite(p + 1);
5836 if (ends_excmd(*p))
5837 {
5838 EMSG2(_("E406: Empty argument: %s"), *arg);
5839 break;
5840 }
5841
5842 /*
5843 * parse the arguments after "contains"
5844 */
5845 count = 0;
5846 while (!ends_excmd(*p))
5847 {
5848 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5849 ;
5850 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5851 if (name == NULL)
5852 {
5853 failed = TRUE;
5854 break;
5855 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005856 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005857 if ( STRCMP(name + 1, "ALLBUT") == 0
5858 || STRCMP(name + 1, "ALL") == 0
5859 || STRCMP(name + 1, "TOP") == 0
5860 || STRCMP(name + 1, "CONTAINED") == 0)
5861 {
5862 if (TOUPPER_ASC(**arg) != 'C')
5863 {
5864 EMSG2(_("E407: %s not allowed here"), name + 1);
5865 failed = TRUE;
5866 vim_free(name);
5867 break;
5868 }
5869 if (count != 0)
5870 {
5871 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5872 failed = TRUE;
5873 vim_free(name);
5874 break;
5875 }
5876 if (name[1] == 'A')
5877 id = SYNID_ALLBUT;
5878 else if (name[1] == 'T')
5879 id = SYNID_TOP;
5880 else
5881 id = SYNID_CONTAINED;
5882 id += current_syn_inc_tag;
5883 }
5884 else if (name[1] == '@')
5885 {
5886 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5887 }
5888 else
5889 {
5890 /*
5891 * Handle full group name.
5892 */
5893 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5894 id = syn_check_group(name + 1, (int)(end - p));
5895 else
5896 {
5897 /*
5898 * Handle match of regexp with group names.
5899 */
5900 *name = '^';
5901 STRCAT(name, "$");
5902 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5903 if (regmatch.regprog == NULL)
5904 {
5905 failed = TRUE;
5906 vim_free(name);
5907 break;
5908 }
5909
5910 regmatch.rm_ic = TRUE;
5911 id = 0;
5912 for (i = highlight_ga.ga_len; --i >= 0; )
5913 {
5914 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5915 (colnr_T)0))
5916 {
5917 if (round == 2)
5918 {
5919 /* Got more items than expected; can happen
5920 * when adding items that match:
5921 * "contains=a.*b,axb".
5922 * Go back to first round */
5923 if (count >= total_count)
5924 {
5925 vim_free(retval);
5926 round = 1;
5927 }
5928 else
5929 retval[count] = i + 1;
5930 }
5931 ++count;
5932 id = -1; /* remember that we found one */
5933 }
5934 }
5935 vim_free(regmatch.regprog);
5936 }
5937 }
5938 vim_free(name);
5939 if (id == 0)
5940 {
5941 EMSG2(_("E409: Unknown group name: %s"), p);
5942 failed = TRUE;
5943 break;
5944 }
5945 if (id > 0)
5946 {
5947 if (round == 2)
5948 {
5949 /* Got more items than expected, go back to first round */
5950 if (count >= total_count)
5951 {
5952 vim_free(retval);
5953 round = 1;
5954 }
5955 else
5956 retval[count] = id;
5957 }
5958 ++count;
5959 }
5960 p = skipwhite(end);
5961 if (*p != ',')
5962 break;
5963 p = skipwhite(p + 1); /* skip comma in between arguments */
5964 }
5965 if (failed)
5966 break;
5967 if (round == 1)
5968 {
5969 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5970 if (retval == NULL)
5971 break;
5972 retval[count] = 0; /* zero means end of the list */
5973 total_count = count;
5974 }
5975 }
5976
5977 *arg = p;
5978 if (failed || retval == NULL)
5979 {
5980 vim_free(retval);
5981 return FAIL;
5982 }
5983
5984 if (*list == NULL)
5985 *list = retval;
5986 else
5987 vim_free(retval); /* list already found, don't overwrite it */
5988
5989 return OK;
5990}
5991
5992/*
5993 * Make a copy of an ID list.
5994 */
5995 static short *
5996copy_id_list(list)
5997 short *list;
5998{
5999 int len;
6000 int count;
6001 short *retval;
6002
6003 if (list == NULL)
6004 return NULL;
6005
6006 for (count = 0; list[count]; ++count)
6007 ;
6008 len = (count + 1) * sizeof(short);
6009 retval = (short *)alloc((unsigned)len);
6010 if (retval != NULL)
6011 mch_memmove(retval, list, (size_t)len);
6012
6013 return retval;
6014}
6015
6016/*
6017 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6018 * "cur_si" can be NULL if not checking the "containedin" list.
6019 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6020 * the current item.
6021 * This function is called very often, keep it fast!!
6022 */
6023 static int
6024in_id_list(cur_si, list, ssp, contained)
6025 stateitem_T *cur_si; /* current item or NULL */
6026 short *list; /* id list */
6027 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6028 int contained; /* group id is contained */
6029{
6030 int retval;
6031 short *scl_list;
6032 short item;
6033 short id = ssp->id;
6034 static int depth = 0;
6035 int r;
6036
6037 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006038 if (cur_si != NULL && ssp->cont_in_list != NULL
6039 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006040 {
6041 /* Ignore transparent items without a contains argument. Double check
6042 * that we don't go back past the first one. */
6043 while ((cur_si->si_flags & HL_TRANS_CONT)
6044 && cur_si > (stateitem_T *)(current_state.ga_data))
6045 --cur_si;
6046 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6047 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006048 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6049 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006050 return TRUE;
6051 }
6052
6053 if (list == NULL)
6054 return FALSE;
6055
6056 /*
6057 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6058 * inside anything. Only allow not-contained groups.
6059 */
6060 if (list == ID_LIST_ALL)
6061 return !contained;
6062
6063 /*
6064 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6065 * contains list. We also require that "id" is at the same ":syn include"
6066 * level as the list.
6067 */
6068 item = *list;
6069 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6070 {
6071 if (item < SYNID_TOP)
6072 {
6073 /* ALL or ALLBUT: accept all groups in the same file */
6074 if (item - SYNID_ALLBUT != ssp->inc_tag)
6075 return FALSE;
6076 }
6077 else if (item < SYNID_CONTAINED)
6078 {
6079 /* TOP: accept all not-contained groups in the same file */
6080 if (item - SYNID_TOP != ssp->inc_tag || contained)
6081 return FALSE;
6082 }
6083 else
6084 {
6085 /* CONTAINED: accept all contained groups in the same file */
6086 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6087 return FALSE;
6088 }
6089 item = *++list;
6090 retval = FALSE;
6091 }
6092 else
6093 retval = TRUE;
6094
6095 /*
6096 * Return "retval" if id is in the contains list.
6097 */
6098 while (item != 0)
6099 {
6100 if (item == id)
6101 return retval;
6102 if (item >= SYNID_CLUSTER)
6103 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006104 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006105 /* restrict recursiveness to 30 to avoid an endless loop for a
6106 * cluster that includes itself (indirectly) */
6107 if (scl_list != NULL && depth < 30)
6108 {
6109 ++depth;
6110 r = in_id_list(NULL, scl_list, ssp, contained);
6111 --depth;
6112 if (r)
6113 return retval;
6114 }
6115 }
6116 item = *++list;
6117 }
6118 return !retval;
6119}
6120
6121struct subcommand
6122{
6123 char *name; /* subcommand name */
6124 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6125};
6126
6127static struct subcommand subcommands[] =
6128{
6129 {"case", syn_cmd_case},
6130 {"clear", syn_cmd_clear},
6131 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006132 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006133 {"enable", syn_cmd_enable},
6134 {"include", syn_cmd_include},
6135 {"keyword", syn_cmd_keyword},
6136 {"list", syn_cmd_list},
6137 {"manual", syn_cmd_manual},
6138 {"match", syn_cmd_match},
6139 {"on", syn_cmd_on},
6140 {"off", syn_cmd_off},
6141 {"region", syn_cmd_region},
6142 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006143 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144 {"sync", syn_cmd_sync},
6145 {"", syn_cmd_list},
6146 {NULL, NULL}
6147};
6148
6149/*
6150 * ":syntax".
6151 * This searches the subcommands[] table for the subcommand name, and calls a
6152 * syntax_subcommand() function to do the rest.
6153 */
6154 void
6155ex_syntax(eap)
6156 exarg_T *eap;
6157{
6158 char_u *arg = eap->arg;
6159 char_u *subcmd_end;
6160 char_u *subcmd_name;
6161 int i;
6162
6163 syn_cmdlinep = eap->cmdlinep;
6164
6165 /* isolate subcommand name */
6166 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6167 ;
6168 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6169 if (subcmd_name != NULL)
6170 {
6171 if (eap->skip) /* skip error messages for all subcommands */
6172 ++emsg_skip;
6173 for (i = 0; ; ++i)
6174 {
6175 if (subcommands[i].name == NULL)
6176 {
6177 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6178 break;
6179 }
6180 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6181 {
6182 eap->arg = skipwhite(subcmd_end);
6183 (subcommands[i].func)(eap, FALSE);
6184 break;
6185 }
6186 }
6187 vim_free(subcmd_name);
6188 if (eap->skip)
6189 --emsg_skip;
6190 }
6191}
6192
Bram Moolenaar860cae12010-06-05 23:22:07 +02006193 void
6194ex_ownsyntax(eap)
6195 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006196{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006197 char_u *old_value;
6198 char_u *new_value;
6199
Bram Moolenaar860cae12010-06-05 23:22:07 +02006200 if (curwin->w_s == &curwin->w_buffer->b_s)
6201 {
6202 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6203 memset(curwin->w_s, 0, sizeof(synblock_T));
6204#ifdef FEAT_SPELL
6205 curwin->w_p_spell = FALSE; /* No spell checking */
6206 clear_string_option(&curwin->w_s->b_p_spc);
6207 clear_string_option(&curwin->w_s->b_p_spf);
6208 vim_free(curwin->w_s->b_cap_prog);
6209 curwin->w_s->b_cap_prog = NULL;
6210 clear_string_option(&curwin->w_s->b_p_spl);
6211#endif
6212 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006213
6214 /* save value of b:current_syntax */
6215 old_value = get_var_value((char_u *)"b:current_syntax");
6216 if (old_value != NULL)
6217 old_value = vim_strsave(old_value);
6218
6219 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6220 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006221 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006222
6223 /* move value of b:current_syntax to w:current_syntax */
6224 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006225 if (new_value != NULL)
6226 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006227
6228 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006229 if (old_value == NULL)
6230 do_unlet((char_u *)"b:current_syntax", TRUE);
6231 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006232 {
6233 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6234 vim_free(old_value);
6235 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006236}
6237
6238 int
6239syntax_present(win)
6240 win_T *win;
6241{
6242 return (win->w_s->b_syn_patterns.ga_len != 0
6243 || win->w_s->b_syn_clusters.ga_len != 0
6244 || win->w_s->b_keywtab.ht_used > 0
6245 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006246}
6247
6248#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6249
6250static enum
6251{
6252 EXP_SUBCMD, /* expand ":syn" sub-commands */
6253 EXP_CASE /* expand ":syn case" arguments */
6254} expand_what;
6255
Bram Moolenaar4f688582007-07-24 12:34:30 +00006256/*
6257 * Reset include_link, include_default, include_none to 0.
6258 * Called when we are done expanding.
6259 */
6260 void
6261reset_expand_highlight()
6262{
6263 include_link = include_default = include_none = 0;
6264}
6265
6266/*
6267 * Handle command line completion for :match and :echohl command: Add "None"
6268 * as highlight group.
6269 */
6270 void
6271set_context_in_echohl_cmd(xp, arg)
6272 expand_T *xp;
6273 char_u *arg;
6274{
6275 xp->xp_context = EXPAND_HIGHLIGHT;
6276 xp->xp_pattern = arg;
6277 include_none = 1;
6278}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006279
6280/*
6281 * Handle command line completion for :syntax command.
6282 */
6283 void
6284set_context_in_syntax_cmd(xp, arg)
6285 expand_T *xp;
6286 char_u *arg;
6287{
6288 char_u *p;
6289
6290 /* Default: expand subcommands */
6291 xp->xp_context = EXPAND_SYNTAX;
6292 expand_what = EXP_SUBCMD;
6293 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006294 include_link = 0;
6295 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006296
6297 /* (part of) subcommand already typed */
6298 if (*arg != NUL)
6299 {
6300 p = skiptowhite(arg);
6301 if (*p != NUL) /* past first word */
6302 {
6303 xp->xp_pattern = skipwhite(p);
6304 if (*skiptowhite(xp->xp_pattern) != NUL)
6305 xp->xp_context = EXPAND_NOTHING;
6306 else if (STRNICMP(arg, "case", p - arg) == 0)
6307 expand_what = EXP_CASE;
6308 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6309 || STRNICMP(arg, "region", p - arg) == 0
6310 || STRNICMP(arg, "match", p - arg) == 0
6311 || STRNICMP(arg, "list", p - arg) == 0)
6312 xp->xp_context = EXPAND_HIGHLIGHT;
6313 else
6314 xp->xp_context = EXPAND_NOTHING;
6315 }
6316 }
6317}
6318
6319static char *(case_args[]) = {"match", "ignore", NULL};
6320
6321/*
6322 * Function given to ExpandGeneric() to obtain the list syntax names for
6323 * expansion.
6324 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325 char_u *
6326get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006327 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006328 int idx;
6329{
6330 if (expand_what == EXP_SUBCMD)
6331 return (char_u *)subcommands[idx].name;
6332 return (char_u *)case_args[idx];
6333}
6334
6335#endif /* FEAT_CMDL_COMPL */
6336
Bram Moolenaar071d4272004-06-13 20:20:40 +00006337/*
6338 * Function called for expression evaluation: get syntax ID at file position.
6339 */
6340 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006341syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006342 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006343 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006344 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006345 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006346 int *spellp; /* return: can do spell checking */
6347 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348{
6349 /* When the position is not after the current position and in the same
6350 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006351 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006352 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006353 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006354 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006355
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006356 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357
6358 return (trans ? current_trans_id : current_id);
6359}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006360
Bram Moolenaar860cae12010-06-05 23:22:07 +02006361#if defined(FEAT_CONCEAL) || defined(PROTO)
6362/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006363 * Get extra information about the syntax item. Must be called right after
6364 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006365 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006366 * Returns the current flags.
6367 */
6368 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006369get_syntax_info(seqnrp)
6370 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006371{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006372 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006373 return current_flags;
6374}
6375
6376/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006377 * Return conceal substitution character
6378 */
6379 int
6380syn_get_sub_char()
6381{
6382 return current_sub_char;
6383}
6384#endif
6385
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006386#if defined(FEAT_EVAL) || defined(PROTO)
6387/*
6388 * Return the syntax ID at position "i" in the current stack.
6389 * The caller must have called syn_get_id() before to fill the stack.
6390 * Returns -1 when "i" is out of range.
6391 */
6392 int
6393syn_get_stack_item(i)
6394 int i;
6395{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006396 if (i >= current_state.ga_len)
6397 {
6398 /* Need to invalidate the state, because we didn't properly finish it
6399 * for the last character, "keep_state" was TRUE. */
6400 invalidate_current_state();
6401 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006402 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006403 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006404 return CUR_STATE(i).si_id;
6405}
6406#endif
6407
Bram Moolenaar071d4272004-06-13 20:20:40 +00006408#if defined(FEAT_FOLDING) || defined(PROTO)
6409/*
6410 * Function called to get folding level for line "lnum" in window "wp".
6411 */
6412 int
6413syn_get_foldlevel(wp, lnum)
6414 win_T *wp;
6415 long lnum;
6416{
6417 int level = 0;
6418 int i;
6419
6420 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006421 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006422 {
6423 syntax_start(wp, lnum);
6424
6425 for (i = 0; i < current_state.ga_len; ++i)
6426 if (CUR_STATE(i).si_flags & HL_FOLD)
6427 ++level;
6428 }
6429 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006430 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006431 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006432 if (level < 0)
6433 level = 0;
6434 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006435 return level;
6436}
6437#endif
6438
6439#endif /* FEAT_SYN_HL */
6440
6441
6442/**************************************
6443 * Highlighting stuff *
6444 **************************************/
6445
6446/*
6447 * The default highlight groups. These are compiled-in for fast startup and
6448 * they still work when the runtime files can't be found.
6449 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006450 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6451 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006452 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006453#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006454# define CENT(a, b) b
6455#else
6456# define CENT(a, b) a
6457#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006458static char *(highlight_init_both[]) =
6459 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006460 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6461 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6462 CENT("IncSearch term=reverse cterm=reverse",
6463 "IncSearch term=reverse cterm=reverse gui=reverse"),
6464 CENT("ModeMsg term=bold cterm=bold",
6465 "ModeMsg term=bold cterm=bold gui=bold"),
6466 CENT("NonText term=bold ctermfg=Blue",
6467 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6468 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6469 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6470 CENT("StatusLineNC term=reverse cterm=reverse",
6471 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006472#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006473 CENT("VertSplit term=reverse cterm=reverse",
6474 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006475#endif
6476#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006477 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6478 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006479#endif
6480#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006481 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6482 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006483#endif
6484#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006485 CENT("PmenuThumb cterm=reverse",
6486 "PmenuThumb cterm=reverse gui=reverse"),
6487 CENT("PmenuSbar ctermbg=Grey",
6488 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006489#endif
6490#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006491 CENT("TabLineSel term=bold cterm=bold",
6492 "TabLineSel term=bold cterm=bold gui=bold"),
6493 CENT("TabLineFill term=reverse cterm=reverse",
6494 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006495#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006496#ifdef FEAT_GUI
6497 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006498 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006499#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006500 NULL
6501 };
6502
6503static char *(highlight_init_light[]) =
6504 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006505 CENT("Directory term=bold ctermfg=DarkBlue",
6506 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6507 CENT("LineNr term=underline ctermfg=Brown",
6508 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6509 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6510 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6511 CENT("Question term=standout ctermfg=DarkGreen",
6512 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6513 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6514 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006515#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006516 CENT("SpellBad term=reverse ctermbg=LightRed",
6517 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6518 CENT("SpellCap term=reverse ctermbg=LightBlue",
6519 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6520 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6521 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6522 CENT("SpellLocal term=underline ctermbg=Cyan",
6523 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006524#endif
6525#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006526 CENT("Pmenu ctermbg=LightMagenta",
6527 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6528 CENT("PmenuSel ctermbg=LightGrey",
6529 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006530#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006531 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6532 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6533 CENT("Title term=bold ctermfg=DarkMagenta",
6534 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6535 CENT("WarningMsg term=standout ctermfg=DarkRed",
6536 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006537#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006538 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6539 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006540#endif
6541#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006542 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6543 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6544 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6545 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006546#endif
6547#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006548 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6549 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006550#endif
6551#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006552 CENT("Visual term=reverse",
6553 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006554#endif
6555#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006556 CENT("DiffAdd term=bold ctermbg=LightBlue",
6557 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6558 CENT("DiffChange term=bold ctermbg=LightMagenta",
6559 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6560 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6561 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006562#endif
6563#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006564 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6565 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006566#endif
6567#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006568 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006569 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006570 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006571 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006572 CENT("ColorColumn term=reverse ctermbg=LightRed",
6573 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006574#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006575#ifdef FEAT_CONCEAL
6576 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6577 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6578#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006579#ifdef FEAT_AUTOCMD
6580 CENT("MatchParen term=reverse ctermbg=Cyan",
6581 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6582#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006583#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006584 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006585#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006586 NULL
6587 };
6588
6589static char *(highlight_init_dark[]) =
6590 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006591 CENT("Directory term=bold ctermfg=LightCyan",
6592 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6593 CENT("LineNr term=underline ctermfg=Yellow",
6594 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6595 CENT("MoreMsg term=bold ctermfg=LightGreen",
6596 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6597 CENT("Question term=standout ctermfg=LightGreen",
6598 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6599 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6600 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6601 CENT("SpecialKey term=bold ctermfg=LightBlue",
6602 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006603#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006604 CENT("SpellBad term=reverse ctermbg=Red",
6605 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6606 CENT("SpellCap term=reverse ctermbg=Blue",
6607 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6608 CENT("SpellRare term=reverse ctermbg=Magenta",
6609 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6610 CENT("SpellLocal term=underline ctermbg=Cyan",
6611 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006612#endif
6613#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006614 CENT("Pmenu ctermbg=Magenta",
6615 "Pmenu ctermbg=Magenta guibg=Magenta"),
6616 CENT("PmenuSel ctermbg=DarkGrey",
6617 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006618#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006619 CENT("Title term=bold ctermfg=LightMagenta",
6620 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6621 CENT("WarningMsg term=standout ctermfg=LightRed",
6622 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006623#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006624 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6625 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006626#endif
6627#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006628 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6629 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6630 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6631 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006632#endif
6633#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006634 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6635 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006636#endif
6637#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006638 CENT("Visual term=reverse",
6639 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006640#endif
6641#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006642 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6643 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6644 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6645 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6646 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6647 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006648#endif
6649#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006650 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6651 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006652#endif
6653#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006654 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006655 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006656 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006657 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006658 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6659 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006660#endif
6661#ifdef FEAT_AUTOCMD
6662 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6663 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006664#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006665#ifdef FEAT_CONCEAL
6666 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6667 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6668#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006669#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006670 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006671#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006672 NULL
6673 };
6674
6675 void
6676init_highlight(both, reset)
6677 int both; /* include groups where 'bg' doesn't matter */
6678 int reset; /* clear group first */
6679{
6680 int i;
6681 char **pp;
6682 static int had_both = FALSE;
6683#ifdef FEAT_EVAL
6684 char_u *p;
6685
6686 /*
6687 * Try finding the color scheme file. Used when a color file was loaded
6688 * and 'background' or 't_Co' is changed.
6689 */
6690 p = get_var_value((char_u *)"g:colors_name");
6691 if (p != NULL && load_colors(p) == OK)
6692 return;
6693#endif
6694
6695 /*
6696 * Didn't use a color file, use the compiled-in colors.
6697 */
6698 if (both)
6699 {
6700 had_both = TRUE;
6701 pp = highlight_init_both;
6702 for (i = 0; pp[i] != NULL; ++i)
6703 do_highlight((char_u *)pp[i], reset, TRUE);
6704 }
6705 else if (!had_both)
6706 /* Don't do anything before the call with both == TRUE from main().
6707 * Not everything has been setup then, and that call will overrule
6708 * everything anyway. */
6709 return;
6710
6711 if (*p_bg == 'l')
6712 pp = highlight_init_light;
6713 else
6714 pp = highlight_init_dark;
6715 for (i = 0; pp[i] != NULL; ++i)
6716 do_highlight((char_u *)pp[i], reset, TRUE);
6717
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006718 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006719 * depend on the number of colors available.
6720 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006721 * to avoid Statement highlighted text disappears.
6722 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006723 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006724 do_highlight((char_u *)(*p_bg == 'l'
6725 ? "Visual cterm=NONE ctermbg=LightGrey"
6726 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006727 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006728 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006729 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6730 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006731 if (*p_bg == 'l')
6732 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6733 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006734
Bram Moolenaar071d4272004-06-13 20:20:40 +00006735#ifdef FEAT_SYN_HL
6736 /*
6737 * If syntax highlighting is enabled load the highlighting for it.
6738 */
6739 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006740 {
6741 static int recursive = 0;
6742
6743 if (recursive >= 5)
6744 EMSG(_("E679: recursive loop loading syncolor.vim"));
6745 else
6746 {
6747 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006748 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006749 --recursive;
6750 }
6751 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006752#endif
6753}
6754
6755/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006756 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006757 * Return OK for success, FAIL for failure.
6758 */
6759 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006760load_colors(name)
6761 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006762{
6763 char_u *buf;
6764 int retval = FAIL;
6765 static int recursive = FALSE;
6766
6767 /* When being called recursively, this is probably because setting
6768 * 'background' caused the highlighting to be reloaded. This means it is
6769 * working, thus we should return OK. */
6770 if (recursive)
6771 return OK;
6772
6773 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006774 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006775 if (buf != NULL)
6776 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006777 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006778 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006780#ifdef FEAT_AUTOCMD
6781 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6782#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006783 }
6784 recursive = FALSE;
6785
6786 return retval;
6787}
6788
6789/*
6790 * Handle the ":highlight .." command.
6791 * When using ":hi clear" this is called recursively for each group with
6792 * "forceit" and "init" both TRUE.
6793 */
6794 void
6795do_highlight(line, forceit, init)
6796 char_u *line;
6797 int forceit;
6798 int init; /* TRUE when called for initializing */
6799{
6800 char_u *name_end;
6801 char_u *p;
6802 char_u *linep;
6803 char_u *key_start;
6804 char_u *arg_start;
6805 char_u *key = NULL, *arg = NULL;
6806 long i;
6807 int off;
6808 int len;
6809 int attr;
6810 int id;
6811 int idx;
6812 int dodefault = FALSE;
6813 int doclear = FALSE;
6814 int dolink = FALSE;
6815 int error = FALSE;
6816 int color;
6817 int is_normal_group = FALSE; /* "Normal" group */
6818#ifdef FEAT_GUI_X11
6819 int is_menu_group = FALSE; /* "Menu" group */
6820 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6821 int is_tooltip_group = FALSE; /* "Tooltip" group */
6822 int do_colors = FALSE; /* need to update colors? */
6823#else
6824# define is_menu_group 0
6825# define is_tooltip_group 0
6826#endif
6827
6828 /*
6829 * If no argument, list current highlighting.
6830 */
6831 if (ends_excmd(*line))
6832 {
6833 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6834 /* TODO: only call when the group has attributes set */
6835 highlight_list_one((int)i);
6836 return;
6837 }
6838
6839 /*
6840 * Isolate the name.
6841 */
6842 name_end = skiptowhite(line);
6843 linep = skipwhite(name_end);
6844
6845 /*
6846 * Check for "default" argument.
6847 */
6848 if (STRNCMP(line, "default", name_end - line) == 0)
6849 {
6850 dodefault = TRUE;
6851 line = linep;
6852 name_end = skiptowhite(line);
6853 linep = skipwhite(name_end);
6854 }
6855
6856 /*
6857 * Check for "clear" or "link" argument.
6858 */
6859 if (STRNCMP(line, "clear", name_end - line) == 0)
6860 doclear = TRUE;
6861 if (STRNCMP(line, "link", name_end - line) == 0)
6862 dolink = TRUE;
6863
6864 /*
6865 * ":highlight {group-name}": list highlighting for one group.
6866 */
6867 if (!doclear && !dolink && ends_excmd(*linep))
6868 {
6869 id = syn_namen2id(line, (int)(name_end - line));
6870 if (id == 0)
6871 EMSG2(_("E411: highlight group not found: %s"), line);
6872 else
6873 highlight_list_one(id);
6874 return;
6875 }
6876
6877 /*
6878 * Handle ":highlight link {from} {to}" command.
6879 */
6880 if (dolink)
6881 {
6882 char_u *from_start = linep;
6883 char_u *from_end;
6884 char_u *to_start;
6885 char_u *to_end;
6886 int from_id;
6887 int to_id;
6888
6889 from_end = skiptowhite(from_start);
6890 to_start = skipwhite(from_end);
6891 to_end = skiptowhite(to_start);
6892
6893 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6894 {
6895 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6896 from_start);
6897 return;
6898 }
6899
6900 if (!ends_excmd(*skipwhite(to_end)))
6901 {
6902 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6903 return;
6904 }
6905
6906 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6907 if (STRNCMP(to_start, "NONE", 4) == 0)
6908 to_id = 0;
6909 else
6910 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6911
6912 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6913 {
6914 /*
6915 * Don't allow a link when there already is some highlighting
6916 * for the group, unless '!' is used
6917 */
6918 if (to_id > 0 && !forceit && !init
6919 && hl_has_settings(from_id - 1, dodefault))
6920 {
6921 if (sourcing_name == NULL && !dodefault)
6922 EMSG(_("E414: group has settings, highlight link ignored"));
6923 }
6924 else
6925 {
6926 if (!init)
6927 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6928 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006929#ifdef FEAT_EVAL
6930 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6931#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006932 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006933 }
6934 }
6935
6936 /* Only call highlight_changed() once, after sourcing a syntax file */
6937 need_highlight_changed = TRUE;
6938
6939 return;
6940 }
6941
6942 if (doclear)
6943 {
6944 /*
6945 * ":highlight clear [group]" command.
6946 */
6947 line = linep;
6948 if (ends_excmd(*line))
6949 {
6950#ifdef FEAT_GUI
6951 /* First, we do not destroy the old values, but allocate the new
6952 * ones and update the display. THEN we destroy the old values.
6953 * If we destroy the old values first, then the old values
6954 * (such as GuiFont's or GuiFontset's) will still be displayed but
6955 * invalid because they were free'd.
6956 */
6957 if (gui.in_use)
6958 {
6959# ifdef FEAT_BEVAL_TIP
6960 gui_init_tooltip_font();
6961# endif
6962# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6963 gui_init_menu_font();
6964# endif
6965 }
6966# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6967 gui_mch_def_colors();
6968# endif
6969# ifdef FEAT_GUI_X11
6970# ifdef FEAT_MENU
6971
6972 /* This only needs to be done when there is no Menu highlight
6973 * group defined by default, which IS currently the case.
6974 */
6975 gui_mch_new_menu_colors();
6976# endif
6977 if (gui.in_use)
6978 {
6979 gui_new_scrollbar_colors();
6980# ifdef FEAT_BEVAL
6981 gui_mch_new_tooltip_colors();
6982# endif
6983# ifdef FEAT_MENU
6984 gui_mch_new_menu_font();
6985# endif
6986 }
6987# endif
6988
6989 /* Ok, we're done allocating the new default graphics items.
6990 * The screen should already be refreshed at this point.
6991 * It is now Ok to clear out the old data.
6992 */
6993#endif
6994#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006995 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006996#endif
6997 restore_cterm_colors();
6998
6999 /*
7000 * Clear all default highlight groups and load the defaults.
7001 */
7002 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7003 highlight_clear(idx);
7004 init_highlight(TRUE, TRUE);
7005#ifdef FEAT_GUI
7006 if (gui.in_use)
7007 highlight_gui_started();
7008#endif
7009 highlight_changed();
7010 redraw_later_clear();
7011 return;
7012 }
7013 name_end = skiptowhite(line);
7014 linep = skipwhite(name_end);
7015 }
7016
7017 /*
7018 * Find the group name in the table. If it does not exist yet, add it.
7019 */
7020 id = syn_check_group(line, (int)(name_end - line));
7021 if (id == 0) /* failed (out of memory) */
7022 return;
7023 idx = id - 1; /* index is ID minus one */
7024
7025 /* Return if "default" was used and the group already has settings. */
7026 if (dodefault && hl_has_settings(idx, TRUE))
7027 return;
7028
7029 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7030 is_normal_group = TRUE;
7031#ifdef FEAT_GUI_X11
7032 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7033 is_menu_group = TRUE;
7034 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7035 is_scrollbar_group = TRUE;
7036 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7037 is_tooltip_group = TRUE;
7038#endif
7039
7040 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7041 if (doclear || (forceit && init))
7042 {
7043 highlight_clear(idx);
7044 if (!doclear)
7045 HL_TABLE()[idx].sg_set = 0;
7046 }
7047
7048 if (!doclear)
7049 while (!ends_excmd(*linep))
7050 {
7051 key_start = linep;
7052 if (*linep == '=')
7053 {
7054 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7055 error = TRUE;
7056 break;
7057 }
7058
7059 /*
7060 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7061 * "guibg").
7062 */
7063 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7064 ++linep;
7065 vim_free(key);
7066 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7067 if (key == NULL)
7068 {
7069 error = TRUE;
7070 break;
7071 }
7072 linep = skipwhite(linep);
7073
7074 if (STRCMP(key, "NONE") == 0)
7075 {
7076 if (!init || HL_TABLE()[idx].sg_set == 0)
7077 {
7078 if (!init)
7079 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7080 highlight_clear(idx);
7081 }
7082 continue;
7083 }
7084
7085 /*
7086 * Check for the equal sign.
7087 */
7088 if (*linep != '=')
7089 {
7090 EMSG2(_("E416: missing equal sign: %s"), key_start);
7091 error = TRUE;
7092 break;
7093 }
7094 ++linep;
7095
7096 /*
7097 * Isolate the argument.
7098 */
7099 linep = skipwhite(linep);
7100 if (*linep == '\'') /* guifg='color name' */
7101 {
7102 arg_start = ++linep;
7103 linep = vim_strchr(linep, '\'');
7104 if (linep == NULL)
7105 {
7106 EMSG2(_(e_invarg2), key_start);
7107 error = TRUE;
7108 break;
7109 }
7110 }
7111 else
7112 {
7113 arg_start = linep;
7114 linep = skiptowhite(linep);
7115 }
7116 if (linep == arg_start)
7117 {
7118 EMSG2(_("E417: missing argument: %s"), key_start);
7119 error = TRUE;
7120 break;
7121 }
7122 vim_free(arg);
7123 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7124 if (arg == NULL)
7125 {
7126 error = TRUE;
7127 break;
7128 }
7129 if (*linep == '\'')
7130 ++linep;
7131
7132 /*
7133 * Store the argument.
7134 */
7135 if ( STRCMP(key, "TERM") == 0
7136 || STRCMP(key, "CTERM") == 0
7137 || STRCMP(key, "GUI") == 0)
7138 {
7139 attr = 0;
7140 off = 0;
7141 while (arg[off] != NUL)
7142 {
7143 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7144 {
7145 len = (int)STRLEN(hl_name_table[i]);
7146 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7147 {
7148 attr |= hl_attr_table[i];
7149 off += len;
7150 break;
7151 }
7152 }
7153 if (i < 0)
7154 {
7155 EMSG2(_("E418: Illegal value: %s"), arg);
7156 error = TRUE;
7157 break;
7158 }
7159 if (arg[off] == ',') /* another one follows */
7160 ++off;
7161 }
7162 if (error)
7163 break;
7164 if (*key == 'T')
7165 {
7166 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7167 {
7168 if (!init)
7169 HL_TABLE()[idx].sg_set |= SG_TERM;
7170 HL_TABLE()[idx].sg_term = attr;
7171 }
7172 }
7173 else if (*key == 'C')
7174 {
7175 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7176 {
7177 if (!init)
7178 HL_TABLE()[idx].sg_set |= SG_CTERM;
7179 HL_TABLE()[idx].sg_cterm = attr;
7180 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7181 }
7182 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007183#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007184 else
7185 {
7186 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7187 {
7188 if (!init)
7189 HL_TABLE()[idx].sg_set |= SG_GUI;
7190 HL_TABLE()[idx].sg_gui = attr;
7191 }
7192 }
7193#endif
7194 }
7195 else if (STRCMP(key, "FONT") == 0)
7196 {
7197 /* in non-GUI fonts are simply ignored */
7198#ifdef FEAT_GUI
7199 if (!gui.shell_created)
7200 {
7201 /* GUI not started yet, always accept the name. */
7202 vim_free(HL_TABLE()[idx].sg_font_name);
7203 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7204 }
7205 else
7206 {
7207 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7208# ifdef FEAT_XFONTSET
7209 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7210# endif
7211 /* First, save the current font/fontset.
7212 * Then try to allocate the font/fontset.
7213 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7214 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7215 */
7216
7217 HL_TABLE()[idx].sg_font = NOFONT;
7218# ifdef FEAT_XFONTSET
7219 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7220# endif
7221 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7222 is_tooltip_group);
7223
7224# ifdef FEAT_XFONTSET
7225 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7226 {
7227 /* New fontset was accepted. Free the old one, if there was
7228 * one.
7229 */
7230 gui_mch_free_fontset(temp_sg_fontset);
7231 vim_free(HL_TABLE()[idx].sg_font_name);
7232 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7233 }
7234 else
7235 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7236# endif
7237 if (HL_TABLE()[idx].sg_font != NOFONT)
7238 {
7239 /* New font was accepted. Free the old one, if there was
7240 * one.
7241 */
7242 gui_mch_free_font(temp_sg_font);
7243 vim_free(HL_TABLE()[idx].sg_font_name);
7244 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7245 }
7246 else
7247 HL_TABLE()[idx].sg_font = temp_sg_font;
7248 }
7249#endif
7250 }
7251 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7252 {
7253 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7254 {
7255 if (!init)
7256 HL_TABLE()[idx].sg_set |= SG_CTERM;
7257
7258 /* When setting the foreground color, and previously the "bold"
7259 * flag was set for a light color, reset it now */
7260 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7261 {
7262 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7263 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7264 }
7265
7266 if (VIM_ISDIGIT(*arg))
7267 color = atoi((char *)arg);
7268 else if (STRICMP(arg, "fg") == 0)
7269 {
7270 if (cterm_normal_fg_color)
7271 color = cterm_normal_fg_color - 1;
7272 else
7273 {
7274 EMSG(_("E419: FG color unknown"));
7275 error = TRUE;
7276 break;
7277 }
7278 }
7279 else if (STRICMP(arg, "bg") == 0)
7280 {
7281 if (cterm_normal_bg_color > 0)
7282 color = cterm_normal_bg_color - 1;
7283 else
7284 {
7285 EMSG(_("E420: BG color unknown"));
7286 error = TRUE;
7287 break;
7288 }
7289 }
7290 else
7291 {
7292 static char *(color_names[28]) = {
7293 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7294 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7295 "Gray", "Grey",
7296 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7297 "Blue", "LightBlue", "Green", "LightGreen",
7298 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7299 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7300 static int color_numbers_16[28] = {0, 1, 2, 3,
7301 4, 5, 6, 6,
7302 7, 7,
7303 7, 7, 8, 8,
7304 9, 9, 10, 10,
7305 11, 11, 12, 12, 13,
7306 13, 14, 14, 15, -1};
7307 /* for xterm with 88 colors... */
7308 static int color_numbers_88[28] = {0, 4, 2, 6,
7309 1, 5, 32, 72,
7310 84, 84,
7311 7, 7, 82, 82,
7312 12, 43, 10, 61,
7313 14, 63, 9, 74, 13,
7314 75, 11, 78, 15, -1};
7315 /* for xterm with 256 colors... */
7316 static int color_numbers_256[28] = {0, 4, 2, 6,
7317 1, 5, 130, 130,
7318 248, 248,
7319 7, 7, 242, 242,
7320 12, 81, 10, 121,
7321 14, 159, 9, 224, 13,
7322 225, 11, 229, 15, -1};
7323 /* for terminals with less than 16 colors... */
7324 static int color_numbers_8[28] = {0, 4, 2, 6,
7325 1, 5, 3, 3,
7326 7, 7,
7327 7, 7, 0+8, 0+8,
7328 4+8, 4+8, 2+8, 2+8,
7329 6+8, 6+8, 1+8, 1+8, 5+8,
7330 5+8, 3+8, 3+8, 7+8, -1};
7331#if defined(__QNXNTO__)
7332 static int *color_numbers_8_qansi = color_numbers_8;
7333 /* On qnx, the 8 & 16 color arrays are the same */
7334 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7335 color_numbers_8_qansi = color_numbers_16;
7336#endif
7337
7338 /* reduce calls to STRICMP a bit, it can be slow */
7339 off = TOUPPER_ASC(*arg);
7340 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7341 if (off == color_names[i][0]
7342 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7343 break;
7344 if (i < 0)
7345 {
7346 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7347 error = TRUE;
7348 break;
7349 }
7350
7351 /* Use the _16 table to check if its a valid color name. */
7352 color = color_numbers_16[i];
7353 if (color >= 0)
7354 {
7355 if (t_colors == 8)
7356 {
7357 /* t_Co is 8: use the 8 colors table */
7358#if defined(__QNXNTO__)
7359 color = color_numbers_8_qansi[i];
7360#else
7361 color = color_numbers_8[i];
7362#endif
7363 if (key[5] == 'F')
7364 {
7365 /* set/reset bold attribute to get light foreground
7366 * colors (on some terminals, e.g. "linux") */
7367 if (color & 8)
7368 {
7369 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7370 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7371 }
7372 else
7373 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7374 }
7375 color &= 7; /* truncate to 8 colors */
7376 }
7377 else if (t_colors == 16 || t_colors == 88
7378 || t_colors == 256)
7379 {
7380 /*
7381 * Guess: if the termcap entry ends in 'm', it is
7382 * probably an xterm-like terminal. Use the changed
7383 * order for colors.
7384 */
7385 if (*T_CAF != NUL)
7386 p = T_CAF;
7387 else
7388 p = T_CSF;
7389 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7390 switch (t_colors)
7391 {
7392 case 16:
7393 color = color_numbers_8[i];
7394 break;
7395 case 88:
7396 color = color_numbers_88[i];
7397 break;
7398 case 256:
7399 color = color_numbers_256[i];
7400 break;
7401 }
7402 }
7403 }
7404 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007405 /* Add one to the argument, to avoid zero. Zero is used for
7406 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007407 if (key[5] == 'F')
7408 {
7409 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7410 if (is_normal_group)
7411 {
7412 cterm_normal_fg_color = color + 1;
7413 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7414#ifdef FEAT_GUI
7415 /* Don't do this 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 (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007421 term_fg_color(color);
7422 }
7423 }
7424 }
7425 else
7426 {
7427 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7428 if (is_normal_group)
7429 {
7430 cterm_normal_bg_color = color + 1;
7431#ifdef FEAT_GUI
7432 /* Don't mess with 'background' if the GUI is used. */
7433 if (!gui.in_use && !gui.starting)
7434#endif
7435 {
7436 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007437 if (color >= 0)
7438 {
7439 if (termcap_active)
7440 term_bg_color(color);
7441 if (t_colors < 16)
7442 i = (color == 0 || color == 4);
7443 else
7444 i = (color < 7 || color == 8);
7445 /* Set the 'background' option if the value is
7446 * wrong. */
7447 if (i != (*p_bg == 'd'))
7448 set_option_value((char_u *)"bg", 0L,
7449 i ? (char_u *)"dark"
7450 : (char_u *)"light", 0);
7451 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007452 }
7453 }
7454 }
7455 }
7456 }
7457 else if (STRCMP(key, "GUIFG") == 0)
7458 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007459#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007460 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007461 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007462 if (!init)
7463 HL_TABLE()[idx].sg_set |= SG_GUI;
7464
Bram Moolenaar61623362010-07-14 22:04:22 +02007465# ifdef FEAT_GUI
7466 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007467 i = color_name2handle(arg);
7468 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7469 {
7470 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007471# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007472 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7473 if (STRCMP(arg, "NONE"))
7474 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7475 else
7476 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007477# ifdef FEAT_GUI
7478# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007479 if (is_menu_group)
7480 gui.menu_fg_pixel = i;
7481 if (is_scrollbar_group)
7482 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007483# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007484 if (is_tooltip_group)
7485 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007486# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007487 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007488# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007489 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007490# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007491 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007492#endif
7493 }
7494 else if (STRCMP(key, "GUIBG") == 0)
7495 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007496#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007497 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007498 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007499 if (!init)
7500 HL_TABLE()[idx].sg_set |= SG_GUI;
7501
Bram Moolenaar61623362010-07-14 22:04:22 +02007502# ifdef FEAT_GUI
7503 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007504 i = color_name2handle(arg);
7505 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7506 {
7507 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007508# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007509 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7510 if (STRCMP(arg, "NONE") != 0)
7511 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7512 else
7513 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007514# ifdef FEAT_GUI
7515# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007516 if (is_menu_group)
7517 gui.menu_bg_pixel = i;
7518 if (is_scrollbar_group)
7519 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007520# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007521 if (is_tooltip_group)
7522 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007523# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007524 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007525# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007526 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007527# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007528 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007529#endif
7530 }
7531 else if (STRCMP(key, "GUISP") == 0)
7532 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007533#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007534 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7535 {
7536 if (!init)
7537 HL_TABLE()[idx].sg_set |= SG_GUI;
7538
Bram Moolenaar61623362010-07-14 22:04:22 +02007539# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007540 i = color_name2handle(arg);
7541 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7542 {
7543 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007544# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007545 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7546 if (STRCMP(arg, "NONE") != 0)
7547 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7548 else
7549 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007550# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007551 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007552# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007553 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007554#endif
7555 }
7556 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7557 {
7558 char_u buf[100];
7559 char_u *tname;
7560
7561 if (!init)
7562 HL_TABLE()[idx].sg_set |= SG_TERM;
7563
7564 /*
7565 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007566 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007567 */
7568 if (STRNCMP(arg, "t_", 2) == 0)
7569 {
7570 off = 0;
7571 buf[0] = 0;
7572 while (arg[off] != NUL)
7573 {
7574 /* Isolate one termcap name */
7575 for (len = 0; arg[off + len] &&
7576 arg[off + len] != ','; ++len)
7577 ;
7578 tname = vim_strnsave(arg + off, len);
7579 if (tname == NULL) /* out of memory */
7580 {
7581 error = TRUE;
7582 break;
7583 }
7584 /* lookup the escape sequence for the item */
7585 p = get_term_code(tname);
7586 vim_free(tname);
7587 if (p == NULL) /* ignore non-existing things */
7588 p = (char_u *)"";
7589
7590 /* Append it to the already found stuff */
7591 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7592 {
7593 EMSG2(_("E422: terminal code too long: %s"), arg);
7594 error = TRUE;
7595 break;
7596 }
7597 STRCAT(buf, p);
7598
7599 /* Advance to the next item */
7600 off += len;
7601 if (arg[off] == ',') /* another one follows */
7602 ++off;
7603 }
7604 }
7605 else
7606 {
7607 /*
7608 * Copy characters from arg[] to buf[], translating <> codes.
7609 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007610 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007611 {
7612 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007613 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007614 off += len;
7615 else /* copy as normal char */
7616 buf[off++] = *p++;
7617 }
7618 buf[off] = NUL;
7619 }
7620 if (error)
7621 break;
7622
7623 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7624 p = NULL;
7625 else
7626 p = vim_strsave(buf);
7627 if (key[2] == 'A')
7628 {
7629 vim_free(HL_TABLE()[idx].sg_start);
7630 HL_TABLE()[idx].sg_start = p;
7631 }
7632 else
7633 {
7634 vim_free(HL_TABLE()[idx].sg_stop);
7635 HL_TABLE()[idx].sg_stop = p;
7636 }
7637 }
7638 else
7639 {
7640 EMSG2(_("E423: Illegal argument: %s"), key_start);
7641 error = TRUE;
7642 break;
7643 }
7644
7645 /*
7646 * When highlighting has been given for a group, don't link it.
7647 */
7648 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7649 HL_TABLE()[idx].sg_link = 0;
7650
7651 /*
7652 * Continue with next argument.
7653 */
7654 linep = skipwhite(linep);
7655 }
7656
7657 /*
7658 * If there is an error, and it's a new entry, remove it from the table.
7659 */
7660 if (error && idx == highlight_ga.ga_len)
7661 syn_unadd_group();
7662 else
7663 {
7664 if (is_normal_group)
7665 {
7666 HL_TABLE()[idx].sg_term_attr = 0;
7667 HL_TABLE()[idx].sg_cterm_attr = 0;
7668#ifdef FEAT_GUI
7669 HL_TABLE()[idx].sg_gui_attr = 0;
7670 /*
7671 * Need to update all groups, because they might be using "bg"
7672 * and/or "fg", which have been changed now.
7673 */
7674 if (gui.in_use)
7675 highlight_gui_started();
7676#endif
7677 }
7678#ifdef FEAT_GUI_X11
7679# ifdef FEAT_MENU
7680 else if (is_menu_group)
7681 {
7682 if (gui.in_use && do_colors)
7683 gui_mch_new_menu_colors();
7684 }
7685# endif
7686 else if (is_scrollbar_group)
7687 {
7688 if (gui.in_use && do_colors)
7689 gui_new_scrollbar_colors();
7690 }
7691# ifdef FEAT_BEVAL
7692 else if (is_tooltip_group)
7693 {
7694 if (gui.in_use && do_colors)
7695 gui_mch_new_tooltip_colors();
7696 }
7697# endif
7698#endif
7699 else
7700 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007701#ifdef FEAT_EVAL
7702 HL_TABLE()[idx].sg_scriptID = current_SID;
7703#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007704 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007705 }
7706 vim_free(key);
7707 vim_free(arg);
7708
7709 /* Only call highlight_changed() once, after sourcing a syntax file */
7710 need_highlight_changed = TRUE;
7711}
7712
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007713#if defined(EXITFREE) || defined(PROTO)
7714 void
7715free_highlight()
7716{
7717 int i;
7718
7719 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007720 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007721 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007722 vim_free(HL_TABLE()[i].sg_name);
7723 vim_free(HL_TABLE()[i].sg_name_u);
7724 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007725 ga_clear(&highlight_ga);
7726}
7727#endif
7728
Bram Moolenaar071d4272004-06-13 20:20:40 +00007729/*
7730 * Reset the cterm colors to what they were before Vim was started, if
7731 * possible. Otherwise reset them to zero.
7732 */
7733 void
7734restore_cterm_colors()
7735{
7736#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7737 /* Since t_me has been set, this probably means that the user
7738 * wants to use this as default colors. Need to reset default
7739 * background/foreground colors. */
7740 mch_set_normal_colors();
7741#else
7742 cterm_normal_fg_color = 0;
7743 cterm_normal_fg_bold = 0;
7744 cterm_normal_bg_color = 0;
7745#endif
7746}
7747
7748/*
7749 * Return TRUE if highlight group "idx" has any settings.
7750 * When "check_link" is TRUE also check for an existing link.
7751 */
7752 static int
7753hl_has_settings(idx, check_link)
7754 int idx;
7755 int check_link;
7756{
7757 return ( HL_TABLE()[idx].sg_term_attr != 0
7758 || HL_TABLE()[idx].sg_cterm_attr != 0
7759#ifdef FEAT_GUI
7760 || HL_TABLE()[idx].sg_gui_attr != 0
7761#endif
7762 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7763}
7764
7765/*
7766 * Clear highlighting for one group.
7767 */
7768 static void
7769highlight_clear(idx)
7770 int idx;
7771{
7772 HL_TABLE()[idx].sg_term = 0;
7773 vim_free(HL_TABLE()[idx].sg_start);
7774 HL_TABLE()[idx].sg_start = NULL;
7775 vim_free(HL_TABLE()[idx].sg_stop);
7776 HL_TABLE()[idx].sg_stop = NULL;
7777 HL_TABLE()[idx].sg_term_attr = 0;
7778 HL_TABLE()[idx].sg_cterm = 0;
7779 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7780 HL_TABLE()[idx].sg_cterm_fg = 0;
7781 HL_TABLE()[idx].sg_cterm_bg = 0;
7782 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007783#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007784 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7786 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007787 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7788 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007789 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7790 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007791#endif
7792#ifdef FEAT_GUI
7793 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7794 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7795 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007796 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7797 HL_TABLE()[idx].sg_font = NOFONT;
7798# ifdef FEAT_XFONTSET
7799 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7800 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7801# endif
7802 vim_free(HL_TABLE()[idx].sg_font_name);
7803 HL_TABLE()[idx].sg_font_name = NULL;
7804 HL_TABLE()[idx].sg_gui_attr = 0;
7805#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007806#ifdef FEAT_EVAL
7807 /* Clear the script ID only when there is no link, since that is not
7808 * cleared. */
7809 if (HL_TABLE()[idx].sg_link == 0)
7810 HL_TABLE()[idx].sg_scriptID = 0;
7811#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007812}
7813
7814#if defined(FEAT_GUI) || defined(PROTO)
7815/*
7816 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007817 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007818 * "Tooltip" colors.
7819 */
7820 void
7821set_normal_colors()
7822{
7823 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007824 &gui.norm_pixel, &gui.back_pixel,
7825 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007826 {
7827 gui_mch_new_colors();
7828 must_redraw = CLEAR;
7829 }
7830#ifdef FEAT_GUI_X11
7831 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007832 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7833 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007834 {
7835# ifdef FEAT_MENU
7836 gui_mch_new_menu_colors();
7837# endif
7838 must_redraw = CLEAR;
7839 }
7840# ifdef FEAT_BEVAL
7841 if (set_group_colors((char_u *)"Tooltip",
7842 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7843 FALSE, FALSE, TRUE))
7844 {
7845# ifdef FEAT_TOOLBAR
7846 gui_mch_new_tooltip_colors();
7847# endif
7848 must_redraw = CLEAR;
7849 }
7850#endif
7851 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007852 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7853 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 {
7855 gui_new_scrollbar_colors();
7856 must_redraw = CLEAR;
7857 }
7858#endif
7859}
7860
7861/*
7862 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7863 */
7864 static int
7865set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7866 char_u *name;
7867 guicolor_T *fgp;
7868 guicolor_T *bgp;
7869 int do_menu;
7870 int use_norm;
7871 int do_tooltip;
7872{
7873 int idx;
7874
7875 idx = syn_name2id(name) - 1;
7876 if (idx >= 0)
7877 {
7878 gui_do_one_color(idx, do_menu, do_tooltip);
7879
7880 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7881 *fgp = HL_TABLE()[idx].sg_gui_fg;
7882 else if (use_norm)
7883 *fgp = gui.def_norm_pixel;
7884 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7885 *bgp = HL_TABLE()[idx].sg_gui_bg;
7886 else if (use_norm)
7887 *bgp = gui.def_back_pixel;
7888 return TRUE;
7889 }
7890 return FALSE;
7891}
7892
7893/*
7894 * Get the font of the "Normal" group.
7895 * Returns "" when it's not found or not set.
7896 */
7897 char_u *
7898hl_get_font_name()
7899{
7900 int id;
7901 char_u *s;
7902
7903 id = syn_name2id((char_u *)"Normal");
7904 if (id > 0)
7905 {
7906 s = HL_TABLE()[id - 1].sg_font_name;
7907 if (s != NULL)
7908 return s;
7909 }
7910 return (char_u *)"";
7911}
7912
7913/*
7914 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7915 * actually chosen to be used.
7916 */
7917 void
7918hl_set_font_name(font_name)
7919 char_u *font_name;
7920{
7921 int id;
7922
7923 id = syn_name2id((char_u *)"Normal");
7924 if (id > 0)
7925 {
7926 vim_free(HL_TABLE()[id - 1].sg_font_name);
7927 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7928 }
7929}
7930
7931/*
7932 * Set background color for "Normal" group. Called by gui_set_bg_color()
7933 * when the color is known.
7934 */
7935 void
7936hl_set_bg_color_name(name)
7937 char_u *name; /* must have been allocated */
7938{
7939 int id;
7940
7941 if (name != NULL)
7942 {
7943 id = syn_name2id((char_u *)"Normal");
7944 if (id > 0)
7945 {
7946 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7947 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7948 }
7949 }
7950}
7951
7952/*
7953 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7954 * when the color is known.
7955 */
7956 void
7957hl_set_fg_color_name(name)
7958 char_u *name; /* must have been allocated */
7959{
7960 int id;
7961
7962 if (name != NULL)
7963 {
7964 id = syn_name2id((char_u *)"Normal");
7965 if (id > 0)
7966 {
7967 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7968 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7969 }
7970 }
7971}
7972
7973/*
7974 * Return the handle for a color name.
7975 * Returns INVALCOLOR when failed.
7976 */
7977 static guicolor_T
7978color_name2handle(name)
7979 char_u *name;
7980{
7981 if (STRCMP(name, "NONE") == 0)
7982 return INVALCOLOR;
7983
7984 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7985 return gui.norm_pixel;
7986 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7987 return gui.back_pixel;
7988
7989 return gui_get_color(name);
7990}
7991
7992/*
7993 * Return the handle for a font name.
7994 * Returns NOFONT when failed.
7995 */
7996 static GuiFont
7997font_name2handle(name)
7998 char_u *name;
7999{
8000 if (STRCMP(name, "NONE") == 0)
8001 return NOFONT;
8002
8003 return gui_mch_get_font(name, TRUE);
8004}
8005
8006# ifdef FEAT_XFONTSET
8007/*
8008 * Return the handle for a fontset name.
8009 * Returns NOFONTSET when failed.
8010 */
8011 static GuiFontset
8012fontset_name2handle(name, fixed_width)
8013 char_u *name;
8014 int fixed_width;
8015{
8016 if (STRCMP(name, "NONE") == 0)
8017 return NOFONTSET;
8018
8019 return gui_mch_get_fontset(name, TRUE, fixed_width);
8020}
8021# endif
8022
8023/*
8024 * Get the font or fontset for one highlight group.
8025 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008026 static void
8027hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8028 int idx;
8029 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008030 int do_normal; /* set normal font */
8031 int do_menu UNUSED; /* set menu font */
8032 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008033{
8034# ifdef FEAT_XFONTSET
8035 /* If 'guifontset' is not empty, first try using the name as a
8036 * fontset. If that doesn't work, use it as a font name. */
8037 if (*p_guifontset != NUL
8038# ifdef FONTSET_ALWAYS
8039 || do_menu
8040# endif
8041# ifdef FEAT_BEVAL_TIP
8042 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8043 || do_tooltip
8044# endif
8045 )
8046 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8047# ifdef FONTSET_ALWAYS
8048 || do_menu
8049# endif
8050# ifdef FEAT_BEVAL_TIP
8051 || do_tooltip
8052# endif
8053 );
8054 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8055 {
8056 /* If it worked and it's the Normal group, use it as the
8057 * normal fontset. Same for the Menu group. */
8058 if (do_normal)
8059 gui_init_font(arg, TRUE);
8060# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8061 if (do_menu)
8062 {
8063# ifdef FONTSET_ALWAYS
8064 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8065# else
8066 /* YIKES! This is a bug waiting to crash the program */
8067 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8068# endif
8069 gui_mch_new_menu_font();
8070 }
8071# ifdef FEAT_BEVAL
8072 if (do_tooltip)
8073 {
8074 /* The Athena widget set cannot currently handle switching between
8075 * displaying a single font and a fontset.
8076 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008077 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008078 * XFontStruct is used.
8079 */
8080 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8081 gui_mch_new_tooltip_font();
8082 }
8083# endif
8084# endif
8085 }
8086 else
8087# endif
8088 {
8089 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8090 /* If it worked and it's the Normal group, use it as the
8091 * normal font. Same for the Menu group. */
8092 if (HL_TABLE()[idx].sg_font != NOFONT)
8093 {
8094 if (do_normal)
8095 gui_init_font(arg, FALSE);
8096#ifndef FONTSET_ALWAYS
8097# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8098 if (do_menu)
8099 {
8100 gui.menu_font = HL_TABLE()[idx].sg_font;
8101 gui_mch_new_menu_font();
8102 }
8103# endif
8104#endif
8105 }
8106 }
8107}
8108
8109#endif /* FEAT_GUI */
8110
8111/*
8112 * Table with the specifications for an attribute number.
8113 * Note that this table is used by ALL buffers. This is required because the
8114 * GUI can redraw at any time for any buffer.
8115 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008116static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008117
8118#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8119
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008120static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008121
8122#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8123
8124#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008125static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008126
8127#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8128#endif
8129
8130/*
8131 * Return the attr number for a set of colors and font.
8132 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8133 * if the combination is new.
8134 * Return 0 for error (no more room).
8135 */
8136 static int
8137get_attr_entry(table, aep)
8138 garray_T *table;
8139 attrentry_T *aep;
8140{
8141 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008142 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008143 static int recursive = FALSE;
8144
8145 /*
8146 * Init the table, in case it wasn't done yet.
8147 */
8148 table->ga_itemsize = sizeof(attrentry_T);
8149 table->ga_growsize = 7;
8150
8151 /*
8152 * Try to find an entry with the same specifications.
8153 */
8154 for (i = 0; i < table->ga_len; ++i)
8155 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008156 taep = &(((attrentry_T *)table->ga_data)[i]);
8157 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008158 && (
8159#ifdef FEAT_GUI
8160 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008161 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8162 && aep->ae_u.gui.bg_color
8163 == taep->ae_u.gui.bg_color
8164 && aep->ae_u.gui.sp_color
8165 == taep->ae_u.gui.sp_color
8166 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008167# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008168 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169# endif
8170 ))
8171 ||
8172#endif
8173 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008174 && (aep->ae_u.term.start == NULL)
8175 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008176 && (aep->ae_u.term.start == NULL
8177 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008178 taep->ae_u.term.start) == 0)
8179 && (aep->ae_u.term.stop == NULL)
8180 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008181 && (aep->ae_u.term.stop == NULL
8182 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008183 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008184 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008185 && aep->ae_u.cterm.fg_color
8186 == taep->ae_u.cterm.fg_color
8187 && aep->ae_u.cterm.bg_color
8188 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008189 ))
8190
8191 return i + ATTR_OFF;
8192 }
8193
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008194 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008195 {
8196 /*
8197 * Running out of attribute entries! remove all attributes, and
8198 * compute new ones for all groups.
8199 * When called recursively, we are really out of numbers.
8200 */
8201 if (recursive)
8202 {
8203 EMSG(_("E424: Too many different highlighting attributes in use"));
8204 return 0;
8205 }
8206 recursive = TRUE;
8207
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008208 clear_hl_tables();
8209
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210 must_redraw = CLEAR;
8211
8212 for (i = 0; i < highlight_ga.ga_len; ++i)
8213 set_hl_attr(i);
8214
8215 recursive = FALSE;
8216 }
8217
8218 /*
8219 * This is a new combination of colors and font, add an entry.
8220 */
8221 if (ga_grow(table, 1) == FAIL)
8222 return 0;
8223
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008224 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8225 vim_memset(taep, 0, sizeof(attrentry_T));
8226 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227#ifdef FEAT_GUI
8228 if (table == &gui_attr_table)
8229 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008230 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8231 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8232 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8233 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008234# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008235 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008236# endif
8237 }
8238#endif
8239 if (table == &term_attr_table)
8240 {
8241 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008242 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008243 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008244 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008245 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008246 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008247 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008248 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249 }
8250 else if (table == &cterm_attr_table)
8251 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008252 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8253 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008254 }
8255 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008256 return (table->ga_len - 1 + ATTR_OFF);
8257}
8258
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008259/*
8260 * Clear all highlight tables.
8261 */
8262 void
8263clear_hl_tables()
8264{
8265 int i;
8266 attrentry_T *taep;
8267
8268#ifdef FEAT_GUI
8269 ga_clear(&gui_attr_table);
8270#endif
8271 for (i = 0; i < term_attr_table.ga_len; ++i)
8272 {
8273 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8274 vim_free(taep->ae_u.term.start);
8275 vim_free(taep->ae_u.term.stop);
8276 }
8277 ga_clear(&term_attr_table);
8278 ga_clear(&cterm_attr_table);
8279}
8280
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008281#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008282/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008283 * Combine special attributes (e.g., for spelling) with other attributes
8284 * (e.g., for syntax highlighting).
8285 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008286 * This creates a new group when required.
8287 * Since we expect there to be few spelling mistakes we don't cache the
8288 * result.
8289 * Return the resulting attributes.
8290 */
8291 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008292hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008293 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008294 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008295{
8296 attrentry_T *char_aep = NULL;
8297 attrentry_T *spell_aep;
8298 attrentry_T new_en;
8299
8300 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008301 return prim_attr;
8302 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8303 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008304#ifdef FEAT_GUI
8305 if (gui.in_use)
8306 {
8307 if (char_attr > HL_ALL)
8308 char_aep = syn_gui_attr2entry(char_attr);
8309 if (char_aep != NULL)
8310 new_en = *char_aep;
8311 else
8312 {
8313 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008314 new_en.ae_u.gui.fg_color = INVALCOLOR;
8315 new_en.ae_u.gui.bg_color = INVALCOLOR;
8316 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008317 if (char_attr <= HL_ALL)
8318 new_en.ae_attr = char_attr;
8319 }
8320
Bram Moolenaar30abd282005-06-22 22:35:10 +00008321 if (prim_attr <= HL_ALL)
8322 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008323 else
8324 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008325 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008326 if (spell_aep != NULL)
8327 {
8328 new_en.ae_attr |= spell_aep->ae_attr;
8329 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8330 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8331 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8332 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8333 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8334 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8335 if (spell_aep->ae_u.gui.font != NOFONT)
8336 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8337# ifdef FEAT_XFONTSET
8338 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8339 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8340# endif
8341 }
8342 }
8343 return get_attr_entry(&gui_attr_table, &new_en);
8344 }
8345#endif
8346
8347 if (t_colors > 1)
8348 {
8349 if (char_attr > HL_ALL)
8350 char_aep = syn_cterm_attr2entry(char_attr);
8351 if (char_aep != NULL)
8352 new_en = *char_aep;
8353 else
8354 {
8355 vim_memset(&new_en, 0, sizeof(new_en));
8356 if (char_attr <= HL_ALL)
8357 new_en.ae_attr = char_attr;
8358 }
8359
Bram Moolenaar30abd282005-06-22 22:35:10 +00008360 if (prim_attr <= HL_ALL)
8361 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008362 else
8363 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008364 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008365 if (spell_aep != NULL)
8366 {
8367 new_en.ae_attr |= spell_aep->ae_attr;
8368 if (spell_aep->ae_u.cterm.fg_color > 0)
8369 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8370 if (spell_aep->ae_u.cterm.bg_color > 0)
8371 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8372 }
8373 }
8374 return get_attr_entry(&cterm_attr_table, &new_en);
8375 }
8376
8377 if (char_attr > HL_ALL)
8378 char_aep = syn_term_attr2entry(char_attr);
8379 if (char_aep != NULL)
8380 new_en = *char_aep;
8381 else
8382 {
8383 vim_memset(&new_en, 0, sizeof(new_en));
8384 if (char_attr <= HL_ALL)
8385 new_en.ae_attr = char_attr;
8386 }
8387
Bram Moolenaar30abd282005-06-22 22:35:10 +00008388 if (prim_attr <= HL_ALL)
8389 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008390 else
8391 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008392 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008393 if (spell_aep != NULL)
8394 {
8395 new_en.ae_attr |= spell_aep->ae_attr;
8396 if (spell_aep->ae_u.term.start != NULL)
8397 {
8398 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8399 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8400 }
8401 }
8402 }
8403 return get_attr_entry(&term_attr_table, &new_en);
8404}
8405#endif
8406
Bram Moolenaar071d4272004-06-13 20:20:40 +00008407#ifdef FEAT_GUI
8408
8409 attrentry_T *
8410syn_gui_attr2entry(attr)
8411 int attr;
8412{
8413 attr -= ATTR_OFF;
8414 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8415 return NULL;
8416 return &(GUI_ATTR_ENTRY(attr));
8417}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008418#endif /* FEAT_GUI */
8419
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008420/*
8421 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8422 * Only to be used when "attr" > HL_ALL.
8423 */
8424 int
8425syn_attr2attr(attr)
8426 int attr;
8427{
8428 attrentry_T *aep;
8429
8430#ifdef FEAT_GUI
8431 if (gui.in_use)
8432 aep = syn_gui_attr2entry(attr);
8433 else
8434#endif
8435 if (t_colors > 1)
8436 aep = syn_cterm_attr2entry(attr);
8437 else
8438 aep = syn_term_attr2entry(attr);
8439
8440 if (aep == NULL) /* highlighting not set */
8441 return 0;
8442 return aep->ae_attr;
8443}
8444
8445
Bram Moolenaar071d4272004-06-13 20:20:40 +00008446 attrentry_T *
8447syn_term_attr2entry(attr)
8448 int attr;
8449{
8450 attr -= ATTR_OFF;
8451 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8452 return NULL;
8453 return &(TERM_ATTR_ENTRY(attr));
8454}
8455
8456 attrentry_T *
8457syn_cterm_attr2entry(attr)
8458 int attr;
8459{
8460 attr -= ATTR_OFF;
8461 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8462 return NULL;
8463 return &(CTERM_ATTR_ENTRY(attr));
8464}
8465
8466#define LIST_ATTR 1
8467#define LIST_STRING 2
8468#define LIST_INT 3
8469
8470 static void
8471highlight_list_one(id)
8472 int id;
8473{
8474 struct hl_group *sgp;
8475 int didh = FALSE;
8476
8477 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8478
8479 didh = highlight_list_arg(id, didh, LIST_ATTR,
8480 sgp->sg_term, NULL, "term");
8481 didh = highlight_list_arg(id, didh, LIST_STRING,
8482 0, sgp->sg_start, "start");
8483 didh = highlight_list_arg(id, didh, LIST_STRING,
8484 0, sgp->sg_stop, "stop");
8485
8486 didh = highlight_list_arg(id, didh, LIST_ATTR,
8487 sgp->sg_cterm, NULL, "cterm");
8488 didh = highlight_list_arg(id, didh, LIST_INT,
8489 sgp->sg_cterm_fg, NULL, "ctermfg");
8490 didh = highlight_list_arg(id, didh, LIST_INT,
8491 sgp->sg_cterm_bg, NULL, "ctermbg");
8492
Bram Moolenaar61623362010-07-14 22:04:22 +02008493#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008494 didh = highlight_list_arg(id, didh, LIST_ATTR,
8495 sgp->sg_gui, NULL, "gui");
8496 didh = highlight_list_arg(id, didh, LIST_STRING,
8497 0, sgp->sg_gui_fg_name, "guifg");
8498 didh = highlight_list_arg(id, didh, LIST_STRING,
8499 0, sgp->sg_gui_bg_name, "guibg");
8500 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008501 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008502#endif
8503#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008504 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505 0, sgp->sg_font_name, "font");
8506#endif
8507
Bram Moolenaar661b1822005-07-28 22:36:45 +00008508 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008509 {
8510 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008511 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008512 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8513 msg_putchar(' ');
8514 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8515 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008516
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008517 if (!didh)
8518 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008519#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008520 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008521 last_set_msg(sgp->sg_scriptID);
8522#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008523}
8524
8525 static int
8526highlight_list_arg(id, didh, type, iarg, sarg, name)
8527 int id;
8528 int didh;
8529 int type;
8530 int iarg;
8531 char_u *sarg;
8532 char *name;
8533{
8534 char_u buf[100];
8535 char_u *ts;
8536 int i;
8537
Bram Moolenaar661b1822005-07-28 22:36:45 +00008538 if (got_int)
8539 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008540 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8541 {
8542 ts = buf;
8543 if (type == LIST_INT)
8544 sprintf((char *)buf, "%d", iarg - 1);
8545 else if (type == LIST_STRING)
8546 ts = sarg;
8547 else /* type == LIST_ATTR */
8548 {
8549 buf[0] = NUL;
8550 for (i = 0; hl_attr_table[i] != 0; ++i)
8551 {
8552 if (iarg & hl_attr_table[i])
8553 {
8554 if (buf[0] != NUL)
8555 STRCAT(buf, ",");
8556 STRCAT(buf, hl_name_table[i]);
8557 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8558 }
8559 }
8560 }
8561
8562 (void)syn_list_header(didh,
8563 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8564 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008565 if (!got_int)
8566 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008567 if (*name != NUL)
8568 {
8569 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8570 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8571 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008572 msg_outtrans(ts);
8573 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008574 }
8575 return didh;
8576}
8577
8578#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8579/*
8580 * Return "1" if highlight group "id" has attribute "flag".
8581 * Return NULL otherwise.
8582 */
8583 char_u *
8584highlight_has_attr(id, flag, modec)
8585 int id;
8586 int flag;
8587 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8588{
8589 int attr;
8590
8591 if (id <= 0 || id > highlight_ga.ga_len)
8592 return NULL;
8593
Bram Moolenaar61623362010-07-14 22:04:22 +02008594#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008595 if (modec == 'g')
8596 attr = HL_TABLE()[id - 1].sg_gui;
8597 else
8598#endif
8599 if (modec == 'c')
8600 attr = HL_TABLE()[id - 1].sg_cterm;
8601 else
8602 attr = HL_TABLE()[id - 1].sg_term;
8603
8604 if (attr & flag)
8605 return (char_u *)"1";
8606 return NULL;
8607}
8608#endif
8609
8610#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8611/*
8612 * Return color name of highlight group "id".
8613 */
8614 char_u *
8615highlight_color(id, what, modec)
8616 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008617 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008618 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8619{
8620 static char_u name[20];
8621 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008622 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008623 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008624 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008625
8626 if (id <= 0 || id > highlight_ga.ga_len)
8627 return NULL;
8628
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008629 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008630 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008631 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008632 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008633 font = TRUE;
8634 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008635 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008636 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8637 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008638 if (modec == 'g')
8639 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008640# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008641 /* return font name */
8642 if (font)
8643 return HL_TABLE()[id - 1].sg_font_name;
8644
Bram Moolenaar071d4272004-06-13 20:20:40 +00008645 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008646 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008647 {
8648 guicolor_T color;
8649 long_u rgb;
8650 static char_u buf[10];
8651
8652 if (fg)
8653 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008654 else if (sp)
8655 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008656 else
8657 color = HL_TABLE()[id - 1].sg_gui_bg;
8658 if (color == INVALCOLOR)
8659 return NULL;
8660 rgb = gui_mch_get_rgb(color);
8661 sprintf((char *)buf, "#%02x%02x%02x",
8662 (unsigned)(rgb >> 16),
8663 (unsigned)(rgb >> 8) & 255,
8664 (unsigned)rgb & 255);
8665 return buf;
8666 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008667#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008668 if (fg)
8669 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008670 if (sp)
8671 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008672 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8673 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008674 if (font || sp)
8675 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008676 if (modec == 'c')
8677 {
8678 if (fg)
8679 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8680 else
8681 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8682 sprintf((char *)name, "%d", n);
8683 return name;
8684 }
8685 /* term doesn't have color */
8686 return NULL;
8687}
8688#endif
8689
8690#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8691 || defined(PROTO)
8692/*
8693 * Return color name of highlight group "id" as RGB value.
8694 */
8695 long_u
8696highlight_gui_color_rgb(id, fg)
8697 int id;
8698 int fg; /* TRUE = fg, FALSE = bg */
8699{
8700 guicolor_T color;
8701
8702 if (id <= 0 || id > highlight_ga.ga_len)
8703 return 0L;
8704
8705 if (fg)
8706 color = HL_TABLE()[id - 1].sg_gui_fg;
8707 else
8708 color = HL_TABLE()[id - 1].sg_gui_bg;
8709
8710 if (color == INVALCOLOR)
8711 return 0L;
8712
8713 return gui_mch_get_rgb(color);
8714}
8715#endif
8716
8717/*
8718 * Output the syntax list header.
8719 * Return TRUE when started a new line.
8720 */
8721 static int
8722syn_list_header(did_header, outlen, id)
8723 int did_header; /* did header already */
8724 int outlen; /* length of string that comes */
8725 int id; /* highlight group id */
8726{
8727 int endcol = 19;
8728 int newline = TRUE;
8729
8730 if (!did_header)
8731 {
8732 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008733 if (got_int)
8734 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008735 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8736 endcol = 15;
8737 }
8738 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008739 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008740 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008741 if (got_int)
8742 return TRUE;
8743 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008744 else
8745 {
8746 if (msg_col >= endcol) /* wrap around is like starting a new line */
8747 newline = FALSE;
8748 }
8749
8750 if (msg_col >= endcol) /* output at least one space */
8751 endcol = msg_col + 1;
8752 if (Columns <= endcol) /* avoid hang for tiny window */
8753 endcol = Columns - 1;
8754
8755 msg_advance(endcol);
8756
8757 /* Show "xxx" with the attributes. */
8758 if (!did_header)
8759 {
8760 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8761 msg_putchar(' ');
8762 }
8763
8764 return newline;
8765}
8766
8767/*
8768 * Set the attribute numbers for a highlight group.
8769 * Called after one of the attributes has changed.
8770 */
8771 static void
8772set_hl_attr(idx)
8773 int idx; /* index in array */
8774{
8775 attrentry_T at_en;
8776 struct hl_group *sgp = HL_TABLE() + idx;
8777
8778 /* The "Normal" group doesn't need an attribute number */
8779 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8780 return;
8781
8782#ifdef FEAT_GUI
8783 /*
8784 * For the GUI mode: If there are other than "normal" highlighting
8785 * attributes, need to allocate an attr number.
8786 */
8787 if (sgp->sg_gui_fg == INVALCOLOR
8788 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008789 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008790 && sgp->sg_font == NOFONT
8791# ifdef FEAT_XFONTSET
8792 && sgp->sg_fontset == NOFONTSET
8793# endif
8794 )
8795 {
8796 sgp->sg_gui_attr = sgp->sg_gui;
8797 }
8798 else
8799 {
8800 at_en.ae_attr = sgp->sg_gui;
8801 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8802 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008803 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008804 at_en.ae_u.gui.font = sgp->sg_font;
8805# ifdef FEAT_XFONTSET
8806 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8807# endif
8808 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8809 }
8810#endif
8811 /*
8812 * For the term mode: If there are other than "normal" highlighting
8813 * attributes, need to allocate an attr number.
8814 */
8815 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8816 sgp->sg_term_attr = sgp->sg_term;
8817 else
8818 {
8819 at_en.ae_attr = sgp->sg_term;
8820 at_en.ae_u.term.start = sgp->sg_start;
8821 at_en.ae_u.term.stop = sgp->sg_stop;
8822 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8823 }
8824
8825 /*
8826 * For the color term mode: If there are other than "normal"
8827 * highlighting attributes, need to allocate an attr number.
8828 */
8829 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8830 sgp->sg_cterm_attr = sgp->sg_cterm;
8831 else
8832 {
8833 at_en.ae_attr = sgp->sg_cterm;
8834 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8835 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8836 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8837 }
8838}
8839
8840/*
8841 * Lookup a highlight group name and return it's ID.
8842 * If it is not found, 0 is returned.
8843 */
8844 int
8845syn_name2id(name)
8846 char_u *name;
8847{
8848 int i;
8849 char_u name_u[200];
8850
8851 /* Avoid using stricmp() too much, it's slow on some systems */
8852 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8853 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008854 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008855 vim_strup(name_u);
8856 for (i = highlight_ga.ga_len; --i >= 0; )
8857 if (HL_TABLE()[i].sg_name_u != NULL
8858 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8859 break;
8860 return i + 1;
8861}
8862
8863#if defined(FEAT_EVAL) || defined(PROTO)
8864/*
8865 * Return TRUE if highlight group "name" exists.
8866 */
8867 int
8868highlight_exists(name)
8869 char_u *name;
8870{
8871 return (syn_name2id(name) > 0);
8872}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008873
8874# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8875/*
8876 * Return the name of highlight group "id".
8877 * When not a valid ID return an empty string.
8878 */
8879 char_u *
8880syn_id2name(id)
8881 int id;
8882{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008883 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008884 return (char_u *)"";
8885 return HL_TABLE()[id - 1].sg_name;
8886}
8887# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008888#endif
8889
8890/*
8891 * Like syn_name2id(), but take a pointer + length argument.
8892 */
8893 int
8894syn_namen2id(linep, len)
8895 char_u *linep;
8896 int len;
8897{
8898 char_u *name;
8899 int id = 0;
8900
8901 name = vim_strnsave(linep, len);
8902 if (name != NULL)
8903 {
8904 id = syn_name2id(name);
8905 vim_free(name);
8906 }
8907 return id;
8908}
8909
8910/*
8911 * Find highlight group name in the table and return it's ID.
8912 * The argument is a pointer to the name and the length of the name.
8913 * If it doesn't exist yet, a new entry is created.
8914 * Return 0 for failure.
8915 */
8916 int
8917syn_check_group(pp, len)
8918 char_u *pp;
8919 int len;
8920{
8921 int id;
8922 char_u *name;
8923
8924 name = vim_strnsave(pp, len);
8925 if (name == NULL)
8926 return 0;
8927
8928 id = syn_name2id(name);
8929 if (id == 0) /* doesn't exist yet */
8930 id = syn_add_group(name);
8931 else
8932 vim_free(name);
8933 return id;
8934}
8935
8936/*
8937 * Add new highlight group and return it's ID.
8938 * "name" must be an allocated string, it will be consumed.
8939 * Return 0 for failure.
8940 */
8941 static int
8942syn_add_group(name)
8943 char_u *name;
8944{
8945 char_u *p;
8946
8947 /* Check that the name is ASCII letters, digits and underscore. */
8948 for (p = name; *p != NUL; ++p)
8949 {
8950 if (!vim_isprintc(*p))
8951 {
8952 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008953 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008954 return 0;
8955 }
8956 else if (!ASCII_ISALNUM(*p) && *p != '_')
8957 {
8958 /* This is an error, but since there previously was no check only
8959 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008960 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008961 MSG(_("W18: Invalid character in group name"));
8962 break;
8963 }
8964 }
8965
8966 /*
8967 * First call for this growarray: init growing array.
8968 */
8969 if (highlight_ga.ga_data == NULL)
8970 {
8971 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8972 highlight_ga.ga_growsize = 10;
8973 }
8974
8975 /*
8976 * Make room for at least one other syntax_highlight entry.
8977 */
8978 if (ga_grow(&highlight_ga, 1) == FAIL)
8979 {
8980 vim_free(name);
8981 return 0;
8982 }
8983
8984 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8985 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8986 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8987#ifdef FEAT_GUI
8988 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8989 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008990 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008991#endif
8992 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008993
8994 return highlight_ga.ga_len; /* ID is index plus one */
8995}
8996
8997/*
8998 * When, just after calling syn_add_group(), an error is discovered, this
8999 * function deletes the new name.
9000 */
9001 static void
9002syn_unadd_group()
9003{
9004 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009005 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9006 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9007}
9008
9009/*
9010 * Translate a group ID to highlight attributes.
9011 */
9012 int
9013syn_id2attr(hl_id)
9014 int hl_id;
9015{
9016 int attr;
9017 struct hl_group *sgp;
9018
9019 hl_id = syn_get_final_id(hl_id);
9020 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9021
9022#ifdef FEAT_GUI
9023 /*
9024 * Only use GUI attr when the GUI is being used.
9025 */
9026 if (gui.in_use)
9027 attr = sgp->sg_gui_attr;
9028 else
9029#endif
9030 if (t_colors > 1)
9031 attr = sgp->sg_cterm_attr;
9032 else
9033 attr = sgp->sg_term_attr;
9034
9035 return attr;
9036}
9037
9038#ifdef FEAT_GUI
9039/*
9040 * Get the GUI colors and attributes for a group ID.
9041 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9042 */
9043 int
9044syn_id2colors(hl_id, fgp, bgp)
9045 int hl_id;
9046 guicolor_T *fgp;
9047 guicolor_T *bgp;
9048{
9049 struct hl_group *sgp;
9050
9051 hl_id = syn_get_final_id(hl_id);
9052 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9053
9054 *fgp = sgp->sg_gui_fg;
9055 *bgp = sgp->sg_gui_bg;
9056 return sgp->sg_gui;
9057}
9058#endif
9059
9060/*
9061 * Translate a group ID to the final group ID (following links).
9062 */
9063 int
9064syn_get_final_id(hl_id)
9065 int hl_id;
9066{
9067 int count;
9068 struct hl_group *sgp;
9069
9070 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9071 return 0; /* Can be called from eval!! */
9072
9073 /*
9074 * Follow links until there is no more.
9075 * Look out for loops! Break after 100 links.
9076 */
9077 for (count = 100; --count >= 0; )
9078 {
9079 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9080 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9081 break;
9082 hl_id = sgp->sg_link;
9083 }
9084
9085 return hl_id;
9086}
9087
9088#ifdef FEAT_GUI
9089/*
9090 * Call this function just after the GUI has started.
9091 * It finds the font and color handles for the highlighting groups.
9092 */
9093 void
9094highlight_gui_started()
9095{
9096 int idx;
9097
9098 /* First get the colors from the "Normal" and "Menu" group, if set */
9099 set_normal_colors();
9100
9101 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9102 gui_do_one_color(idx, FALSE, FALSE);
9103
9104 highlight_changed();
9105}
9106
9107 static void
9108gui_do_one_color(idx, do_menu, do_tooltip)
9109 int idx;
9110 int do_menu; /* TRUE: might set the menu font */
9111 int do_tooltip; /* TRUE: might set the tooltip font */
9112{
9113 int didit = FALSE;
9114
9115 if (HL_TABLE()[idx].sg_font_name != NULL)
9116 {
9117 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9118 do_tooltip);
9119 didit = TRUE;
9120 }
9121 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9122 {
9123 HL_TABLE()[idx].sg_gui_fg =
9124 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9125 didit = TRUE;
9126 }
9127 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9128 {
9129 HL_TABLE()[idx].sg_gui_bg =
9130 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9131 didit = TRUE;
9132 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009133 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9134 {
9135 HL_TABLE()[idx].sg_gui_sp =
9136 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9137 didit = TRUE;
9138 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009139 if (didit) /* need to get a new attr number */
9140 set_hl_attr(idx);
9141}
9142
9143#endif
9144
9145/*
9146 * Translate the 'highlight' option into attributes in highlight_attr[] and
9147 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9148 * corresponding highlights to use on top of HLF_SNC is computed.
9149 * Called only when the 'highlight' option has been changed and upon first
9150 * screen redraw after any :highlight command.
9151 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9152 */
9153 int
9154highlight_changed()
9155{
9156 int hlf;
9157 int i;
9158 char_u *p;
9159 int attr;
9160 char_u *end;
9161 int id;
9162#ifdef USER_HIGHLIGHT
9163 char_u userhl[10];
9164# ifdef FEAT_STL_OPT
9165 int id_SNC = -1;
9166 int id_S = -1;
9167 int hlcnt;
9168# endif
9169#endif
9170 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9171
9172 need_highlight_changed = FALSE;
9173
9174 /*
9175 * Clear all attributes.
9176 */
9177 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9178 highlight_attr[hlf] = 0;
9179
9180 /*
9181 * First set all attributes to their default value.
9182 * Then use the attributes from the 'highlight' option.
9183 */
9184 for (i = 0; i < 2; ++i)
9185 {
9186 if (i)
9187 p = p_hl;
9188 else
9189 p = get_highlight_default();
9190 if (p == NULL) /* just in case */
9191 continue;
9192
9193 while (*p)
9194 {
9195 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9196 if (hl_flags[hlf] == *p)
9197 break;
9198 ++p;
9199 if (hlf == (int)HLF_COUNT || *p == NUL)
9200 return FAIL;
9201
9202 /*
9203 * Allow several hl_flags to be combined, like "bu" for
9204 * bold-underlined.
9205 */
9206 attr = 0;
9207 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9208 {
9209 if (vim_iswhite(*p)) /* ignore white space */
9210 continue;
9211
9212 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9213 return FAIL;
9214
9215 switch (*p)
9216 {
9217 case 'b': attr |= HL_BOLD;
9218 break;
9219 case 'i': attr |= HL_ITALIC;
9220 break;
9221 case '-':
9222 case 'n': /* no highlighting */
9223 break;
9224 case 'r': attr |= HL_INVERSE;
9225 break;
9226 case 's': attr |= HL_STANDOUT;
9227 break;
9228 case 'u': attr |= HL_UNDERLINE;
9229 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009230 case 'c': attr |= HL_UNDERCURL;
9231 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009232 case ':': ++p; /* highlight group name */
9233 if (attr || *p == NUL) /* no combinations */
9234 return FAIL;
9235 end = vim_strchr(p, ',');
9236 if (end == NULL)
9237 end = p + STRLEN(p);
9238 id = syn_check_group(p, (int)(end - p));
9239 if (id == 0)
9240 return FAIL;
9241 attr = syn_id2attr(id);
9242 p = end - 1;
9243#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9244 if (hlf == (int)HLF_SNC)
9245 id_SNC = syn_get_final_id(id);
9246 else if (hlf == (int)HLF_S)
9247 id_S = syn_get_final_id(id);
9248#endif
9249 break;
9250 default: return FAIL;
9251 }
9252 }
9253 highlight_attr[hlf] = attr;
9254
9255 p = skip_to_option_part(p); /* skip comma and spaces */
9256 }
9257 }
9258
9259#ifdef USER_HIGHLIGHT
9260 /* Setup the user highlights
9261 *
9262 * Temporarily utilize 10 more hl entries. Have to be in there
9263 * simultaneously in case of table overflows in get_attr_entry()
9264 */
9265# ifdef FEAT_STL_OPT
9266 if (ga_grow(&highlight_ga, 10) == FAIL)
9267 return FAIL;
9268 hlcnt = highlight_ga.ga_len;
9269 if (id_S == 0)
9270 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009271 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009272 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9273 id_S = hlcnt + 10;
9274 }
9275# endif
9276 for (i = 0; i < 9; i++)
9277 {
9278 sprintf((char *)userhl, "User%d", i + 1);
9279 id = syn_name2id(userhl);
9280 if (id == 0)
9281 {
9282 highlight_user[i] = 0;
9283# ifdef FEAT_STL_OPT
9284 highlight_stlnc[i] = 0;
9285# endif
9286 }
9287 else
9288 {
9289# ifdef FEAT_STL_OPT
9290 struct hl_group *hlt = HL_TABLE();
9291# endif
9292
9293 highlight_user[i] = syn_id2attr(id);
9294# ifdef FEAT_STL_OPT
9295 if (id_SNC == 0)
9296 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009297 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009298 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9299 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009300# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009301 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9302# endif
9303 }
9304 else
9305 mch_memmove(&hlt[hlcnt + i],
9306 &hlt[id_SNC - 1],
9307 sizeof(struct hl_group));
9308 hlt[hlcnt + i].sg_link = 0;
9309
9310 /* Apply difference between UserX and HLF_S to HLF_SNC */
9311 hlt[hlcnt + i].sg_term ^=
9312 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9313 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9314 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9315 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9316 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9317 hlt[hlcnt + i].sg_cterm ^=
9318 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9319 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9320 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9321 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9322 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009323# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009324 hlt[hlcnt + i].sg_gui ^=
9325 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009326# endif
9327# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009328 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9329 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9330 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9331 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009332 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9333 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009334 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9335 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9336# ifdef FEAT_XFONTSET
9337 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9338 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9339# endif
9340# endif
9341 highlight_ga.ga_len = hlcnt + i + 1;
9342 set_hl_attr(hlcnt + i); /* At long last we can apply */
9343 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9344# endif
9345 }
9346 }
9347# ifdef FEAT_STL_OPT
9348 highlight_ga.ga_len = hlcnt;
9349# endif
9350
9351#endif /* USER_HIGHLIGHT */
9352
9353 return OK;
9354}
9355
Bram Moolenaar4f688582007-07-24 12:34:30 +00009356#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009357
9358static void highlight_list __ARGS((void));
9359static void highlight_list_two __ARGS((int cnt, int attr));
9360
9361/*
9362 * Handle command line completion for :highlight command.
9363 */
9364 void
9365set_context_in_highlight_cmd(xp, arg)
9366 expand_T *xp;
9367 char_u *arg;
9368{
9369 char_u *p;
9370
9371 /* Default: expand group names */
9372 xp->xp_context = EXPAND_HIGHLIGHT;
9373 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009374 include_link = 2;
9375 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009376
9377 /* (part of) subcommand already typed */
9378 if (*arg != NUL)
9379 {
9380 p = skiptowhite(arg);
9381 if (*p != NUL) /* past "default" or group name */
9382 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009383 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009384 if (STRNCMP("default", arg, p - arg) == 0)
9385 {
9386 arg = skipwhite(p);
9387 xp->xp_pattern = arg;
9388 p = skiptowhite(arg);
9389 }
9390 if (*p != NUL) /* past group name */
9391 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009392 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009393 if (arg[1] == 'i' && arg[0] == 'N')
9394 highlight_list();
9395 if (STRNCMP("link", arg, p - arg) == 0
9396 || STRNCMP("clear", arg, p - arg) == 0)
9397 {
9398 xp->xp_pattern = skipwhite(p);
9399 p = skiptowhite(xp->xp_pattern);
9400 if (*p != NUL) /* past first group name */
9401 {
9402 xp->xp_pattern = skipwhite(p);
9403 p = skiptowhite(xp->xp_pattern);
9404 }
9405 }
9406 if (*p != NUL) /* past group name(s) */
9407 xp->xp_context = EXPAND_NOTHING;
9408 }
9409 }
9410 }
9411}
9412
9413/*
9414 * List highlighting matches in a nice way.
9415 */
9416 static void
9417highlight_list()
9418{
9419 int i;
9420
9421 for (i = 10; --i >= 0; )
9422 highlight_list_two(i, hl_attr(HLF_D));
9423 for (i = 40; --i >= 0; )
9424 highlight_list_two(99, 0);
9425}
9426
9427 static void
9428highlight_list_two(cnt, attr)
9429 int cnt;
9430 int attr;
9431{
9432 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9433 msg_clr_eos();
9434 out_flush();
9435 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9436}
9437
9438#endif /* FEAT_CMDL_COMPL */
9439
9440#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9441 || defined(FEAT_SIGNS) || defined(PROTO)
9442/*
9443 * Function given to ExpandGeneric() to obtain the list of group names.
9444 * Also used for synIDattr() function.
9445 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009446 char_u *
9447get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009448 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009449 int idx;
9450{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009451#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009452 if (idx == highlight_ga.ga_len && include_none != 0)
9453 return (char_u *)"none";
9454 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009455 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009456 if (idx == highlight_ga.ga_len + include_none + include_default
9457 && include_link != 0)
9458 return (char_u *)"link";
9459 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9460 && include_link != 0)
9461 return (char_u *)"clear";
9462#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009463 if (idx < 0 || idx >= highlight_ga.ga_len)
9464 return NULL;
9465 return HL_TABLE()[idx].sg_name;
9466}
9467#endif
9468
Bram Moolenaar4f688582007-07-24 12:34:30 +00009469#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009470/*
9471 * Free all the highlight group fonts.
9472 * Used when quitting for systems which need it.
9473 */
9474 void
9475free_highlight_fonts()
9476{
9477 int idx;
9478
9479 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9480 {
9481 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9482 HL_TABLE()[idx].sg_font = NOFONT;
9483# ifdef FEAT_XFONTSET
9484 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9485 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9486# endif
9487 }
9488
9489 gui_mch_free_font(gui.norm_font);
9490# ifdef FEAT_XFONTSET
9491 gui_mch_free_fontset(gui.fontset);
9492# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009493# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009494 gui_mch_free_font(gui.bold_font);
9495 gui_mch_free_font(gui.ital_font);
9496 gui_mch_free_font(gui.boldital_font);
9497# endif
9498}
9499#endif
9500
9501/**************************************
9502 * End of Highlighting stuff *
9503 **************************************/