blob: d2a99a4f7985ceb43c9459d29c7b810f28d7bce5 [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 Moolenaar860cae12010-06-05 23:22:07 +02004540 arg = skipwhite(arg + 7);
4541 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004543 {
4544 opt->flags |= flagtab[fidx].flags;
4545 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004547 if (flagtab[fidx].flags == HL_SYNC_HERE
4548 || flagtab[fidx].flags == HL_SYNC_THERE)
4549 {
4550 if (opt->sync_idx == NULL)
4551 {
4552 EMSG(_("E393: group[t]here not accepted here"));
4553 return NULL;
4554 }
4555 gname_start = arg;
4556 arg = skiptowhite(arg);
4557 if (gname_start == arg)
4558 return NULL;
4559 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4560 if (gname == NULL)
4561 return NULL;
4562 if (STRCMP(gname, "NONE") == 0)
4563 *opt->sync_idx = NONE_IDX;
4564 else
4565 {
4566 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004567 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4568 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4569 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004570 {
4571 *opt->sync_idx = i;
4572 break;
4573 }
4574 if (i < 0)
4575 {
4576 EMSG2(_("E394: Didn't find region item for %s"), gname);
4577 vim_free(gname);
4578 return NULL;
4579 }
4580 }
4581
4582 vim_free(gname);
4583 arg = skipwhite(arg);
4584 }
4585#ifdef FEAT_FOLDING
4586 else if (flagtab[fidx].flags == HL_FOLD
4587 && foldmethodIsSyntax(curwin))
4588 /* Need to update folds later. */
4589 foldUpdateAll(curwin);
4590#endif
4591 }
4592 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004593
4594 return arg;
4595}
4596
4597/*
4598 * Adjustments to syntax item when declared in a ":syn include"'d file.
4599 * Set the contained flag, and if the item is not already contained, add it
4600 * to the specified top-level group, if any.
4601 */
4602 static void
4603syn_incl_toplevel(id, flagsp)
4604 int id;
4605 int *flagsp;
4606{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004607 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608 return;
4609 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004610 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611 {
4612 /* We have to alloc this, because syn_combine_list() will free it. */
4613 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004614 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004615
4616 if (grp_list != NULL)
4617 {
4618 grp_list[0] = id;
4619 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004620 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004621 CLUSTER_ADD);
4622 }
4623 }
4624}
4625
4626/*
4627 * Handle ":syntax include [@{group-name}] filename" command.
4628 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 static void
4630syn_cmd_include(eap, syncing)
4631 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004632 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633{
4634 char_u *arg = eap->arg;
4635 int sgl_id = 1;
4636 char_u *group_name_end;
4637 char_u *rest;
4638 char_u *errormsg = NULL;
4639 int prev_toplvl_grp;
4640 int prev_syn_inc_tag;
4641 int source = FALSE;
4642
4643 eap->nextcmd = find_nextcmd(arg);
4644 if (eap->skip)
4645 return;
4646
4647 if (arg[0] == '@')
4648 {
4649 ++arg;
4650 rest = get_group_name(arg, &group_name_end);
4651 if (rest == NULL)
4652 {
4653 EMSG((char_u *)_("E397: Filename required"));
4654 return;
4655 }
4656 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4657 /* separate_nextcmd() and expand_filename() depend on this */
4658 eap->arg = rest;
4659 }
4660
4661 /*
4662 * Everything that's left, up to the next command, should be the
4663 * filename to include.
4664 */
4665 eap->argt |= (XFILE | NOSPC);
4666 separate_nextcmd(eap);
4667 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4668 {
4669 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4670 * file. Need to expand the file name first. In other cases
4671 * ":runtime!" is used. */
4672 source = TRUE;
4673 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4674 {
4675 if (errormsg != NULL)
4676 EMSG(errormsg);
4677 return;
4678 }
4679 }
4680
4681 /*
4682 * Save and restore the existing top-level grouplist id and ":syn
4683 * include" tag around the actual inclusion.
4684 */
4685 prev_syn_inc_tag = current_syn_inc_tag;
4686 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004687 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4688 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004689 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4690 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004691 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004692 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693 current_syn_inc_tag = prev_syn_inc_tag;
4694}
4695
4696/*
4697 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4698 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004699 static void
4700syn_cmd_keyword(eap, syncing)
4701 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004702 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703{
4704 char_u *arg = eap->arg;
4705 char_u *group_name_end;
4706 int syn_id;
4707 char_u *rest;
4708 char_u *keyword_copy;
4709 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004710 char_u *kw;
4711 syn_opt_arg_T syn_opt_arg;
4712 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004713 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004714
4715 rest = get_group_name(arg, &group_name_end);
4716
4717 if (rest != NULL)
4718 {
4719 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4720
4721 /* allocate a buffer, for removing the backslashes in the keyword */
4722 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4723 if (keyword_copy != NULL)
4724 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004725 syn_opt_arg.flags = 0;
4726 syn_opt_arg.keyword = TRUE;
4727 syn_opt_arg.sync_idx = NULL;
4728 syn_opt_arg.has_cont_list = FALSE;
4729 syn_opt_arg.cont_in_list = NULL;
4730 syn_opt_arg.next_list = NULL;
4731
Bram Moolenaar071d4272004-06-13 20:20:40 +00004732 /*
4733 * The options given apply to ALL keywords, so all options must be
4734 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004735 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004737 cnt = 0;
4738 p = keyword_copy;
4739 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004741 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004742 if (rest == NULL || ends_excmd(*rest))
4743 break;
4744 /* Copy the keyword, removing backslashes, and add a NUL. */
4745 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004747 if (*rest == '\\' && rest[1] != NUL)
4748 ++rest;
4749 *p++ = *rest++;
4750 }
4751 *p++ = NUL;
4752 ++cnt;
4753 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004755 if (!eap->skip)
4756 {
4757 /* Adjust flags for use of ":syn include". */
4758 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4759
4760 /*
4761 * 2: Add an entry for each keyword.
4762 */
4763 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4764 {
4765 for (p = vim_strchr(kw, '['); ; )
4766 {
4767 if (p != NULL)
4768 *p = NUL;
4769 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004770 syn_opt_arg.cont_in_list,
4771 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004772 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004773 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004774 if (p[1] == NUL)
4775 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004776 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004777 kw = p + 2; /* skip over the NUL */
4778 break;
4779 }
4780 if (p[1] == ']')
4781 {
4782 kw = p + 1; /* skip over the "]" */
4783 break;
4784 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004785#ifdef FEAT_MBYTE
4786 if (has_mbyte)
4787 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004788 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004789
4790 mch_memmove(p, p + 1, l);
4791 p += l;
4792 }
4793 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004795 {
4796 p[0] = p[1];
4797 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004798 }
4799 }
4800 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004801 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004802
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004804 vim_free(syn_opt_arg.cont_in_list);
4805 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004806 }
4807 }
4808
4809 if (rest != NULL)
4810 eap->nextcmd = check_nextcmd(rest);
4811 else
4812 EMSG2(_(e_invarg2), arg);
4813
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004814 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004815 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816}
4817
4818/*
4819 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4820 *
4821 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4822 */
4823 static void
4824syn_cmd_match(eap, syncing)
4825 exarg_T *eap;
4826 int syncing; /* TRUE for ":syntax sync match .. " */
4827{
4828 char_u *arg = eap->arg;
4829 char_u *group_name_end;
4830 char_u *rest;
4831 synpat_T item; /* the item found in the line */
4832 int syn_id;
4833 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004834 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004836 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837
4838 /* Isolate the group name, check for validity */
4839 rest = get_group_name(arg, &group_name_end);
4840
4841 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004842 syn_opt_arg.flags = 0;
4843 syn_opt_arg.keyword = FALSE;
4844 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4845 syn_opt_arg.has_cont_list = TRUE;
4846 syn_opt_arg.cont_list = NULL;
4847 syn_opt_arg.cont_in_list = NULL;
4848 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004849 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850
4851 /* get the pattern. */
4852 init_syn_patterns();
4853 vim_memset(&item, 0, sizeof(item));
4854 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004855 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4856 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857
4858 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004859 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860
4861 if (rest != NULL) /* all arguments are valid */
4862 {
4863 /*
4864 * Check for trailing command and illegal trailing arguments.
4865 */
4866 eap->nextcmd = check_nextcmd(rest);
4867 if (!ends_excmd(*rest) || eap->skip)
4868 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004869 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870 && (syn_id = syn_check_group(arg,
4871 (int)(group_name_end - arg))) != 0)
4872 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004873 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004874 /*
4875 * Store the pattern in the syn_items list
4876 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004877 idx = curwin->w_s->b_syn_patterns.ga_len;
4878 SYN_ITEMS(curwin->w_s)[idx] = item;
4879 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4880 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4881 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4882 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4883 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4884 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4885 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4886 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004887 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004888#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004889 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004890#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004891 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004892 curwin->w_s->b_syn_containedin = TRUE;
4893 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4894 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895
4896 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004897 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004898 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004900 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004901 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902#endif
4903
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004904 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004905 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906 return; /* don't free the progs and patterns now */
4907 }
4908 }
4909
4910 /*
4911 * Something failed, free the allocated memory.
4912 */
4913 vim_free(item.sp_prog);
4914 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004915 vim_free(syn_opt_arg.cont_list);
4916 vim_free(syn_opt_arg.cont_in_list);
4917 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918
4919 if (rest == NULL)
4920 EMSG2(_(e_invarg2), arg);
4921}
4922
4923/*
4924 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4925 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4926 */
4927 static void
4928syn_cmd_region(eap, syncing)
4929 exarg_T *eap;
4930 int syncing; /* TRUE for ":syntax sync region .." */
4931{
4932 char_u *arg = eap->arg;
4933 char_u *group_name_end;
4934 char_u *rest; /* next arg, NULL on error */
4935 char_u *key_end;
4936 char_u *key = NULL;
4937 char_u *p;
4938 int item;
4939#define ITEM_START 0
4940#define ITEM_SKIP 1
4941#define ITEM_END 2
4942#define ITEM_MATCHGROUP 3
4943 struct pat_ptr
4944 {
4945 synpat_T *pp_synp; /* pointer to syn_pattern */
4946 int pp_matchgroup_id; /* matchgroup ID */
4947 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4948 } *(pat_ptrs[3]);
4949 /* patterns found in the line */
4950 struct pat_ptr *ppp;
4951 struct pat_ptr *ppp_next;
4952 int pat_count = 0; /* nr of syn_patterns found */
4953 int syn_id;
4954 int matchgroup_id = 0;
4955 int not_enough = FALSE; /* not enough arguments */
4956 int illegal = FALSE; /* illegal arguments */
4957 int success = FALSE;
4958 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004959 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004960 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961
4962 /* Isolate the group name, check for validity */
4963 rest = get_group_name(arg, &group_name_end);
4964
4965 pat_ptrs[0] = NULL;
4966 pat_ptrs[1] = NULL;
4967 pat_ptrs[2] = NULL;
4968
4969 init_syn_patterns();
4970
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004971 syn_opt_arg.flags = 0;
4972 syn_opt_arg.keyword = FALSE;
4973 syn_opt_arg.sync_idx = NULL;
4974 syn_opt_arg.has_cont_list = TRUE;
4975 syn_opt_arg.cont_list = NULL;
4976 syn_opt_arg.cont_in_list = NULL;
4977 syn_opt_arg.next_list = NULL;
4978
Bram Moolenaar071d4272004-06-13 20:20:40 +00004979 /*
4980 * get the options, patterns and matchgroup.
4981 */
4982 while (rest != NULL && !ends_excmd(*rest))
4983 {
4984 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004985 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986 if (rest == NULL || ends_excmd(*rest))
4987 break;
4988
4989 /* must be a pattern or matchgroup then */
4990 key_end = rest;
4991 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4992 ++key_end;
4993 vim_free(key);
4994 key = vim_strnsave_up(rest, (int)(key_end - rest));
4995 if (key == NULL) /* out of memory */
4996 {
4997 rest = NULL;
4998 break;
4999 }
5000 if (STRCMP(key, "MATCHGROUP") == 0)
5001 item = ITEM_MATCHGROUP;
5002 else if (STRCMP(key, "START") == 0)
5003 item = ITEM_START;
5004 else if (STRCMP(key, "END") == 0)
5005 item = ITEM_END;
5006 else if (STRCMP(key, "SKIP") == 0)
5007 {
5008 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5009 {
5010 illegal = TRUE;
5011 break;
5012 }
5013 item = ITEM_SKIP;
5014 }
5015 else
5016 break;
5017 rest = skipwhite(key_end);
5018 if (*rest != '=')
5019 {
5020 rest = NULL;
5021 EMSG2(_("E398: Missing '=': %s"), arg);
5022 break;
5023 }
5024 rest = skipwhite(rest + 1);
5025 if (*rest == NUL)
5026 {
5027 not_enough = TRUE;
5028 break;
5029 }
5030
5031 if (item == ITEM_MATCHGROUP)
5032 {
5033 p = skiptowhite(rest);
5034 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5035 matchgroup_id = 0;
5036 else
5037 {
5038 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5039 if (matchgroup_id == 0)
5040 {
5041 illegal = TRUE;
5042 break;
5043 }
5044 }
5045 rest = skipwhite(p);
5046 }
5047 else
5048 {
5049 /*
5050 * Allocate room for a syn_pattern, and link it in the list of
5051 * syn_patterns for this item, at the start (because the list is
5052 * used from end to start).
5053 */
5054 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5055 if (ppp == NULL)
5056 {
5057 rest = NULL;
5058 break;
5059 }
5060 ppp->pp_next = pat_ptrs[item];
5061 pat_ptrs[item] = ppp;
5062 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5063 if (ppp->pp_synp == NULL)
5064 {
5065 rest = NULL;
5066 break;
5067 }
5068
5069 /*
5070 * Get the syntax pattern and the following offset(s).
5071 */
5072 /* Enable the appropriate \z specials. */
5073 if (item == ITEM_START)
5074 reg_do_extmatch = REX_SET;
5075 else if (item == ITEM_SKIP || item == ITEM_END)
5076 reg_do_extmatch = REX_USE;
5077 rest = get_syn_pattern(rest, ppp->pp_synp);
5078 reg_do_extmatch = 0;
5079 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005080 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005081 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5082 ppp->pp_matchgroup_id = matchgroup_id;
5083 ++pat_count;
5084 }
5085 }
5086 vim_free(key);
5087 if (illegal || not_enough)
5088 rest = NULL;
5089
5090 /*
5091 * Must have a "start" and "end" pattern.
5092 */
5093 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5094 pat_ptrs[ITEM_END] == NULL))
5095 {
5096 not_enough = TRUE;
5097 rest = NULL;
5098 }
5099
5100 if (rest != NULL)
5101 {
5102 /*
5103 * Check for trailing garbage or command.
5104 * If OK, add the item.
5105 */
5106 eap->nextcmd = check_nextcmd(rest);
5107 if (!ends_excmd(*rest) || eap->skip)
5108 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005109 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005110 && (syn_id = syn_check_group(arg,
5111 (int)(group_name_end - arg))) != 0)
5112 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005113 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 /*
5115 * Store the start/skip/end in the syn_items list
5116 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005117 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118 for (item = ITEM_START; item <= ITEM_END; ++item)
5119 {
5120 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5121 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005122 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5123 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5124 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 (item == ITEM_START) ? SPTYPE_START :
5126 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005127 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5128 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5129 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5130 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005131 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005132#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005133 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005134#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 if (item == ITEM_START)
5136 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005137 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005138 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005139 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005140 syn_opt_arg.cont_in_list;
5141 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005142 curwin->w_s->b_syn_containedin = TRUE;
5143 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005144 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005145 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005146 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147 ++idx;
5148#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005149 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005150 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005151#endif
5152 }
5153 }
5154
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005155 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005156 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 success = TRUE; /* don't free the progs and patterns now */
5158 }
5159 }
5160
5161 /*
5162 * Free the allocated memory.
5163 */
5164 for (item = ITEM_START; item <= ITEM_END; ++item)
5165 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5166 {
5167 if (!success)
5168 {
5169 vim_free(ppp->pp_synp->sp_prog);
5170 vim_free(ppp->pp_synp->sp_pattern);
5171 }
5172 vim_free(ppp->pp_synp);
5173 ppp_next = ppp->pp_next;
5174 vim_free(ppp);
5175 }
5176
5177 if (!success)
5178 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005179 vim_free(syn_opt_arg.cont_list);
5180 vim_free(syn_opt_arg.cont_in_list);
5181 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005182 if (not_enough)
5183 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5184 else if (illegal || rest == NULL)
5185 EMSG2(_(e_invarg2), arg);
5186 }
5187}
5188
5189/*
5190 * A simple syntax group ID comparison function suitable for use in qsort()
5191 */
5192 static int
5193#ifdef __BORLANDC__
5194_RTLENTRYF
5195#endif
5196syn_compare_stub(v1, v2)
5197 const void *v1;
5198 const void *v2;
5199{
5200 const short *s1 = v1;
5201 const short *s2 = v2;
5202
5203 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5204}
5205
5206/*
5207 * Combines lists of syntax clusters.
5208 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5209 */
5210 static void
5211syn_combine_list(clstr1, clstr2, list_op)
5212 short **clstr1;
5213 short **clstr2;
5214 int list_op;
5215{
5216 int count1 = 0;
5217 int count2 = 0;
5218 short *g1;
5219 short *g2;
5220 short *clstr = NULL;
5221 int count;
5222 int round;
5223
5224 /*
5225 * Handle degenerate cases.
5226 */
5227 if (*clstr2 == NULL)
5228 return;
5229 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5230 {
5231 if (list_op == CLUSTER_REPLACE)
5232 vim_free(*clstr1);
5233 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5234 *clstr1 = *clstr2;
5235 else
5236 vim_free(*clstr2);
5237 return;
5238 }
5239
5240 for (g1 = *clstr1; *g1; g1++)
5241 ++count1;
5242 for (g2 = *clstr2; *g2; g2++)
5243 ++count2;
5244
5245 /*
5246 * For speed purposes, sort both lists.
5247 */
5248 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5249 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5250
5251 /*
5252 * We proceed in two passes; in round 1, we count the elements to place
5253 * in the new list, and in round 2, we allocate and populate the new
5254 * list. For speed, we use a mergesort-like method, adding the smaller
5255 * of the current elements in each list to the new list.
5256 */
5257 for (round = 1; round <= 2; round++)
5258 {
5259 g1 = *clstr1;
5260 g2 = *clstr2;
5261 count = 0;
5262
5263 /*
5264 * First, loop through the lists until one of them is empty.
5265 */
5266 while (*g1 && *g2)
5267 {
5268 /*
5269 * We always want to add from the first list.
5270 */
5271 if (*g1 < *g2)
5272 {
5273 if (round == 2)
5274 clstr[count] = *g1;
5275 count++;
5276 g1++;
5277 continue;
5278 }
5279 /*
5280 * We only want to add from the second list if we're adding the
5281 * lists.
5282 */
5283 if (list_op == CLUSTER_ADD)
5284 {
5285 if (round == 2)
5286 clstr[count] = *g2;
5287 count++;
5288 }
5289 if (*g1 == *g2)
5290 g1++;
5291 g2++;
5292 }
5293
5294 /*
5295 * Now add the leftovers from whichever list didn't get finished
5296 * first. As before, we only want to add from the second list if
5297 * we're adding the lists.
5298 */
5299 for (; *g1; g1++, count++)
5300 if (round == 2)
5301 clstr[count] = *g1;
5302 if (list_op == CLUSTER_ADD)
5303 for (; *g2; g2++, count++)
5304 if (round == 2)
5305 clstr[count] = *g2;
5306
5307 if (round == 1)
5308 {
5309 /*
5310 * If the group ended up empty, we don't need to allocate any
5311 * space for it.
5312 */
5313 if (count == 0)
5314 {
5315 clstr = NULL;
5316 break;
5317 }
5318 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5319 if (clstr == NULL)
5320 break;
5321 clstr[count] = 0;
5322 }
5323 }
5324
5325 /*
5326 * Finally, put the new list in place.
5327 */
5328 vim_free(*clstr1);
5329 vim_free(*clstr2);
5330 *clstr1 = clstr;
5331}
5332
5333/*
5334 * Lookup a syntax cluster name and return it's ID.
5335 * If it is not found, 0 is returned.
5336 */
5337 static int
5338syn_scl_name2id(name)
5339 char_u *name;
5340{
5341 int i;
5342 char_u *name_u;
5343
5344 /* Avoid using stricmp() too much, it's slow on some systems */
5345 name_u = vim_strsave_up(name);
5346 if (name_u == NULL)
5347 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005348 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5349 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5350 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005351 break;
5352 vim_free(name_u);
5353 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5354}
5355
5356/*
5357 * Like syn_scl_name2id(), but take a pointer + length argument.
5358 */
5359 static int
5360syn_scl_namen2id(linep, len)
5361 char_u *linep;
5362 int len;
5363{
5364 char_u *name;
5365 int id = 0;
5366
5367 name = vim_strnsave(linep, len);
5368 if (name != NULL)
5369 {
5370 id = syn_scl_name2id(name);
5371 vim_free(name);
5372 }
5373 return id;
5374}
5375
5376/*
5377 * Find syntax cluster name in the table and return it's ID.
5378 * The argument is a pointer to the name and the length of the name.
5379 * If it doesn't exist yet, a new entry is created.
5380 * Return 0 for failure.
5381 */
5382 static int
5383syn_check_cluster(pp, len)
5384 char_u *pp;
5385 int len;
5386{
5387 int id;
5388 char_u *name;
5389
5390 name = vim_strnsave(pp, len);
5391 if (name == NULL)
5392 return 0;
5393
5394 id = syn_scl_name2id(name);
5395 if (id == 0) /* doesn't exist yet */
5396 id = syn_add_cluster(name);
5397 else
5398 vim_free(name);
5399 return id;
5400}
5401
5402/*
5403 * Add new syntax cluster and return it's ID.
5404 * "name" must be an allocated string, it will be consumed.
5405 * Return 0 for failure.
5406 */
5407 static int
5408syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005409 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005411 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412
5413 /*
5414 * First call for this growarray: init growing array.
5415 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005416 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005418 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5419 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005420 }
5421
5422 /*
5423 * Make room for at least one other cluster entry.
5424 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005425 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005426 {
5427 vim_free(name);
5428 return 0;
5429 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005430 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005431
Bram Moolenaar860cae12010-06-05 23:22:07 +02005432 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5433 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5434 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5435 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5436 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005437
Bram Moolenaar217ad922005-03-20 22:37:15 +00005438 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005439 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005440 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005441 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005442
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443 return len + SYNID_CLUSTER;
5444}
5445
5446/*
5447 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5448 * [add={groupname},..] [remove={groupname},..]".
5449 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 static void
5451syn_cmd_cluster(eap, syncing)
5452 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005453 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005454{
5455 char_u *arg = eap->arg;
5456 char_u *group_name_end;
5457 char_u *rest;
5458 int scl_id;
5459 short *clstr_list;
5460 int got_clstr = FALSE;
5461 int opt_len;
5462 int list_op;
5463
5464 eap->nextcmd = find_nextcmd(arg);
5465 if (eap->skip)
5466 return;
5467
5468 rest = get_group_name(arg, &group_name_end);
5469
5470 if (rest != NULL)
5471 {
5472 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005473 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005474
5475 for (;;)
5476 {
5477 if (STRNICMP(rest, "add", 3) == 0
5478 && (vim_iswhite(rest[3]) || rest[3] == '='))
5479 {
5480 opt_len = 3;
5481 list_op = CLUSTER_ADD;
5482 }
5483 else if (STRNICMP(rest, "remove", 6) == 0
5484 && (vim_iswhite(rest[6]) || rest[6] == '='))
5485 {
5486 opt_len = 6;
5487 list_op = CLUSTER_SUBTRACT;
5488 }
5489 else if (STRNICMP(rest, "contains", 8) == 0
5490 && (vim_iswhite(rest[8]) || rest[8] == '='))
5491 {
5492 opt_len = 8;
5493 list_op = CLUSTER_REPLACE;
5494 }
5495 else
5496 break;
5497
5498 clstr_list = NULL;
5499 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5500 {
5501 EMSG2(_(e_invarg2), rest);
5502 break;
5503 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005504 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005505 &clstr_list, list_op);
5506 got_clstr = TRUE;
5507 }
5508
5509 if (got_clstr)
5510 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005511 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005512 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513 }
5514 }
5515
5516 if (!got_clstr)
5517 EMSG(_("E400: No cluster specified"));
5518 if (rest == NULL || !ends_excmd(*rest))
5519 EMSG2(_(e_invarg2), arg);
5520}
5521
5522/*
5523 * On first call for current buffer: Init growing array.
5524 */
5525 static void
5526init_syn_patterns()
5527{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005528 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5529 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005530}
5531
5532/*
5533 * Get one pattern for a ":syntax match" or ":syntax region" command.
5534 * Stores the pattern and program in a synpat_T.
5535 * Returns a pointer to the next argument, or NULL in case of an error.
5536 */
5537 static char_u *
5538get_syn_pattern(arg, ci)
5539 char_u *arg;
5540 synpat_T *ci;
5541{
5542 char_u *end;
5543 int *p;
5544 int idx;
5545 char_u *cpo_save;
5546
5547 /* need at least three chars */
5548 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5549 return NULL;
5550
5551 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5552 if (*end != *arg) /* end delimiter not found */
5553 {
5554 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5555 return NULL;
5556 }
5557 /* store the pattern and compiled regexp program */
5558 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5559 return NULL;
5560
5561 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5562 cpo_save = p_cpo;
5563 p_cpo = (char_u *)"";
5564 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5565 p_cpo = cpo_save;
5566
5567 if (ci->sp_prog == NULL)
5568 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005569 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005570
5571 /*
5572 * Check for a match, highlight or region offset.
5573 */
5574 ++end;
5575 do
5576 {
5577 for (idx = SPO_COUNT; --idx >= 0; )
5578 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5579 break;
5580 if (idx >= 0)
5581 {
5582 p = &(ci->sp_offsets[idx]);
5583 if (idx != SPO_LC_OFF)
5584 switch (end[3])
5585 {
5586 case 's': break;
5587 case 'b': break;
5588 case 'e': idx += SPO_COUNT; break;
5589 default: idx = -1; break;
5590 }
5591 if (idx >= 0)
5592 {
5593 ci->sp_off_flags |= (1 << idx);
5594 if (idx == SPO_LC_OFF) /* lc=99 */
5595 {
5596 end += 3;
5597 *p = getdigits(&end);
5598
5599 /* "lc=" offset automatically sets "ms=" offset */
5600 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5601 {
5602 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5603 ci->sp_offsets[SPO_MS_OFF] = *p;
5604 }
5605 }
5606 else /* yy=x+99 */
5607 {
5608 end += 4;
5609 if (*end == '+')
5610 {
5611 ++end;
5612 *p = getdigits(&end); /* positive offset */
5613 }
5614 else if (*end == '-')
5615 {
5616 ++end;
5617 *p = -getdigits(&end); /* negative offset */
5618 }
5619 }
5620 if (*end != ',')
5621 break;
5622 ++end;
5623 }
5624 }
5625 } while (idx >= 0);
5626
5627 if (!ends_excmd(*end) && !vim_iswhite(*end))
5628 {
5629 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5630 return NULL;
5631 }
5632 return skipwhite(end);
5633}
5634
5635/*
5636 * Handle ":syntax sync .." command.
5637 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005638 static void
5639syn_cmd_sync(eap, syncing)
5640 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005641 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005642{
5643 char_u *arg_start = eap->arg;
5644 char_u *arg_end;
5645 char_u *key = NULL;
5646 char_u *next_arg;
5647 int illegal = FALSE;
5648 int finished = FALSE;
5649 long n;
5650 char_u *cpo_save;
5651
5652 if (ends_excmd(*arg_start))
5653 {
5654 syn_cmd_list(eap, TRUE);
5655 return;
5656 }
5657
5658 while (!ends_excmd(*arg_start))
5659 {
5660 arg_end = skiptowhite(arg_start);
5661 next_arg = skipwhite(arg_end);
5662 vim_free(key);
5663 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5664 if (STRCMP(key, "CCOMMENT") == 0)
5665 {
5666 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005667 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 if (!ends_excmd(*next_arg))
5669 {
5670 arg_end = skiptowhite(next_arg);
5671 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005672 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005673 (int)(arg_end - next_arg));
5674 next_arg = skipwhite(arg_end);
5675 }
5676 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005677 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678 }
5679 else if ( STRNCMP(key, "LINES", 5) == 0
5680 || STRNCMP(key, "MINLINES", 8) == 0
5681 || STRNCMP(key, "MAXLINES", 8) == 0
5682 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5683 {
5684 if (key[4] == 'S')
5685 arg_end = key + 6;
5686 else if (key[0] == 'L')
5687 arg_end = key + 11;
5688 else
5689 arg_end = key + 9;
5690 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5691 {
5692 illegal = TRUE;
5693 break;
5694 }
5695 n = getdigits(&arg_end);
5696 if (!eap->skip)
5697 {
5698 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005699 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005701 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005702 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005703 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 }
5705 }
5706 else if (STRCMP(key, "FROMSTART") == 0)
5707 {
5708 if (!eap->skip)
5709 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005710 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5711 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005712 }
5713 }
5714 else if (STRCMP(key, "LINECONT") == 0)
5715 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005716 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005717 {
5718 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5719 finished = TRUE;
5720 break;
5721 }
5722 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5723 if (*arg_end != *next_arg) /* end delimiter not found */
5724 {
5725 illegal = TRUE;
5726 break;
5727 }
5728
5729 if (!eap->skip)
5730 {
5731 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005732 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005733 (int)(arg_end - next_arg - 1))) == NULL)
5734 {
5735 finished = TRUE;
5736 break;
5737 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005738 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739
5740 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5741 cpo_save = p_cpo;
5742 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005743 curwin->w_s->b_syn_linecont_prog =
5744 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005745 p_cpo = cpo_save;
5746
Bram Moolenaar860cae12010-06-05 23:22:07 +02005747 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005749 vim_free(curwin->w_s->b_syn_linecont_pat);
5750 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005751 finished = TRUE;
5752 break;
5753 }
5754 }
5755 next_arg = skipwhite(arg_end + 1);
5756 }
5757 else
5758 {
5759 eap->arg = next_arg;
5760 if (STRCMP(key, "MATCH") == 0)
5761 syn_cmd_match(eap, TRUE);
5762 else if (STRCMP(key, "REGION") == 0)
5763 syn_cmd_region(eap, TRUE);
5764 else if (STRCMP(key, "CLEAR") == 0)
5765 syn_cmd_clear(eap, TRUE);
5766 else
5767 illegal = TRUE;
5768 finished = TRUE;
5769 break;
5770 }
5771 arg_start = next_arg;
5772 }
5773 vim_free(key);
5774 if (illegal)
5775 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5776 else if (!finished)
5777 {
5778 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005779 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005780 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005781 }
5782}
5783
5784/*
5785 * Convert a line of highlight group names into a list of group ID numbers.
5786 * "arg" should point to the "contains" or "nextgroup" keyword.
5787 * "arg" is advanced to after the last group name.
5788 * Careful: the argument is modified (NULs added).
5789 * returns FAIL for some error, OK for success.
5790 */
5791 static int
5792get_id_list(arg, keylen, list)
5793 char_u **arg;
5794 int keylen; /* length of keyword */
5795 short **list; /* where to store the resulting list, if not
5796 NULL, the list is silently skipped! */
5797{
5798 char_u *p = NULL;
5799 char_u *end;
5800 int round;
5801 int count;
5802 int total_count = 0;
5803 short *retval = NULL;
5804 char_u *name;
5805 regmatch_T regmatch;
5806 int id;
5807 int i;
5808 int failed = FALSE;
5809
5810 /*
5811 * We parse the list twice:
5812 * round == 1: count the number of items, allocate the array.
5813 * round == 2: fill the array with the items.
5814 * In round 1 new groups may be added, causing the number of items to
5815 * grow when a regexp is used. In that case round 1 is done once again.
5816 */
5817 for (round = 1; round <= 2; ++round)
5818 {
5819 /*
5820 * skip "contains"
5821 */
5822 p = skipwhite(*arg + keylen);
5823 if (*p != '=')
5824 {
5825 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5826 break;
5827 }
5828 p = skipwhite(p + 1);
5829 if (ends_excmd(*p))
5830 {
5831 EMSG2(_("E406: Empty argument: %s"), *arg);
5832 break;
5833 }
5834
5835 /*
5836 * parse the arguments after "contains"
5837 */
5838 count = 0;
5839 while (!ends_excmd(*p))
5840 {
5841 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5842 ;
5843 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5844 if (name == NULL)
5845 {
5846 failed = TRUE;
5847 break;
5848 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005849 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850 if ( STRCMP(name + 1, "ALLBUT") == 0
5851 || STRCMP(name + 1, "ALL") == 0
5852 || STRCMP(name + 1, "TOP") == 0
5853 || STRCMP(name + 1, "CONTAINED") == 0)
5854 {
5855 if (TOUPPER_ASC(**arg) != 'C')
5856 {
5857 EMSG2(_("E407: %s not allowed here"), name + 1);
5858 failed = TRUE;
5859 vim_free(name);
5860 break;
5861 }
5862 if (count != 0)
5863 {
5864 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5865 failed = TRUE;
5866 vim_free(name);
5867 break;
5868 }
5869 if (name[1] == 'A')
5870 id = SYNID_ALLBUT;
5871 else if (name[1] == 'T')
5872 id = SYNID_TOP;
5873 else
5874 id = SYNID_CONTAINED;
5875 id += current_syn_inc_tag;
5876 }
5877 else if (name[1] == '@')
5878 {
5879 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5880 }
5881 else
5882 {
5883 /*
5884 * Handle full group name.
5885 */
5886 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5887 id = syn_check_group(name + 1, (int)(end - p));
5888 else
5889 {
5890 /*
5891 * Handle match of regexp with group names.
5892 */
5893 *name = '^';
5894 STRCAT(name, "$");
5895 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5896 if (regmatch.regprog == NULL)
5897 {
5898 failed = TRUE;
5899 vim_free(name);
5900 break;
5901 }
5902
5903 regmatch.rm_ic = TRUE;
5904 id = 0;
5905 for (i = highlight_ga.ga_len; --i >= 0; )
5906 {
5907 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5908 (colnr_T)0))
5909 {
5910 if (round == 2)
5911 {
5912 /* Got more items than expected; can happen
5913 * when adding items that match:
5914 * "contains=a.*b,axb".
5915 * Go back to first round */
5916 if (count >= total_count)
5917 {
5918 vim_free(retval);
5919 round = 1;
5920 }
5921 else
5922 retval[count] = i + 1;
5923 }
5924 ++count;
5925 id = -1; /* remember that we found one */
5926 }
5927 }
5928 vim_free(regmatch.regprog);
5929 }
5930 }
5931 vim_free(name);
5932 if (id == 0)
5933 {
5934 EMSG2(_("E409: Unknown group name: %s"), p);
5935 failed = TRUE;
5936 break;
5937 }
5938 if (id > 0)
5939 {
5940 if (round == 2)
5941 {
5942 /* Got more items than expected, go back to first round */
5943 if (count >= total_count)
5944 {
5945 vim_free(retval);
5946 round = 1;
5947 }
5948 else
5949 retval[count] = id;
5950 }
5951 ++count;
5952 }
5953 p = skipwhite(end);
5954 if (*p != ',')
5955 break;
5956 p = skipwhite(p + 1); /* skip comma in between arguments */
5957 }
5958 if (failed)
5959 break;
5960 if (round == 1)
5961 {
5962 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5963 if (retval == NULL)
5964 break;
5965 retval[count] = 0; /* zero means end of the list */
5966 total_count = count;
5967 }
5968 }
5969
5970 *arg = p;
5971 if (failed || retval == NULL)
5972 {
5973 vim_free(retval);
5974 return FAIL;
5975 }
5976
5977 if (*list == NULL)
5978 *list = retval;
5979 else
5980 vim_free(retval); /* list already found, don't overwrite it */
5981
5982 return OK;
5983}
5984
5985/*
5986 * Make a copy of an ID list.
5987 */
5988 static short *
5989copy_id_list(list)
5990 short *list;
5991{
5992 int len;
5993 int count;
5994 short *retval;
5995
5996 if (list == NULL)
5997 return NULL;
5998
5999 for (count = 0; list[count]; ++count)
6000 ;
6001 len = (count + 1) * sizeof(short);
6002 retval = (short *)alloc((unsigned)len);
6003 if (retval != NULL)
6004 mch_memmove(retval, list, (size_t)len);
6005
6006 return retval;
6007}
6008
6009/*
6010 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6011 * "cur_si" can be NULL if not checking the "containedin" list.
6012 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6013 * the current item.
6014 * This function is called very often, keep it fast!!
6015 */
6016 static int
6017in_id_list(cur_si, list, ssp, contained)
6018 stateitem_T *cur_si; /* current item or NULL */
6019 short *list; /* id list */
6020 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6021 int contained; /* group id is contained */
6022{
6023 int retval;
6024 short *scl_list;
6025 short item;
6026 short id = ssp->id;
6027 static int depth = 0;
6028 int r;
6029
6030 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006031 if (cur_si != NULL && ssp->cont_in_list != NULL
6032 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006033 {
6034 /* Ignore transparent items without a contains argument. Double check
6035 * that we don't go back past the first one. */
6036 while ((cur_si->si_flags & HL_TRANS_CONT)
6037 && cur_si > (stateitem_T *)(current_state.ga_data))
6038 --cur_si;
6039 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6040 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006041 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6042 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006043 return TRUE;
6044 }
6045
6046 if (list == NULL)
6047 return FALSE;
6048
6049 /*
6050 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6051 * inside anything. Only allow not-contained groups.
6052 */
6053 if (list == ID_LIST_ALL)
6054 return !contained;
6055
6056 /*
6057 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6058 * contains list. We also require that "id" is at the same ":syn include"
6059 * level as the list.
6060 */
6061 item = *list;
6062 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6063 {
6064 if (item < SYNID_TOP)
6065 {
6066 /* ALL or ALLBUT: accept all groups in the same file */
6067 if (item - SYNID_ALLBUT != ssp->inc_tag)
6068 return FALSE;
6069 }
6070 else if (item < SYNID_CONTAINED)
6071 {
6072 /* TOP: accept all not-contained groups in the same file */
6073 if (item - SYNID_TOP != ssp->inc_tag || contained)
6074 return FALSE;
6075 }
6076 else
6077 {
6078 /* CONTAINED: accept all contained groups in the same file */
6079 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6080 return FALSE;
6081 }
6082 item = *++list;
6083 retval = FALSE;
6084 }
6085 else
6086 retval = TRUE;
6087
6088 /*
6089 * Return "retval" if id is in the contains list.
6090 */
6091 while (item != 0)
6092 {
6093 if (item == id)
6094 return retval;
6095 if (item >= SYNID_CLUSTER)
6096 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006097 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006098 /* restrict recursiveness to 30 to avoid an endless loop for a
6099 * cluster that includes itself (indirectly) */
6100 if (scl_list != NULL && depth < 30)
6101 {
6102 ++depth;
6103 r = in_id_list(NULL, scl_list, ssp, contained);
6104 --depth;
6105 if (r)
6106 return retval;
6107 }
6108 }
6109 item = *++list;
6110 }
6111 return !retval;
6112}
6113
6114struct subcommand
6115{
6116 char *name; /* subcommand name */
6117 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6118};
6119
6120static struct subcommand subcommands[] =
6121{
6122 {"case", syn_cmd_case},
6123 {"clear", syn_cmd_clear},
6124 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006125 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006126 {"enable", syn_cmd_enable},
6127 {"include", syn_cmd_include},
6128 {"keyword", syn_cmd_keyword},
6129 {"list", syn_cmd_list},
6130 {"manual", syn_cmd_manual},
6131 {"match", syn_cmd_match},
6132 {"on", syn_cmd_on},
6133 {"off", syn_cmd_off},
6134 {"region", syn_cmd_region},
6135 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006136 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006137 {"sync", syn_cmd_sync},
6138 {"", syn_cmd_list},
6139 {NULL, NULL}
6140};
6141
6142/*
6143 * ":syntax".
6144 * This searches the subcommands[] table for the subcommand name, and calls a
6145 * syntax_subcommand() function to do the rest.
6146 */
6147 void
6148ex_syntax(eap)
6149 exarg_T *eap;
6150{
6151 char_u *arg = eap->arg;
6152 char_u *subcmd_end;
6153 char_u *subcmd_name;
6154 int i;
6155
6156 syn_cmdlinep = eap->cmdlinep;
6157
6158 /* isolate subcommand name */
6159 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6160 ;
6161 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6162 if (subcmd_name != NULL)
6163 {
6164 if (eap->skip) /* skip error messages for all subcommands */
6165 ++emsg_skip;
6166 for (i = 0; ; ++i)
6167 {
6168 if (subcommands[i].name == NULL)
6169 {
6170 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6171 break;
6172 }
6173 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6174 {
6175 eap->arg = skipwhite(subcmd_end);
6176 (subcommands[i].func)(eap, FALSE);
6177 break;
6178 }
6179 }
6180 vim_free(subcmd_name);
6181 if (eap->skip)
6182 --emsg_skip;
6183 }
6184}
6185
Bram Moolenaar860cae12010-06-05 23:22:07 +02006186 void
6187ex_ownsyntax(eap)
6188 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006189{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006190 char_u *old_value;
6191 char_u *new_value;
6192
Bram Moolenaar860cae12010-06-05 23:22:07 +02006193 if (curwin->w_s == &curwin->w_buffer->b_s)
6194 {
6195 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6196 memset(curwin->w_s, 0, sizeof(synblock_T));
6197#ifdef FEAT_SPELL
6198 curwin->w_p_spell = FALSE; /* No spell checking */
6199 clear_string_option(&curwin->w_s->b_p_spc);
6200 clear_string_option(&curwin->w_s->b_p_spf);
6201 vim_free(curwin->w_s->b_cap_prog);
6202 curwin->w_s->b_cap_prog = NULL;
6203 clear_string_option(&curwin->w_s->b_p_spl);
6204#endif
6205 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006206
6207 /* save value of b:current_syntax */
6208 old_value = get_var_value((char_u *)"b:current_syntax");
6209 if (old_value != NULL)
6210 old_value = vim_strsave(old_value);
6211
6212 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6213 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006214 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006215
6216 /* move value of b:current_syntax to w:current_syntax */
6217 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006218 if (new_value != NULL)
6219 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006220
6221 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006222 if (old_value == NULL)
6223 do_unlet((char_u *)"b:current_syntax", TRUE);
6224 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006225 {
6226 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6227 vim_free(old_value);
6228 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006229}
6230
6231 int
6232syntax_present(win)
6233 win_T *win;
6234{
6235 return (win->w_s->b_syn_patterns.ga_len != 0
6236 || win->w_s->b_syn_clusters.ga_len != 0
6237 || win->w_s->b_keywtab.ht_used > 0
6238 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006239}
6240
6241#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6242
6243static enum
6244{
6245 EXP_SUBCMD, /* expand ":syn" sub-commands */
6246 EXP_CASE /* expand ":syn case" arguments */
6247} expand_what;
6248
Bram Moolenaar4f688582007-07-24 12:34:30 +00006249/*
6250 * Reset include_link, include_default, include_none to 0.
6251 * Called when we are done expanding.
6252 */
6253 void
6254reset_expand_highlight()
6255{
6256 include_link = include_default = include_none = 0;
6257}
6258
6259/*
6260 * Handle command line completion for :match and :echohl command: Add "None"
6261 * as highlight group.
6262 */
6263 void
6264set_context_in_echohl_cmd(xp, arg)
6265 expand_T *xp;
6266 char_u *arg;
6267{
6268 xp->xp_context = EXPAND_HIGHLIGHT;
6269 xp->xp_pattern = arg;
6270 include_none = 1;
6271}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006272
6273/*
6274 * Handle command line completion for :syntax command.
6275 */
6276 void
6277set_context_in_syntax_cmd(xp, arg)
6278 expand_T *xp;
6279 char_u *arg;
6280{
6281 char_u *p;
6282
6283 /* Default: expand subcommands */
6284 xp->xp_context = EXPAND_SYNTAX;
6285 expand_what = EXP_SUBCMD;
6286 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006287 include_link = 0;
6288 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006289
6290 /* (part of) subcommand already typed */
6291 if (*arg != NUL)
6292 {
6293 p = skiptowhite(arg);
6294 if (*p != NUL) /* past first word */
6295 {
6296 xp->xp_pattern = skipwhite(p);
6297 if (*skiptowhite(xp->xp_pattern) != NUL)
6298 xp->xp_context = EXPAND_NOTHING;
6299 else if (STRNICMP(arg, "case", p - arg) == 0)
6300 expand_what = EXP_CASE;
6301 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6302 || STRNICMP(arg, "region", p - arg) == 0
6303 || STRNICMP(arg, "match", p - arg) == 0
6304 || STRNICMP(arg, "list", p - arg) == 0)
6305 xp->xp_context = EXPAND_HIGHLIGHT;
6306 else
6307 xp->xp_context = EXPAND_NOTHING;
6308 }
6309 }
6310}
6311
6312static char *(case_args[]) = {"match", "ignore", NULL};
6313
6314/*
6315 * Function given to ExpandGeneric() to obtain the list syntax names for
6316 * expansion.
6317 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006318 char_u *
6319get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006320 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006321 int idx;
6322{
6323 if (expand_what == EXP_SUBCMD)
6324 return (char_u *)subcommands[idx].name;
6325 return (char_u *)case_args[idx];
6326}
6327
6328#endif /* FEAT_CMDL_COMPL */
6329
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330/*
6331 * Function called for expression evaluation: get syntax ID at file position.
6332 */
6333 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006334syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006335 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006337 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006338 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006339 int *spellp; /* return: can do spell checking */
6340 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006341{
6342 /* When the position is not after the current position and in the same
6343 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006344 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006345 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006346 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006347 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006348
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006349 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350
6351 return (trans ? current_trans_id : current_id);
6352}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353
Bram Moolenaar860cae12010-06-05 23:22:07 +02006354#if defined(FEAT_CONCEAL) || defined(PROTO)
6355/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006356 * Get extra information about the syntax item. Must be called right after
6357 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006358 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006359 * Returns the current flags.
6360 */
6361 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006362get_syntax_info(seqnrp)
6363 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006364{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006365 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006366 return current_flags;
6367}
6368
6369/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006370 * Return conceal substitution character
6371 */
6372 int
6373syn_get_sub_char()
6374{
6375 return current_sub_char;
6376}
6377#endif
6378
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006379#if defined(FEAT_EVAL) || defined(PROTO)
6380/*
6381 * Return the syntax ID at position "i" in the current stack.
6382 * The caller must have called syn_get_id() before to fill the stack.
6383 * Returns -1 when "i" is out of range.
6384 */
6385 int
6386syn_get_stack_item(i)
6387 int i;
6388{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006389 if (i >= current_state.ga_len)
6390 {
6391 /* Need to invalidate the state, because we didn't properly finish it
6392 * for the last character, "keep_state" was TRUE. */
6393 invalidate_current_state();
6394 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006395 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006396 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006397 return CUR_STATE(i).si_id;
6398}
6399#endif
6400
Bram Moolenaar071d4272004-06-13 20:20:40 +00006401#if defined(FEAT_FOLDING) || defined(PROTO)
6402/*
6403 * Function called to get folding level for line "lnum" in window "wp".
6404 */
6405 int
6406syn_get_foldlevel(wp, lnum)
6407 win_T *wp;
6408 long lnum;
6409{
6410 int level = 0;
6411 int i;
6412
6413 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006414 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415 {
6416 syntax_start(wp, lnum);
6417
6418 for (i = 0; i < current_state.ga_len; ++i)
6419 if (CUR_STATE(i).si_flags & HL_FOLD)
6420 ++level;
6421 }
6422 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006423 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006424 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006425 if (level < 0)
6426 level = 0;
6427 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006428 return level;
6429}
6430#endif
6431
6432#endif /* FEAT_SYN_HL */
6433
6434
6435/**************************************
6436 * Highlighting stuff *
6437 **************************************/
6438
6439/*
6440 * The default highlight groups. These are compiled-in for fast startup and
6441 * they still work when the runtime files can't be found.
6442 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006443 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6444 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006445 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006446#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006447# define CENT(a, b) b
6448#else
6449# define CENT(a, b) a
6450#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006451static char *(highlight_init_both[]) =
6452 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006453 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6454 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6455 CENT("IncSearch term=reverse cterm=reverse",
6456 "IncSearch term=reverse cterm=reverse gui=reverse"),
6457 CENT("ModeMsg term=bold cterm=bold",
6458 "ModeMsg term=bold cterm=bold gui=bold"),
6459 CENT("NonText term=bold ctermfg=Blue",
6460 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6461 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6462 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6463 CENT("StatusLineNC term=reverse cterm=reverse",
6464 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006465#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006466 CENT("VertSplit term=reverse cterm=reverse",
6467 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006468#endif
6469#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006470 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6471 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006472#endif
6473#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006474 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6475 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006476#endif
6477#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006478 CENT("PmenuThumb cterm=reverse",
6479 "PmenuThumb cterm=reverse gui=reverse"),
6480 CENT("PmenuSbar ctermbg=Grey",
6481 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006482#endif
6483#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006484 CENT("TabLineSel term=bold cterm=bold",
6485 "TabLineSel term=bold cterm=bold gui=bold"),
6486 CENT("TabLineFill term=reverse cterm=reverse",
6487 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006488#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006489#ifdef FEAT_GUI
6490 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006491 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006493 NULL
6494 };
6495
6496static char *(highlight_init_light[]) =
6497 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006498 CENT("Directory term=bold ctermfg=DarkBlue",
6499 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6500 CENT("LineNr term=underline ctermfg=Brown",
6501 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6502 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6503 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6504 CENT("Question term=standout ctermfg=DarkGreen",
6505 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6506 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6507 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006508#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006509 CENT("SpellBad term=reverse ctermbg=LightRed",
6510 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6511 CENT("SpellCap term=reverse ctermbg=LightBlue",
6512 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6513 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6514 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6515 CENT("SpellLocal term=underline ctermbg=Cyan",
6516 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006517#endif
6518#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006519 CENT("Pmenu ctermbg=LightMagenta",
6520 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6521 CENT("PmenuSel ctermbg=LightGrey",
6522 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006523#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006524 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6525 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6526 CENT("Title term=bold ctermfg=DarkMagenta",
6527 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6528 CENT("WarningMsg term=standout ctermfg=DarkRed",
6529 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006530#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006531 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6532 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006533#endif
6534#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006535 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6536 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6537 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6538 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006539#endif
6540#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006541 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6542 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006543#endif
6544#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006545 CENT("Visual term=reverse",
6546 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006547#endif
6548#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006549 CENT("DiffAdd term=bold ctermbg=LightBlue",
6550 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6551 CENT("DiffChange term=bold ctermbg=LightMagenta",
6552 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6553 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6554 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006555#endif
6556#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006557 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6558 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006559#endif
6560#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006561 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006562 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006563 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006564 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006565 CENT("ColorColumn term=reverse ctermbg=LightRed",
6566 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006567#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006568#ifdef FEAT_CONCEAL
6569 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6570 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6571#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006572#ifdef FEAT_AUTOCMD
6573 CENT("MatchParen term=reverse ctermbg=Cyan",
6574 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6575#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006576#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006577 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006578#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006579 NULL
6580 };
6581
6582static char *(highlight_init_dark[]) =
6583 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006584 CENT("Directory term=bold ctermfg=LightCyan",
6585 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6586 CENT("LineNr term=underline ctermfg=Yellow",
6587 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6588 CENT("MoreMsg term=bold ctermfg=LightGreen",
6589 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6590 CENT("Question term=standout ctermfg=LightGreen",
6591 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6592 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6593 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6594 CENT("SpecialKey term=bold ctermfg=LightBlue",
6595 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006596#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006597 CENT("SpellBad term=reverse ctermbg=Red",
6598 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6599 CENT("SpellCap term=reverse ctermbg=Blue",
6600 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6601 CENT("SpellRare term=reverse ctermbg=Magenta",
6602 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6603 CENT("SpellLocal term=underline ctermbg=Cyan",
6604 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006605#endif
6606#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006607 CENT("Pmenu ctermbg=Magenta",
6608 "Pmenu ctermbg=Magenta guibg=Magenta"),
6609 CENT("PmenuSel ctermbg=DarkGrey",
6610 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006611#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006612 CENT("Title term=bold ctermfg=LightMagenta",
6613 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6614 CENT("WarningMsg term=standout ctermfg=LightRed",
6615 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006616#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006617 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6618 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006619#endif
6620#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006621 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6622 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6623 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6624 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006625#endif
6626#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006627 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6628 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006629#endif
6630#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006631 CENT("Visual term=reverse",
6632 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006633#endif
6634#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006635 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6636 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6637 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6638 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6639 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6640 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006641#endif
6642#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006643 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6644 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006645#endif
6646#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006647 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006648 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006649 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006650 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006651 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6652 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006653#endif
6654#ifdef FEAT_AUTOCMD
6655 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6656 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006657#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006658#ifdef FEAT_CONCEAL
6659 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6660 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6661#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006662#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006663 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006664#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006665 NULL
6666 };
6667
6668 void
6669init_highlight(both, reset)
6670 int both; /* include groups where 'bg' doesn't matter */
6671 int reset; /* clear group first */
6672{
6673 int i;
6674 char **pp;
6675 static int had_both = FALSE;
6676#ifdef FEAT_EVAL
6677 char_u *p;
6678
6679 /*
6680 * Try finding the color scheme file. Used when a color file was loaded
6681 * and 'background' or 't_Co' is changed.
6682 */
6683 p = get_var_value((char_u *)"g:colors_name");
6684 if (p != NULL && load_colors(p) == OK)
6685 return;
6686#endif
6687
6688 /*
6689 * Didn't use a color file, use the compiled-in colors.
6690 */
6691 if (both)
6692 {
6693 had_both = TRUE;
6694 pp = highlight_init_both;
6695 for (i = 0; pp[i] != NULL; ++i)
6696 do_highlight((char_u *)pp[i], reset, TRUE);
6697 }
6698 else if (!had_both)
6699 /* Don't do anything before the call with both == TRUE from main().
6700 * Not everything has been setup then, and that call will overrule
6701 * everything anyway. */
6702 return;
6703
6704 if (*p_bg == 'l')
6705 pp = highlight_init_light;
6706 else
6707 pp = highlight_init_dark;
6708 for (i = 0; pp[i] != NULL; ++i)
6709 do_highlight((char_u *)pp[i], reset, TRUE);
6710
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006711 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006712 * depend on the number of colors available.
6713 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006714 * to avoid Statement highlighted text disappears.
6715 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006716 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006717 do_highlight((char_u *)(*p_bg == 'l'
6718 ? "Visual cterm=NONE ctermbg=LightGrey"
6719 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006720 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006721 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006722 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6723 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006724 if (*p_bg == 'l')
6725 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6726 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006727
Bram Moolenaar071d4272004-06-13 20:20:40 +00006728#ifdef FEAT_SYN_HL
6729 /*
6730 * If syntax highlighting is enabled load the highlighting for it.
6731 */
6732 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006733 {
6734 static int recursive = 0;
6735
6736 if (recursive >= 5)
6737 EMSG(_("E679: recursive loop loading syncolor.vim"));
6738 else
6739 {
6740 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006741 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006742 --recursive;
6743 }
6744 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745#endif
6746}
6747
6748/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006749 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006750 * Return OK for success, FAIL for failure.
6751 */
6752 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006753load_colors(name)
6754 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006755{
6756 char_u *buf;
6757 int retval = FAIL;
6758 static int recursive = FALSE;
6759
6760 /* When being called recursively, this is probably because setting
6761 * 'background' caused the highlighting to be reloaded. This means it is
6762 * working, thus we should return OK. */
6763 if (recursive)
6764 return OK;
6765
6766 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006767 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006768 if (buf != NULL)
6769 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006770 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006771 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006772 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006773#ifdef FEAT_AUTOCMD
6774 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6775#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006776 }
6777 recursive = FALSE;
6778
6779 return retval;
6780}
6781
6782/*
6783 * Handle the ":highlight .." command.
6784 * When using ":hi clear" this is called recursively for each group with
6785 * "forceit" and "init" both TRUE.
6786 */
6787 void
6788do_highlight(line, forceit, init)
6789 char_u *line;
6790 int forceit;
6791 int init; /* TRUE when called for initializing */
6792{
6793 char_u *name_end;
6794 char_u *p;
6795 char_u *linep;
6796 char_u *key_start;
6797 char_u *arg_start;
6798 char_u *key = NULL, *arg = NULL;
6799 long i;
6800 int off;
6801 int len;
6802 int attr;
6803 int id;
6804 int idx;
6805 int dodefault = FALSE;
6806 int doclear = FALSE;
6807 int dolink = FALSE;
6808 int error = FALSE;
6809 int color;
6810 int is_normal_group = FALSE; /* "Normal" group */
6811#ifdef FEAT_GUI_X11
6812 int is_menu_group = FALSE; /* "Menu" group */
6813 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6814 int is_tooltip_group = FALSE; /* "Tooltip" group */
6815 int do_colors = FALSE; /* need to update colors? */
6816#else
6817# define is_menu_group 0
6818# define is_tooltip_group 0
6819#endif
6820
6821 /*
6822 * If no argument, list current highlighting.
6823 */
6824 if (ends_excmd(*line))
6825 {
6826 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6827 /* TODO: only call when the group has attributes set */
6828 highlight_list_one((int)i);
6829 return;
6830 }
6831
6832 /*
6833 * Isolate the name.
6834 */
6835 name_end = skiptowhite(line);
6836 linep = skipwhite(name_end);
6837
6838 /*
6839 * Check for "default" argument.
6840 */
6841 if (STRNCMP(line, "default", name_end - line) == 0)
6842 {
6843 dodefault = TRUE;
6844 line = linep;
6845 name_end = skiptowhite(line);
6846 linep = skipwhite(name_end);
6847 }
6848
6849 /*
6850 * Check for "clear" or "link" argument.
6851 */
6852 if (STRNCMP(line, "clear", name_end - line) == 0)
6853 doclear = TRUE;
6854 if (STRNCMP(line, "link", name_end - line) == 0)
6855 dolink = TRUE;
6856
6857 /*
6858 * ":highlight {group-name}": list highlighting for one group.
6859 */
6860 if (!doclear && !dolink && ends_excmd(*linep))
6861 {
6862 id = syn_namen2id(line, (int)(name_end - line));
6863 if (id == 0)
6864 EMSG2(_("E411: highlight group not found: %s"), line);
6865 else
6866 highlight_list_one(id);
6867 return;
6868 }
6869
6870 /*
6871 * Handle ":highlight link {from} {to}" command.
6872 */
6873 if (dolink)
6874 {
6875 char_u *from_start = linep;
6876 char_u *from_end;
6877 char_u *to_start;
6878 char_u *to_end;
6879 int from_id;
6880 int to_id;
6881
6882 from_end = skiptowhite(from_start);
6883 to_start = skipwhite(from_end);
6884 to_end = skiptowhite(to_start);
6885
6886 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6887 {
6888 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6889 from_start);
6890 return;
6891 }
6892
6893 if (!ends_excmd(*skipwhite(to_end)))
6894 {
6895 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6896 return;
6897 }
6898
6899 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6900 if (STRNCMP(to_start, "NONE", 4) == 0)
6901 to_id = 0;
6902 else
6903 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6904
6905 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6906 {
6907 /*
6908 * Don't allow a link when there already is some highlighting
6909 * for the group, unless '!' is used
6910 */
6911 if (to_id > 0 && !forceit && !init
6912 && hl_has_settings(from_id - 1, dodefault))
6913 {
6914 if (sourcing_name == NULL && !dodefault)
6915 EMSG(_("E414: group has settings, highlight link ignored"));
6916 }
6917 else
6918 {
6919 if (!init)
6920 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6921 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006922#ifdef FEAT_EVAL
6923 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6924#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006925 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006926 }
6927 }
6928
6929 /* Only call highlight_changed() once, after sourcing a syntax file */
6930 need_highlight_changed = TRUE;
6931
6932 return;
6933 }
6934
6935 if (doclear)
6936 {
6937 /*
6938 * ":highlight clear [group]" command.
6939 */
6940 line = linep;
6941 if (ends_excmd(*line))
6942 {
6943#ifdef FEAT_GUI
6944 /* First, we do not destroy the old values, but allocate the new
6945 * ones and update the display. THEN we destroy the old values.
6946 * If we destroy the old values first, then the old values
6947 * (such as GuiFont's or GuiFontset's) will still be displayed but
6948 * invalid because they were free'd.
6949 */
6950 if (gui.in_use)
6951 {
6952# ifdef FEAT_BEVAL_TIP
6953 gui_init_tooltip_font();
6954# endif
6955# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6956 gui_init_menu_font();
6957# endif
6958 }
6959# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6960 gui_mch_def_colors();
6961# endif
6962# ifdef FEAT_GUI_X11
6963# ifdef FEAT_MENU
6964
6965 /* This only needs to be done when there is no Menu highlight
6966 * group defined by default, which IS currently the case.
6967 */
6968 gui_mch_new_menu_colors();
6969# endif
6970 if (gui.in_use)
6971 {
6972 gui_new_scrollbar_colors();
6973# ifdef FEAT_BEVAL
6974 gui_mch_new_tooltip_colors();
6975# endif
6976# ifdef FEAT_MENU
6977 gui_mch_new_menu_font();
6978# endif
6979 }
6980# endif
6981
6982 /* Ok, we're done allocating the new default graphics items.
6983 * The screen should already be refreshed at this point.
6984 * It is now Ok to clear out the old data.
6985 */
6986#endif
6987#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006988 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006989#endif
6990 restore_cterm_colors();
6991
6992 /*
6993 * Clear all default highlight groups and load the defaults.
6994 */
6995 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6996 highlight_clear(idx);
6997 init_highlight(TRUE, TRUE);
6998#ifdef FEAT_GUI
6999 if (gui.in_use)
7000 highlight_gui_started();
7001#endif
7002 highlight_changed();
7003 redraw_later_clear();
7004 return;
7005 }
7006 name_end = skiptowhite(line);
7007 linep = skipwhite(name_end);
7008 }
7009
7010 /*
7011 * Find the group name in the table. If it does not exist yet, add it.
7012 */
7013 id = syn_check_group(line, (int)(name_end - line));
7014 if (id == 0) /* failed (out of memory) */
7015 return;
7016 idx = id - 1; /* index is ID minus one */
7017
7018 /* Return if "default" was used and the group already has settings. */
7019 if (dodefault && hl_has_settings(idx, TRUE))
7020 return;
7021
7022 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7023 is_normal_group = TRUE;
7024#ifdef FEAT_GUI_X11
7025 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7026 is_menu_group = TRUE;
7027 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7028 is_scrollbar_group = TRUE;
7029 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7030 is_tooltip_group = TRUE;
7031#endif
7032
7033 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7034 if (doclear || (forceit && init))
7035 {
7036 highlight_clear(idx);
7037 if (!doclear)
7038 HL_TABLE()[idx].sg_set = 0;
7039 }
7040
7041 if (!doclear)
7042 while (!ends_excmd(*linep))
7043 {
7044 key_start = linep;
7045 if (*linep == '=')
7046 {
7047 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7048 error = TRUE;
7049 break;
7050 }
7051
7052 /*
7053 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7054 * "guibg").
7055 */
7056 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7057 ++linep;
7058 vim_free(key);
7059 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7060 if (key == NULL)
7061 {
7062 error = TRUE;
7063 break;
7064 }
7065 linep = skipwhite(linep);
7066
7067 if (STRCMP(key, "NONE") == 0)
7068 {
7069 if (!init || HL_TABLE()[idx].sg_set == 0)
7070 {
7071 if (!init)
7072 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7073 highlight_clear(idx);
7074 }
7075 continue;
7076 }
7077
7078 /*
7079 * Check for the equal sign.
7080 */
7081 if (*linep != '=')
7082 {
7083 EMSG2(_("E416: missing equal sign: %s"), key_start);
7084 error = TRUE;
7085 break;
7086 }
7087 ++linep;
7088
7089 /*
7090 * Isolate the argument.
7091 */
7092 linep = skipwhite(linep);
7093 if (*linep == '\'') /* guifg='color name' */
7094 {
7095 arg_start = ++linep;
7096 linep = vim_strchr(linep, '\'');
7097 if (linep == NULL)
7098 {
7099 EMSG2(_(e_invarg2), key_start);
7100 error = TRUE;
7101 break;
7102 }
7103 }
7104 else
7105 {
7106 arg_start = linep;
7107 linep = skiptowhite(linep);
7108 }
7109 if (linep == arg_start)
7110 {
7111 EMSG2(_("E417: missing argument: %s"), key_start);
7112 error = TRUE;
7113 break;
7114 }
7115 vim_free(arg);
7116 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7117 if (arg == NULL)
7118 {
7119 error = TRUE;
7120 break;
7121 }
7122 if (*linep == '\'')
7123 ++linep;
7124
7125 /*
7126 * Store the argument.
7127 */
7128 if ( STRCMP(key, "TERM") == 0
7129 || STRCMP(key, "CTERM") == 0
7130 || STRCMP(key, "GUI") == 0)
7131 {
7132 attr = 0;
7133 off = 0;
7134 while (arg[off] != NUL)
7135 {
7136 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7137 {
7138 len = (int)STRLEN(hl_name_table[i]);
7139 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7140 {
7141 attr |= hl_attr_table[i];
7142 off += len;
7143 break;
7144 }
7145 }
7146 if (i < 0)
7147 {
7148 EMSG2(_("E418: Illegal value: %s"), arg);
7149 error = TRUE;
7150 break;
7151 }
7152 if (arg[off] == ',') /* another one follows */
7153 ++off;
7154 }
7155 if (error)
7156 break;
7157 if (*key == 'T')
7158 {
7159 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7160 {
7161 if (!init)
7162 HL_TABLE()[idx].sg_set |= SG_TERM;
7163 HL_TABLE()[idx].sg_term = attr;
7164 }
7165 }
7166 else if (*key == 'C')
7167 {
7168 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7169 {
7170 if (!init)
7171 HL_TABLE()[idx].sg_set |= SG_CTERM;
7172 HL_TABLE()[idx].sg_cterm = attr;
7173 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7174 }
7175 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007176#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007177 else
7178 {
7179 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7180 {
7181 if (!init)
7182 HL_TABLE()[idx].sg_set |= SG_GUI;
7183 HL_TABLE()[idx].sg_gui = attr;
7184 }
7185 }
7186#endif
7187 }
7188 else if (STRCMP(key, "FONT") == 0)
7189 {
7190 /* in non-GUI fonts are simply ignored */
7191#ifdef FEAT_GUI
7192 if (!gui.shell_created)
7193 {
7194 /* GUI not started yet, always accept the name. */
7195 vim_free(HL_TABLE()[idx].sg_font_name);
7196 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7197 }
7198 else
7199 {
7200 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7201# ifdef FEAT_XFONTSET
7202 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7203# endif
7204 /* First, save the current font/fontset.
7205 * Then try to allocate the font/fontset.
7206 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7207 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7208 */
7209
7210 HL_TABLE()[idx].sg_font = NOFONT;
7211# ifdef FEAT_XFONTSET
7212 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7213# endif
7214 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7215 is_tooltip_group);
7216
7217# ifdef FEAT_XFONTSET
7218 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7219 {
7220 /* New fontset was accepted. Free the old one, if there was
7221 * one.
7222 */
7223 gui_mch_free_fontset(temp_sg_fontset);
7224 vim_free(HL_TABLE()[idx].sg_font_name);
7225 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7226 }
7227 else
7228 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7229# endif
7230 if (HL_TABLE()[idx].sg_font != NOFONT)
7231 {
7232 /* New font was accepted. Free the old one, if there was
7233 * one.
7234 */
7235 gui_mch_free_font(temp_sg_font);
7236 vim_free(HL_TABLE()[idx].sg_font_name);
7237 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7238 }
7239 else
7240 HL_TABLE()[idx].sg_font = temp_sg_font;
7241 }
7242#endif
7243 }
7244 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7245 {
7246 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7247 {
7248 if (!init)
7249 HL_TABLE()[idx].sg_set |= SG_CTERM;
7250
7251 /* When setting the foreground color, and previously the "bold"
7252 * flag was set for a light color, reset it now */
7253 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7254 {
7255 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7256 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7257 }
7258
7259 if (VIM_ISDIGIT(*arg))
7260 color = atoi((char *)arg);
7261 else if (STRICMP(arg, "fg") == 0)
7262 {
7263 if (cterm_normal_fg_color)
7264 color = cterm_normal_fg_color - 1;
7265 else
7266 {
7267 EMSG(_("E419: FG color unknown"));
7268 error = TRUE;
7269 break;
7270 }
7271 }
7272 else if (STRICMP(arg, "bg") == 0)
7273 {
7274 if (cterm_normal_bg_color > 0)
7275 color = cterm_normal_bg_color - 1;
7276 else
7277 {
7278 EMSG(_("E420: BG color unknown"));
7279 error = TRUE;
7280 break;
7281 }
7282 }
7283 else
7284 {
7285 static char *(color_names[28]) = {
7286 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7287 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7288 "Gray", "Grey",
7289 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7290 "Blue", "LightBlue", "Green", "LightGreen",
7291 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7292 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7293 static int color_numbers_16[28] = {0, 1, 2, 3,
7294 4, 5, 6, 6,
7295 7, 7,
7296 7, 7, 8, 8,
7297 9, 9, 10, 10,
7298 11, 11, 12, 12, 13,
7299 13, 14, 14, 15, -1};
7300 /* for xterm with 88 colors... */
7301 static int color_numbers_88[28] = {0, 4, 2, 6,
7302 1, 5, 32, 72,
7303 84, 84,
7304 7, 7, 82, 82,
7305 12, 43, 10, 61,
7306 14, 63, 9, 74, 13,
7307 75, 11, 78, 15, -1};
7308 /* for xterm with 256 colors... */
7309 static int color_numbers_256[28] = {0, 4, 2, 6,
7310 1, 5, 130, 130,
7311 248, 248,
7312 7, 7, 242, 242,
7313 12, 81, 10, 121,
7314 14, 159, 9, 224, 13,
7315 225, 11, 229, 15, -1};
7316 /* for terminals with less than 16 colors... */
7317 static int color_numbers_8[28] = {0, 4, 2, 6,
7318 1, 5, 3, 3,
7319 7, 7,
7320 7, 7, 0+8, 0+8,
7321 4+8, 4+8, 2+8, 2+8,
7322 6+8, 6+8, 1+8, 1+8, 5+8,
7323 5+8, 3+8, 3+8, 7+8, -1};
7324#if defined(__QNXNTO__)
7325 static int *color_numbers_8_qansi = color_numbers_8;
7326 /* On qnx, the 8 & 16 color arrays are the same */
7327 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7328 color_numbers_8_qansi = color_numbers_16;
7329#endif
7330
7331 /* reduce calls to STRICMP a bit, it can be slow */
7332 off = TOUPPER_ASC(*arg);
7333 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7334 if (off == color_names[i][0]
7335 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7336 break;
7337 if (i < 0)
7338 {
7339 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7340 error = TRUE;
7341 break;
7342 }
7343
7344 /* Use the _16 table to check if its a valid color name. */
7345 color = color_numbers_16[i];
7346 if (color >= 0)
7347 {
7348 if (t_colors == 8)
7349 {
7350 /* t_Co is 8: use the 8 colors table */
7351#if defined(__QNXNTO__)
7352 color = color_numbers_8_qansi[i];
7353#else
7354 color = color_numbers_8[i];
7355#endif
7356 if (key[5] == 'F')
7357 {
7358 /* set/reset bold attribute to get light foreground
7359 * colors (on some terminals, e.g. "linux") */
7360 if (color & 8)
7361 {
7362 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7363 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7364 }
7365 else
7366 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7367 }
7368 color &= 7; /* truncate to 8 colors */
7369 }
7370 else if (t_colors == 16 || t_colors == 88
7371 || t_colors == 256)
7372 {
7373 /*
7374 * Guess: if the termcap entry ends in 'm', it is
7375 * probably an xterm-like terminal. Use the changed
7376 * order for colors.
7377 */
7378 if (*T_CAF != NUL)
7379 p = T_CAF;
7380 else
7381 p = T_CSF;
7382 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7383 switch (t_colors)
7384 {
7385 case 16:
7386 color = color_numbers_8[i];
7387 break;
7388 case 88:
7389 color = color_numbers_88[i];
7390 break;
7391 case 256:
7392 color = color_numbers_256[i];
7393 break;
7394 }
7395 }
7396 }
7397 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007398 /* Add one to the argument, to avoid zero. Zero is used for
7399 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007400 if (key[5] == 'F')
7401 {
7402 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7403 if (is_normal_group)
7404 {
7405 cterm_normal_fg_color = color + 1;
7406 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7407#ifdef FEAT_GUI
7408 /* Don't do this if the GUI is used. */
7409 if (!gui.in_use && !gui.starting)
7410#endif
7411 {
7412 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007413 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007414 term_fg_color(color);
7415 }
7416 }
7417 }
7418 else
7419 {
7420 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7421 if (is_normal_group)
7422 {
7423 cterm_normal_bg_color = color + 1;
7424#ifdef FEAT_GUI
7425 /* Don't mess with 'background' if the GUI is used. */
7426 if (!gui.in_use && !gui.starting)
7427#endif
7428 {
7429 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007430 if (color >= 0)
7431 {
7432 if (termcap_active)
7433 term_bg_color(color);
7434 if (t_colors < 16)
7435 i = (color == 0 || color == 4);
7436 else
7437 i = (color < 7 || color == 8);
7438 /* Set the 'background' option if the value is
7439 * wrong. */
7440 if (i != (*p_bg == 'd'))
7441 set_option_value((char_u *)"bg", 0L,
7442 i ? (char_u *)"dark"
7443 : (char_u *)"light", 0);
7444 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007445 }
7446 }
7447 }
7448 }
7449 }
7450 else if (STRCMP(key, "GUIFG") == 0)
7451 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007452#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007453 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007454 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007455 if (!init)
7456 HL_TABLE()[idx].sg_set |= SG_GUI;
7457
Bram Moolenaar61623362010-07-14 22:04:22 +02007458# ifdef FEAT_GUI
7459 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007460 i = color_name2handle(arg);
7461 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7462 {
7463 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007464# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007465 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7466 if (STRCMP(arg, "NONE"))
7467 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7468 else
7469 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007470# ifdef FEAT_GUI
7471# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007472 if (is_menu_group)
7473 gui.menu_fg_pixel = i;
7474 if (is_scrollbar_group)
7475 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007476# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007477 if (is_tooltip_group)
7478 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007479# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007480 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007481# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007482 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007483# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007484 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007485#endif
7486 }
7487 else if (STRCMP(key, "GUIBG") == 0)
7488 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007489#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007490 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007491 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007492 if (!init)
7493 HL_TABLE()[idx].sg_set |= SG_GUI;
7494
Bram Moolenaar61623362010-07-14 22:04:22 +02007495# ifdef FEAT_GUI
7496 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007497 i = color_name2handle(arg);
7498 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7499 {
7500 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007501# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007502 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7503 if (STRCMP(arg, "NONE") != 0)
7504 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7505 else
7506 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007507# ifdef FEAT_GUI
7508# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007509 if (is_menu_group)
7510 gui.menu_bg_pixel = i;
7511 if (is_scrollbar_group)
7512 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007513# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007514 if (is_tooltip_group)
7515 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007516# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007517 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007518# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007519 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007520# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007521 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007522#endif
7523 }
7524 else if (STRCMP(key, "GUISP") == 0)
7525 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007526#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007527 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7528 {
7529 if (!init)
7530 HL_TABLE()[idx].sg_set |= SG_GUI;
7531
Bram Moolenaar61623362010-07-14 22:04:22 +02007532# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007533 i = color_name2handle(arg);
7534 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7535 {
7536 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007537# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007538 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7539 if (STRCMP(arg, "NONE") != 0)
7540 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7541 else
7542 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007543# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007544 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007545# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007546 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007547#endif
7548 }
7549 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7550 {
7551 char_u buf[100];
7552 char_u *tname;
7553
7554 if (!init)
7555 HL_TABLE()[idx].sg_set |= SG_TERM;
7556
7557 /*
7558 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007559 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007560 */
7561 if (STRNCMP(arg, "t_", 2) == 0)
7562 {
7563 off = 0;
7564 buf[0] = 0;
7565 while (arg[off] != NUL)
7566 {
7567 /* Isolate one termcap name */
7568 for (len = 0; arg[off + len] &&
7569 arg[off + len] != ','; ++len)
7570 ;
7571 tname = vim_strnsave(arg + off, len);
7572 if (tname == NULL) /* out of memory */
7573 {
7574 error = TRUE;
7575 break;
7576 }
7577 /* lookup the escape sequence for the item */
7578 p = get_term_code(tname);
7579 vim_free(tname);
7580 if (p == NULL) /* ignore non-existing things */
7581 p = (char_u *)"";
7582
7583 /* Append it to the already found stuff */
7584 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7585 {
7586 EMSG2(_("E422: terminal code too long: %s"), arg);
7587 error = TRUE;
7588 break;
7589 }
7590 STRCAT(buf, p);
7591
7592 /* Advance to the next item */
7593 off += len;
7594 if (arg[off] == ',') /* another one follows */
7595 ++off;
7596 }
7597 }
7598 else
7599 {
7600 /*
7601 * Copy characters from arg[] to buf[], translating <> codes.
7602 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007603 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007604 {
7605 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007606 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007607 off += len;
7608 else /* copy as normal char */
7609 buf[off++] = *p++;
7610 }
7611 buf[off] = NUL;
7612 }
7613 if (error)
7614 break;
7615
7616 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7617 p = NULL;
7618 else
7619 p = vim_strsave(buf);
7620 if (key[2] == 'A')
7621 {
7622 vim_free(HL_TABLE()[idx].sg_start);
7623 HL_TABLE()[idx].sg_start = p;
7624 }
7625 else
7626 {
7627 vim_free(HL_TABLE()[idx].sg_stop);
7628 HL_TABLE()[idx].sg_stop = p;
7629 }
7630 }
7631 else
7632 {
7633 EMSG2(_("E423: Illegal argument: %s"), key_start);
7634 error = TRUE;
7635 break;
7636 }
7637
7638 /*
7639 * When highlighting has been given for a group, don't link it.
7640 */
7641 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7642 HL_TABLE()[idx].sg_link = 0;
7643
7644 /*
7645 * Continue with next argument.
7646 */
7647 linep = skipwhite(linep);
7648 }
7649
7650 /*
7651 * If there is an error, and it's a new entry, remove it from the table.
7652 */
7653 if (error && idx == highlight_ga.ga_len)
7654 syn_unadd_group();
7655 else
7656 {
7657 if (is_normal_group)
7658 {
7659 HL_TABLE()[idx].sg_term_attr = 0;
7660 HL_TABLE()[idx].sg_cterm_attr = 0;
7661#ifdef FEAT_GUI
7662 HL_TABLE()[idx].sg_gui_attr = 0;
7663 /*
7664 * Need to update all groups, because they might be using "bg"
7665 * and/or "fg", which have been changed now.
7666 */
7667 if (gui.in_use)
7668 highlight_gui_started();
7669#endif
7670 }
7671#ifdef FEAT_GUI_X11
7672# ifdef FEAT_MENU
7673 else if (is_menu_group)
7674 {
7675 if (gui.in_use && do_colors)
7676 gui_mch_new_menu_colors();
7677 }
7678# endif
7679 else if (is_scrollbar_group)
7680 {
7681 if (gui.in_use && do_colors)
7682 gui_new_scrollbar_colors();
7683 }
7684# ifdef FEAT_BEVAL
7685 else if (is_tooltip_group)
7686 {
7687 if (gui.in_use && do_colors)
7688 gui_mch_new_tooltip_colors();
7689 }
7690# endif
7691#endif
7692 else
7693 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007694#ifdef FEAT_EVAL
7695 HL_TABLE()[idx].sg_scriptID = current_SID;
7696#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007697 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007698 }
7699 vim_free(key);
7700 vim_free(arg);
7701
7702 /* Only call highlight_changed() once, after sourcing a syntax file */
7703 need_highlight_changed = TRUE;
7704}
7705
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007706#if defined(EXITFREE) || defined(PROTO)
7707 void
7708free_highlight()
7709{
7710 int i;
7711
7712 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007713 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007714 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007715 vim_free(HL_TABLE()[i].sg_name);
7716 vim_free(HL_TABLE()[i].sg_name_u);
7717 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007718 ga_clear(&highlight_ga);
7719}
7720#endif
7721
Bram Moolenaar071d4272004-06-13 20:20:40 +00007722/*
7723 * Reset the cterm colors to what they were before Vim was started, if
7724 * possible. Otherwise reset them to zero.
7725 */
7726 void
7727restore_cterm_colors()
7728{
7729#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7730 /* Since t_me has been set, this probably means that the user
7731 * wants to use this as default colors. Need to reset default
7732 * background/foreground colors. */
7733 mch_set_normal_colors();
7734#else
7735 cterm_normal_fg_color = 0;
7736 cterm_normal_fg_bold = 0;
7737 cterm_normal_bg_color = 0;
7738#endif
7739}
7740
7741/*
7742 * Return TRUE if highlight group "idx" has any settings.
7743 * When "check_link" is TRUE also check for an existing link.
7744 */
7745 static int
7746hl_has_settings(idx, check_link)
7747 int idx;
7748 int check_link;
7749{
7750 return ( HL_TABLE()[idx].sg_term_attr != 0
7751 || HL_TABLE()[idx].sg_cterm_attr != 0
7752#ifdef FEAT_GUI
7753 || HL_TABLE()[idx].sg_gui_attr != 0
7754#endif
7755 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7756}
7757
7758/*
7759 * Clear highlighting for one group.
7760 */
7761 static void
7762highlight_clear(idx)
7763 int idx;
7764{
7765 HL_TABLE()[idx].sg_term = 0;
7766 vim_free(HL_TABLE()[idx].sg_start);
7767 HL_TABLE()[idx].sg_start = NULL;
7768 vim_free(HL_TABLE()[idx].sg_stop);
7769 HL_TABLE()[idx].sg_stop = NULL;
7770 HL_TABLE()[idx].sg_term_attr = 0;
7771 HL_TABLE()[idx].sg_cterm = 0;
7772 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7773 HL_TABLE()[idx].sg_cterm_fg = 0;
7774 HL_TABLE()[idx].sg_cterm_bg = 0;
7775 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007776#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007777 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7779 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7781 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007782 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7783 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007784#endif
7785#ifdef FEAT_GUI
7786 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7787 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7788 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007789 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7790 HL_TABLE()[idx].sg_font = NOFONT;
7791# ifdef FEAT_XFONTSET
7792 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7793 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7794# endif
7795 vim_free(HL_TABLE()[idx].sg_font_name);
7796 HL_TABLE()[idx].sg_font_name = NULL;
7797 HL_TABLE()[idx].sg_gui_attr = 0;
7798#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007799#ifdef FEAT_EVAL
7800 /* Clear the script ID only when there is no link, since that is not
7801 * cleared. */
7802 if (HL_TABLE()[idx].sg_link == 0)
7803 HL_TABLE()[idx].sg_scriptID = 0;
7804#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007805}
7806
7807#if defined(FEAT_GUI) || defined(PROTO)
7808/*
7809 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007810 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 * "Tooltip" colors.
7812 */
7813 void
7814set_normal_colors()
7815{
7816 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007817 &gui.norm_pixel, &gui.back_pixel,
7818 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007819 {
7820 gui_mch_new_colors();
7821 must_redraw = CLEAR;
7822 }
7823#ifdef FEAT_GUI_X11
7824 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007825 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7826 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007827 {
7828# ifdef FEAT_MENU
7829 gui_mch_new_menu_colors();
7830# endif
7831 must_redraw = CLEAR;
7832 }
7833# ifdef FEAT_BEVAL
7834 if (set_group_colors((char_u *)"Tooltip",
7835 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7836 FALSE, FALSE, TRUE))
7837 {
7838# ifdef FEAT_TOOLBAR
7839 gui_mch_new_tooltip_colors();
7840# endif
7841 must_redraw = CLEAR;
7842 }
7843#endif
7844 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007845 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7846 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007847 {
7848 gui_new_scrollbar_colors();
7849 must_redraw = CLEAR;
7850 }
7851#endif
7852}
7853
7854/*
7855 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7856 */
7857 static int
7858set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7859 char_u *name;
7860 guicolor_T *fgp;
7861 guicolor_T *bgp;
7862 int do_menu;
7863 int use_norm;
7864 int do_tooltip;
7865{
7866 int idx;
7867
7868 idx = syn_name2id(name) - 1;
7869 if (idx >= 0)
7870 {
7871 gui_do_one_color(idx, do_menu, do_tooltip);
7872
7873 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7874 *fgp = HL_TABLE()[idx].sg_gui_fg;
7875 else if (use_norm)
7876 *fgp = gui.def_norm_pixel;
7877 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7878 *bgp = HL_TABLE()[idx].sg_gui_bg;
7879 else if (use_norm)
7880 *bgp = gui.def_back_pixel;
7881 return TRUE;
7882 }
7883 return FALSE;
7884}
7885
7886/*
7887 * Get the font of the "Normal" group.
7888 * Returns "" when it's not found or not set.
7889 */
7890 char_u *
7891hl_get_font_name()
7892{
7893 int id;
7894 char_u *s;
7895
7896 id = syn_name2id((char_u *)"Normal");
7897 if (id > 0)
7898 {
7899 s = HL_TABLE()[id - 1].sg_font_name;
7900 if (s != NULL)
7901 return s;
7902 }
7903 return (char_u *)"";
7904}
7905
7906/*
7907 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7908 * actually chosen to be used.
7909 */
7910 void
7911hl_set_font_name(font_name)
7912 char_u *font_name;
7913{
7914 int id;
7915
7916 id = syn_name2id((char_u *)"Normal");
7917 if (id > 0)
7918 {
7919 vim_free(HL_TABLE()[id - 1].sg_font_name);
7920 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7921 }
7922}
7923
7924/*
7925 * Set background color for "Normal" group. Called by gui_set_bg_color()
7926 * when the color is known.
7927 */
7928 void
7929hl_set_bg_color_name(name)
7930 char_u *name; /* must have been allocated */
7931{
7932 int id;
7933
7934 if (name != NULL)
7935 {
7936 id = syn_name2id((char_u *)"Normal");
7937 if (id > 0)
7938 {
7939 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7940 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7941 }
7942 }
7943}
7944
7945/*
7946 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7947 * when the color is known.
7948 */
7949 void
7950hl_set_fg_color_name(name)
7951 char_u *name; /* must have been allocated */
7952{
7953 int id;
7954
7955 if (name != NULL)
7956 {
7957 id = syn_name2id((char_u *)"Normal");
7958 if (id > 0)
7959 {
7960 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7961 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7962 }
7963 }
7964}
7965
7966/*
7967 * Return the handle for a color name.
7968 * Returns INVALCOLOR when failed.
7969 */
7970 static guicolor_T
7971color_name2handle(name)
7972 char_u *name;
7973{
7974 if (STRCMP(name, "NONE") == 0)
7975 return INVALCOLOR;
7976
7977 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7978 return gui.norm_pixel;
7979 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7980 return gui.back_pixel;
7981
7982 return gui_get_color(name);
7983}
7984
7985/*
7986 * Return the handle for a font name.
7987 * Returns NOFONT when failed.
7988 */
7989 static GuiFont
7990font_name2handle(name)
7991 char_u *name;
7992{
7993 if (STRCMP(name, "NONE") == 0)
7994 return NOFONT;
7995
7996 return gui_mch_get_font(name, TRUE);
7997}
7998
7999# ifdef FEAT_XFONTSET
8000/*
8001 * Return the handle for a fontset name.
8002 * Returns NOFONTSET when failed.
8003 */
8004 static GuiFontset
8005fontset_name2handle(name, fixed_width)
8006 char_u *name;
8007 int fixed_width;
8008{
8009 if (STRCMP(name, "NONE") == 0)
8010 return NOFONTSET;
8011
8012 return gui_mch_get_fontset(name, TRUE, fixed_width);
8013}
8014# endif
8015
8016/*
8017 * Get the font or fontset for one highlight group.
8018 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008019 static void
8020hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8021 int idx;
8022 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008023 int do_normal; /* set normal font */
8024 int do_menu UNUSED; /* set menu font */
8025 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008026{
8027# ifdef FEAT_XFONTSET
8028 /* If 'guifontset' is not empty, first try using the name as a
8029 * fontset. If that doesn't work, use it as a font name. */
8030 if (*p_guifontset != NUL
8031# ifdef FONTSET_ALWAYS
8032 || do_menu
8033# endif
8034# ifdef FEAT_BEVAL_TIP
8035 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8036 || do_tooltip
8037# endif
8038 )
8039 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8040# ifdef FONTSET_ALWAYS
8041 || do_menu
8042# endif
8043# ifdef FEAT_BEVAL_TIP
8044 || do_tooltip
8045# endif
8046 );
8047 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8048 {
8049 /* If it worked and it's the Normal group, use it as the
8050 * normal fontset. Same for the Menu group. */
8051 if (do_normal)
8052 gui_init_font(arg, TRUE);
8053# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8054 if (do_menu)
8055 {
8056# ifdef FONTSET_ALWAYS
8057 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8058# else
8059 /* YIKES! This is a bug waiting to crash the program */
8060 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8061# endif
8062 gui_mch_new_menu_font();
8063 }
8064# ifdef FEAT_BEVAL
8065 if (do_tooltip)
8066 {
8067 /* The Athena widget set cannot currently handle switching between
8068 * displaying a single font and a fontset.
8069 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008070 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008071 * XFontStruct is used.
8072 */
8073 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8074 gui_mch_new_tooltip_font();
8075 }
8076# endif
8077# endif
8078 }
8079 else
8080# endif
8081 {
8082 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8083 /* If it worked and it's the Normal group, use it as the
8084 * normal font. Same for the Menu group. */
8085 if (HL_TABLE()[idx].sg_font != NOFONT)
8086 {
8087 if (do_normal)
8088 gui_init_font(arg, FALSE);
8089#ifndef FONTSET_ALWAYS
8090# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8091 if (do_menu)
8092 {
8093 gui.menu_font = HL_TABLE()[idx].sg_font;
8094 gui_mch_new_menu_font();
8095 }
8096# endif
8097#endif
8098 }
8099 }
8100}
8101
8102#endif /* FEAT_GUI */
8103
8104/*
8105 * Table with the specifications for an attribute number.
8106 * Note that this table is used by ALL buffers. This is required because the
8107 * GUI can redraw at any time for any buffer.
8108 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008109static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008110
8111#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8112
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008113static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114
8115#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8116
8117#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008118static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008119
8120#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8121#endif
8122
8123/*
8124 * Return the attr number for a set of colors and font.
8125 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8126 * if the combination is new.
8127 * Return 0 for error (no more room).
8128 */
8129 static int
8130get_attr_entry(table, aep)
8131 garray_T *table;
8132 attrentry_T *aep;
8133{
8134 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008135 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008136 static int recursive = FALSE;
8137
8138 /*
8139 * Init the table, in case it wasn't done yet.
8140 */
8141 table->ga_itemsize = sizeof(attrentry_T);
8142 table->ga_growsize = 7;
8143
8144 /*
8145 * Try to find an entry with the same specifications.
8146 */
8147 for (i = 0; i < table->ga_len; ++i)
8148 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008149 taep = &(((attrentry_T *)table->ga_data)[i]);
8150 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008151 && (
8152#ifdef FEAT_GUI
8153 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008154 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8155 && aep->ae_u.gui.bg_color
8156 == taep->ae_u.gui.bg_color
8157 && aep->ae_u.gui.sp_color
8158 == taep->ae_u.gui.sp_color
8159 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008160# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008161 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008162# endif
8163 ))
8164 ||
8165#endif
8166 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008167 && (aep->ae_u.term.start == NULL)
8168 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169 && (aep->ae_u.term.start == NULL
8170 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008171 taep->ae_u.term.start) == 0)
8172 && (aep->ae_u.term.stop == NULL)
8173 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008174 && (aep->ae_u.term.stop == NULL
8175 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008176 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008177 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008178 && aep->ae_u.cterm.fg_color
8179 == taep->ae_u.cterm.fg_color
8180 && aep->ae_u.cterm.bg_color
8181 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182 ))
8183
8184 return i + ATTR_OFF;
8185 }
8186
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008187 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008188 {
8189 /*
8190 * Running out of attribute entries! remove all attributes, and
8191 * compute new ones for all groups.
8192 * When called recursively, we are really out of numbers.
8193 */
8194 if (recursive)
8195 {
8196 EMSG(_("E424: Too many different highlighting attributes in use"));
8197 return 0;
8198 }
8199 recursive = TRUE;
8200
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008201 clear_hl_tables();
8202
Bram Moolenaar071d4272004-06-13 20:20:40 +00008203 must_redraw = CLEAR;
8204
8205 for (i = 0; i < highlight_ga.ga_len; ++i)
8206 set_hl_attr(i);
8207
8208 recursive = FALSE;
8209 }
8210
8211 /*
8212 * This is a new combination of colors and font, add an entry.
8213 */
8214 if (ga_grow(table, 1) == FAIL)
8215 return 0;
8216
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008217 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8218 vim_memset(taep, 0, sizeof(attrentry_T));
8219 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008220#ifdef FEAT_GUI
8221 if (table == &gui_attr_table)
8222 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008223 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8224 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8225 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8226 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008228 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008229# endif
8230 }
8231#endif
8232 if (table == &term_attr_table)
8233 {
8234 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008235 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008236 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008237 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008239 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008240 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008241 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008242 }
8243 else if (table == &cterm_attr_table)
8244 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008245 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8246 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008247 }
8248 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008249 return (table->ga_len - 1 + ATTR_OFF);
8250}
8251
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008252/*
8253 * Clear all highlight tables.
8254 */
8255 void
8256clear_hl_tables()
8257{
8258 int i;
8259 attrentry_T *taep;
8260
8261#ifdef FEAT_GUI
8262 ga_clear(&gui_attr_table);
8263#endif
8264 for (i = 0; i < term_attr_table.ga_len; ++i)
8265 {
8266 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8267 vim_free(taep->ae_u.term.start);
8268 vim_free(taep->ae_u.term.stop);
8269 }
8270 ga_clear(&term_attr_table);
8271 ga_clear(&cterm_attr_table);
8272}
8273
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008274#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008275/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008276 * Combine special attributes (e.g., for spelling) with other attributes
8277 * (e.g., for syntax highlighting).
8278 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008279 * This creates a new group when required.
8280 * Since we expect there to be few spelling mistakes we don't cache the
8281 * result.
8282 * Return the resulting attributes.
8283 */
8284 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008285hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008286 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008287 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008288{
8289 attrentry_T *char_aep = NULL;
8290 attrentry_T *spell_aep;
8291 attrentry_T new_en;
8292
8293 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008294 return prim_attr;
8295 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8296 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008297#ifdef FEAT_GUI
8298 if (gui.in_use)
8299 {
8300 if (char_attr > HL_ALL)
8301 char_aep = syn_gui_attr2entry(char_attr);
8302 if (char_aep != NULL)
8303 new_en = *char_aep;
8304 else
8305 {
8306 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008307 new_en.ae_u.gui.fg_color = INVALCOLOR;
8308 new_en.ae_u.gui.bg_color = INVALCOLOR;
8309 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008310 if (char_attr <= HL_ALL)
8311 new_en.ae_attr = char_attr;
8312 }
8313
Bram Moolenaar30abd282005-06-22 22:35:10 +00008314 if (prim_attr <= HL_ALL)
8315 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008316 else
8317 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008318 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008319 if (spell_aep != NULL)
8320 {
8321 new_en.ae_attr |= spell_aep->ae_attr;
8322 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8323 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8324 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8325 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8326 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8327 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8328 if (spell_aep->ae_u.gui.font != NOFONT)
8329 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8330# ifdef FEAT_XFONTSET
8331 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8332 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8333# endif
8334 }
8335 }
8336 return get_attr_entry(&gui_attr_table, &new_en);
8337 }
8338#endif
8339
8340 if (t_colors > 1)
8341 {
8342 if (char_attr > HL_ALL)
8343 char_aep = syn_cterm_attr2entry(char_attr);
8344 if (char_aep != NULL)
8345 new_en = *char_aep;
8346 else
8347 {
8348 vim_memset(&new_en, 0, sizeof(new_en));
8349 if (char_attr <= HL_ALL)
8350 new_en.ae_attr = char_attr;
8351 }
8352
Bram Moolenaar30abd282005-06-22 22:35:10 +00008353 if (prim_attr <= HL_ALL)
8354 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008355 else
8356 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008357 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008358 if (spell_aep != NULL)
8359 {
8360 new_en.ae_attr |= spell_aep->ae_attr;
8361 if (spell_aep->ae_u.cterm.fg_color > 0)
8362 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8363 if (spell_aep->ae_u.cterm.bg_color > 0)
8364 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8365 }
8366 }
8367 return get_attr_entry(&cterm_attr_table, &new_en);
8368 }
8369
8370 if (char_attr > HL_ALL)
8371 char_aep = syn_term_attr2entry(char_attr);
8372 if (char_aep != NULL)
8373 new_en = *char_aep;
8374 else
8375 {
8376 vim_memset(&new_en, 0, sizeof(new_en));
8377 if (char_attr <= HL_ALL)
8378 new_en.ae_attr = char_attr;
8379 }
8380
Bram Moolenaar30abd282005-06-22 22:35:10 +00008381 if (prim_attr <= HL_ALL)
8382 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008383 else
8384 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008385 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008386 if (spell_aep != NULL)
8387 {
8388 new_en.ae_attr |= spell_aep->ae_attr;
8389 if (spell_aep->ae_u.term.start != NULL)
8390 {
8391 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8392 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8393 }
8394 }
8395 }
8396 return get_attr_entry(&term_attr_table, &new_en);
8397}
8398#endif
8399
Bram Moolenaar071d4272004-06-13 20:20:40 +00008400#ifdef FEAT_GUI
8401
8402 attrentry_T *
8403syn_gui_attr2entry(attr)
8404 int attr;
8405{
8406 attr -= ATTR_OFF;
8407 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8408 return NULL;
8409 return &(GUI_ATTR_ENTRY(attr));
8410}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411#endif /* FEAT_GUI */
8412
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008413/*
8414 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8415 * Only to be used when "attr" > HL_ALL.
8416 */
8417 int
8418syn_attr2attr(attr)
8419 int attr;
8420{
8421 attrentry_T *aep;
8422
8423#ifdef FEAT_GUI
8424 if (gui.in_use)
8425 aep = syn_gui_attr2entry(attr);
8426 else
8427#endif
8428 if (t_colors > 1)
8429 aep = syn_cterm_attr2entry(attr);
8430 else
8431 aep = syn_term_attr2entry(attr);
8432
8433 if (aep == NULL) /* highlighting not set */
8434 return 0;
8435 return aep->ae_attr;
8436}
8437
8438
Bram Moolenaar071d4272004-06-13 20:20:40 +00008439 attrentry_T *
8440syn_term_attr2entry(attr)
8441 int attr;
8442{
8443 attr -= ATTR_OFF;
8444 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8445 return NULL;
8446 return &(TERM_ATTR_ENTRY(attr));
8447}
8448
8449 attrentry_T *
8450syn_cterm_attr2entry(attr)
8451 int attr;
8452{
8453 attr -= ATTR_OFF;
8454 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8455 return NULL;
8456 return &(CTERM_ATTR_ENTRY(attr));
8457}
8458
8459#define LIST_ATTR 1
8460#define LIST_STRING 2
8461#define LIST_INT 3
8462
8463 static void
8464highlight_list_one(id)
8465 int id;
8466{
8467 struct hl_group *sgp;
8468 int didh = FALSE;
8469
8470 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8471
8472 didh = highlight_list_arg(id, didh, LIST_ATTR,
8473 sgp->sg_term, NULL, "term");
8474 didh = highlight_list_arg(id, didh, LIST_STRING,
8475 0, sgp->sg_start, "start");
8476 didh = highlight_list_arg(id, didh, LIST_STRING,
8477 0, sgp->sg_stop, "stop");
8478
8479 didh = highlight_list_arg(id, didh, LIST_ATTR,
8480 sgp->sg_cterm, NULL, "cterm");
8481 didh = highlight_list_arg(id, didh, LIST_INT,
8482 sgp->sg_cterm_fg, NULL, "ctermfg");
8483 didh = highlight_list_arg(id, didh, LIST_INT,
8484 sgp->sg_cterm_bg, NULL, "ctermbg");
8485
Bram Moolenaar61623362010-07-14 22:04:22 +02008486#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008487 didh = highlight_list_arg(id, didh, LIST_ATTR,
8488 sgp->sg_gui, NULL, "gui");
8489 didh = highlight_list_arg(id, didh, LIST_STRING,
8490 0, sgp->sg_gui_fg_name, "guifg");
8491 didh = highlight_list_arg(id, didh, LIST_STRING,
8492 0, sgp->sg_gui_bg_name, "guibg");
8493 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008494 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008495#endif
8496#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008497 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008498 0, sgp->sg_font_name, "font");
8499#endif
8500
Bram Moolenaar661b1822005-07-28 22:36:45 +00008501 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008502 {
8503 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008504 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008505 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8506 msg_putchar(' ');
8507 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8508 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008509
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008510 if (!didh)
8511 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008512#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008513 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008514 last_set_msg(sgp->sg_scriptID);
8515#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008516}
8517
8518 static int
8519highlight_list_arg(id, didh, type, iarg, sarg, name)
8520 int id;
8521 int didh;
8522 int type;
8523 int iarg;
8524 char_u *sarg;
8525 char *name;
8526{
8527 char_u buf[100];
8528 char_u *ts;
8529 int i;
8530
Bram Moolenaar661b1822005-07-28 22:36:45 +00008531 if (got_int)
8532 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008533 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8534 {
8535 ts = buf;
8536 if (type == LIST_INT)
8537 sprintf((char *)buf, "%d", iarg - 1);
8538 else if (type == LIST_STRING)
8539 ts = sarg;
8540 else /* type == LIST_ATTR */
8541 {
8542 buf[0] = NUL;
8543 for (i = 0; hl_attr_table[i] != 0; ++i)
8544 {
8545 if (iarg & hl_attr_table[i])
8546 {
8547 if (buf[0] != NUL)
8548 STRCAT(buf, ",");
8549 STRCAT(buf, hl_name_table[i]);
8550 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8551 }
8552 }
8553 }
8554
8555 (void)syn_list_header(didh,
8556 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8557 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008558 if (!got_int)
8559 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008560 if (*name != NUL)
8561 {
8562 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8563 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8564 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008565 msg_outtrans(ts);
8566 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008567 }
8568 return didh;
8569}
8570
8571#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8572/*
8573 * Return "1" if highlight group "id" has attribute "flag".
8574 * Return NULL otherwise.
8575 */
8576 char_u *
8577highlight_has_attr(id, flag, modec)
8578 int id;
8579 int flag;
8580 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8581{
8582 int attr;
8583
8584 if (id <= 0 || id > highlight_ga.ga_len)
8585 return NULL;
8586
Bram Moolenaar61623362010-07-14 22:04:22 +02008587#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008588 if (modec == 'g')
8589 attr = HL_TABLE()[id - 1].sg_gui;
8590 else
8591#endif
8592 if (modec == 'c')
8593 attr = HL_TABLE()[id - 1].sg_cterm;
8594 else
8595 attr = HL_TABLE()[id - 1].sg_term;
8596
8597 if (attr & flag)
8598 return (char_u *)"1";
8599 return NULL;
8600}
8601#endif
8602
8603#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8604/*
8605 * Return color name of highlight group "id".
8606 */
8607 char_u *
8608highlight_color(id, what, modec)
8609 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008610 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008611 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8612{
8613 static char_u name[20];
8614 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008615 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008616 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008617 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008618
8619 if (id <= 0 || id > highlight_ga.ga_len)
8620 return NULL;
8621
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008622 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008623 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008624 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008625 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008626 font = TRUE;
8627 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008628 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008629 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8630 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008631 if (modec == 'g')
8632 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008633# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008634 /* return font name */
8635 if (font)
8636 return HL_TABLE()[id - 1].sg_font_name;
8637
Bram Moolenaar071d4272004-06-13 20:20:40 +00008638 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008639 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008640 {
8641 guicolor_T color;
8642 long_u rgb;
8643 static char_u buf[10];
8644
8645 if (fg)
8646 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008647 else if (sp)
8648 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008649 else
8650 color = HL_TABLE()[id - 1].sg_gui_bg;
8651 if (color == INVALCOLOR)
8652 return NULL;
8653 rgb = gui_mch_get_rgb(color);
8654 sprintf((char *)buf, "#%02x%02x%02x",
8655 (unsigned)(rgb >> 16),
8656 (unsigned)(rgb >> 8) & 255,
8657 (unsigned)rgb & 255);
8658 return buf;
8659 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008660#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008661 if (fg)
8662 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008663 if (sp)
8664 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8666 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008667 if (font || sp)
8668 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669 if (modec == 'c')
8670 {
8671 if (fg)
8672 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8673 else
8674 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8675 sprintf((char *)name, "%d", n);
8676 return name;
8677 }
8678 /* term doesn't have color */
8679 return NULL;
8680}
8681#endif
8682
8683#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8684 || defined(PROTO)
8685/*
8686 * Return color name of highlight group "id" as RGB value.
8687 */
8688 long_u
8689highlight_gui_color_rgb(id, fg)
8690 int id;
8691 int fg; /* TRUE = fg, FALSE = bg */
8692{
8693 guicolor_T color;
8694
8695 if (id <= 0 || id > highlight_ga.ga_len)
8696 return 0L;
8697
8698 if (fg)
8699 color = HL_TABLE()[id - 1].sg_gui_fg;
8700 else
8701 color = HL_TABLE()[id - 1].sg_gui_bg;
8702
8703 if (color == INVALCOLOR)
8704 return 0L;
8705
8706 return gui_mch_get_rgb(color);
8707}
8708#endif
8709
8710/*
8711 * Output the syntax list header.
8712 * Return TRUE when started a new line.
8713 */
8714 static int
8715syn_list_header(did_header, outlen, id)
8716 int did_header; /* did header already */
8717 int outlen; /* length of string that comes */
8718 int id; /* highlight group id */
8719{
8720 int endcol = 19;
8721 int newline = TRUE;
8722
8723 if (!did_header)
8724 {
8725 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008726 if (got_int)
8727 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008728 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8729 endcol = 15;
8730 }
8731 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008732 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008733 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008734 if (got_int)
8735 return TRUE;
8736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008737 else
8738 {
8739 if (msg_col >= endcol) /* wrap around is like starting a new line */
8740 newline = FALSE;
8741 }
8742
8743 if (msg_col >= endcol) /* output at least one space */
8744 endcol = msg_col + 1;
8745 if (Columns <= endcol) /* avoid hang for tiny window */
8746 endcol = Columns - 1;
8747
8748 msg_advance(endcol);
8749
8750 /* Show "xxx" with the attributes. */
8751 if (!did_header)
8752 {
8753 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8754 msg_putchar(' ');
8755 }
8756
8757 return newline;
8758}
8759
8760/*
8761 * Set the attribute numbers for a highlight group.
8762 * Called after one of the attributes has changed.
8763 */
8764 static void
8765set_hl_attr(idx)
8766 int idx; /* index in array */
8767{
8768 attrentry_T at_en;
8769 struct hl_group *sgp = HL_TABLE() + idx;
8770
8771 /* The "Normal" group doesn't need an attribute number */
8772 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8773 return;
8774
8775#ifdef FEAT_GUI
8776 /*
8777 * For the GUI mode: If there are other than "normal" highlighting
8778 * attributes, need to allocate an attr number.
8779 */
8780 if (sgp->sg_gui_fg == INVALCOLOR
8781 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008782 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008783 && sgp->sg_font == NOFONT
8784# ifdef FEAT_XFONTSET
8785 && sgp->sg_fontset == NOFONTSET
8786# endif
8787 )
8788 {
8789 sgp->sg_gui_attr = sgp->sg_gui;
8790 }
8791 else
8792 {
8793 at_en.ae_attr = sgp->sg_gui;
8794 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8795 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008796 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008797 at_en.ae_u.gui.font = sgp->sg_font;
8798# ifdef FEAT_XFONTSET
8799 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8800# endif
8801 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8802 }
8803#endif
8804 /*
8805 * For the term mode: If there are other than "normal" highlighting
8806 * attributes, need to allocate an attr number.
8807 */
8808 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8809 sgp->sg_term_attr = sgp->sg_term;
8810 else
8811 {
8812 at_en.ae_attr = sgp->sg_term;
8813 at_en.ae_u.term.start = sgp->sg_start;
8814 at_en.ae_u.term.stop = sgp->sg_stop;
8815 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8816 }
8817
8818 /*
8819 * For the color term mode: If there are other than "normal"
8820 * highlighting attributes, need to allocate an attr number.
8821 */
8822 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8823 sgp->sg_cterm_attr = sgp->sg_cterm;
8824 else
8825 {
8826 at_en.ae_attr = sgp->sg_cterm;
8827 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8828 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8829 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8830 }
8831}
8832
8833/*
8834 * Lookup a highlight group name and return it's ID.
8835 * If it is not found, 0 is returned.
8836 */
8837 int
8838syn_name2id(name)
8839 char_u *name;
8840{
8841 int i;
8842 char_u name_u[200];
8843
8844 /* Avoid using stricmp() too much, it's slow on some systems */
8845 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8846 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008847 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008848 vim_strup(name_u);
8849 for (i = highlight_ga.ga_len; --i >= 0; )
8850 if (HL_TABLE()[i].sg_name_u != NULL
8851 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8852 break;
8853 return i + 1;
8854}
8855
8856#if defined(FEAT_EVAL) || defined(PROTO)
8857/*
8858 * Return TRUE if highlight group "name" exists.
8859 */
8860 int
8861highlight_exists(name)
8862 char_u *name;
8863{
8864 return (syn_name2id(name) > 0);
8865}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008866
8867# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8868/*
8869 * Return the name of highlight group "id".
8870 * When not a valid ID return an empty string.
8871 */
8872 char_u *
8873syn_id2name(id)
8874 int id;
8875{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008876 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008877 return (char_u *)"";
8878 return HL_TABLE()[id - 1].sg_name;
8879}
8880# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008881#endif
8882
8883/*
8884 * Like syn_name2id(), but take a pointer + length argument.
8885 */
8886 int
8887syn_namen2id(linep, len)
8888 char_u *linep;
8889 int len;
8890{
8891 char_u *name;
8892 int id = 0;
8893
8894 name = vim_strnsave(linep, len);
8895 if (name != NULL)
8896 {
8897 id = syn_name2id(name);
8898 vim_free(name);
8899 }
8900 return id;
8901}
8902
8903/*
8904 * Find highlight group name in the table and return it's ID.
8905 * The argument is a pointer to the name and the length of the name.
8906 * If it doesn't exist yet, a new entry is created.
8907 * Return 0 for failure.
8908 */
8909 int
8910syn_check_group(pp, len)
8911 char_u *pp;
8912 int len;
8913{
8914 int id;
8915 char_u *name;
8916
8917 name = vim_strnsave(pp, len);
8918 if (name == NULL)
8919 return 0;
8920
8921 id = syn_name2id(name);
8922 if (id == 0) /* doesn't exist yet */
8923 id = syn_add_group(name);
8924 else
8925 vim_free(name);
8926 return id;
8927}
8928
8929/*
8930 * Add new highlight group and return it's ID.
8931 * "name" must be an allocated string, it will be consumed.
8932 * Return 0 for failure.
8933 */
8934 static int
8935syn_add_group(name)
8936 char_u *name;
8937{
8938 char_u *p;
8939
8940 /* Check that the name is ASCII letters, digits and underscore. */
8941 for (p = name; *p != NUL; ++p)
8942 {
8943 if (!vim_isprintc(*p))
8944 {
8945 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008946 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008947 return 0;
8948 }
8949 else if (!ASCII_ISALNUM(*p) && *p != '_')
8950 {
8951 /* This is an error, but since there previously was no check only
8952 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008953 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008954 MSG(_("W18: Invalid character in group name"));
8955 break;
8956 }
8957 }
8958
8959 /*
8960 * First call for this growarray: init growing array.
8961 */
8962 if (highlight_ga.ga_data == NULL)
8963 {
8964 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8965 highlight_ga.ga_growsize = 10;
8966 }
8967
8968 /*
8969 * Make room for at least one other syntax_highlight entry.
8970 */
8971 if (ga_grow(&highlight_ga, 1) == FAIL)
8972 {
8973 vim_free(name);
8974 return 0;
8975 }
8976
8977 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8978 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8979 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8980#ifdef FEAT_GUI
8981 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8982 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008983 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008984#endif
8985 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008986
8987 return highlight_ga.ga_len; /* ID is index plus one */
8988}
8989
8990/*
8991 * When, just after calling syn_add_group(), an error is discovered, this
8992 * function deletes the new name.
8993 */
8994 static void
8995syn_unadd_group()
8996{
8997 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008998 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8999 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9000}
9001
9002/*
9003 * Translate a group ID to highlight attributes.
9004 */
9005 int
9006syn_id2attr(hl_id)
9007 int hl_id;
9008{
9009 int attr;
9010 struct hl_group *sgp;
9011
9012 hl_id = syn_get_final_id(hl_id);
9013 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9014
9015#ifdef FEAT_GUI
9016 /*
9017 * Only use GUI attr when the GUI is being used.
9018 */
9019 if (gui.in_use)
9020 attr = sgp->sg_gui_attr;
9021 else
9022#endif
9023 if (t_colors > 1)
9024 attr = sgp->sg_cterm_attr;
9025 else
9026 attr = sgp->sg_term_attr;
9027
9028 return attr;
9029}
9030
9031#ifdef FEAT_GUI
9032/*
9033 * Get the GUI colors and attributes for a group ID.
9034 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9035 */
9036 int
9037syn_id2colors(hl_id, fgp, bgp)
9038 int hl_id;
9039 guicolor_T *fgp;
9040 guicolor_T *bgp;
9041{
9042 struct hl_group *sgp;
9043
9044 hl_id = syn_get_final_id(hl_id);
9045 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9046
9047 *fgp = sgp->sg_gui_fg;
9048 *bgp = sgp->sg_gui_bg;
9049 return sgp->sg_gui;
9050}
9051#endif
9052
9053/*
9054 * Translate a group ID to the final group ID (following links).
9055 */
9056 int
9057syn_get_final_id(hl_id)
9058 int hl_id;
9059{
9060 int count;
9061 struct hl_group *sgp;
9062
9063 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9064 return 0; /* Can be called from eval!! */
9065
9066 /*
9067 * Follow links until there is no more.
9068 * Look out for loops! Break after 100 links.
9069 */
9070 for (count = 100; --count >= 0; )
9071 {
9072 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9073 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9074 break;
9075 hl_id = sgp->sg_link;
9076 }
9077
9078 return hl_id;
9079}
9080
9081#ifdef FEAT_GUI
9082/*
9083 * Call this function just after the GUI has started.
9084 * It finds the font and color handles for the highlighting groups.
9085 */
9086 void
9087highlight_gui_started()
9088{
9089 int idx;
9090
9091 /* First get the colors from the "Normal" and "Menu" group, if set */
9092 set_normal_colors();
9093
9094 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9095 gui_do_one_color(idx, FALSE, FALSE);
9096
9097 highlight_changed();
9098}
9099
9100 static void
9101gui_do_one_color(idx, do_menu, do_tooltip)
9102 int idx;
9103 int do_menu; /* TRUE: might set the menu font */
9104 int do_tooltip; /* TRUE: might set the tooltip font */
9105{
9106 int didit = FALSE;
9107
9108 if (HL_TABLE()[idx].sg_font_name != NULL)
9109 {
9110 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9111 do_tooltip);
9112 didit = TRUE;
9113 }
9114 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9115 {
9116 HL_TABLE()[idx].sg_gui_fg =
9117 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9118 didit = TRUE;
9119 }
9120 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9121 {
9122 HL_TABLE()[idx].sg_gui_bg =
9123 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9124 didit = TRUE;
9125 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009126 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9127 {
9128 HL_TABLE()[idx].sg_gui_sp =
9129 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9130 didit = TRUE;
9131 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009132 if (didit) /* need to get a new attr number */
9133 set_hl_attr(idx);
9134}
9135
9136#endif
9137
9138/*
9139 * Translate the 'highlight' option into attributes in highlight_attr[] and
9140 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9141 * corresponding highlights to use on top of HLF_SNC is computed.
9142 * Called only when the 'highlight' option has been changed and upon first
9143 * screen redraw after any :highlight command.
9144 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9145 */
9146 int
9147highlight_changed()
9148{
9149 int hlf;
9150 int i;
9151 char_u *p;
9152 int attr;
9153 char_u *end;
9154 int id;
9155#ifdef USER_HIGHLIGHT
9156 char_u userhl[10];
9157# ifdef FEAT_STL_OPT
9158 int id_SNC = -1;
9159 int id_S = -1;
9160 int hlcnt;
9161# endif
9162#endif
9163 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9164
9165 need_highlight_changed = FALSE;
9166
9167 /*
9168 * Clear all attributes.
9169 */
9170 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9171 highlight_attr[hlf] = 0;
9172
9173 /*
9174 * First set all attributes to their default value.
9175 * Then use the attributes from the 'highlight' option.
9176 */
9177 for (i = 0; i < 2; ++i)
9178 {
9179 if (i)
9180 p = p_hl;
9181 else
9182 p = get_highlight_default();
9183 if (p == NULL) /* just in case */
9184 continue;
9185
9186 while (*p)
9187 {
9188 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9189 if (hl_flags[hlf] == *p)
9190 break;
9191 ++p;
9192 if (hlf == (int)HLF_COUNT || *p == NUL)
9193 return FAIL;
9194
9195 /*
9196 * Allow several hl_flags to be combined, like "bu" for
9197 * bold-underlined.
9198 */
9199 attr = 0;
9200 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9201 {
9202 if (vim_iswhite(*p)) /* ignore white space */
9203 continue;
9204
9205 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9206 return FAIL;
9207
9208 switch (*p)
9209 {
9210 case 'b': attr |= HL_BOLD;
9211 break;
9212 case 'i': attr |= HL_ITALIC;
9213 break;
9214 case '-':
9215 case 'n': /* no highlighting */
9216 break;
9217 case 'r': attr |= HL_INVERSE;
9218 break;
9219 case 's': attr |= HL_STANDOUT;
9220 break;
9221 case 'u': attr |= HL_UNDERLINE;
9222 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009223 case 'c': attr |= HL_UNDERCURL;
9224 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009225 case ':': ++p; /* highlight group name */
9226 if (attr || *p == NUL) /* no combinations */
9227 return FAIL;
9228 end = vim_strchr(p, ',');
9229 if (end == NULL)
9230 end = p + STRLEN(p);
9231 id = syn_check_group(p, (int)(end - p));
9232 if (id == 0)
9233 return FAIL;
9234 attr = syn_id2attr(id);
9235 p = end - 1;
9236#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9237 if (hlf == (int)HLF_SNC)
9238 id_SNC = syn_get_final_id(id);
9239 else if (hlf == (int)HLF_S)
9240 id_S = syn_get_final_id(id);
9241#endif
9242 break;
9243 default: return FAIL;
9244 }
9245 }
9246 highlight_attr[hlf] = attr;
9247
9248 p = skip_to_option_part(p); /* skip comma and spaces */
9249 }
9250 }
9251
9252#ifdef USER_HIGHLIGHT
9253 /* Setup the user highlights
9254 *
9255 * Temporarily utilize 10 more hl entries. Have to be in there
9256 * simultaneously in case of table overflows in get_attr_entry()
9257 */
9258# ifdef FEAT_STL_OPT
9259 if (ga_grow(&highlight_ga, 10) == FAIL)
9260 return FAIL;
9261 hlcnt = highlight_ga.ga_len;
9262 if (id_S == 0)
9263 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009264 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009265 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9266 id_S = hlcnt + 10;
9267 }
9268# endif
9269 for (i = 0; i < 9; i++)
9270 {
9271 sprintf((char *)userhl, "User%d", i + 1);
9272 id = syn_name2id(userhl);
9273 if (id == 0)
9274 {
9275 highlight_user[i] = 0;
9276# ifdef FEAT_STL_OPT
9277 highlight_stlnc[i] = 0;
9278# endif
9279 }
9280 else
9281 {
9282# ifdef FEAT_STL_OPT
9283 struct hl_group *hlt = HL_TABLE();
9284# endif
9285
9286 highlight_user[i] = syn_id2attr(id);
9287# ifdef FEAT_STL_OPT
9288 if (id_SNC == 0)
9289 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009290 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009291 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9292 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009293# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009294 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9295# endif
9296 }
9297 else
9298 mch_memmove(&hlt[hlcnt + i],
9299 &hlt[id_SNC - 1],
9300 sizeof(struct hl_group));
9301 hlt[hlcnt + i].sg_link = 0;
9302
9303 /* Apply difference between UserX and HLF_S to HLF_SNC */
9304 hlt[hlcnt + i].sg_term ^=
9305 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9306 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9307 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9308 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9309 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9310 hlt[hlcnt + i].sg_cterm ^=
9311 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9312 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9313 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9314 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9315 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009316# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 hlt[hlcnt + i].sg_gui ^=
9318 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009319# endif
9320# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009321 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9322 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9323 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9324 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009325 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9326 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009327 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9328 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9329# ifdef FEAT_XFONTSET
9330 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9331 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9332# endif
9333# endif
9334 highlight_ga.ga_len = hlcnt + i + 1;
9335 set_hl_attr(hlcnt + i); /* At long last we can apply */
9336 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9337# endif
9338 }
9339 }
9340# ifdef FEAT_STL_OPT
9341 highlight_ga.ga_len = hlcnt;
9342# endif
9343
9344#endif /* USER_HIGHLIGHT */
9345
9346 return OK;
9347}
9348
Bram Moolenaar4f688582007-07-24 12:34:30 +00009349#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009350
9351static void highlight_list __ARGS((void));
9352static void highlight_list_two __ARGS((int cnt, int attr));
9353
9354/*
9355 * Handle command line completion for :highlight command.
9356 */
9357 void
9358set_context_in_highlight_cmd(xp, arg)
9359 expand_T *xp;
9360 char_u *arg;
9361{
9362 char_u *p;
9363
9364 /* Default: expand group names */
9365 xp->xp_context = EXPAND_HIGHLIGHT;
9366 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009367 include_link = 2;
9368 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009369
9370 /* (part of) subcommand already typed */
9371 if (*arg != NUL)
9372 {
9373 p = skiptowhite(arg);
9374 if (*p != NUL) /* past "default" or group name */
9375 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009376 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009377 if (STRNCMP("default", arg, p - arg) == 0)
9378 {
9379 arg = skipwhite(p);
9380 xp->xp_pattern = arg;
9381 p = skiptowhite(arg);
9382 }
9383 if (*p != NUL) /* past group name */
9384 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009385 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009386 if (arg[1] == 'i' && arg[0] == 'N')
9387 highlight_list();
9388 if (STRNCMP("link", arg, p - arg) == 0
9389 || STRNCMP("clear", arg, p - arg) == 0)
9390 {
9391 xp->xp_pattern = skipwhite(p);
9392 p = skiptowhite(xp->xp_pattern);
9393 if (*p != NUL) /* past first group name */
9394 {
9395 xp->xp_pattern = skipwhite(p);
9396 p = skiptowhite(xp->xp_pattern);
9397 }
9398 }
9399 if (*p != NUL) /* past group name(s) */
9400 xp->xp_context = EXPAND_NOTHING;
9401 }
9402 }
9403 }
9404}
9405
9406/*
9407 * List highlighting matches in a nice way.
9408 */
9409 static void
9410highlight_list()
9411{
9412 int i;
9413
9414 for (i = 10; --i >= 0; )
9415 highlight_list_two(i, hl_attr(HLF_D));
9416 for (i = 40; --i >= 0; )
9417 highlight_list_two(99, 0);
9418}
9419
9420 static void
9421highlight_list_two(cnt, attr)
9422 int cnt;
9423 int attr;
9424{
9425 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9426 msg_clr_eos();
9427 out_flush();
9428 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9429}
9430
9431#endif /* FEAT_CMDL_COMPL */
9432
9433#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9434 || defined(FEAT_SIGNS) || defined(PROTO)
9435/*
9436 * Function given to ExpandGeneric() to obtain the list of group names.
9437 * Also used for synIDattr() function.
9438 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009439 char_u *
9440get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009441 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009442 int idx;
9443{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009444#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009445 if (idx == highlight_ga.ga_len && include_none != 0)
9446 return (char_u *)"none";
9447 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009448 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009449 if (idx == highlight_ga.ga_len + include_none + include_default
9450 && include_link != 0)
9451 return (char_u *)"link";
9452 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9453 && include_link != 0)
9454 return (char_u *)"clear";
9455#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009456 if (idx < 0 || idx >= highlight_ga.ga_len)
9457 return NULL;
9458 return HL_TABLE()[idx].sg_name;
9459}
9460#endif
9461
Bram Moolenaar4f688582007-07-24 12:34:30 +00009462#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009463/*
9464 * Free all the highlight group fonts.
9465 * Used when quitting for systems which need it.
9466 */
9467 void
9468free_highlight_fonts()
9469{
9470 int idx;
9471
9472 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9473 {
9474 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9475 HL_TABLE()[idx].sg_font = NOFONT;
9476# ifdef FEAT_XFONTSET
9477 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9478 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9479# endif
9480 }
9481
9482 gui_mch_free_font(gui.norm_font);
9483# ifdef FEAT_XFONTSET
9484 gui_mch_free_fontset(gui.fontset);
9485# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009486# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009487 gui_mch_free_font(gui.bold_font);
9488 gui_mch_free_font(gui.ital_font);
9489 gui_mch_free_font(gui.boldital_font);
9490# endif
9491}
9492#endif
9493
9494/**************************************
9495 * End of Highlighting stuff *
9496 **************************************/