blob: 5880803645ca52064220fdae932923e1f6d34c41 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
36#ifdef FEAT_GUI
37/* for when using the GUI */
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
71#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000072/* Flags to indicate an additional string for highlight name completion. */
73static int include_none = 0; /* when 1 include "None" */
74static int include_default = 0; /* when 1 include "default" */
75static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#endif
77
78/*
79 * The "term", "cterm" and "gui" arguments can be any combination of the
80 * following names, separated by commas (but no spaces!).
81 */
82static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000083 {"bold", "standout", "underline", "undercurl",
84 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000085static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000086 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087
88static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
89static void syn_unadd_group __ARGS((void));
90static void set_hl_attr __ARGS((int idx));
91static void highlight_list_one __ARGS((int id));
92static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
93static int syn_add_group __ARGS((char_u *name));
94static int syn_list_header __ARGS((int did_header, int outlen, int id));
95static int hl_has_settings __ARGS((int idx, int check_link));
96static void highlight_clear __ARGS((int idx));
97
98#ifdef FEAT_GUI
99static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
100static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
101static guicolor_T color_name2handle __ARGS((char_u *name));
102static GuiFont font_name2handle __ARGS((char_u *name));
103# ifdef FEAT_XFONTSET
104static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
105# endif
106static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
107#endif
108
109/*
110 * An attribute number is the index in attr_table plus ATTR_OFF.
111 */
112#define ATTR_OFF (HL_ALL + 1)
113
114#if defined(FEAT_SYN_HL) || defined(PROTO)
115
116#define SYN_NAMELEN 50 /* maximum length of a syntax name */
117
118/* different types of offsets that are possible */
119#define SPO_MS_OFF 0 /* match start offset */
120#define SPO_ME_OFF 1 /* match end offset */
121#define SPO_HS_OFF 2 /* highl. start offset */
122#define SPO_HE_OFF 3 /* highl. end offset */
123#define SPO_RS_OFF 4 /* region start offset */
124#define SPO_RE_OFF 5 /* region end offset */
125#define SPO_LC_OFF 6 /* leading context offset */
126#define SPO_COUNT 7
127
128static char *(spo_name_tab[SPO_COUNT]) =
129 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
130
131/*
132 * The patterns that are being searched for are stored in a syn_pattern.
133 * A match item consists of one pattern.
134 * A start/end item consists of n start patterns and m end patterns.
135 * A start/skip/end item consists of n start patterns, one skip pattern and m
136 * end patterns.
137 * For the latter two, the patterns are always consecutive: start-skip-end.
138 *
139 * A character offset can be given for the matched text (_m_start and _m_end)
140 * and for the actually highlighted text (_h_start and _h_end).
141 */
142typedef struct syn_pattern
143{
144 char sp_type; /* see SPTYPE_ defines below */
145 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200146 int sp_flags; /* see HL_ defines below */
147#ifdef FEAT_CONCEAL
148 int sp_char; /* conceal substitute character */
149#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000150 struct sp_syn sp_syn; /* struct passed to in_id_list() */
151 short sp_syn_match_id; /* highlight group ID of pattern */
152 char_u *sp_pattern; /* regexp to match, pattern */
153 regprog_T *sp_prog; /* regexp to match, program */
154 int sp_ic; /* ignore-case flag for sp_prog */
155 short sp_off_flags; /* see below */
156 int sp_offsets[SPO_COUNT]; /* offsets */
157 short *sp_cont_list; /* cont. group IDs, if non-zero */
158 short *sp_next_list; /* next group IDs, if non-zero */
159 int sp_sync_idx; /* sync item index (syncing only) */
160 int sp_line_id; /* ID of last line where tried */
161 int sp_startcol; /* next match in sp_line_id line */
162} synpat_T;
163
164/* The sp_off_flags are computed like this:
165 * offset from the start of the matched text: (1 << SPO_XX_OFF)
166 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
167 * When both are present, only one is used.
168 */
169
170#define SPTYPE_MATCH 1 /* match keyword with this group ID */
171#define SPTYPE_START 2 /* match a regexp, start of item */
172#define SPTYPE_END 3 /* match a regexp, end of item */
173#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
174
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175
176#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
177
178#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
179
180/*
181 * Flags for b_syn_sync_flags:
182 */
183#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
184#define SF_MATCH 0x02 /* sync by matching a pattern */
185
186#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
187
Bram Moolenaar071d4272004-06-13 20:20:40 +0000188#define MAXKEYWLEN 80 /* maximum length of a keyword */
189
190/*
191 * The attributes of the syntax item that has been recognized.
192 */
193static int current_attr = 0; /* attr of current syntax word */
194#ifdef FEAT_EVAL
195static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000196static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200198#ifdef FEAT_CONCEAL
199static int current_flags = 0;
200static int current_sub_char = 0;
201#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202
Bram Moolenaar217ad922005-03-20 22:37:15 +0000203typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204{
205 char_u *scl_name; /* syntax cluster name */
206 char_u *scl_name_u; /* uppercase of scl_name */
207 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000208} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209
210/*
211 * Methods of combining two clusters
212 */
213#define CLUSTER_REPLACE 1 /* replace first list with second */
214#define CLUSTER_ADD 2 /* add second list to first */
215#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
216
Bram Moolenaar217ad922005-03-20 22:37:15 +0000217#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218
219/*
220 * Syntax group IDs have different types:
221 * 0 - 9999 normal syntax groups
222 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
223 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
224 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
225 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
226 */
227#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
228#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
229#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
230#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
231
232/*
233 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
234 * expand_filename(). Most of the other syntax commands don't need it, so
235 * instead of passing it to them, we stow it here.
236 */
237static char_u **syn_cmdlinep;
238
239/*
240 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200241 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000242 * rules in each ":syn include"'d file.
243 */
244static int current_syn_inc_tag = 0;
245static int running_syn_inc_tag = 0;
246
247/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000248 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
249 * This avoids adding a pointer to the hashtable item.
250 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
251 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
252 * HI2KE() converts a hashitem pointer to a var pointer.
253 */
254static keyentry_T dumkey;
255#define KE2HIKEY(kp) ((kp)->keyword)
256#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
257#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
258
259/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260 * To reduce the time spent in keepend(), remember at which level in the state
261 * stack the first item with "keepend" is present. When "-1", there is no
262 * "keepend" on the stack.
263 */
264static int keepend_level = -1;
265
266/*
267 * For the current state we need to remember more than just the idx.
268 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
269 * (The end positions have the column number of the next char)
270 */
271typedef struct state_item
272{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000273 int si_idx; /* index of syntax pattern or
274 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000275 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000276 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277 int si_m_lnum; /* lnum of the match */
278 int si_m_startcol; /* starting column of the match */
279 lpos_T si_m_endpos; /* just after end posn of the match */
280 lpos_T si_h_startpos; /* start position of the highlighting */
281 lpos_T si_h_endpos; /* end position of the highlighting */
282 lpos_T si_eoe_pos; /* end position of end pattern */
283 int si_end_idx; /* group ID for end pattern or zero */
284 int si_ends; /* if match ends before si_m_endpos */
285 int si_attr; /* attributes in this state */
286 long si_flags; /* HL_HAS_EOL flag in this state, and
287 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200288#ifdef FEAT_CONCEAL
289 int si_char; /* substitution character for conceal */
290#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000291 short *si_cont_list; /* list of contained groups */
292 short *si_next_list; /* nextgroup IDs after this item ends */
293 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
294 * pattern */
295} stateitem_T;
296
297#define KEYWORD_IDX -1 /* value of si_idx for keywords */
298#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
299 but contained groups */
300
301/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000302 * Struct to reduce the number of arguments to get_syn_options(), it's used
303 * very often.
304 */
305typedef struct
306{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000307 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000308 int keyword; /* TRUE for ":syn keyword" */
309 int *sync_idx; /* syntax item for "grouphere" argument, NULL
310 if not allowed */
311 char has_cont_list; /* TRUE if "cont_list" can be used */
312 short *cont_list; /* group IDs for "contains" argument */
313 short *cont_in_list; /* group IDs for "containedin" argument */
314 short *next_list; /* group IDs for "nextgroup" argument */
315} syn_opt_arg_T;
316
317/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318 * The next possible match in the current line for any pattern is remembered,
319 * to avoid having to try for a match in each column.
320 * If next_match_idx == -1, not tried (in this line) yet.
321 * If next_match_col == MAXCOL, no match found in this line.
322 * (All end positions have the column of the char after the end)
323 */
324static int next_match_col; /* column for start of next match */
325static lpos_T next_match_m_endpos; /* position for end of next match */
326static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
327static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
328static int next_match_idx; /* index of matched item */
329static long next_match_flags; /* flags for next match */
330static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
331static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
332static int next_match_end_idx; /* ID of group for end pattn or zero */
333static reg_extmatch_T *next_match_extmatch = NULL;
334
335/*
336 * A state stack is an array of integers or stateitem_T, stored in a
337 * garray_T. A state stack is invalid if it's itemsize entry is zero.
338 */
339#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
340#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
341
342/*
343 * The current state (within the line) of the recognition engine.
344 * When current_state.ga_itemsize is 0 the current state is invalid.
345 */
346static win_T *syn_win; /* current window for highlighting */
347static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200348static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349static linenr_T current_lnum = 0; /* lnum of current state */
350static colnr_T current_col = 0; /* column of current state */
351static int current_state_stored = 0; /* TRUE if stored current state
352 * after setting current_finished */
353static int current_finished = 0; /* current line has been finished */
354static garray_T current_state /* current stack of state_items */
355 = {0, 0, 0, 0, NULL};
356static short *current_next_list = NULL; /* when non-zero, nextgroup list */
357static int current_next_flags = 0; /* flags for current_next_list */
358static int current_line_id = 0; /* unique number for current line */
359
360#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
361
362static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
363static int syn_match_linecont __ARGS((linenr_T lnum));
364static void syn_start_line __ARGS((void));
365static void syn_update_ends __ARGS((int startofline));
366static void syn_stack_alloc __ARGS((void));
367static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200368static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000370static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371static void load_current_state __ARGS((synstate_T *from));
372static void invalidate_current_state __ARGS((void));
373static int syn_stack_equal __ARGS((synstate_T *sp));
374static void validate_current_state __ARGS((void));
375static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000376static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377static int did_match_already __ARGS((int idx, garray_T *gap));
378static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
379static void check_state_ends __ARGS((void));
380static void update_si_attr __ARGS((int idx));
381static void check_keepend __ARGS((void));
382static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
383static short *copy_id_list __ARGS((short *list));
384static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
385static int push_current_state __ARGS((int idx));
386static void pop_current_state __ARGS((void));
387
Bram Moolenaar860cae12010-06-05 23:22:07 +0200388static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000389static 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));
390static void clear_syn_state __ARGS((synstate_T *p));
391static void clear_current_state __ARGS((void));
392
393static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
394static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
395static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
396static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
397static char_u *syn_getcurline __ARGS((void));
398static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200399static 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 +0000400static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000401static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200403static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
404static void syn_clear_pattern __ARGS((synblock_T *block, int i));
405static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000406static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200407static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408static void syn_clear_one __ARGS((int id, int syncing));
409static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
410static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
411static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
412static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
413static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
414static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
415static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
416static void syn_lines_msg __ARGS((void));
417static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200418static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419static void syn_list_one __ARGS((int id, int syncing, int link_only));
420static void syn_list_cluster __ARGS((int id));
421static void put_id_list __ARGS((char_u *name, short *list, int attr));
422static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000423static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
424static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
425static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200426static 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 +0000427static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200428static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
430static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
431static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
432static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
433#ifdef __BORLANDC__
434static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
435#else
436static int syn_compare_stub __ARGS((const void *v1, const void *v2));
437#endif
438static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
439static int syn_scl_name2id __ARGS((char_u *name));
440static int syn_scl_namen2id __ARGS((char_u *linep, int len));
441static int syn_check_cluster __ARGS((char_u *pp, int len));
442static int syn_add_cluster __ARGS((char_u *name));
443static void init_syn_patterns __ARGS((void));
444static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
445static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
446static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
447static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
448static void syn_incl_toplevel __ARGS((int id, int *flagsp));
449
450/*
451 * Start the syntax recognition for a line. This function is normally called
452 * from the screen updating, once for each displayed line.
453 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
454 * it. Careful: curbuf and curwin are likely to point to another buffer and
455 * window.
456 */
457 void
458syntax_start(wp, lnum)
459 win_T *wp;
460 linenr_T lnum;
461{
462 synstate_T *p;
463 synstate_T *last_valid = NULL;
464 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000465 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000466 linenr_T parsed_lnum;
467 linenr_T first_stored;
468 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000469 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470
Bram Moolenaar071d4272004-06-13 20:20:40 +0000471 /*
472 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000473 * Also do this when a change was made, the current state may be invalid
474 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200476 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 {
478 invalidate_current_state();
479 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200480 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000482 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 syn_win = wp;
484
485 /*
486 * Allocate syntax stack when needed.
487 */
488 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200489 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000490 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200491 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492
493 /*
494 * If the state of the end of the previous line is useful, store it.
495 */
496 if (VALID_STATE(&current_state)
497 && current_lnum < lnum
498 && current_lnum < syn_buf->b_ml.ml_line_count)
499 {
500 (void)syn_finish_line(FALSE);
501 if (!current_state_stored)
502 {
503 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000504 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505 }
506
507 /*
508 * If the current_lnum is now the same as "lnum", keep the current
509 * state (this happens very often!). Otherwise invalidate
510 * current_state and figure it out below.
511 */
512 if (current_lnum != lnum)
513 invalidate_current_state();
514 }
515 else
516 invalidate_current_state();
517
518 /*
519 * Try to synchronize from a saved state in b_sst_array[].
520 * Only do this if lnum is not before and not to far beyond a saved state.
521 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200522 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200525 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 {
527 if (p->sst_lnum > lnum)
528 break;
529 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
530 {
531 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200532 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 last_min_valid = p;
534 }
535 }
536 if (last_min_valid != NULL)
537 load_current_state(last_min_valid);
538 }
539
540 /*
541 * If "lnum" is before or far beyond a line with a saved state, need to
542 * re-synchronize.
543 */
544 if (INVALID_STATE(&current_state))
545 {
546 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200547 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 }
549 else
550 first_stored = current_lnum;
551
552 /*
553 * Advance from the sync point or saved state until the current line.
554 * Save some entries for syncing with later on.
555 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200556 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000557 dist = 999999;
558 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200559 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560 while (current_lnum < lnum)
561 {
562 syn_start_line();
563 (void)syn_finish_line(FALSE);
564 ++current_lnum;
565
566 /* If we parsed at least "minlines" lines or started at a valid
567 * state, the current state is considered valid. */
568 if (current_lnum >= first_stored)
569 {
570 /* Check if the saved state entry is for the current line and is
571 * equal to the current state. If so, then validate all saved
572 * states that depended on a change before the parsed line. */
573 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000574 prev = syn_stack_find_entry(current_lnum - 1);
575 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200576 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000577 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000578 sp = prev;
579 while (sp != NULL && sp->sst_lnum < current_lnum)
580 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 if (sp != NULL
582 && sp->sst_lnum == current_lnum
583 && syn_stack_equal(sp))
584 {
585 parsed_lnum = current_lnum;
586 prev = sp;
587 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
588 {
589 if (sp->sst_lnum <= lnum)
590 /* valid state before desired line, use this one */
591 prev = sp;
592 else if (sp->sst_change_lnum == 0)
593 /* past saved states depending on change, break here. */
594 break;
595 sp->sst_change_lnum = 0;
596 sp = sp->sst_next;
597 }
598 load_current_state(prev);
599 }
600 /* Store the state at this line when it's the first one, the line
601 * where we start parsing, or some distance from the previously
602 * saved state. But only when parsed at least 'minlines'. */
603 else if (prev == NULL
604 || current_lnum == lnum
605 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000606 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607 }
608
609 /* This can take a long time: break when CTRL-C pressed. The current
610 * state will be wrong then. */
611 line_breakcheck();
612 if (got_int)
613 {
614 current_lnum = lnum;
615 break;
616 }
617 }
618
619 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620}
621
622/*
623 * We cannot simply discard growarrays full of state_items or buf_states; we
624 * have to manually release their extmatch pointers first.
625 */
626 static void
627clear_syn_state(p)
628 synstate_T *p;
629{
630 int i;
631 garray_T *gap;
632
633 if (p->sst_stacksize > SST_FIX_STATES)
634 {
635 gap = &(p->sst_union.sst_ga);
636 for (i = 0; i < gap->ga_len; i++)
637 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
638 ga_clear(gap);
639 }
640 else
641 {
642 for (i = 0; i < p->sst_stacksize; i++)
643 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
644 }
645}
646
647/*
648 * Cleanup the current_state stack.
649 */
650 static void
651clear_current_state()
652{
653 int i;
654 stateitem_T *sip;
655
656 sip = (stateitem_T *)(current_state.ga_data);
657 for (i = 0; i < current_state.ga_len; i++)
658 unref_extmatch(sip[i].si_extmatch);
659 ga_clear(&current_state);
660}
661
662/*
663 * Try to find a synchronisation point for line "lnum".
664 *
665 * This sets current_lnum and the current state. One of three methods is
666 * used:
667 * 1. Search backwards for the end of a C-comment.
668 * 2. Search backwards for given sync patterns.
669 * 3. Simply start on a given number of lines above "lnum".
670 */
671 static void
672syn_sync(wp, start_lnum, last_valid)
673 win_T *wp;
674 linenr_T start_lnum;
675 synstate_T *last_valid;
676{
677 buf_T *curbuf_save;
678 win_T *curwin_save;
679 pos_T cursor_save;
680 int idx;
681 linenr_T lnum;
682 linenr_T end_lnum;
683 linenr_T break_lnum;
684 int had_sync_point;
685 stateitem_T *cur_si;
686 synpat_T *spp;
687 char_u *line;
688 int found_flags = 0;
689 int found_match_idx = 0;
690 linenr_T found_current_lnum = 0;
691 int found_current_col= 0;
692 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000693 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694
695 /*
696 * Clear any current state that might be hanging around.
697 */
698 invalidate_current_state();
699
700 /*
701 * Start at least "minlines" back. Default starting point for parsing is
702 * there.
703 * Start further back, to avoid that scrolling backwards will result in
704 * resyncing for every line. Now it resyncs only one out of N lines,
705 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
706 * Watch out for overflow when minlines is MAXLNUM.
707 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200708 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709 start_lnum = 1;
710 else
711 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200712 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200714 else if (syn_block->b_syn_sync_minlines < 10)
715 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200717 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
718 if (syn_block->b_syn_sync_maxlines != 0
719 && lnum > syn_block->b_syn_sync_maxlines)
720 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000721 if (lnum >= start_lnum)
722 start_lnum = 1;
723 else
724 start_lnum -= lnum;
725 }
726 current_lnum = start_lnum;
727
728 /*
729 * 1. Search backwards for the end of a C-style comment.
730 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200731 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000732 {
733 /* Need to make syn_buf the current buffer for a moment, to be able to
734 * use find_start_comment(). */
735 curwin_save = curwin;
736 curwin = wp;
737 curbuf_save = curbuf;
738 curbuf = syn_buf;
739
740 /*
741 * Skip lines that end in a backslash.
742 */
743 for ( ; start_lnum > 1; --start_lnum)
744 {
745 line = ml_get(start_lnum - 1);
746 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
747 break;
748 }
749 current_lnum = start_lnum;
750
751 /* set cursor to start of search */
752 cursor_save = wp->w_cursor;
753 wp->w_cursor.lnum = start_lnum;
754 wp->w_cursor.col = 0;
755
756 /*
757 * If the line is inside a comment, need to find the syntax item that
758 * defines the comment.
759 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
760 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200761 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200763 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
764 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
765 == syn_block->b_syn_sync_id
766 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 {
768 validate_current_state();
769 if (push_current_state(idx) == OK)
770 update_si_attr(current_state.ga_len - 1);
771 break;
772 }
773 }
774
775 /* restore cursor and buffer */
776 wp->w_cursor = cursor_save;
777 curwin = curwin_save;
778 curbuf = curbuf_save;
779 }
780
781 /*
782 * 2. Search backwards for given sync patterns.
783 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200784 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200786 if (syn_block->b_syn_sync_maxlines != 0
787 && start_lnum > syn_block->b_syn_sync_maxlines)
788 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000789 else
790 break_lnum = 0;
791
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000792 found_m_endpos.lnum = 0;
793 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 end_lnum = start_lnum;
795 lnum = start_lnum;
796 while (--lnum > break_lnum)
797 {
798 /* This can take a long time: break when CTRL-C pressed. */
799 line_breakcheck();
800 if (got_int)
801 {
802 invalidate_current_state();
803 current_lnum = start_lnum;
804 break;
805 }
806
807 /* Check if we have run into a valid saved state stack now. */
808 if (last_valid != NULL && lnum == last_valid->sst_lnum)
809 {
810 load_current_state(last_valid);
811 break;
812 }
813
814 /*
815 * Check if the previous line has the line-continuation pattern.
816 */
817 if (lnum > 1 && syn_match_linecont(lnum - 1))
818 continue;
819
820 /*
821 * Start with nothing on the state stack
822 */
823 validate_current_state();
824
825 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
826 {
827 syn_start_line();
828 for (;;)
829 {
830 had_sync_point = syn_finish_line(TRUE);
831 /*
832 * When a sync point has been found, remember where, and
833 * continue to look for another one, further on in the line.
834 */
835 if (had_sync_point && current_state.ga_len)
836 {
837 cur_si = &CUR_STATE(current_state.ga_len - 1);
838 if (cur_si->si_m_endpos.lnum > start_lnum)
839 {
840 /* ignore match that goes to after where started */
841 current_lnum = end_lnum;
842 break;
843 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000844 if (cur_si->si_idx < 0)
845 {
846 /* Cannot happen? */
847 found_flags = 0;
848 found_match_idx = KEYWORD_IDX;
849 }
850 else
851 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200852 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000853 found_flags = spp->sp_flags;
854 found_match_idx = spp->sp_sync_idx;
855 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000856 found_current_lnum = current_lnum;
857 found_current_col = current_col;
858 found_m_endpos = cur_si->si_m_endpos;
859 /*
860 * Continue after the match (be aware of a zero-length
861 * match).
862 */
863 if (found_m_endpos.lnum > current_lnum)
864 {
865 current_lnum = found_m_endpos.lnum;
866 current_col = found_m_endpos.col;
867 if (current_lnum >= end_lnum)
868 break;
869 }
870 else if (found_m_endpos.col > current_col)
871 current_col = found_m_endpos.col;
872 else
873 ++current_col;
874
875 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000876 * an item that ends here, need to do that now. Be
877 * careful not to go past the NUL. */
878 prev_current_col = current_col;
879 if (syn_getcurline()[current_col] != NUL)
880 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000881 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000882 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000883 }
884 else
885 break;
886 }
887 }
888
889 /*
890 * If a sync point was encountered, break here.
891 */
892 if (found_flags)
893 {
894 /*
895 * Put the item that was specified by the sync point on the
896 * state stack. If there was no item specified, make the
897 * state stack empty.
898 */
899 clear_current_state();
900 if (found_match_idx >= 0
901 && push_current_state(found_match_idx) == OK)
902 update_si_attr(current_state.ga_len - 1);
903
904 /*
905 * When using "grouphere", continue from the sync point
906 * match, until the end of the line. Parsing starts at
907 * the next line.
908 * For "groupthere" the parsing starts at start_lnum.
909 */
910 if (found_flags & HL_SYNC_HERE)
911 {
912 if (current_state.ga_len)
913 {
914 cur_si = &CUR_STATE(current_state.ga_len - 1);
915 cur_si->si_h_startpos.lnum = found_current_lnum;
916 cur_si->si_h_startpos.col = found_current_col;
917 update_si_end(cur_si, (int)current_col, TRUE);
918 check_keepend();
919 }
920 current_col = found_m_endpos.col;
921 current_lnum = found_m_endpos.lnum;
922 (void)syn_finish_line(FALSE);
923 ++current_lnum;
924 }
925 else
926 current_lnum = start_lnum;
927
928 break;
929 }
930
931 end_lnum = lnum;
932 invalidate_current_state();
933 }
934
935 /* Ran into start of the file or exceeded maximum number of lines */
936 if (lnum <= break_lnum)
937 {
938 invalidate_current_state();
939 current_lnum = break_lnum + 1;
940 }
941 }
942
943 validate_current_state();
944}
945
946/*
947 * Return TRUE if the line-continuation pattern matches in line "lnum".
948 */
949 static int
950syn_match_linecont(lnum)
951 linenr_T lnum;
952{
953 regmmatch_T regmatch;
954
Bram Moolenaar860cae12010-06-05 23:22:07 +0200955 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200957 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
958 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000959 return syn_regexec(&regmatch, lnum, (colnr_T)0);
960 }
961 return FALSE;
962}
963
964/*
965 * Prepare the current state for the start of a line.
966 */
967 static void
968syn_start_line()
969{
970 current_finished = FALSE;
971 current_col = 0;
972
973 /*
974 * Need to update the end of a start/skip/end that continues from the
975 * previous line and regions that have "keepend".
976 */
977 if (current_state.ga_len > 0)
978 syn_update_ends(TRUE);
979
980 next_match_idx = -1;
981 ++current_line_id;
982}
983
984/*
985 * Check for items in the stack that need their end updated.
986 * When "startofline" is TRUE the last item is always updated.
987 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
988 */
989 static void
990syn_update_ends(startofline)
991 int startofline;
992{
993 stateitem_T *cur_si;
994 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000995 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996
997 if (startofline)
998 {
999 /* Check for a match carried over from a previous line with a
1000 * contained region. The match ends as soon as the region ends. */
1001 for (i = 0; i < current_state.ga_len; ++i)
1002 {
1003 cur_si = &CUR_STATE(i);
1004 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001005 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006 == SPTYPE_MATCH
1007 && cur_si->si_m_endpos.lnum < current_lnum)
1008 {
1009 cur_si->si_flags |= HL_MATCHCONT;
1010 cur_si->si_m_endpos.lnum = 0;
1011 cur_si->si_m_endpos.col = 0;
1012 cur_si->si_h_endpos = cur_si->si_m_endpos;
1013 cur_si->si_ends = TRUE;
1014 }
1015 }
1016 }
1017
1018 /*
1019 * Need to update the end of a start/skip/end that continues from the
1020 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001021 * influence contained items. If we've just removed "extend"
1022 * (startofline == 0) then we should update ends of normal regions
1023 * contained inside "keepend" because "extend" could have extended
1024 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001025 * Then check for items ending in column 0.
1026 */
1027 i = current_state.ga_len - 1;
1028 if (keepend_level >= 0)
1029 for ( ; i > keepend_level; --i)
1030 if (CUR_STATE(i).si_flags & HL_EXTEND)
1031 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001032
1033 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034 for ( ; i < current_state.ga_len; ++i)
1035 {
1036 cur_si = &CUR_STATE(i);
1037 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001038 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 || (i == current_state.ga_len - 1 && startofline))
1040 {
1041 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1042 cur_si->si_h_startpos.lnum = current_lnum;
1043
1044 if (!(cur_si->si_flags & HL_MATCHCONT))
1045 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001046
1047 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1048 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 }
1050 }
1051 check_keepend();
1052 check_state_ends();
1053}
1054
1055/****************************************
1056 * Handling of the state stack cache.
1057 */
1058
1059/*
1060 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1061 *
1062 * To speed up syntax highlighting, the state stack for the start of some
1063 * lines is cached. These entries can be used to start parsing at that point.
1064 *
1065 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1066 * valid entries. b_sst_first points to the first one, then follow sst_next.
1067 * The entries are sorted on line number. The first entry is often for line 2
1068 * (line 1 always starts with an empty stack).
1069 * There is also a list for free entries. This construction is used to avoid
1070 * having to allocate and free memory blocks too often.
1071 *
1072 * When making changes to the buffer, this is logged in b_mod_*. When calling
1073 * update_screen() to update the display, it will call
1074 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1075 * entries. The entries which are inside the changed area are removed,
1076 * because they must be recomputed. Entries below the changed have their line
1077 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1078 * set to indicate that a check must be made if the changed lines would change
1079 * the cached entry.
1080 *
1081 * When later displaying lines, an entry is stored for each line. Displayed
1082 * lines are likely to be displayed again, in which case the state at the
1083 * start of the line is needed.
1084 * For not displayed lines, an entry is stored for every so many lines. These
1085 * entries will be used e.g., when scrolling backwards. The distance between
1086 * entries depends on the number of lines in the buffer. For small buffers
1087 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1088 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1089 */
1090
Bram Moolenaar860cae12010-06-05 23:22:07 +02001091 static void
1092syn_stack_free_block(block)
1093 synblock_T *block;
1094{
1095 synstate_T *p;
1096
1097 if (block->b_sst_array != NULL)
1098 {
1099 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1100 clear_syn_state(p);
1101 vim_free(block->b_sst_array);
1102 block->b_sst_array = NULL;
1103 block->b_sst_len = 0;
1104 }
1105}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106/*
1107 * Free b_sst_array[] for buffer "buf".
1108 * Used when syntax items changed to force resyncing everywhere.
1109 */
1110 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001111syn_stack_free_all(block)
1112 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 win_T *wp;
1115
Bram Moolenaar860cae12010-06-05 23:22:07 +02001116 syn_stack_free_block(block);
1117
1118
Bram Moolenaar071d4272004-06-13 20:20:40 +00001119#ifdef FEAT_FOLDING
1120 /* When using "syntax" fold method, must update all folds. */
1121 FOR_ALL_WINDOWS(wp)
1122 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001123 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 foldUpdateAll(wp);
1125 }
1126#endif
1127}
1128
1129/*
1130 * Allocate the syntax state stack for syn_buf when needed.
1131 * If the number of entries in b_sst_array[] is much too big or a bit too
1132 * small, reallocate it.
1133 * Also used to allocate b_sst_array[] for the first time.
1134 */
1135 static void
1136syn_stack_alloc()
1137{
1138 long len;
1139 synstate_T *to, *from;
1140 synstate_T *sstp;
1141
1142 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1143 if (len < SST_MIN_ENTRIES)
1144 len = SST_MIN_ENTRIES;
1145 else if (len > SST_MAX_ENTRIES)
1146 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001147 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148 {
1149 /* Allocate 50% too much, to avoid reallocating too often. */
1150 len = syn_buf->b_ml.ml_line_count;
1151 len = (len + len / 2) / SST_DIST + Rows * 2;
1152 if (len < SST_MIN_ENTRIES)
1153 len = SST_MIN_ENTRIES;
1154 else if (len > SST_MAX_ENTRIES)
1155 len = SST_MAX_ENTRIES;
1156
Bram Moolenaar860cae12010-06-05 23:22:07 +02001157 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 {
1159 /* When shrinking the array, cleanup the existing stack.
1160 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001161 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 && syn_stack_cleanup())
1163 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001164 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1165 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166 }
1167
1168 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1169 if (sstp == NULL) /* out of memory! */
1170 return;
1171
1172 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 {
1175 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001176 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001177 from = from->sst_next)
1178 {
1179 ++to;
1180 *to = *from;
1181 to->sst_next = to + 1;
1182 }
1183 }
1184 if (to != sstp - 1)
1185 {
1186 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 syn_block->b_sst_first = sstp;
1188 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 }
1190 else
1191 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001192 syn_block->b_sst_first = NULL;
1193 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194 }
1195
1196 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001197 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 while (++to < sstp + len)
1199 to->sst_next = to + 1;
1200 (sstp + len - 1)->sst_next = NULL;
1201
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 vim_free(syn_block->b_sst_array);
1203 syn_block->b_sst_array = sstp;
1204 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205 }
1206}
1207
1208/*
1209 * Check for changes in a buffer to affect stored syntax states. Uses the
1210 * b_mod_* fields.
1211 * Called from update_screen(), before screen is being updated, once for each
1212 * displayed buffer.
1213 */
1214 void
1215syn_stack_apply_changes(buf)
1216 buf_T *buf;
1217{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001218 win_T *wp;
1219
1220 syn_stack_apply_changes_block(&buf->b_s, buf);
1221
1222 FOR_ALL_WINDOWS(wp)
1223 {
1224 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1225 syn_stack_apply_changes_block(wp->w_s, buf);
1226 }
1227}
1228
1229 static void
1230syn_stack_apply_changes_block(block, buf)
1231 synblock_T *block;
1232 buf_T *buf;
1233{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 synstate_T *p, *prev, *np;
1235 linenr_T n;
1236
Bram Moolenaar860cae12010-06-05 23:22:07 +02001237 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 return;
1239
1240 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001243 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 {
1245 n = p->sst_lnum + buf->b_mod_xlines;
1246 if (n <= buf->b_mod_bot)
1247 {
1248 /* this state is inside the changed area, remove it */
1249 np = p->sst_next;
1250 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 else
1253 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001254 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 p = np;
1256 continue;
1257 }
1258 /* This state is below the changed area. Remember the line
1259 * that needs to be parsed before this entry can be made valid
1260 * again. */
1261 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1262 {
1263 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1264 p->sst_change_lnum += buf->b_mod_xlines;
1265 else
1266 p->sst_change_lnum = buf->b_mod_top;
1267 }
1268 if (p->sst_change_lnum == 0
1269 || p->sst_change_lnum < buf->b_mod_bot)
1270 p->sst_change_lnum = buf->b_mod_bot;
1271
1272 p->sst_lnum = n;
1273 }
1274 prev = p;
1275 p = p->sst_next;
1276 }
1277}
1278
1279/*
1280 * Reduce the number of entries in the state stack for syn_buf.
1281 * Returns TRUE if at least one entry was freed.
1282 */
1283 static int
1284syn_stack_cleanup()
1285{
1286 synstate_T *p, *prev;
1287 disptick_T tick;
1288 int above;
1289 int dist;
1290 int retval = FALSE;
1291
Bram Moolenaar860cae12010-06-05 23:22:07 +02001292 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293 return retval;
1294
1295 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001297 dist = 999999;
1298 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001299 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300
1301 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001302 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303 * be removed. Set "above" when the "tick" for the oldest entry is above
1304 * "b_sst_lasttick" (the display tick wraps around).
1305 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001306 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001307 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001308 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1310 {
1311 if (prev->sst_lnum + dist > p->sst_lnum)
1312 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001314 {
1315 if (!above || p->sst_tick < tick)
1316 tick = p->sst_tick;
1317 above = TRUE;
1318 }
1319 else if (!above && p->sst_tick < tick)
1320 tick = p->sst_tick;
1321 }
1322 }
1323
1324 /*
1325 * Go through the list to make the entries for the oldest tick at an
1326 * interval of several lines.
1327 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1330 {
1331 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1332 {
1333 /* Move this entry from used list to free list */
1334 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001335 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001336 p = prev;
1337 retval = TRUE;
1338 }
1339 }
1340 return retval;
1341}
1342
1343/*
1344 * Free the allocated memory for a syn_state item.
1345 * Move the entry into the free list.
1346 */
1347 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001348syn_stack_free_entry(block, p)
1349 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 synstate_T *p;
1351{
1352 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001353 p->sst_next = block->b_sst_firstfree;
1354 block->b_sst_firstfree = p;
1355 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356}
1357
1358/*
1359 * Find an entry in the list of state stacks at or before "lnum".
1360 * Returns NULL when there is no entry or the first entry is after "lnum".
1361 */
1362 static synstate_T *
1363syn_stack_find_entry(lnum)
1364 linenr_T lnum;
1365{
1366 synstate_T *p, *prev;
1367
1368 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001369 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370 {
1371 if (p->sst_lnum == lnum)
1372 return p;
1373 if (p->sst_lnum > lnum)
1374 break;
1375 }
1376 return prev;
1377}
1378
1379/*
1380 * Try saving the current state in b_sst_array[].
1381 * The current state must be valid for the start of the current_lnum line!
1382 */
1383 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001384store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385{
1386 int i;
1387 synstate_T *p;
1388 bufstate_T *bp;
1389 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001390 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391
1392 /*
1393 * If the current state contains a start or end pattern that continues
1394 * from the previous line, we can't use it. Don't store it then.
1395 */
1396 for (i = current_state.ga_len - 1; i >= 0; --i)
1397 {
1398 cur_si = &CUR_STATE(i);
1399 if (cur_si->si_h_startpos.lnum >= current_lnum
1400 || cur_si->si_m_endpos.lnum >= current_lnum
1401 || cur_si->si_h_endpos.lnum >= current_lnum
1402 || (cur_si->si_end_idx
1403 && cur_si->si_eoe_pos.lnum >= current_lnum))
1404 break;
1405 }
1406 if (i >= 0)
1407 {
1408 if (sp != NULL)
1409 {
1410 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001411 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001413 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414 else
1415 {
1416 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001417 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 if (p->sst_next == sp)
1419 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001420 if (p != NULL) /* just in case */
1421 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001423 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001424 sp = NULL;
1425 }
1426 }
1427 else if (sp == NULL || sp->sst_lnum != current_lnum)
1428 {
1429 /*
1430 * Add a new entry
1431 */
1432 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001433 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 {
1435 (void)syn_stack_cleanup();
1436 /* "sp" may have been moved to the freelist now */
1437 sp = syn_stack_find_entry(current_lnum);
1438 }
1439 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 sp = NULL;
1442 else
1443 {
1444 /* Take the first item from the free list and put it in the used
1445 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001446 p = syn_block->b_sst_firstfree;
1447 syn_block->b_sst_firstfree = p->sst_next;
1448 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449 if (sp == NULL)
1450 {
1451 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001452 p->sst_next = syn_block->b_sst_first;
1453 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454 }
1455 else
1456 {
1457 /* insert in list after *sp */
1458 p->sst_next = sp->sst_next;
1459 sp->sst_next = p;
1460 }
1461 sp = p;
1462 sp->sst_stacksize = 0;
1463 sp->sst_lnum = current_lnum;
1464 }
1465 }
1466 if (sp != NULL)
1467 {
1468 /* When overwriting an existing state stack, clear it first */
1469 clear_syn_state(sp);
1470 sp->sst_stacksize = current_state.ga_len;
1471 if (current_state.ga_len > SST_FIX_STATES)
1472 {
1473 /* Need to clear it, might be something remaining from when the
1474 * length was less than SST_FIX_STATES. */
1475 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1476 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1477 sp->sst_stacksize = 0;
1478 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001480 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1481 }
1482 else
1483 bp = sp->sst_union.sst_stack;
1484 for (i = 0; i < sp->sst_stacksize; ++i)
1485 {
1486 bp[i].bs_idx = CUR_STATE(i).si_idx;
1487 bp[i].bs_flags = CUR_STATE(i).si_flags;
1488 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1489 }
1490 sp->sst_next_flags = current_next_flags;
1491 sp->sst_next_list = current_next_list;
1492 sp->sst_tick = display_tick;
1493 sp->sst_change_lnum = 0;
1494 }
1495 current_state_stored = TRUE;
1496 return sp;
1497}
1498
1499/*
1500 * Copy a state stack from "from" in b_sst_array[] to current_state;
1501 */
1502 static void
1503load_current_state(from)
1504 synstate_T *from;
1505{
1506 int i;
1507 bufstate_T *bp;
1508
1509 clear_current_state();
1510 validate_current_state();
1511 keepend_level = -1;
1512 if (from->sst_stacksize
1513 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1514 {
1515 if (from->sst_stacksize > SST_FIX_STATES)
1516 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1517 else
1518 bp = from->sst_union.sst_stack;
1519 for (i = 0; i < from->sst_stacksize; ++i)
1520 {
1521 CUR_STATE(i).si_idx = bp[i].bs_idx;
1522 CUR_STATE(i).si_flags = bp[i].bs_flags;
1523 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1524 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1525 keepend_level = i;
1526 CUR_STATE(i).si_ends = FALSE;
1527 CUR_STATE(i).si_m_lnum = 0;
1528 if (CUR_STATE(i).si_idx >= 0)
1529 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001530 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001531 else
1532 CUR_STATE(i).si_next_list = NULL;
1533 update_si_attr(i);
1534 }
1535 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 }
1537 current_next_list = from->sst_next_list;
1538 current_next_flags = from->sst_next_flags;
1539 current_lnum = from->sst_lnum;
1540}
1541
1542/*
1543 * Compare saved state stack "*sp" with the current state.
1544 * Return TRUE when they are equal.
1545 */
1546 static int
1547syn_stack_equal(sp)
1548 synstate_T *sp;
1549{
1550 int i, j;
1551 bufstate_T *bp;
1552 reg_extmatch_T *six, *bsx;
1553
1554 /* First a quick check if the stacks have the same size end nextlist. */
1555 if (sp->sst_stacksize == current_state.ga_len
1556 && sp->sst_next_list == current_next_list)
1557 {
1558 /* Need to compare all states on both stacks. */
1559 if (sp->sst_stacksize > SST_FIX_STATES)
1560 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1561 else
1562 bp = sp->sst_union.sst_stack;
1563
1564 for (i = current_state.ga_len; --i >= 0; )
1565 {
1566 /* If the item has another index the state is different. */
1567 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1568 break;
1569 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1570 {
1571 /* When the extmatch pointers are different, the strings in
1572 * them can still be the same. Check if the extmatch
1573 * references are equal. */
1574 bsx = bp[i].bs_extmatch;
1575 six = CUR_STATE(i).si_extmatch;
1576 /* If one of the extmatch pointers is NULL the states are
1577 * different. */
1578 if (bsx == NULL || six == NULL)
1579 break;
1580 for (j = 0; j < NSUBEXP; ++j)
1581 {
1582 /* Check each referenced match string. They must all be
1583 * equal. */
1584 if (bsx->matches[j] != six->matches[j])
1585 {
1586 /* If the pointer is different it can still be the
1587 * same text. Compare the strings, ignore case when
1588 * the start item has the sp_ic flag set. */
1589 if (bsx->matches[j] == NULL
1590 || six->matches[j] == NULL)
1591 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001592 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 ? MB_STRICMP(bsx->matches[j],
1594 six->matches[j]) != 0
1595 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1596 break;
1597 }
1598 }
1599 if (j != NSUBEXP)
1600 break;
1601 }
1602 }
1603 if (i < 0)
1604 return TRUE;
1605 }
1606 return FALSE;
1607}
1608
1609/*
1610 * We stop parsing syntax above line "lnum". If the stored state at or below
1611 * this line depended on a change before it, it now depends on the line below
1612 * the last parsed line.
1613 * The window looks like this:
1614 * line which changed
1615 * displayed line
1616 * displayed line
1617 * lnum -> line below window
1618 */
1619 void
1620syntax_end_parsing(lnum)
1621 linenr_T lnum;
1622{
1623 synstate_T *sp;
1624
1625 sp = syn_stack_find_entry(lnum);
1626 if (sp != NULL && sp->sst_lnum < lnum)
1627 sp = sp->sst_next;
1628
1629 if (sp != NULL && sp->sst_change_lnum != 0)
1630 sp->sst_change_lnum = lnum;
1631}
1632
1633/*
1634 * End of handling of the state stack.
1635 ****************************************/
1636
1637 static void
1638invalidate_current_state()
1639{
1640 clear_current_state();
1641 current_state.ga_itemsize = 0; /* mark current_state invalid */
1642 current_next_list = NULL;
1643 keepend_level = -1;
1644}
1645
1646 static void
1647validate_current_state()
1648{
1649 current_state.ga_itemsize = sizeof(stateitem_T);
1650 current_state.ga_growsize = 3;
1651}
1652
1653/*
1654 * Return TRUE if the syntax at start of lnum changed since last time.
1655 * This will only be called just after get_syntax_attr() for the previous
1656 * line, to check if the next line needs to be redrawn too.
1657 */
1658 int
1659syntax_check_changed(lnum)
1660 linenr_T lnum;
1661{
1662 int retval = TRUE;
1663 synstate_T *sp;
1664
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665 /*
1666 * Check the state stack when:
1667 * - lnum is just below the previously syntaxed line.
1668 * - lnum is not before the lines with saved states.
1669 * - lnum is not past the lines with saved states.
1670 * - lnum is at or before the last changed line.
1671 */
1672 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1673 {
1674 sp = syn_stack_find_entry(lnum);
1675 if (sp != NULL && sp->sst_lnum == lnum)
1676 {
1677 /*
1678 * finish the previous line (needed when not all of the line was
1679 * drawn)
1680 */
1681 (void)syn_finish_line(FALSE);
1682
1683 /*
1684 * Compare the current state with the previously saved state of
1685 * the line.
1686 */
1687 if (syn_stack_equal(sp))
1688 retval = FALSE;
1689
1690 /*
1691 * Store the current state in b_sst_array[] for later use.
1692 */
1693 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001694 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001695 }
1696 }
1697
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 return retval;
1699}
1700
1701/*
1702 * Finish the current line.
1703 * This doesn't return any attributes, it only gets the state at the end of
1704 * the line. It can start anywhere in the line, as long as the current state
1705 * is valid.
1706 */
1707 static int
1708syn_finish_line(syncing)
1709 int syncing; /* called for syncing */
1710{
1711 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001712 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001713
1714 if (!current_finished)
1715 {
1716 while (!current_finished)
1717 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001718 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719 /*
1720 * When syncing, and found some item, need to check the item.
1721 */
1722 if (syncing && current_state.ga_len)
1723 {
1724 /*
1725 * Check for match with sync item.
1726 */
1727 cur_si = &CUR_STATE(current_state.ga_len - 1);
1728 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001729 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001730 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1731 return TRUE;
1732
1733 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001734 * that ends here, need to do that now. Be careful not to go
1735 * past the NUL. */
1736 prev_current_col = current_col;
1737 if (syn_getcurline()[current_col] != NUL)
1738 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001739 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001740 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741 }
1742 ++current_col;
1743 }
1744 }
1745 return FALSE;
1746}
1747
1748/*
1749 * Return highlight attributes for next character.
1750 * Must first call syntax_start() once for the line.
1751 * "col" is normally 0 for the first use in a line, and increments by one each
1752 * time. It's allowed to skip characters and to stop before the end of the
1753 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001754 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1755 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 */
1757 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001758get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001759 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001760 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001761 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762{
1763 int attr = 0;
1764
Bram Moolenaar349955a2007-08-14 21:07:36 +00001765 if (can_spell != NULL)
1766 /* Default: Only do spelling when there is no @Spell cluster or when
1767 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001768 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1769 ? (syn_block->b_spell_cluster_id == 0)
1770 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001771
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001773 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774 return 0;
1775
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001776 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001777 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001778 {
1779 clear_current_state();
1780#ifdef FEAT_EVAL
1781 current_id = 0;
1782 current_trans_id = 0;
1783#endif
1784 return 0;
1785 }
1786
Bram Moolenaar071d4272004-06-13 20:20:40 +00001787 /* Make sure current_state is valid */
1788 if (INVALID_STATE(&current_state))
1789 validate_current_state();
1790
1791 /*
1792 * Skip from the current column to "col", get the attributes for "col".
1793 */
1794 while (current_col <= col)
1795 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001796 attr = syn_current_attr(FALSE, TRUE, can_spell,
1797 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001798 ++current_col;
1799 }
1800
Bram Moolenaar071d4272004-06-13 20:20:40 +00001801 return attr;
1802}
1803
1804/*
1805 * Get syntax attributes for current_lnum, current_col.
1806 */
1807 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001808syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001809 int syncing; /* When 1: called for syncing */
1810 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001811 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001812 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813{
1814 int syn_id;
1815 lpos_T endpos; /* was: char_u *endp; */
1816 lpos_T hl_startpos; /* was: int hl_startcol; */
1817 lpos_T hl_endpos;
1818 lpos_T eos_pos; /* end-of-start match (start region) */
1819 lpos_T eoe_pos; /* end-of-end pattern */
1820 int end_idx; /* group ID for end pattern */
1821 int idx;
1822 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001823 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824 int startcol;
1825 int endcol;
1826 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001827 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001828 short *next_list;
1829 int found_match; /* found usable match */
1830 static int try_next_column = FALSE; /* must try in next col */
1831 int do_keywords;
1832 regmmatch_T regmatch;
1833 lpos_T pos;
1834 int lc_col;
1835 reg_extmatch_T *cur_extmatch = NULL;
1836 char_u *line; /* current line. NOTE: becomes invalid after
1837 looking for a pattern match! */
1838
1839 /* variables for zero-width matches that have a "nextgroup" argument */
1840 int keep_next_list;
1841 int zero_width_next_list = FALSE;
1842 garray_T zero_width_next_ga;
1843
1844 /*
1845 * No character, no attributes! Past end of line?
1846 * Do try matching with an empty line (could be the start of a region).
1847 */
1848 line = syn_getcurline();
1849 if (line[current_col] == NUL && current_col != 0)
1850 {
1851 /*
1852 * If we found a match after the last column, use it.
1853 */
1854 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1855 && next_match_col != MAXCOL)
1856 (void)push_next_match(NULL);
1857
1858 current_finished = TRUE;
1859 current_state_stored = FALSE;
1860 return 0;
1861 }
1862
1863 /* if the current or next character is NUL, we will finish the line now */
1864 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1865 {
1866 current_finished = TRUE;
1867 current_state_stored = FALSE;
1868 }
1869
1870 /*
1871 * When in the previous column there was a match but it could not be used
1872 * (empty match or already matched in this column) need to try again in
1873 * the next column.
1874 */
1875 if (try_next_column)
1876 {
1877 next_match_idx = -1;
1878 try_next_column = FALSE;
1879 }
1880
1881 /* Only check for keywords when not syncing and there are some. */
1882 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001883 && (syn_block->b_keywtab.ht_used > 0
1884 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001885
1886 /* Init the list of zero-width matches with a nextlist. This is used to
1887 * avoid matching the same item in the same position twice. */
1888 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1889
1890 /*
1891 * Repeat matching keywords and patterns, to find contained items at the
1892 * same column. This stops when there are no extra matches at the current
1893 * column.
1894 */
1895 do
1896 {
1897 found_match = FALSE;
1898 keep_next_list = FALSE;
1899 syn_id = 0;
1900
1901 /*
1902 * 1. Check for a current state.
1903 * Only when there is no current state, or if the current state may
1904 * contain other things, we need to check for keywords and patterns.
1905 * Always need to check for contained items if some item has the
1906 * "containedin" argument (takes extra time!).
1907 */
1908 if (current_state.ga_len)
1909 cur_si = &CUR_STATE(current_state.ga_len - 1);
1910 else
1911 cur_si = NULL;
1912
Bram Moolenaar860cae12010-06-05 23:22:07 +02001913 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914 || cur_si->si_cont_list != NULL)
1915 {
1916 /*
1917 * 2. Check for keywords, if on a keyword char after a non-keyword
1918 * char. Don't do this when syncing.
1919 */
1920 if (do_keywords)
1921 {
1922 line = syn_getcurline();
1923 if (vim_iswordc_buf(line + current_col, syn_buf)
1924 && (current_col == 0
1925 || !vim_iswordc_buf(line + current_col - 1
1926#ifdef FEAT_MBYTE
1927 - (has_mbyte
1928 ? (*mb_head_off)(line, line + current_col - 1)
1929 : 0)
1930#endif
1931 , syn_buf)))
1932 {
1933 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001934 &endcol, &flags, &next_list, cur_si,
1935 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001936 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001937 {
1938 if (push_current_state(KEYWORD_IDX) == OK)
1939 {
1940 cur_si = &CUR_STATE(current_state.ga_len - 1);
1941 cur_si->si_m_startcol = current_col;
1942 cur_si->si_h_startpos.lnum = current_lnum;
1943 cur_si->si_h_startpos.col = 0; /* starts right away */
1944 cur_si->si_m_endpos.lnum = current_lnum;
1945 cur_si->si_m_endpos.col = endcol;
1946 cur_si->si_h_endpos.lnum = current_lnum;
1947 cur_si->si_h_endpos.col = endcol;
1948 cur_si->si_ends = TRUE;
1949 cur_si->si_end_idx = 0;
1950 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001951#ifdef FEAT_CONCEAL
1952 cur_si->si_char = cchar;
1953 if (current_state.ga_len > 1)
1954 cur_si->si_flags |=
1955 CUR_STATE(current_state.ga_len - 2).si_flags
1956 & HL_CONCEAL;
1957#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958 cur_si->si_id = syn_id;
1959 cur_si->si_trans_id = syn_id;
1960 if (flags & HL_TRANSP)
1961 {
1962 if (current_state.ga_len < 2)
1963 {
1964 cur_si->si_attr = 0;
1965 cur_si->si_trans_id = 0;
1966 }
1967 else
1968 {
1969 cur_si->si_attr = CUR_STATE(
1970 current_state.ga_len - 2).si_attr;
1971 cur_si->si_trans_id = CUR_STATE(
1972 current_state.ga_len - 2).si_trans_id;
1973 }
1974 }
1975 else
1976 cur_si->si_attr = syn_id2attr(syn_id);
1977 cur_si->si_cont_list = NULL;
1978 cur_si->si_next_list = next_list;
1979 check_keepend();
1980 }
1981 else
1982 vim_free(next_list);
1983 }
1984 }
1985 }
1986
1987 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001988 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001990 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 {
1992 /*
1993 * If we didn't check for a match yet, or we are past it, check
1994 * for any match with a pattern.
1995 */
1996 if (next_match_idx < 0 || next_match_col < (int)current_col)
1997 {
1998 /*
1999 * Check all relevant patterns for a match at this
2000 * position. This is complicated, because matching with a
2001 * pattern takes quite a bit of time, thus we want to
2002 * avoid doing it when it's not needed.
2003 */
2004 next_match_idx = 0; /* no match in this line yet */
2005 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002006 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002007 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002008 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009 if ( spp->sp_syncing == syncing
2010 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2011 && (spp->sp_type == SPTYPE_MATCH
2012 || spp->sp_type == SPTYPE_START)
2013 && (current_next_list != NULL
2014 ? in_id_list(NULL, current_next_list,
2015 &spp->sp_syn, 0)
2016 : (cur_si == NULL
2017 ? !(spp->sp_flags & HL_CONTAINED)
2018 : in_id_list(cur_si,
2019 cur_si->si_cont_list, &spp->sp_syn,
2020 spp->sp_flags & HL_CONTAINED))))
2021 {
2022 /* If we already tried matching in this line, and
2023 * there isn't a match before next_match_col, skip
2024 * this item. */
2025 if (spp->sp_line_id == current_line_id
2026 && spp->sp_startcol >= next_match_col)
2027 continue;
2028 spp->sp_line_id = current_line_id;
2029
2030 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2031 if (lc_col < 0)
2032 lc_col = 0;
2033
2034 regmatch.rmm_ic = spp->sp_ic;
2035 regmatch.regprog = spp->sp_prog;
2036 if (!syn_regexec(&regmatch, current_lnum,
2037 (colnr_T)lc_col))
2038 {
2039 /* no match in this line, try another one */
2040 spp->sp_startcol = MAXCOL;
2041 continue;
2042 }
2043
2044 /*
2045 * Compute the first column of the match.
2046 */
2047 syn_add_start_off(&pos, &regmatch,
2048 spp, SPO_MS_OFF, -1);
2049 if (pos.lnum > current_lnum)
2050 {
2051 /* must have used end of match in a next line,
2052 * we can't handle that */
2053 spp->sp_startcol = MAXCOL;
2054 continue;
2055 }
2056 startcol = pos.col;
2057
2058 /* remember the next column where this pattern
2059 * matches in the current line */
2060 spp->sp_startcol = startcol;
2061
2062 /*
2063 * If a previously found match starts at a lower
2064 * column number, don't use this one.
2065 */
2066 if (startcol >= next_match_col)
2067 continue;
2068
2069 /*
2070 * If we matched this pattern at this position
2071 * before, skip it. Must retry in the next
2072 * column, because it may match from there.
2073 */
2074 if (did_match_already(idx, &zero_width_next_ga))
2075 {
2076 try_next_column = TRUE;
2077 continue;
2078 }
2079
2080 endpos.lnum = regmatch.endpos[0].lnum;
2081 endpos.col = regmatch.endpos[0].col;
2082
2083 /* Compute the highlight start. */
2084 syn_add_start_off(&hl_startpos, &regmatch,
2085 spp, SPO_HS_OFF, -1);
2086
2087 /* Compute the region start. */
2088 /* Default is to use the end of the match. */
2089 syn_add_end_off(&eos_pos, &regmatch,
2090 spp, SPO_RS_OFF, 0);
2091
2092 /*
2093 * Grab the external submatches before they get
2094 * overwritten. Reference count doesn't change.
2095 */
2096 unref_extmatch(cur_extmatch);
2097 cur_extmatch = re_extmatch_out;
2098 re_extmatch_out = NULL;
2099
2100 flags = 0;
2101 eoe_pos.lnum = 0; /* avoid warning */
2102 eoe_pos.col = 0;
2103 end_idx = 0;
2104 hl_endpos.lnum = 0;
2105
2106 /*
2107 * For a "oneline" the end must be found in the
2108 * same line too. Search for it after the end of
2109 * the match with the start pattern. Set the
2110 * resulting end positions at the same time.
2111 */
2112 if (spp->sp_type == SPTYPE_START
2113 && (spp->sp_flags & HL_ONELINE))
2114 {
2115 lpos_T startpos;
2116
2117 startpos = endpos;
2118 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2119 &flags, &eoe_pos, &end_idx, cur_extmatch);
2120 if (endpos.lnum == 0)
2121 continue; /* not found */
2122 }
2123
2124 /*
2125 * For a "match" the size must be > 0 after the
2126 * end offset needs has been added. Except when
2127 * syncing.
2128 */
2129 else if (spp->sp_type == SPTYPE_MATCH)
2130 {
2131 syn_add_end_off(&hl_endpos, &regmatch, spp,
2132 SPO_HE_OFF, 0);
2133 syn_add_end_off(&endpos, &regmatch, spp,
2134 SPO_ME_OFF, 0);
2135 if (endpos.lnum == current_lnum
2136 && (int)endpos.col + syncing < startcol)
2137 {
2138 /*
2139 * If an empty string is matched, may need
2140 * to try matching again at next column.
2141 */
2142 if (regmatch.startpos[0].col
2143 == regmatch.endpos[0].col)
2144 try_next_column = TRUE;
2145 continue;
2146 }
2147 }
2148
2149 /*
2150 * keep the best match so far in next_match_*
2151 */
2152 /* Highlighting must start after startpos and end
2153 * before endpos. */
2154 if (hl_startpos.lnum == current_lnum
2155 && (int)hl_startpos.col < startcol)
2156 hl_startpos.col = startcol;
2157 limit_pos_zero(&hl_endpos, &endpos);
2158
2159 next_match_idx = idx;
2160 next_match_col = startcol;
2161 next_match_m_endpos = endpos;
2162 next_match_h_endpos = hl_endpos;
2163 next_match_h_startpos = hl_startpos;
2164 next_match_flags = flags;
2165 next_match_eos_pos = eos_pos;
2166 next_match_eoe_pos = eoe_pos;
2167 next_match_end_idx = end_idx;
2168 unref_extmatch(next_match_extmatch);
2169 next_match_extmatch = cur_extmatch;
2170 cur_extmatch = NULL;
2171 }
2172 }
2173 }
2174
2175 /*
2176 * If we found a match at the current column, use it.
2177 */
2178 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2179 {
2180 synpat_T *lspp;
2181
2182 /* When a zero-width item matched which has a nextgroup,
2183 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002184 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002185 if (next_match_m_endpos.lnum == current_lnum
2186 && next_match_m_endpos.col == current_col
2187 && lspp->sp_next_list != NULL)
2188 {
2189 current_next_list = lspp->sp_next_list;
2190 current_next_flags = lspp->sp_flags;
2191 keep_next_list = TRUE;
2192 zero_width_next_list = TRUE;
2193
2194 /* Add the index to a list, so that we can check
2195 * later that we don't match it again (and cause an
2196 * endless loop). */
2197 if (ga_grow(&zero_width_next_ga, 1) == OK)
2198 {
2199 ((int *)(zero_width_next_ga.ga_data))
2200 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201 }
2202 next_match_idx = -1;
2203 }
2204 else
2205 cur_si = push_next_match(cur_si);
2206 found_match = TRUE;
2207 }
2208 }
2209 }
2210
2211 /*
2212 * Handle searching for nextgroup match.
2213 */
2214 if (current_next_list != NULL && !keep_next_list)
2215 {
2216 /*
2217 * If a nextgroup was not found, continue looking for one if:
2218 * - this is an empty line and the "skipempty" option was given
2219 * - we are on white space and the "skipwhite" option was given
2220 */
2221 if (!found_match)
2222 {
2223 line = syn_getcurline();
2224 if (((current_next_flags & HL_SKIPWHITE)
2225 && vim_iswhite(line[current_col]))
2226 || ((current_next_flags & HL_SKIPEMPTY)
2227 && *line == NUL))
2228 break;
2229 }
2230
2231 /*
2232 * If a nextgroup was found: Use it, and continue looking for
2233 * contained matches.
2234 * If a nextgroup was not found: Continue looking for a normal
2235 * match.
2236 * When did set current_next_list for a zero-width item and no
2237 * match was found don't loop (would get stuck).
2238 */
2239 current_next_list = NULL;
2240 next_match_idx = -1;
2241 if (!zero_width_next_list)
2242 found_match = TRUE;
2243 }
2244
2245 } while (found_match);
2246
2247 /*
2248 * Use attributes from the current state, if within its highlighting.
2249 * If not, use attributes from the current-but-one state, etc.
2250 */
2251 current_attr = 0;
2252#ifdef FEAT_EVAL
2253 current_id = 0;
2254 current_trans_id = 0;
2255#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002256#ifdef FEAT_CONCEAL
2257 current_flags = 0;
2258#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 if (cur_si != NULL)
2260 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002261#ifndef FEAT_EVAL
2262 int current_trans_id = 0;
2263#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002264 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2265 {
2266 sip = &CUR_STATE(idx);
2267 if ((current_lnum > sip->si_h_startpos.lnum
2268 || (current_lnum == sip->si_h_startpos.lnum
2269 && current_col >= sip->si_h_startpos.col))
2270 && (sip->si_h_endpos.lnum == 0
2271 || current_lnum < sip->si_h_endpos.lnum
2272 || (current_lnum == sip->si_h_endpos.lnum
2273 && current_col < sip->si_h_endpos.col)))
2274 {
2275 current_attr = sip->si_attr;
2276#ifdef FEAT_EVAL
2277 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002278#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002279 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002280#ifdef FEAT_CONCEAL
2281 current_flags = sip->si_flags;
2282 current_sub_char = sip->si_char;
2283#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284 break;
2285 }
2286 }
2287
Bram Moolenaar217ad922005-03-20 22:37:15 +00002288 if (can_spell != NULL)
2289 {
2290 struct sp_syn sps;
2291
2292 /*
2293 * set "can_spell" to TRUE if spell checking is supposed to be
2294 * done in the current item.
2295 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002296 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002297 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002298 /* There is no @Spell cluster: Do spelling for items without
2299 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002300 if (syn_block->b_nospell_cluster_id == 0
2301 || current_trans_id == 0)
2302 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002303 else
2304 {
2305 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002306 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002307 sps.cont_in_list = NULL;
2308 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2309 }
2310 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002311 else
2312 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002313 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002314 * the @Spell cluster. But not when @NoSpell is also there.
2315 * At the toplevel only spell check when ":syn spell toplevel"
2316 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002317 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002318 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002319 else
2320 {
2321 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002322 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002323 sps.cont_in_list = NULL;
2324 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2325
Bram Moolenaar860cae12010-06-05 23:22:07 +02002326 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002327 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002328 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002329 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2330 *can_spell = FALSE;
2331 }
2332 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002333 }
2334 }
2335
2336
Bram Moolenaar071d4272004-06-13 20:20:40 +00002337 /*
2338 * Check for end of current state (and the states before it) at the
2339 * next column. Don't do this for syncing, because we would miss a
2340 * single character match.
2341 * First check if the current state ends at the current column. It
2342 * may be for an empty match and a containing item might end in the
2343 * current column.
2344 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002345 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002346 {
2347 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002348 if (current_state.ga_len > 0
2349 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 {
2351 ++current_col;
2352 check_state_ends();
2353 --current_col;
2354 }
2355 }
2356 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002357 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002358 /* Default: Only do spelling when there is no @Spell cluster or when
2359 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002360 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2361 ? (syn_block->b_spell_cluster_id == 0)
2362 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002363
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002364 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 if (current_next_list != NULL
2366 && syn_getcurline()[current_col + 1] == NUL
2367 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2368 current_next_list = NULL;
2369
2370 if (zero_width_next_ga.ga_len > 0)
2371 ga_clear(&zero_width_next_ga);
2372
2373 /* No longer need external matches. But keep next_match_extmatch. */
2374 unref_extmatch(re_extmatch_out);
2375 re_extmatch_out = NULL;
2376 unref_extmatch(cur_extmatch);
2377
2378 return current_attr;
2379}
2380
2381
2382/*
2383 * Check if we already matched pattern "idx" at the current column.
2384 */
2385 static int
2386did_match_already(idx, gap)
2387 int idx;
2388 garray_T *gap;
2389{
2390 int i;
2391
2392 for (i = current_state.ga_len; --i >= 0; )
2393 if (CUR_STATE(i).si_m_startcol == (int)current_col
2394 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2395 && CUR_STATE(i).si_idx == idx)
2396 return TRUE;
2397
2398 /* Zero-width matches with a nextgroup argument are not put on the syntax
2399 * stack, and can only be matched once anyway. */
2400 for (i = gap->ga_len; --i >= 0; )
2401 if (((int *)(gap->ga_data))[i] == idx)
2402 return TRUE;
2403
2404 return FALSE;
2405}
2406
2407/*
2408 * Push the next match onto the stack.
2409 */
2410 static stateitem_T *
2411push_next_match(cur_si)
2412 stateitem_T *cur_si;
2413{
2414 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002415#ifdef FEAT_CONCEAL
2416 int save_flags;
2417#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002418
Bram Moolenaar860cae12010-06-05 23:22:07 +02002419 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420
2421 /*
2422 * Push the item in current_state stack;
2423 */
2424 if (push_current_state(next_match_idx) == OK)
2425 {
2426 /*
2427 * If it's a start-skip-end type that crosses lines, figure out how
2428 * much it continues in this line. Otherwise just fill in the length.
2429 */
2430 cur_si = &CUR_STATE(current_state.ga_len - 1);
2431 cur_si->si_h_startpos = next_match_h_startpos;
2432 cur_si->si_m_startcol = current_col;
2433 cur_si->si_m_lnum = current_lnum;
2434 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002435#ifdef FEAT_CONCEAL
2436 cur_si->si_char = spp->sp_char;
2437 if (current_state.ga_len > 1)
2438 cur_si->si_flags |=
2439 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2440#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002441 cur_si->si_next_list = spp->sp_next_list;
2442 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2443 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2444 {
2445 /* Try to find the end pattern in the current line */
2446 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2447 check_keepend();
2448 }
2449 else
2450 {
2451 cur_si->si_m_endpos = next_match_m_endpos;
2452 cur_si->si_h_endpos = next_match_h_endpos;
2453 cur_si->si_ends = TRUE;
2454 cur_si->si_flags |= next_match_flags;
2455 cur_si->si_eoe_pos = next_match_eoe_pos;
2456 cur_si->si_end_idx = next_match_end_idx;
2457 }
2458 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2459 keepend_level = current_state.ga_len - 1;
2460 check_keepend();
2461 update_si_attr(current_state.ga_len - 1);
2462
Bram Moolenaar860cae12010-06-05 23:22:07 +02002463#ifdef FEAT_CONCEAL
2464 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2465#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002466 /*
2467 * If the start pattern has another highlight group, push another item
2468 * on the stack for the start pattern.
2469 */
2470 if ( spp->sp_type == SPTYPE_START
2471 && spp->sp_syn_match_id != 0
2472 && push_current_state(next_match_idx) == OK)
2473 {
2474 cur_si = &CUR_STATE(current_state.ga_len - 1);
2475 cur_si->si_h_startpos = next_match_h_startpos;
2476 cur_si->si_m_startcol = current_col;
2477 cur_si->si_m_lnum = current_lnum;
2478 cur_si->si_m_endpos = next_match_eos_pos;
2479 cur_si->si_h_endpos = next_match_eos_pos;
2480 cur_si->si_ends = TRUE;
2481 cur_si->si_end_idx = 0;
2482 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002483#ifdef FEAT_CONCEAL
2484 cur_si->si_flags |= save_flags;
2485 if (cur_si->si_flags & HL_CONCEALENDS)
2486 cur_si->si_flags |= HL_CONCEAL;
2487#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002488 cur_si->si_next_list = NULL;
2489 check_keepend();
2490 update_si_attr(current_state.ga_len - 1);
2491 }
2492 }
2493
2494 next_match_idx = -1; /* try other match next time */
2495
2496 return cur_si;
2497}
2498
2499/*
2500 * Check for end of current state (and the states before it).
2501 */
2502 static void
2503check_state_ends()
2504{
2505 stateitem_T *cur_si;
2506 int had_extend = FALSE;
2507
2508 cur_si = &CUR_STATE(current_state.ga_len - 1);
2509 for (;;)
2510 {
2511 if (cur_si->si_ends
2512 && (cur_si->si_m_endpos.lnum < current_lnum
2513 || (cur_si->si_m_endpos.lnum == current_lnum
2514 && cur_si->si_m_endpos.col <= current_col)))
2515 {
2516 /*
2517 * If there is an end pattern group ID, highlight the end pattern
2518 * now. No need to pop the current item from the stack.
2519 * Only do this if the end pattern continues beyond the current
2520 * position.
2521 */
2522 if (cur_si->si_end_idx
2523 && (cur_si->si_eoe_pos.lnum > current_lnum
2524 || (cur_si->si_eoe_pos.lnum == current_lnum
2525 && cur_si->si_eoe_pos.col > current_col)))
2526 {
2527 cur_si->si_idx = cur_si->si_end_idx;
2528 cur_si->si_end_idx = 0;
2529 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2530 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2531 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002532#ifdef FEAT_CONCEAL
2533 if (cur_si->si_flags & HL_CONCEALENDS)
2534 cur_si->si_flags |= HL_CONCEAL;
2535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002536 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002537
2538 /* what matches next may be different now, clear it */
2539 next_match_idx = 0;
2540 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002541 break;
2542 }
2543 else
2544 {
2545 /* handle next_list, unless at end of line and no "skipnl" or
2546 * "skipempty" */
2547 current_next_list = cur_si->si_next_list;
2548 current_next_flags = cur_si->si_flags;
2549 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2550 && syn_getcurline()[current_col] == NUL)
2551 current_next_list = NULL;
2552
2553 /* When the ended item has "extend", another item with
2554 * "keepend" now needs to check for its end. */
2555 if (cur_si->si_flags & HL_EXTEND)
2556 had_extend = TRUE;
2557
2558 pop_current_state();
2559
2560 if (current_state.ga_len == 0)
2561 break;
2562
Bram Moolenaar81993f42008-01-11 20:27:45 +00002563 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564 {
2565 syn_update_ends(FALSE);
2566 if (current_state.ga_len == 0)
2567 break;
2568 }
2569
2570 cur_si = &CUR_STATE(current_state.ga_len - 1);
2571
2572 /*
2573 * Only for a region the search for the end continues after
2574 * the end of the contained item. If the contained match
2575 * included the end-of-line, break here, the region continues.
2576 * Don't do this when:
2577 * - "keepend" is used for the contained item
2578 * - not at the end of the line (could be end="x$"me=e-1).
2579 * - "excludenl" is used (HL_HAS_EOL won't be set)
2580 */
2581 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002582 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002583 == SPTYPE_START
2584 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2585 {
2586 update_si_end(cur_si, (int)current_col, TRUE);
2587 check_keepend();
2588 if ((current_next_flags & HL_HAS_EOL)
2589 && keepend_level < 0
2590 && syn_getcurline()[current_col] == NUL)
2591 break;
2592 }
2593 }
2594 }
2595 else
2596 break;
2597 }
2598}
2599
2600/*
2601 * Update an entry in the current_state stack for a match or region. This
2602 * fills in si_attr, si_next_list and si_cont_list.
2603 */
2604 static void
2605update_si_attr(idx)
2606 int idx;
2607{
2608 stateitem_T *sip = &CUR_STATE(idx);
2609 synpat_T *spp;
2610
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002611 /* This should not happen... */
2612 if (sip->si_idx < 0)
2613 return;
2614
Bram Moolenaar860cae12010-06-05 23:22:07 +02002615 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002616 if (sip->si_flags & HL_MATCH)
2617 sip->si_id = spp->sp_syn_match_id;
2618 else
2619 sip->si_id = spp->sp_syn.id;
2620 sip->si_attr = syn_id2attr(sip->si_id);
2621 sip->si_trans_id = sip->si_id;
2622 if (sip->si_flags & HL_MATCH)
2623 sip->si_cont_list = NULL;
2624 else
2625 sip->si_cont_list = spp->sp_cont_list;
2626
2627 /*
2628 * For transparent items, take attr from outer item.
2629 * Also take cont_list, if there is none.
2630 * Don't do this for the matchgroup of a start or end pattern.
2631 */
2632 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2633 {
2634 if (idx == 0)
2635 {
2636 sip->si_attr = 0;
2637 sip->si_trans_id = 0;
2638 if (sip->si_cont_list == NULL)
2639 sip->si_cont_list = ID_LIST_ALL;
2640 }
2641 else
2642 {
2643 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2644 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002645 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2646 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 if (sip->si_cont_list == NULL)
2648 {
2649 sip->si_flags |= HL_TRANS_CONT;
2650 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2651 }
2652 }
2653 }
2654}
2655
2656/*
2657 * Check the current stack for patterns with "keepend" flag.
2658 * Propagate the match-end to contained items, until a "skipend" item is found.
2659 */
2660 static void
2661check_keepend()
2662{
2663 int i;
2664 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002665 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 stateitem_T *sip;
2667
2668 /*
2669 * This check can consume a lot of time; only do it from the level where
2670 * there really is a keepend.
2671 */
2672 if (keepend_level < 0)
2673 return;
2674
2675 /*
2676 * Find the last index of an "extend" item. "keepend" items before that
2677 * won't do anything. If there is no "extend" item "i" will be
2678 * "keepend_level" and all "keepend" items will work normally.
2679 */
2680 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2681 if (CUR_STATE(i).si_flags & HL_EXTEND)
2682 break;
2683
2684 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002685 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002686 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002687 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688 for ( ; i < current_state.ga_len; ++i)
2689 {
2690 sip = &CUR_STATE(i);
2691 if (maxpos.lnum != 0)
2692 {
2693 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002694 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2696 sip->si_ends = TRUE;
2697 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002698 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2699 {
2700 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 || maxpos.lnum > sip->si_m_endpos.lnum
2702 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002703 && maxpos.col > sip->si_m_endpos.col))
2704 maxpos = sip->si_m_endpos;
2705 if (maxpos_h.lnum == 0
2706 || maxpos_h.lnum > sip->si_h_endpos.lnum
2707 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2708 && maxpos_h.col > sip->si_h_endpos.col))
2709 maxpos_h = sip->si_h_endpos;
2710 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 }
2712}
2713
2714/*
2715 * Update an entry in the current_state stack for a start-skip-end pattern.
2716 * This finds the end of the current item, if it's in the current line.
2717 *
2718 * Return the flags for the matched END.
2719 */
2720 static void
2721update_si_end(sip, startcol, force)
2722 stateitem_T *sip;
2723 int startcol; /* where to start searching for the end */
2724 int force; /* when TRUE overrule a previous end */
2725{
2726 lpos_T startpos;
2727 lpos_T endpos;
2728 lpos_T hl_endpos;
2729 lpos_T end_endpos;
2730 int end_idx;
2731
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002732 /* return quickly for a keyword */
2733 if (sip->si_idx < 0)
2734 return;
2735
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 /* Don't update when it's already done. Can be a match of an end pattern
2737 * that started in a previous line. Watch out: can also be a "keepend"
2738 * from a containing item. */
2739 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2740 return;
2741
2742 /*
2743 * We need to find the end of the region. It may continue in the next
2744 * line.
2745 */
2746 end_idx = 0;
2747 startpos.lnum = current_lnum;
2748 startpos.col = startcol;
2749 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2750 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2751
2752 if (endpos.lnum == 0)
2753 {
2754 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002755 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002756 {
2757 /* a "oneline" never continues in the next line */
2758 sip->si_ends = TRUE;
2759 sip->si_m_endpos.lnum = current_lnum;
2760 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2761 }
2762 else
2763 {
2764 /* continues in the next line */
2765 sip->si_ends = FALSE;
2766 sip->si_m_endpos.lnum = 0;
2767 }
2768 sip->si_h_endpos = sip->si_m_endpos;
2769 }
2770 else
2771 {
2772 /* match within this line */
2773 sip->si_m_endpos = endpos;
2774 sip->si_h_endpos = hl_endpos;
2775 sip->si_eoe_pos = end_endpos;
2776 sip->si_ends = TRUE;
2777 sip->si_end_idx = end_idx;
2778 }
2779}
2780
2781/*
2782 * Add a new state to the current state stack.
2783 * It is cleared and the index set to "idx".
2784 * Return FAIL if it's not possible (out of memory).
2785 */
2786 static int
2787push_current_state(idx)
2788 int idx;
2789{
2790 if (ga_grow(&current_state, 1) == FAIL)
2791 return FAIL;
2792 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2793 CUR_STATE(current_state.ga_len).si_idx = idx;
2794 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795 return OK;
2796}
2797
2798/*
2799 * Remove a state from the current_state stack.
2800 */
2801 static void
2802pop_current_state()
2803{
2804 if (current_state.ga_len)
2805 {
2806 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2807 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808 }
2809 /* after the end of a pattern, try matching a keyword or pattern */
2810 next_match_idx = -1;
2811
2812 /* if first state with "keepend" is popped, reset keepend_level */
2813 if (keepend_level >= current_state.ga_len)
2814 keepend_level = -1;
2815}
2816
2817/*
2818 * Find the end of a start/skip/end syntax region after "startpos".
2819 * Only checks one line.
2820 * Also handles a match item that continued from a previous line.
2821 * If not found, the syntax item continues in the next line. m_endpos->lnum
2822 * will be 0.
2823 * If found, the end of the region and the end of the highlighting is
2824 * computed.
2825 */
2826 static void
2827find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2828 end_idx, start_ext)
2829 int idx; /* index of the pattern */
2830 lpos_T *startpos; /* where to start looking for an END match */
2831 lpos_T *m_endpos; /* return: end of match */
2832 lpos_T *hl_endpos; /* return: end of highlighting */
2833 long *flagsp; /* return: flags of matching END */
2834 lpos_T *end_endpos; /* return: end of end pattern match */
2835 int *end_idx; /* return: group ID for end pat. match, or 0 */
2836 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2837{
2838 colnr_T matchcol;
2839 synpat_T *spp, *spp_skip;
2840 int start_idx;
2841 int best_idx;
2842 regmmatch_T regmatch;
2843 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2844 lpos_T pos;
2845 char_u *line;
2846 int had_match = FALSE;
2847
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002848 /* just in case we are invoked for a keyword */
2849 if (idx < 0)
2850 return;
2851
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852 /*
2853 * Check for being called with a START pattern.
2854 * Can happen with a match that continues to the next line, because it
2855 * contained a region.
2856 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002857 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002858 if (spp->sp_type != SPTYPE_START)
2859 {
2860 *hl_endpos = *startpos;
2861 return;
2862 }
2863
2864 /*
2865 * Find the SKIP or first END pattern after the last START pattern.
2866 */
2867 for (;;)
2868 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002869 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 if (spp->sp_type != SPTYPE_START)
2871 break;
2872 ++idx;
2873 }
2874
2875 /*
2876 * Lookup the SKIP pattern (if present)
2877 */
2878 if (spp->sp_type == SPTYPE_SKIP)
2879 {
2880 spp_skip = spp;
2881 ++idx;
2882 }
2883 else
2884 spp_skip = NULL;
2885
2886 /* Setup external matches for syn_regexec(). */
2887 unref_extmatch(re_extmatch_in);
2888 re_extmatch_in = ref_extmatch(start_ext);
2889
2890 matchcol = startpos->col; /* start looking for a match at sstart */
2891 start_idx = idx; /* remember the first END pattern. */
2892 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2893 for (;;)
2894 {
2895 /*
2896 * Find end pattern that matches first after "matchcol".
2897 */
2898 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002899 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002900 {
2901 int lc_col = matchcol;
2902
Bram Moolenaar860cae12010-06-05 23:22:07 +02002903 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2905 break;
2906 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2907 if (lc_col < 0)
2908 lc_col = 0;
2909
2910 regmatch.rmm_ic = spp->sp_ic;
2911 regmatch.regprog = spp->sp_prog;
2912 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2913 {
2914 if (best_idx == -1 || regmatch.startpos[0].col
2915 < best_regmatch.startpos[0].col)
2916 {
2917 best_idx = idx;
2918 best_regmatch.startpos[0] = regmatch.startpos[0];
2919 best_regmatch.endpos[0] = regmatch.endpos[0];
2920 }
2921 }
2922 }
2923
2924 /*
2925 * If all end patterns have been tried, and there is no match, the
2926 * item continues until end-of-line.
2927 */
2928 if (best_idx == -1)
2929 break;
2930
2931 /*
2932 * If the skip pattern matches before the end pattern,
2933 * continue searching after the skip pattern.
2934 */
2935 if (spp_skip != NULL)
2936 {
2937 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2938
2939 if (lc_col < 0)
2940 lc_col = 0;
2941 regmatch.rmm_ic = spp_skip->sp_ic;
2942 regmatch.regprog = spp_skip->sp_prog;
2943 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2944 && regmatch.startpos[0].col
2945 <= best_regmatch.startpos[0].col)
2946 {
2947 /* Add offset to skip pattern match */
2948 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2949
2950 /* If the skip pattern goes on to the next line, there is no
2951 * match with an end pattern in this line. */
2952 if (pos.lnum > startpos->lnum)
2953 break;
2954
2955 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2956
2957 /* take care of an empty match or negative offset */
2958 if (pos.col <= matchcol)
2959 ++matchcol;
2960 else if (pos.col <= regmatch.endpos[0].col)
2961 matchcol = pos.col;
2962 else
2963 /* Be careful not to jump over the NUL at the end-of-line */
2964 for (matchcol = regmatch.endpos[0].col;
2965 line[matchcol] != NUL && matchcol < pos.col;
2966 ++matchcol)
2967 ;
2968
2969 /* if the skip pattern includes end-of-line, break here */
2970 if (line[matchcol] == NUL)
2971 break;
2972
2973 continue; /* start with first end pattern again */
2974 }
2975 }
2976
2977 /*
2978 * Match from start pattern to end pattern.
2979 * Correct for match and highlight offset of end pattern.
2980 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002981 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002982 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2983 /* can't end before the start */
2984 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2985 m_endpos->col = startpos->col;
2986
2987 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2988 /* can't end before the start */
2989 if (end_endpos->lnum == startpos->lnum
2990 && end_endpos->col < startpos->col)
2991 end_endpos->col = startpos->col;
2992 /* can't end after the match */
2993 limit_pos(end_endpos, m_endpos);
2994
2995 /*
2996 * If the end group is highlighted differently, adjust the pointers.
2997 */
2998 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2999 {
3000 *end_idx = best_idx;
3001 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3002 {
3003 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3004 hl_endpos->col = best_regmatch.endpos[0].col;
3005 }
3006 else
3007 {
3008 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3009 hl_endpos->col = best_regmatch.startpos[0].col;
3010 }
3011 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3012
3013 /* can't end before the start */
3014 if (hl_endpos->lnum == startpos->lnum
3015 && hl_endpos->col < startpos->col)
3016 hl_endpos->col = startpos->col;
3017 limit_pos(hl_endpos, m_endpos);
3018
3019 /* now the match ends where the highlighting ends, it is turned
3020 * into the matchgroup for the end */
3021 *m_endpos = *hl_endpos;
3022 }
3023 else
3024 {
3025 *end_idx = 0;
3026 *hl_endpos = *end_endpos;
3027 }
3028
3029 *flagsp = spp->sp_flags;
3030
3031 had_match = TRUE;
3032 break;
3033 }
3034
3035 /* no match for an END pattern in this line */
3036 if (!had_match)
3037 m_endpos->lnum = 0;
3038
3039 /* Remove external matches. */
3040 unref_extmatch(re_extmatch_in);
3041 re_extmatch_in = NULL;
3042}
3043
3044/*
3045 * Limit "pos" not to be after "limit".
3046 */
3047 static void
3048limit_pos(pos, limit)
3049 lpos_T *pos;
3050 lpos_T *limit;
3051{
3052 if (pos->lnum > limit->lnum)
3053 *pos = *limit;
3054 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3055 pos->col = limit->col;
3056}
3057
3058/*
3059 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3060 */
3061 static void
3062limit_pos_zero(pos, limit)
3063 lpos_T *pos;
3064 lpos_T *limit;
3065{
3066 if (pos->lnum == 0)
3067 *pos = *limit;
3068 else
3069 limit_pos(pos, limit);
3070}
3071
3072/*
3073 * Add offset to matched text for end of match or highlight.
3074 */
3075 static void
3076syn_add_end_off(result, regmatch, spp, idx, extra)
3077 lpos_T *result; /* returned position */
3078 regmmatch_T *regmatch; /* start/end of match */
3079 synpat_T *spp; /* matched pattern */
3080 int idx; /* index of offset */
3081 int extra; /* extra chars for offset to start */
3082{
3083 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003084 int off;
3085 char_u *base;
3086 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087
3088 if (spp->sp_off_flags & (1 << idx))
3089 {
3090 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003091 col = regmatch->startpos[0].col;
3092 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 }
3094 else
3095 {
3096 result->lnum = regmatch->endpos[0].lnum;
3097 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003098 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003099 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003100 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3101 * is a matchgroup. Watch out for match with last NL in the buffer. */
3102 if (result->lnum > syn_buf->b_ml.ml_line_count)
3103 col = 0;
3104 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003105 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003106 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3107 p = base + col;
3108 if (off > 0)
3109 {
3110 while (off-- > 0 && *p != NUL)
3111 mb_ptr_adv(p);
3112 }
3113 else if (off < 0)
3114 {
3115 while (off++ < 0 && base < p)
3116 mb_ptr_back(base, p);
3117 }
3118 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003119 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003120 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121}
3122
3123/*
3124 * Add offset to matched text for start of match or highlight.
3125 * Avoid resulting column to become negative.
3126 */
3127 static void
3128syn_add_start_off(result, regmatch, spp, idx, extra)
3129 lpos_T *result; /* returned position */
3130 regmmatch_T *regmatch; /* start/end of match */
3131 synpat_T *spp;
3132 int idx;
3133 int extra; /* extra chars for offset to end */
3134{
3135 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003136 int off;
3137 char_u *base;
3138 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003139
3140 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3141 {
3142 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003143 col = regmatch->endpos[0].col;
3144 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003145 }
3146 else
3147 {
3148 result->lnum = regmatch->startpos[0].lnum;
3149 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003150 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003152 if (result->lnum > syn_buf->b_ml.ml_line_count)
3153 {
3154 /* a "\n" at the end of the pattern may take us below the last line */
3155 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003156 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003157 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003158 if (off != 0)
3159 {
3160 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3161 p = base + col;
3162 if (off > 0)
3163 {
3164 while (off-- && *p != NUL)
3165 mb_ptr_adv(p);
3166 }
3167 else if (off < 0)
3168 {
3169 while (off++ && base < p)
3170 mb_ptr_back(base, p);
3171 }
3172 col = (int)(p - base);
3173 }
3174 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003175}
3176
3177/*
3178 * Get current line in syntax buffer.
3179 */
3180 static char_u *
3181syn_getcurline()
3182{
3183 return ml_get_buf(syn_buf, current_lnum, FALSE);
3184}
3185
3186/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003187 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003188 * Returns TRUE when there is a match.
3189 */
3190 static int
3191syn_regexec(rmp, lnum, col)
3192 regmmatch_T *rmp;
3193 linenr_T lnum;
3194 colnr_T col;
3195{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003196 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003197 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 {
3199 rmp->startpos[0].lnum += lnum;
3200 rmp->endpos[0].lnum += lnum;
3201 return TRUE;
3202 }
3203 return FALSE;
3204}
3205
3206/*
3207 * Check one position in a line for a matching keyword.
3208 * The caller must check if a keyword can start at startcol.
3209 * Return it's ID if found, 0 otherwise.
3210 */
3211 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003212check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003213 char_u *line;
3214 int startcol; /* position in line to check for keyword */
3215 int *endcolp; /* return: character after found keyword */
3216 long *flagsp; /* return: flags of matching keyword */
3217 short **next_listp; /* return: next_list of matching keyword */
3218 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003219 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003221 keyentry_T *kp;
3222 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003224 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003226 hashtab_T *ht;
3227 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228
3229 /* Find first character after the keyword. First character was already
3230 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003231 kwp = line + startcol;
3232 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233 do
3234 {
3235#ifdef FEAT_MBYTE
3236 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003237 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238 else
3239#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003240 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003242 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243
Bram Moolenaardad6b692005-01-25 22:14:34 +00003244 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245 return 0;
3246
3247 /*
3248 * Must make a copy of the keyword, so we can add a NUL and make it
3249 * lowercase.
3250 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003251 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003252
3253 /*
3254 * Try twice:
3255 * 1. matching case
3256 * 2. ignoring case
3257 */
3258 for (round = 1; round <= 2; ++round)
3259 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003260 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003261 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003263 if (round == 2) /* ignore case */
3264 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003265
3266 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003267 * Find keywords that match. There can be several with different
3268 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269 * When current_next_list is non-zero accept only that group, otherwise:
3270 * Accept a not-contained keyword at toplevel.
3271 * Accept a keyword at other levels only if it is in the contains list.
3272 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003273 hi = hash_find(ht, keyword);
3274 if (!HASHITEM_EMPTY(hi))
3275 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003277 if (current_next_list != 0
3278 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3279 : (cur_si == NULL
3280 ? !(kp->flags & HL_CONTAINED)
3281 : in_id_list(cur_si, cur_si->si_cont_list,
3282 &kp->k_syn, kp->flags & HL_CONTAINED)))
3283 {
3284 *endcolp = startcol + kwlen;
3285 *flagsp = kp->flags;
3286 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003287#ifdef FEAT_CONCEAL
3288 *ccharp = kp->k_char;
3289#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003290 return kp->k_syn.id;
3291 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 }
3293 }
3294 return 0;
3295}
3296
3297/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003298 * Handle ":syntax conceal" command.
3299 */
3300 static void
3301syn_cmd_conceal(eap, syncing)
3302 exarg_T *eap UNUSED;
3303 int syncing UNUSED;
3304{
3305#ifdef FEAT_CONCEAL
3306 char_u *arg = eap->arg;
3307 char_u *next;
3308
3309 eap->nextcmd = find_nextcmd(arg);
3310 if (eap->skip)
3311 return;
3312
3313 next = skiptowhite(arg);
3314 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3315 curwin->w_s->b_syn_conceal = TRUE;
3316 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3317 curwin->w_s->b_syn_conceal = FALSE;
3318 else
3319 EMSG2(_("E390: Illegal argument: %s"), arg);
3320#endif
3321}
3322
3323/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003324 * Handle ":syntax case" command.
3325 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003326 static void
3327syn_cmd_case(eap, syncing)
3328 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003329 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330{
3331 char_u *arg = eap->arg;
3332 char_u *next;
3333
3334 eap->nextcmd = find_nextcmd(arg);
3335 if (eap->skip)
3336 return;
3337
3338 next = skiptowhite(arg);
3339 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003340 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003341 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003342 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 else
3344 EMSG2(_("E390: Illegal argument: %s"), arg);
3345}
3346
3347/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003348 * Handle ":syntax spell" command.
3349 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003350 static void
3351syn_cmd_spell(eap, syncing)
3352 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003353 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003354{
3355 char_u *arg = eap->arg;
3356 char_u *next;
3357
3358 eap->nextcmd = find_nextcmd(arg);
3359 if (eap->skip)
3360 return;
3361
3362 next = skiptowhite(arg);
3363 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003364 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003365 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003366 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003367 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003368 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003369 else
3370 EMSG2(_("E390: Illegal argument: %s"), arg);
3371}
3372
3373/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 * Clear all syntax info for one buffer.
3375 */
3376 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003377syntax_clear(block)
3378 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003379{
3380 int i;
3381
Bram Moolenaar860cae12010-06-05 23:22:07 +02003382 block->b_syn_error = FALSE; /* clear previous error */
3383 block->b_syn_ic = FALSE; /* Use case, by default */
3384 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3385 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386
3387 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003388 clear_keywtab(&block->b_keywtab);
3389 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390
3391 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003392 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3393 syn_clear_pattern(block, i);
3394 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395
3396 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003397 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3398 syn_clear_cluster(block, i);
3399 ga_clear(&block->b_syn_clusters);
3400 block->b_spell_cluster_id = 0;
3401 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003402
Bram Moolenaar860cae12010-06-05 23:22:07 +02003403 block->b_syn_sync_flags = 0;
3404 block->b_syn_sync_minlines = 0;
3405 block->b_syn_sync_maxlines = 0;
3406 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003407
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408 vim_free(block->b_syn_linecont_prog);
3409 block->b_syn_linecont_prog = NULL;
3410 vim_free(block->b_syn_linecont_pat);
3411 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003412#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414#endif
3415
3416 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003417 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003418 invalidate_current_state();
3419}
3420
3421/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003422 * Get rid of ownsyntax for window "wp".
3423 */
3424 void
3425reset_synblock(wp)
3426 win_T *wp;
3427{
3428 if (wp->w_s != &wp->w_buffer->b_s)
3429 {
3430 syntax_clear(wp->w_s);
3431 vim_free(wp->w_s);
3432 wp->w_s = &wp->w_buffer->b_s;
3433 }
3434}
3435
3436/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003437 * Clear syncing info for one buffer.
3438 */
3439 static void
3440syntax_sync_clear()
3441{
3442 int i;
3443
3444 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003445 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3446 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3447 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448
Bram Moolenaar860cae12010-06-05 23:22:07 +02003449 curwin->w_s->b_syn_sync_flags = 0;
3450 curwin->w_s->b_syn_sync_minlines = 0;
3451 curwin->w_s->b_syn_sync_maxlines = 0;
3452 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453
Bram Moolenaar860cae12010-06-05 23:22:07 +02003454 vim_free(curwin->w_s->b_syn_linecont_prog);
3455 curwin->w_s->b_syn_linecont_prog = NULL;
3456 vim_free(curwin->w_s->b_syn_linecont_pat);
3457 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003458
Bram Moolenaar860cae12010-06-05 23:22:07 +02003459 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460}
3461
3462/*
3463 * Remove one pattern from the buffer's pattern list.
3464 */
3465 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003466syn_remove_pattern(block, idx)
3467 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468 int idx;
3469{
3470 synpat_T *spp;
3471
Bram Moolenaar860cae12010-06-05 23:22:07 +02003472 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473#ifdef FEAT_FOLDING
3474 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3480 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481}
3482
3483/*
3484 * Clear and free one syntax pattern. When clearing all, must be called from
3485 * last to first!
3486 */
3487 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488syn_clear_pattern(block, i)
3489 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490 int i;
3491{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003492 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3493 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003495 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003497 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3498 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3499 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003500 }
3501}
3502
3503/*
3504 * Clear and free one syntax cluster.
3505 */
3506 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003507syn_clear_cluster(block, i)
3508 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 int i;
3510{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 vim_free(SYN_CLSTR(block)[i].scl_name);
3512 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3513 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514}
3515
3516/*
3517 * Handle ":syntax clear" command.
3518 */
3519 static void
3520syn_cmd_clear(eap, syncing)
3521 exarg_T *eap;
3522 int syncing;
3523{
3524 char_u *arg = eap->arg;
3525 char_u *arg_end;
3526 int id;
3527
3528 eap->nextcmd = find_nextcmd(arg);
3529 if (eap->skip)
3530 return;
3531
3532 /*
3533 * We have to disable this within ":syn include @group filename",
3534 * because otherwise @group would get deleted.
3535 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3536 * clear".
3537 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003538 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003539 return;
3540
3541 if (ends_excmd(*arg))
3542 {
3543 /*
3544 * No argument: Clear all syntax items.
3545 */
3546 if (syncing)
3547 syntax_sync_clear();
3548 else
3549 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003550 syntax_clear(curwin->w_s);
3551 if (curwin->w_s == &curwin->w_buffer->b_s)
3552 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003553 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003554 }
3555 }
3556 else
3557 {
3558 /*
3559 * Clear the group IDs that are in the argument.
3560 */
3561 while (!ends_excmd(*arg))
3562 {
3563 arg_end = skiptowhite(arg);
3564 if (*arg == '@')
3565 {
3566 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3567 if (id == 0)
3568 {
3569 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3570 break;
3571 }
3572 else
3573 {
3574 /*
3575 * We can't physically delete a cluster without changing
3576 * the IDs of other clusters, so we do the next best thing
3577 * and make it empty.
3578 */
3579 short scl_id = id - SYNID_CLUSTER;
3580
Bram Moolenaar860cae12010-06-05 23:22:07 +02003581 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3582 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583 }
3584 }
3585 else
3586 {
3587 id = syn_namen2id(arg, (int)(arg_end - arg));
3588 if (id == 0)
3589 {
3590 EMSG2(_(e_nogroup), arg);
3591 break;
3592 }
3593 else
3594 syn_clear_one(id, syncing);
3595 }
3596 arg = skipwhite(arg_end);
3597 }
3598 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003599 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003600 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003601}
3602
3603/*
3604 * Clear one syntax group for the current buffer.
3605 */
3606 static void
3607syn_clear_one(id, syncing)
3608 int id;
3609 int syncing;
3610{
3611 synpat_T *spp;
3612 int idx;
3613
3614 /* Clear keywords only when not ":syn sync clear group-name" */
3615 if (!syncing)
3616 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003617 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3618 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003619 }
3620
3621 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003622 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003624 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3626 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003627 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628 }
3629}
3630
3631/*
3632 * Handle ":syntax on" command.
3633 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634 static void
3635syn_cmd_on(eap, syncing)
3636 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003637 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638{
3639 syn_cmd_onoff(eap, "syntax");
3640}
3641
3642/*
3643 * Handle ":syntax enable" command.
3644 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 static void
3646syn_cmd_enable(eap, syncing)
3647 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003648 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003649{
3650 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3651 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003652 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653}
3654
3655/*
3656 * Handle ":syntax reset" command.
3657 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 static void
3659syn_cmd_reset(eap, syncing)
3660 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003661 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662{
3663 eap->nextcmd = check_nextcmd(eap->arg);
3664 if (!eap->skip)
3665 {
3666 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3667 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003668 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669 }
3670}
3671
3672/*
3673 * Handle ":syntax manual" command.
3674 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 static void
3676syn_cmd_manual(eap, syncing)
3677 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003678 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679{
3680 syn_cmd_onoff(eap, "manual");
3681}
3682
3683/*
3684 * Handle ":syntax off" command.
3685 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686 static void
3687syn_cmd_off(eap, syncing)
3688 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003689 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690{
3691 syn_cmd_onoff(eap, "nosyntax");
3692}
3693
3694 static void
3695syn_cmd_onoff(eap, name)
3696 exarg_T *eap;
3697 char *name;
3698{
3699 char_u buf[100];
3700
3701 eap->nextcmd = check_nextcmd(eap->arg);
3702 if (!eap->skip)
3703 {
3704 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003705 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706 do_cmdline_cmd(buf);
3707 }
3708}
3709
3710/*
3711 * Handle ":syntax [list]" command: list current syntax words.
3712 */
3713 static void
3714syn_cmd_list(eap, syncing)
3715 exarg_T *eap;
3716 int syncing; /* when TRUE: list syncing items */
3717{
3718 char_u *arg = eap->arg;
3719 int id;
3720 char_u *arg_end;
3721
3722 eap->nextcmd = find_nextcmd(arg);
3723 if (eap->skip)
3724 return;
3725
Bram Moolenaar860cae12010-06-05 23:22:07 +02003726 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 {
3728 MSG(_("No Syntax items defined for this buffer"));
3729 return;
3730 }
3731
3732 if (syncing)
3733 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003734 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735 {
3736 MSG_PUTS(_("syncing on C-style comments"));
3737 syn_lines_msg();
3738 syn_match_msg();
3739 return;
3740 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003741 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003743 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 MSG_PUTS(_("no syncing"));
3745 else
3746 {
3747 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003748 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749 MSG_PUTS(_(" lines before top line"));
3750 syn_match_msg();
3751 }
3752 return;
3753 }
3754 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003755 if (curwin->w_s->b_syn_sync_minlines > 0
3756 || curwin->w_s->b_syn_sync_maxlines > 0
3757 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003758 {
3759 MSG_PUTS(_("\nsyncing on items"));
3760 syn_lines_msg();
3761 syn_match_msg();
3762 }
3763 }
3764 else
3765 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3766 if (ends_excmd(*arg))
3767 {
3768 /*
3769 * No argument: List all group IDs and all syntax clusters.
3770 */
3771 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3772 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003773 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003774 syn_list_cluster(id);
3775 }
3776 else
3777 {
3778 /*
3779 * List the group IDs and syntax clusters that are in the argument.
3780 */
3781 while (!ends_excmd(*arg) && !got_int)
3782 {
3783 arg_end = skiptowhite(arg);
3784 if (*arg == '@')
3785 {
3786 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3787 if (id == 0)
3788 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3789 else
3790 syn_list_cluster(id - SYNID_CLUSTER);
3791 }
3792 else
3793 {
3794 id = syn_namen2id(arg, (int)(arg_end - arg));
3795 if (id == 0)
3796 EMSG2(_(e_nogroup), arg);
3797 else
3798 syn_list_one(id, syncing, TRUE);
3799 }
3800 arg = skipwhite(arg_end);
3801 }
3802 }
3803 eap->nextcmd = check_nextcmd(arg);
3804}
3805
3806 static void
3807syn_lines_msg()
3808{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003809 if (curwin->w_s->b_syn_sync_maxlines > 0
3810 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811 {
3812 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003813 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 {
3815 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003816 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3817 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 MSG_PUTS(", ");
3819 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003820 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003821 {
3822 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003823 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 }
3825 MSG_PUTS(_(" lines before top line"));
3826 }
3827}
3828
3829 static void
3830syn_match_msg()
3831{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003832 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 {
3834 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003835 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836 MSG_PUTS(_(" line breaks"));
3837 }
3838}
3839
3840static int last_matchgroup;
3841
3842struct name_list
3843{
3844 int flag;
3845 char *name;
3846};
3847
3848static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3849
3850/*
3851 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3852 */
3853 static void
3854syn_list_one(id, syncing, link_only)
3855 int id;
3856 int syncing; /* when TRUE: list syncing items */
3857 int link_only; /* when TRUE; list link-only too */
3858{
3859 int attr;
3860 int idx;
3861 int did_header = FALSE;
3862 synpat_T *spp;
3863 static struct name_list namelist1[] =
3864 {
3865 {HL_DISPLAY, "display"},
3866 {HL_CONTAINED, "contained"},
3867 {HL_ONELINE, "oneline"},
3868 {HL_KEEPEND, "keepend"},
3869 {HL_EXTEND, "extend"},
3870 {HL_EXCLUDENL, "excludenl"},
3871 {HL_TRANSP, "transparent"},
3872 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003873#ifdef FEAT_CONCEAL
3874 {HL_CONCEAL, "conceal"},
3875 {HL_CONCEALENDS, "concealends"},
3876#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877 {0, NULL}
3878 };
3879 static struct name_list namelist2[] =
3880 {
3881 {HL_SKIPWHITE, "skipwhite"},
3882 {HL_SKIPNL, "skipnl"},
3883 {HL_SKIPEMPTY, "skipempty"},
3884 {0, NULL}
3885 };
3886
3887 attr = hl_attr(HLF_D); /* highlight like directories */
3888
3889 /* list the keywords for "id" */
3890 if (!syncing)
3891 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003892 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3893 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894 did_header, attr);
3895 }
3896
3897 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003898 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003899 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003900 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3902 continue;
3903
3904 (void)syn_list_header(did_header, 999, id);
3905 did_header = TRUE;
3906 last_matchgroup = 0;
3907 if (spp->sp_type == SPTYPE_MATCH)
3908 {
3909 put_pattern("match", ' ', spp, attr);
3910 msg_putchar(' ');
3911 }
3912 else if (spp->sp_type == SPTYPE_START)
3913 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003914 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3915 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3916 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3917 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3918 while (idx < curwin->w_s->b_syn_patterns.ga_len
3919 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3920 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 --idx;
3922 msg_putchar(' ');
3923 }
3924 syn_list_flags(namelist1, spp->sp_flags, attr);
3925
3926 if (spp->sp_cont_list != NULL)
3927 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3928
3929 if (spp->sp_syn.cont_in_list != NULL)
3930 put_id_list((char_u *)"containedin",
3931 spp->sp_syn.cont_in_list, attr);
3932
3933 if (spp->sp_next_list != NULL)
3934 {
3935 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3936 syn_list_flags(namelist2, spp->sp_flags, attr);
3937 }
3938 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3939 {
3940 if (spp->sp_flags & HL_SYNC_HERE)
3941 msg_puts_attr((char_u *)"grouphere", attr);
3942 else
3943 msg_puts_attr((char_u *)"groupthere", attr);
3944 msg_putchar(' ');
3945 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003946 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3948 else
3949 MSG_PUTS("NONE");
3950 msg_putchar(' ');
3951 }
3952 }
3953
3954 /* list the link, if there is one */
3955 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3956 {
3957 (void)syn_list_header(did_header, 999, id);
3958 msg_puts_attr((char_u *)"links to", attr);
3959 msg_putchar(' ');
3960 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3961 }
3962}
3963
3964 static void
3965syn_list_flags(nl, flags, attr)
3966 struct name_list *nl;
3967 int flags;
3968 int attr;
3969{
3970 int i;
3971
3972 for (i = 0; nl[i].flag != 0; ++i)
3973 if (flags & nl[i].flag)
3974 {
3975 msg_puts_attr((char_u *)nl[i].name, attr);
3976 msg_putchar(' ');
3977 }
3978}
3979
3980/*
3981 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3982 */
3983 static void
3984syn_list_cluster(id)
3985 int id;
3986{
3987 int endcol = 15;
3988
3989 /* slight hack: roughly duplicate the guts of syn_list_header() */
3990 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02003991 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992
3993 if (msg_col >= endcol) /* output at least one space */
3994 endcol = msg_col + 1;
3995 if (Columns <= endcol) /* avoid hang for tiny window */
3996 endcol = Columns - 1;
3997
3998 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003999 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004001 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 hl_attr(HLF_D));
4003 }
4004 else
4005 {
4006 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4007 msg_puts((char_u *)"=NONE");
4008 }
4009}
4010
4011 static void
4012put_id_list(name, list, attr)
4013 char_u *name;
4014 short *list;
4015 int attr;
4016{
4017 short *p;
4018
4019 msg_puts_attr(name, attr);
4020 msg_putchar('=');
4021 for (p = list; *p; ++p)
4022 {
4023 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4024 {
4025 if (p[1])
4026 MSG_PUTS("ALLBUT");
4027 else
4028 MSG_PUTS("ALL");
4029 }
4030 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4031 {
4032 MSG_PUTS("TOP");
4033 }
4034 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4035 {
4036 MSG_PUTS("CONTAINED");
4037 }
4038 else if (*p >= SYNID_CLUSTER)
4039 {
4040 short scl_id = *p - SYNID_CLUSTER;
4041
4042 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004043 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 }
4045 else
4046 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4047 if (p[1])
4048 msg_putchar(',');
4049 }
4050 msg_putchar(' ');
4051}
4052
4053 static void
4054put_pattern(s, c, spp, attr)
4055 char *s;
4056 int c;
4057 synpat_T *spp;
4058 int attr;
4059{
4060 long n;
4061 int mask;
4062 int first;
4063 static char *sepchars = "/+=-#@\"|'^&";
4064 int i;
4065
4066 /* May have to write "matchgroup=group" */
4067 if (last_matchgroup != spp->sp_syn_match_id)
4068 {
4069 last_matchgroup = spp->sp_syn_match_id;
4070 msg_puts_attr((char_u *)"matchgroup", attr);
4071 msg_putchar('=');
4072 if (last_matchgroup == 0)
4073 msg_outtrans((char_u *)"NONE");
4074 else
4075 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4076 msg_putchar(' ');
4077 }
4078
4079 /* output the name of the pattern and an '=' or ' ' */
4080 msg_puts_attr((char_u *)s, attr);
4081 msg_putchar(c);
4082
4083 /* output the pattern, in between a char that is not in the pattern */
4084 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4085 if (sepchars[++i] == NUL)
4086 {
4087 i = 0; /* no good char found, just use the first one */
4088 break;
4089 }
4090 msg_putchar(sepchars[i]);
4091 msg_outtrans(spp->sp_pattern);
4092 msg_putchar(sepchars[i]);
4093
4094 /* output any pattern options */
4095 first = TRUE;
4096 for (i = 0; i < SPO_COUNT; ++i)
4097 {
4098 mask = (1 << i);
4099 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4100 {
4101 if (!first)
4102 msg_putchar(','); /* separate with commas */
4103 msg_puts((char_u *)spo_name_tab[i]);
4104 n = spp->sp_offsets[i];
4105 if (i != SPO_LC_OFF)
4106 {
4107 if (spp->sp_off_flags & mask)
4108 msg_putchar('s');
4109 else
4110 msg_putchar('e');
4111 if (n > 0)
4112 msg_putchar('+');
4113 }
4114 if (n || i == SPO_LC_OFF)
4115 msg_outnum(n);
4116 first = FALSE;
4117 }
4118 }
4119 msg_putchar(' ');
4120}
4121
4122/*
4123 * List or clear the keywords for one syntax group.
4124 * Return TRUE if the header has been printed.
4125 */
4126 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004127syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004129 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 int did_header; /* header has already been printed */
4131 int attr;
4132{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004134 hashitem_T *hi;
4135 keyentry_T *kp;
4136 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 int prev_contained = 0;
4138 short *prev_next_list = NULL;
4139 short *prev_cont_in_list = NULL;
4140 int prev_skipnl = 0;
4141 int prev_skipwhite = 0;
4142 int prev_skipempty = 0;
4143
Bram Moolenaar071d4272004-06-13 20:20:40 +00004144 /*
4145 * Unfortunately, this list of keywords is not sorted on alphabet but on
4146 * hash value...
4147 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004148 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004149 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004150 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004151 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004153 --todo;
4154 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004156 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004158 if (prev_contained != (kp->flags & HL_CONTAINED)
4159 || prev_skipnl != (kp->flags & HL_SKIPNL)
4160 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4161 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4162 || prev_cont_in_list != kp->k_syn.cont_in_list
4163 || prev_next_list != kp->next_list)
4164 outlen = 9999;
4165 else
4166 outlen = (int)STRLEN(kp->keyword);
4167 /* output "contained" and "nextgroup" on each line */
4168 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004169 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004170 prev_contained = 0;
4171 prev_next_list = NULL;
4172 prev_cont_in_list = NULL;
4173 prev_skipnl = 0;
4174 prev_skipwhite = 0;
4175 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004177 did_header = TRUE;
4178 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004180 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004182 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004184 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004186 put_id_list((char_u *)"containedin",
4187 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004189 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004191 if (kp->next_list != prev_next_list)
4192 {
4193 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4194 msg_putchar(' ');
4195 prev_next_list = kp->next_list;
4196 if (kp->flags & HL_SKIPNL)
4197 {
4198 msg_puts_attr((char_u *)"skipnl", attr);
4199 msg_putchar(' ');
4200 prev_skipnl = (kp->flags & HL_SKIPNL);
4201 }
4202 if (kp->flags & HL_SKIPWHITE)
4203 {
4204 msg_puts_attr((char_u *)"skipwhite", attr);
4205 msg_putchar(' ');
4206 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4207 }
4208 if (kp->flags & HL_SKIPEMPTY)
4209 {
4210 msg_puts_attr((char_u *)"skipempty", attr);
4211 msg_putchar(' ');
4212 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4213 }
4214 }
4215 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 }
4218 }
4219 }
4220
4221 return did_header;
4222}
4223
4224 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004225syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004227 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004229 hashitem_T *hi;
4230 keyentry_T *kp;
4231 keyentry_T *kp_prev;
4232 keyentry_T *kp_next;
4233 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004234
Bram Moolenaardad6b692005-01-25 22:14:34 +00004235 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004236 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004237 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004238 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004239 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004241 --todo;
4242 kp_prev = NULL;
4243 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004245 if (kp->k_syn.id == id)
4246 {
4247 kp_next = kp->ke_next;
4248 if (kp_prev == NULL)
4249 {
4250 if (kp_next == NULL)
4251 hash_remove(ht, hi);
4252 else
4253 hi->hi_key = KE2HIKEY(kp_next);
4254 }
4255 else
4256 kp_prev->ke_next = kp_next;
4257 vim_free(kp->next_list);
4258 vim_free(kp->k_syn.cont_in_list);
4259 vim_free(kp);
4260 kp = kp_next;
4261 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 {
4264 kp_prev = kp;
4265 kp = kp->ke_next;
4266 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004267 }
4268 }
4269 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004270 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271}
4272
4273/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004274 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004275 */
4276 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277clear_keywtab(ht)
4278 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004279{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004280 hashitem_T *hi;
4281 int todo;
4282 keyentry_T *kp;
4283 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004285 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004286 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004287 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004288 if (!HASHITEM_EMPTY(hi))
4289 {
4290 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004291 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004292 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004293 kp_next = kp->ke_next;
4294 vim_free(kp->next_list);
4295 vim_free(kp->k_syn.cont_in_list);
4296 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 hash_clear(ht);
4301 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302}
4303
4304/*
4305 * Add a keyword to the list of keywords.
4306 */
4307 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004308add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 char_u *name; /* name of keyword */
4310 int id; /* group ID for this keyword */
4311 int flags; /* flags for this keyword */
4312 short *cont_in_list; /* containedin for this keyword */
4313 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004314 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004316 keyentry_T *kp;
4317 hashtab_T *ht;
4318 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004319 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004320 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004321 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322
Bram Moolenaar860cae12010-06-05 23:22:07 +02004323 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004324 name_ic = str_foldcase(name, (int)STRLEN(name),
4325 name_folded, MAXKEYWLEN + 1);
4326 else
4327 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4329 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 kp->k_syn.id = id;
4333 kp->k_syn.inc_tag = current_syn_inc_tag;
4334 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004335 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004338 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004339 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340
Bram Moolenaar860cae12010-06-05 23:22:07 +02004341 if (curwin->w_s->b_syn_ic)
4342 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004344 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345
Bram Moolenaardad6b692005-01-25 22:14:34 +00004346 hash = hash_hash(kp->keyword);
4347 hi = hash_lookup(ht, kp->keyword, hash);
4348 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004350 /* new keyword, add to hashtable */
4351 kp->ke_next = NULL;
4352 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 /* keyword already exists, prepend to list */
4357 kp->ke_next = HI2KE(hi);
4358 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360}
4361
4362/*
4363 * Get the start and end of the group name argument.
4364 * Return a pointer to the first argument.
4365 * Return NULL if the end of the command was found instead of further args.
4366 */
4367 static char_u *
4368get_group_name(arg, name_end)
4369 char_u *arg; /* start of the argument */
4370 char_u **name_end; /* pointer to end of the name */
4371{
4372 char_u *rest;
4373
4374 *name_end = skiptowhite(arg);
4375 rest = skipwhite(*name_end);
4376
4377 /*
4378 * Check if there are enough arguments. The first argument may be a
4379 * pattern, where '|' is allowed, so only check for NUL.
4380 */
4381 if (ends_excmd(*arg) || *rest == NUL)
4382 return NULL;
4383 return rest;
4384}
4385
4386/*
4387 * Check for syntax command option arguments.
4388 * This can be called at any place in the list of arguments, and just picks
4389 * out the arguments that are known. Can be called several times in a row to
4390 * collect all options in between other arguments.
4391 * Return a pointer to the next argument (which isn't an option).
4392 * Return NULL for any error;
4393 */
4394 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004395get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004396 char_u *arg; /* next argument to be checked */
4397 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004398 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004400 char_u *gname_start, *gname;
4401 int syn_id;
4402 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004403 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004404 int i;
4405 int fidx;
4406 static struct flag
4407 {
4408 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004409 int argtype;
4410 int flags;
4411 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4412 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4413 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4414 {"eExXtTeEnNdD", 0, HL_EXTEND},
4415 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4416 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4417 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4418 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4419 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4420 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4421 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4422 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4423 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004424 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4425 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4426 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004427 {"cCoOnNtTaAiInNsS", 1, 0},
4428 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4429 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004431 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432
4433 if (arg == NULL) /* already detected error */
4434 return NULL;
4435
Bram Moolenaar860cae12010-06-05 23:22:07 +02004436#ifdef FEAT_CONCEAL
4437 if (curwin->w_s->b_syn_conceal)
4438 opt->flags |= HL_CONCEAL;
4439#endif
4440
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 for (;;)
4442 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004443 /*
4444 * This is used very often when a large number of keywords is defined.
4445 * Need to skip quickly when no option name is found.
4446 * Also avoid tolower(), it's slow.
4447 */
4448 if (strchr(first_letters, *arg) == NULL)
4449 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450
4451 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4452 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004453 p = flagtab[fidx].name;
4454 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4455 if (arg[len] != p[i] && arg[len] != p[i + 1])
4456 break;
4457 if (p[i] == NUL && (vim_iswhite(arg[len])
4458 || (flagtab[fidx].argtype > 0
4459 ? arg[len] == '='
4460 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004461 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004462 if (opt->keyword
4463 && (flagtab[fidx].flags == HL_DISPLAY
4464 || flagtab[fidx].flags == HL_FOLD
4465 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 /* treat "display", "fold" and "extend" as a keyword */
4467 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 break;
4469 }
4470 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004471 if (fidx < 0) /* no match found */
4472 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004474 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004476 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 {
4478 EMSG(_("E395: contains argument not accepted here"));
4479 return NULL;
4480 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004481 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 return NULL;
4483 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004484 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004486#if 0 /* cannot happen */
4487 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488 {
4489 EMSG(_("E396: containedin argument not accepted here"));
4490 return NULL;
4491 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004492#endif
4493 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494 return NULL;
4495 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004496 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004498 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 return NULL;
4500 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004501 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4502 {
4503#ifdef FEAT_MBYTE
4504 /* cchar=? */
4505 if (has_mbyte)
4506 {
4507# ifdef FEAT_CONCEAL
4508 *conceal_char = mb_ptr2char(arg + 6);
4509# endif
4510 arg += mb_ptr2len(arg + 6) - 1;
4511 }
4512 else
4513#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004514 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004515#ifdef FEAT_CONCEAL
4516 *conceal_char = arg[6];
4517#else
4518 ;
4519#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004520 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004521 arg = skipwhite(arg + 7);
4522 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 {
4525 opt->flags |= flagtab[fidx].flags;
4526 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 if (flagtab[fidx].flags == HL_SYNC_HERE
4529 || flagtab[fidx].flags == HL_SYNC_THERE)
4530 {
4531 if (opt->sync_idx == NULL)
4532 {
4533 EMSG(_("E393: group[t]here not accepted here"));
4534 return NULL;
4535 }
4536 gname_start = arg;
4537 arg = skiptowhite(arg);
4538 if (gname_start == arg)
4539 return NULL;
4540 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4541 if (gname == NULL)
4542 return NULL;
4543 if (STRCMP(gname, "NONE") == 0)
4544 *opt->sync_idx = NONE_IDX;
4545 else
4546 {
4547 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004548 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4549 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4550 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004551 {
4552 *opt->sync_idx = i;
4553 break;
4554 }
4555 if (i < 0)
4556 {
4557 EMSG2(_("E394: Didn't find region item for %s"), gname);
4558 vim_free(gname);
4559 return NULL;
4560 }
4561 }
4562
4563 vim_free(gname);
4564 arg = skipwhite(arg);
4565 }
4566#ifdef FEAT_FOLDING
4567 else if (flagtab[fidx].flags == HL_FOLD
4568 && foldmethodIsSyntax(curwin))
4569 /* Need to update folds later. */
4570 foldUpdateAll(curwin);
4571#endif
4572 }
4573 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004574
4575 return arg;
4576}
4577
4578/*
4579 * Adjustments to syntax item when declared in a ":syn include"'d file.
4580 * Set the contained flag, and if the item is not already contained, add it
4581 * to the specified top-level group, if any.
4582 */
4583 static void
4584syn_incl_toplevel(id, flagsp)
4585 int id;
4586 int *flagsp;
4587{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004588 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 return;
4590 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004591 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004592 {
4593 /* We have to alloc this, because syn_combine_list() will free it. */
4594 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004595 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596
4597 if (grp_list != NULL)
4598 {
4599 grp_list[0] = id;
4600 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004601 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004602 CLUSTER_ADD);
4603 }
4604 }
4605}
4606
4607/*
4608 * Handle ":syntax include [@{group-name}] filename" command.
4609 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 static void
4611syn_cmd_include(eap, syncing)
4612 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004613 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004614{
4615 char_u *arg = eap->arg;
4616 int sgl_id = 1;
4617 char_u *group_name_end;
4618 char_u *rest;
4619 char_u *errormsg = NULL;
4620 int prev_toplvl_grp;
4621 int prev_syn_inc_tag;
4622 int source = FALSE;
4623
4624 eap->nextcmd = find_nextcmd(arg);
4625 if (eap->skip)
4626 return;
4627
4628 if (arg[0] == '@')
4629 {
4630 ++arg;
4631 rest = get_group_name(arg, &group_name_end);
4632 if (rest == NULL)
4633 {
4634 EMSG((char_u *)_("E397: Filename required"));
4635 return;
4636 }
4637 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4638 /* separate_nextcmd() and expand_filename() depend on this */
4639 eap->arg = rest;
4640 }
4641
4642 /*
4643 * Everything that's left, up to the next command, should be the
4644 * filename to include.
4645 */
4646 eap->argt |= (XFILE | NOSPC);
4647 separate_nextcmd(eap);
4648 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4649 {
4650 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4651 * file. Need to expand the file name first. In other cases
4652 * ":runtime!" is used. */
4653 source = TRUE;
4654 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4655 {
4656 if (errormsg != NULL)
4657 EMSG(errormsg);
4658 return;
4659 }
4660 }
4661
4662 /*
4663 * Save and restore the existing top-level grouplist id and ":syn
4664 * include" tag around the actual inclusion.
4665 */
4666 prev_syn_inc_tag = current_syn_inc_tag;
4667 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004668 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4669 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004670 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4671 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004672 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004673 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 current_syn_inc_tag = prev_syn_inc_tag;
4675}
4676
4677/*
4678 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4679 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004680 static void
4681syn_cmd_keyword(eap, syncing)
4682 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004683 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684{
4685 char_u *arg = eap->arg;
4686 char_u *group_name_end;
4687 int syn_id;
4688 char_u *rest;
4689 char_u *keyword_copy;
4690 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004691 char_u *kw;
4692 syn_opt_arg_T syn_opt_arg;
4693 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004694 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695
4696 rest = get_group_name(arg, &group_name_end);
4697
4698 if (rest != NULL)
4699 {
4700 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4701
4702 /* allocate a buffer, for removing the backslashes in the keyword */
4703 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4704 if (keyword_copy != NULL)
4705 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004706 syn_opt_arg.flags = 0;
4707 syn_opt_arg.keyword = TRUE;
4708 syn_opt_arg.sync_idx = NULL;
4709 syn_opt_arg.has_cont_list = FALSE;
4710 syn_opt_arg.cont_in_list = NULL;
4711 syn_opt_arg.next_list = NULL;
4712
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 /*
4714 * The options given apply to ALL keywords, so all options must be
4715 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004716 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004717 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004718 cnt = 0;
4719 p = keyword_copy;
4720 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004722 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004723 if (rest == NULL || ends_excmd(*rest))
4724 break;
4725 /* Copy the keyword, removing backslashes, and add a NUL. */
4726 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004727 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004728 if (*rest == '\\' && rest[1] != NUL)
4729 ++rest;
4730 *p++ = *rest++;
4731 }
4732 *p++ = NUL;
4733 ++cnt;
4734 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004736 if (!eap->skip)
4737 {
4738 /* Adjust flags for use of ":syn include". */
4739 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4740
4741 /*
4742 * 2: Add an entry for each keyword.
4743 */
4744 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4745 {
4746 for (p = vim_strchr(kw, '['); ; )
4747 {
4748 if (p != NULL)
4749 *p = NUL;
4750 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004751 syn_opt_arg.cont_in_list,
4752 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004753 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004754 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004755 if (p[1] == NUL)
4756 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004757 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004758 kw = p + 2; /* skip over the NUL */
4759 break;
4760 }
4761 if (p[1] == ']')
4762 {
4763 kw = p + 1; /* skip over the "]" */
4764 break;
4765 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004766#ifdef FEAT_MBYTE
4767 if (has_mbyte)
4768 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004769 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004770
4771 mch_memmove(p, p + 1, l);
4772 p += l;
4773 }
4774 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004775#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004776 {
4777 p[0] = p[1];
4778 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004779 }
4780 }
4781 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004782 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004783
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004785 vim_free(syn_opt_arg.cont_in_list);
4786 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787 }
4788 }
4789
4790 if (rest != NULL)
4791 eap->nextcmd = check_nextcmd(rest);
4792 else
4793 EMSG2(_(e_invarg2), arg);
4794
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004795 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004796 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004797}
4798
4799/*
4800 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4801 *
4802 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4803 */
4804 static void
4805syn_cmd_match(eap, syncing)
4806 exarg_T *eap;
4807 int syncing; /* TRUE for ":syntax sync match .. " */
4808{
4809 char_u *arg = eap->arg;
4810 char_u *group_name_end;
4811 char_u *rest;
4812 synpat_T item; /* the item found in the line */
4813 int syn_id;
4814 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004815 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004817 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818
4819 /* Isolate the group name, check for validity */
4820 rest = get_group_name(arg, &group_name_end);
4821
4822 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004823 syn_opt_arg.flags = 0;
4824 syn_opt_arg.keyword = FALSE;
4825 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4826 syn_opt_arg.has_cont_list = TRUE;
4827 syn_opt_arg.cont_list = NULL;
4828 syn_opt_arg.cont_in_list = NULL;
4829 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004830 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831
4832 /* get the pattern. */
4833 init_syn_patterns();
4834 vim_memset(&item, 0, sizeof(item));
4835 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004836 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4837 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838
4839 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004840 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841
4842 if (rest != NULL) /* all arguments are valid */
4843 {
4844 /*
4845 * Check for trailing command and illegal trailing arguments.
4846 */
4847 eap->nextcmd = check_nextcmd(rest);
4848 if (!ends_excmd(*rest) || eap->skip)
4849 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004850 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851 && (syn_id = syn_check_group(arg,
4852 (int)(group_name_end - arg))) != 0)
4853 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004854 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004855 /*
4856 * Store the pattern in the syn_items list
4857 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004858 idx = curwin->w_s->b_syn_patterns.ga_len;
4859 SYN_ITEMS(curwin->w_s)[idx] = item;
4860 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4861 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4862 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4863 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4864 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4865 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4866 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4867 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004868 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004869#ifdef FEAT_CONCEAL
4870 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4871#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004872 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004873 curwin->w_s->b_syn_containedin = TRUE;
4874 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4875 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876
4877 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004878 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004879 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004880#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004881 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004882 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883#endif
4884
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004885 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004886 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 return; /* don't free the progs and patterns now */
4888 }
4889 }
4890
4891 /*
4892 * Something failed, free the allocated memory.
4893 */
4894 vim_free(item.sp_prog);
4895 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896 vim_free(syn_opt_arg.cont_list);
4897 vim_free(syn_opt_arg.cont_in_list);
4898 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899
4900 if (rest == NULL)
4901 EMSG2(_(e_invarg2), arg);
4902}
4903
4904/*
4905 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4906 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4907 */
4908 static void
4909syn_cmd_region(eap, syncing)
4910 exarg_T *eap;
4911 int syncing; /* TRUE for ":syntax sync region .." */
4912{
4913 char_u *arg = eap->arg;
4914 char_u *group_name_end;
4915 char_u *rest; /* next arg, NULL on error */
4916 char_u *key_end;
4917 char_u *key = NULL;
4918 char_u *p;
4919 int item;
4920#define ITEM_START 0
4921#define ITEM_SKIP 1
4922#define ITEM_END 2
4923#define ITEM_MATCHGROUP 3
4924 struct pat_ptr
4925 {
4926 synpat_T *pp_synp; /* pointer to syn_pattern */
4927 int pp_matchgroup_id; /* matchgroup ID */
4928 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4929 } *(pat_ptrs[3]);
4930 /* patterns found in the line */
4931 struct pat_ptr *ppp;
4932 struct pat_ptr *ppp_next;
4933 int pat_count = 0; /* nr of syn_patterns found */
4934 int syn_id;
4935 int matchgroup_id = 0;
4936 int not_enough = FALSE; /* not enough arguments */
4937 int illegal = FALSE; /* illegal arguments */
4938 int success = FALSE;
4939 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004940 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004941 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942
4943 /* Isolate the group name, check for validity */
4944 rest = get_group_name(arg, &group_name_end);
4945
4946 pat_ptrs[0] = NULL;
4947 pat_ptrs[1] = NULL;
4948 pat_ptrs[2] = NULL;
4949
4950 init_syn_patterns();
4951
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004952 syn_opt_arg.flags = 0;
4953 syn_opt_arg.keyword = FALSE;
4954 syn_opt_arg.sync_idx = NULL;
4955 syn_opt_arg.has_cont_list = TRUE;
4956 syn_opt_arg.cont_list = NULL;
4957 syn_opt_arg.cont_in_list = NULL;
4958 syn_opt_arg.next_list = NULL;
4959
Bram Moolenaar071d4272004-06-13 20:20:40 +00004960 /*
4961 * get the options, patterns and matchgroup.
4962 */
4963 while (rest != NULL && !ends_excmd(*rest))
4964 {
4965 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004966 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967 if (rest == NULL || ends_excmd(*rest))
4968 break;
4969
4970 /* must be a pattern or matchgroup then */
4971 key_end = rest;
4972 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4973 ++key_end;
4974 vim_free(key);
4975 key = vim_strnsave_up(rest, (int)(key_end - rest));
4976 if (key == NULL) /* out of memory */
4977 {
4978 rest = NULL;
4979 break;
4980 }
4981 if (STRCMP(key, "MATCHGROUP") == 0)
4982 item = ITEM_MATCHGROUP;
4983 else if (STRCMP(key, "START") == 0)
4984 item = ITEM_START;
4985 else if (STRCMP(key, "END") == 0)
4986 item = ITEM_END;
4987 else if (STRCMP(key, "SKIP") == 0)
4988 {
4989 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4990 {
4991 illegal = TRUE;
4992 break;
4993 }
4994 item = ITEM_SKIP;
4995 }
4996 else
4997 break;
4998 rest = skipwhite(key_end);
4999 if (*rest != '=')
5000 {
5001 rest = NULL;
5002 EMSG2(_("E398: Missing '=': %s"), arg);
5003 break;
5004 }
5005 rest = skipwhite(rest + 1);
5006 if (*rest == NUL)
5007 {
5008 not_enough = TRUE;
5009 break;
5010 }
5011
5012 if (item == ITEM_MATCHGROUP)
5013 {
5014 p = skiptowhite(rest);
5015 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5016 matchgroup_id = 0;
5017 else
5018 {
5019 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5020 if (matchgroup_id == 0)
5021 {
5022 illegal = TRUE;
5023 break;
5024 }
5025 }
5026 rest = skipwhite(p);
5027 }
5028 else
5029 {
5030 /*
5031 * Allocate room for a syn_pattern, and link it in the list of
5032 * syn_patterns for this item, at the start (because the list is
5033 * used from end to start).
5034 */
5035 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5036 if (ppp == NULL)
5037 {
5038 rest = NULL;
5039 break;
5040 }
5041 ppp->pp_next = pat_ptrs[item];
5042 pat_ptrs[item] = ppp;
5043 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5044 if (ppp->pp_synp == NULL)
5045 {
5046 rest = NULL;
5047 break;
5048 }
5049
5050 /*
5051 * Get the syntax pattern and the following offset(s).
5052 */
5053 /* Enable the appropriate \z specials. */
5054 if (item == ITEM_START)
5055 reg_do_extmatch = REX_SET;
5056 else if (item == ITEM_SKIP || item == ITEM_END)
5057 reg_do_extmatch = REX_USE;
5058 rest = get_syn_pattern(rest, ppp->pp_synp);
5059 reg_do_extmatch = 0;
5060 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005061 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005062 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5063 ppp->pp_matchgroup_id = matchgroup_id;
5064 ++pat_count;
5065 }
5066 }
5067 vim_free(key);
5068 if (illegal || not_enough)
5069 rest = NULL;
5070
5071 /*
5072 * Must have a "start" and "end" pattern.
5073 */
5074 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5075 pat_ptrs[ITEM_END] == NULL))
5076 {
5077 not_enough = TRUE;
5078 rest = NULL;
5079 }
5080
5081 if (rest != NULL)
5082 {
5083 /*
5084 * Check for trailing garbage or command.
5085 * If OK, add the item.
5086 */
5087 eap->nextcmd = check_nextcmd(rest);
5088 if (!ends_excmd(*rest) || eap->skip)
5089 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005090 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005091 && (syn_id = syn_check_group(arg,
5092 (int)(group_name_end - arg))) != 0)
5093 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005094 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005095 /*
5096 * Store the start/skip/end in the syn_items list
5097 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005098 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099 for (item = ITEM_START; item <= ITEM_END; ++item)
5100 {
5101 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5102 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005103 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5104 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5105 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 (item == ITEM_START) ? SPTYPE_START :
5107 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005108 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5109 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5110 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5111 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005112 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005113#ifdef FEAT_CONCEAL
5114 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5115#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005116 if (item == ITEM_START)
5117 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005118 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005119 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005120 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005121 syn_opt_arg.cont_in_list;
5122 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005123 curwin->w_s->b_syn_containedin = TRUE;
5124 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005125 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005126 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005127 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128 ++idx;
5129#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005130 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005131 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132#endif
5133 }
5134 }
5135
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005136 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005137 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005138 success = TRUE; /* don't free the progs and patterns now */
5139 }
5140 }
5141
5142 /*
5143 * Free the allocated memory.
5144 */
5145 for (item = ITEM_START; item <= ITEM_END; ++item)
5146 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5147 {
5148 if (!success)
5149 {
5150 vim_free(ppp->pp_synp->sp_prog);
5151 vim_free(ppp->pp_synp->sp_pattern);
5152 }
5153 vim_free(ppp->pp_synp);
5154 ppp_next = ppp->pp_next;
5155 vim_free(ppp);
5156 }
5157
5158 if (!success)
5159 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005160 vim_free(syn_opt_arg.cont_list);
5161 vim_free(syn_opt_arg.cont_in_list);
5162 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005163 if (not_enough)
5164 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5165 else if (illegal || rest == NULL)
5166 EMSG2(_(e_invarg2), arg);
5167 }
5168}
5169
5170/*
5171 * A simple syntax group ID comparison function suitable for use in qsort()
5172 */
5173 static int
5174#ifdef __BORLANDC__
5175_RTLENTRYF
5176#endif
5177syn_compare_stub(v1, v2)
5178 const void *v1;
5179 const void *v2;
5180{
5181 const short *s1 = v1;
5182 const short *s2 = v2;
5183
5184 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5185}
5186
5187/*
5188 * Combines lists of syntax clusters.
5189 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5190 */
5191 static void
5192syn_combine_list(clstr1, clstr2, list_op)
5193 short **clstr1;
5194 short **clstr2;
5195 int list_op;
5196{
5197 int count1 = 0;
5198 int count2 = 0;
5199 short *g1;
5200 short *g2;
5201 short *clstr = NULL;
5202 int count;
5203 int round;
5204
5205 /*
5206 * Handle degenerate cases.
5207 */
5208 if (*clstr2 == NULL)
5209 return;
5210 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5211 {
5212 if (list_op == CLUSTER_REPLACE)
5213 vim_free(*clstr1);
5214 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5215 *clstr1 = *clstr2;
5216 else
5217 vim_free(*clstr2);
5218 return;
5219 }
5220
5221 for (g1 = *clstr1; *g1; g1++)
5222 ++count1;
5223 for (g2 = *clstr2; *g2; g2++)
5224 ++count2;
5225
5226 /*
5227 * For speed purposes, sort both lists.
5228 */
5229 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5230 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5231
5232 /*
5233 * We proceed in two passes; in round 1, we count the elements to place
5234 * in the new list, and in round 2, we allocate and populate the new
5235 * list. For speed, we use a mergesort-like method, adding the smaller
5236 * of the current elements in each list to the new list.
5237 */
5238 for (round = 1; round <= 2; round++)
5239 {
5240 g1 = *clstr1;
5241 g2 = *clstr2;
5242 count = 0;
5243
5244 /*
5245 * First, loop through the lists until one of them is empty.
5246 */
5247 while (*g1 && *g2)
5248 {
5249 /*
5250 * We always want to add from the first list.
5251 */
5252 if (*g1 < *g2)
5253 {
5254 if (round == 2)
5255 clstr[count] = *g1;
5256 count++;
5257 g1++;
5258 continue;
5259 }
5260 /*
5261 * We only want to add from the second list if we're adding the
5262 * lists.
5263 */
5264 if (list_op == CLUSTER_ADD)
5265 {
5266 if (round == 2)
5267 clstr[count] = *g2;
5268 count++;
5269 }
5270 if (*g1 == *g2)
5271 g1++;
5272 g2++;
5273 }
5274
5275 /*
5276 * Now add the leftovers from whichever list didn't get finished
5277 * first. As before, we only want to add from the second list if
5278 * we're adding the lists.
5279 */
5280 for (; *g1; g1++, count++)
5281 if (round == 2)
5282 clstr[count] = *g1;
5283 if (list_op == CLUSTER_ADD)
5284 for (; *g2; g2++, count++)
5285 if (round == 2)
5286 clstr[count] = *g2;
5287
5288 if (round == 1)
5289 {
5290 /*
5291 * If the group ended up empty, we don't need to allocate any
5292 * space for it.
5293 */
5294 if (count == 0)
5295 {
5296 clstr = NULL;
5297 break;
5298 }
5299 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5300 if (clstr == NULL)
5301 break;
5302 clstr[count] = 0;
5303 }
5304 }
5305
5306 /*
5307 * Finally, put the new list in place.
5308 */
5309 vim_free(*clstr1);
5310 vim_free(*clstr2);
5311 *clstr1 = clstr;
5312}
5313
5314/*
5315 * Lookup a syntax cluster name and return it's ID.
5316 * If it is not found, 0 is returned.
5317 */
5318 static int
5319syn_scl_name2id(name)
5320 char_u *name;
5321{
5322 int i;
5323 char_u *name_u;
5324
5325 /* Avoid using stricmp() too much, it's slow on some systems */
5326 name_u = vim_strsave_up(name);
5327 if (name_u == NULL)
5328 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005329 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5330 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5331 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005332 break;
5333 vim_free(name_u);
5334 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5335}
5336
5337/*
5338 * Like syn_scl_name2id(), but take a pointer + length argument.
5339 */
5340 static int
5341syn_scl_namen2id(linep, len)
5342 char_u *linep;
5343 int len;
5344{
5345 char_u *name;
5346 int id = 0;
5347
5348 name = vim_strnsave(linep, len);
5349 if (name != NULL)
5350 {
5351 id = syn_scl_name2id(name);
5352 vim_free(name);
5353 }
5354 return id;
5355}
5356
5357/*
5358 * Find syntax cluster name in the table and return it's ID.
5359 * The argument is a pointer to the name and the length of the name.
5360 * If it doesn't exist yet, a new entry is created.
5361 * Return 0 for failure.
5362 */
5363 static int
5364syn_check_cluster(pp, len)
5365 char_u *pp;
5366 int len;
5367{
5368 int id;
5369 char_u *name;
5370
5371 name = vim_strnsave(pp, len);
5372 if (name == NULL)
5373 return 0;
5374
5375 id = syn_scl_name2id(name);
5376 if (id == 0) /* doesn't exist yet */
5377 id = syn_add_cluster(name);
5378 else
5379 vim_free(name);
5380 return id;
5381}
5382
5383/*
5384 * Add new syntax cluster and return it's ID.
5385 * "name" must be an allocated string, it will be consumed.
5386 * Return 0 for failure.
5387 */
5388 static int
5389syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005390 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005391{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005392 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393
5394 /*
5395 * First call for this growarray: init growing array.
5396 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005397 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005398 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005399 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5400 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005401 }
5402
5403 /*
5404 * Make room for at least one other cluster entry.
5405 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005406 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407 {
5408 vim_free(name);
5409 return 0;
5410 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005411 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005412
Bram Moolenaar860cae12010-06-05 23:22:07 +02005413 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5414 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5415 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5416 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5417 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005418
Bram Moolenaar217ad922005-03-20 22:37:15 +00005419 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005420 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005421 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005422 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005423
Bram Moolenaar071d4272004-06-13 20:20:40 +00005424 return len + SYNID_CLUSTER;
5425}
5426
5427/*
5428 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5429 * [add={groupname},..] [remove={groupname},..]".
5430 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005431 static void
5432syn_cmd_cluster(eap, syncing)
5433 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005434 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005435{
5436 char_u *arg = eap->arg;
5437 char_u *group_name_end;
5438 char_u *rest;
5439 int scl_id;
5440 short *clstr_list;
5441 int got_clstr = FALSE;
5442 int opt_len;
5443 int list_op;
5444
5445 eap->nextcmd = find_nextcmd(arg);
5446 if (eap->skip)
5447 return;
5448
5449 rest = get_group_name(arg, &group_name_end);
5450
5451 if (rest != NULL)
5452 {
5453 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005454 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005455
5456 for (;;)
5457 {
5458 if (STRNICMP(rest, "add", 3) == 0
5459 && (vim_iswhite(rest[3]) || rest[3] == '='))
5460 {
5461 opt_len = 3;
5462 list_op = CLUSTER_ADD;
5463 }
5464 else if (STRNICMP(rest, "remove", 6) == 0
5465 && (vim_iswhite(rest[6]) || rest[6] == '='))
5466 {
5467 opt_len = 6;
5468 list_op = CLUSTER_SUBTRACT;
5469 }
5470 else if (STRNICMP(rest, "contains", 8) == 0
5471 && (vim_iswhite(rest[8]) || rest[8] == '='))
5472 {
5473 opt_len = 8;
5474 list_op = CLUSTER_REPLACE;
5475 }
5476 else
5477 break;
5478
5479 clstr_list = NULL;
5480 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5481 {
5482 EMSG2(_(e_invarg2), rest);
5483 break;
5484 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005485 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005486 &clstr_list, list_op);
5487 got_clstr = TRUE;
5488 }
5489
5490 if (got_clstr)
5491 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005492 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005493 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494 }
5495 }
5496
5497 if (!got_clstr)
5498 EMSG(_("E400: No cluster specified"));
5499 if (rest == NULL || !ends_excmd(*rest))
5500 EMSG2(_(e_invarg2), arg);
5501}
5502
5503/*
5504 * On first call for current buffer: Init growing array.
5505 */
5506 static void
5507init_syn_patterns()
5508{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005509 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5510 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005511}
5512
5513/*
5514 * Get one pattern for a ":syntax match" or ":syntax region" command.
5515 * Stores the pattern and program in a synpat_T.
5516 * Returns a pointer to the next argument, or NULL in case of an error.
5517 */
5518 static char_u *
5519get_syn_pattern(arg, ci)
5520 char_u *arg;
5521 synpat_T *ci;
5522{
5523 char_u *end;
5524 int *p;
5525 int idx;
5526 char_u *cpo_save;
5527
5528 /* need at least three chars */
5529 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5530 return NULL;
5531
5532 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5533 if (*end != *arg) /* end delimiter not found */
5534 {
5535 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5536 return NULL;
5537 }
5538 /* store the pattern and compiled regexp program */
5539 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5540 return NULL;
5541
5542 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5543 cpo_save = p_cpo;
5544 p_cpo = (char_u *)"";
5545 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5546 p_cpo = cpo_save;
5547
5548 if (ci->sp_prog == NULL)
5549 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005550 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005551
5552 /*
5553 * Check for a match, highlight or region offset.
5554 */
5555 ++end;
5556 do
5557 {
5558 for (idx = SPO_COUNT; --idx >= 0; )
5559 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5560 break;
5561 if (idx >= 0)
5562 {
5563 p = &(ci->sp_offsets[idx]);
5564 if (idx != SPO_LC_OFF)
5565 switch (end[3])
5566 {
5567 case 's': break;
5568 case 'b': break;
5569 case 'e': idx += SPO_COUNT; break;
5570 default: idx = -1; break;
5571 }
5572 if (idx >= 0)
5573 {
5574 ci->sp_off_flags |= (1 << idx);
5575 if (idx == SPO_LC_OFF) /* lc=99 */
5576 {
5577 end += 3;
5578 *p = getdigits(&end);
5579
5580 /* "lc=" offset automatically sets "ms=" offset */
5581 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5582 {
5583 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5584 ci->sp_offsets[SPO_MS_OFF] = *p;
5585 }
5586 }
5587 else /* yy=x+99 */
5588 {
5589 end += 4;
5590 if (*end == '+')
5591 {
5592 ++end;
5593 *p = getdigits(&end); /* positive offset */
5594 }
5595 else if (*end == '-')
5596 {
5597 ++end;
5598 *p = -getdigits(&end); /* negative offset */
5599 }
5600 }
5601 if (*end != ',')
5602 break;
5603 ++end;
5604 }
5605 }
5606 } while (idx >= 0);
5607
5608 if (!ends_excmd(*end) && !vim_iswhite(*end))
5609 {
5610 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5611 return NULL;
5612 }
5613 return skipwhite(end);
5614}
5615
5616/*
5617 * Handle ":syntax sync .." command.
5618 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 static void
5620syn_cmd_sync(eap, syncing)
5621 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005622 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005623{
5624 char_u *arg_start = eap->arg;
5625 char_u *arg_end;
5626 char_u *key = NULL;
5627 char_u *next_arg;
5628 int illegal = FALSE;
5629 int finished = FALSE;
5630 long n;
5631 char_u *cpo_save;
5632
5633 if (ends_excmd(*arg_start))
5634 {
5635 syn_cmd_list(eap, TRUE);
5636 return;
5637 }
5638
5639 while (!ends_excmd(*arg_start))
5640 {
5641 arg_end = skiptowhite(arg_start);
5642 next_arg = skipwhite(arg_end);
5643 vim_free(key);
5644 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5645 if (STRCMP(key, "CCOMMENT") == 0)
5646 {
5647 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005648 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005649 if (!ends_excmd(*next_arg))
5650 {
5651 arg_end = skiptowhite(next_arg);
5652 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005653 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654 (int)(arg_end - next_arg));
5655 next_arg = skipwhite(arg_end);
5656 }
5657 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005658 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005659 }
5660 else if ( STRNCMP(key, "LINES", 5) == 0
5661 || STRNCMP(key, "MINLINES", 8) == 0
5662 || STRNCMP(key, "MAXLINES", 8) == 0
5663 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5664 {
5665 if (key[4] == 'S')
5666 arg_end = key + 6;
5667 else if (key[0] == 'L')
5668 arg_end = key + 11;
5669 else
5670 arg_end = key + 9;
5671 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5672 {
5673 illegal = TRUE;
5674 break;
5675 }
5676 n = getdigits(&arg_end);
5677 if (!eap->skip)
5678 {
5679 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005680 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005681 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005682 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005684 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 }
5686 }
5687 else if (STRCMP(key, "FROMSTART") == 0)
5688 {
5689 if (!eap->skip)
5690 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005691 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5692 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005693 }
5694 }
5695 else if (STRCMP(key, "LINECONT") == 0)
5696 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005697 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005698 {
5699 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5700 finished = TRUE;
5701 break;
5702 }
5703 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5704 if (*arg_end != *next_arg) /* end delimiter not found */
5705 {
5706 illegal = TRUE;
5707 break;
5708 }
5709
5710 if (!eap->skip)
5711 {
5712 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005713 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 (int)(arg_end - next_arg - 1))) == NULL)
5715 {
5716 finished = TRUE;
5717 break;
5718 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005719 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720
5721 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5722 cpo_save = p_cpo;
5723 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005724 curwin->w_s->b_syn_linecont_prog =
5725 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726 p_cpo = cpo_save;
5727
Bram Moolenaar860cae12010-06-05 23:22:07 +02005728 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005729 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005730 vim_free(curwin->w_s->b_syn_linecont_pat);
5731 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 finished = TRUE;
5733 break;
5734 }
5735 }
5736 next_arg = skipwhite(arg_end + 1);
5737 }
5738 else
5739 {
5740 eap->arg = next_arg;
5741 if (STRCMP(key, "MATCH") == 0)
5742 syn_cmd_match(eap, TRUE);
5743 else if (STRCMP(key, "REGION") == 0)
5744 syn_cmd_region(eap, TRUE);
5745 else if (STRCMP(key, "CLEAR") == 0)
5746 syn_cmd_clear(eap, TRUE);
5747 else
5748 illegal = TRUE;
5749 finished = TRUE;
5750 break;
5751 }
5752 arg_start = next_arg;
5753 }
5754 vim_free(key);
5755 if (illegal)
5756 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5757 else if (!finished)
5758 {
5759 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005760 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005761 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 }
5763}
5764
5765/*
5766 * Convert a line of highlight group names into a list of group ID numbers.
5767 * "arg" should point to the "contains" or "nextgroup" keyword.
5768 * "arg" is advanced to after the last group name.
5769 * Careful: the argument is modified (NULs added).
5770 * returns FAIL for some error, OK for success.
5771 */
5772 static int
5773get_id_list(arg, keylen, list)
5774 char_u **arg;
5775 int keylen; /* length of keyword */
5776 short **list; /* where to store the resulting list, if not
5777 NULL, the list is silently skipped! */
5778{
5779 char_u *p = NULL;
5780 char_u *end;
5781 int round;
5782 int count;
5783 int total_count = 0;
5784 short *retval = NULL;
5785 char_u *name;
5786 regmatch_T regmatch;
5787 int id;
5788 int i;
5789 int failed = FALSE;
5790
5791 /*
5792 * We parse the list twice:
5793 * round == 1: count the number of items, allocate the array.
5794 * round == 2: fill the array with the items.
5795 * In round 1 new groups may be added, causing the number of items to
5796 * grow when a regexp is used. In that case round 1 is done once again.
5797 */
5798 for (round = 1; round <= 2; ++round)
5799 {
5800 /*
5801 * skip "contains"
5802 */
5803 p = skipwhite(*arg + keylen);
5804 if (*p != '=')
5805 {
5806 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5807 break;
5808 }
5809 p = skipwhite(p + 1);
5810 if (ends_excmd(*p))
5811 {
5812 EMSG2(_("E406: Empty argument: %s"), *arg);
5813 break;
5814 }
5815
5816 /*
5817 * parse the arguments after "contains"
5818 */
5819 count = 0;
5820 while (!ends_excmd(*p))
5821 {
5822 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5823 ;
5824 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5825 if (name == NULL)
5826 {
5827 failed = TRUE;
5828 break;
5829 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005830 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005831 if ( STRCMP(name + 1, "ALLBUT") == 0
5832 || STRCMP(name + 1, "ALL") == 0
5833 || STRCMP(name + 1, "TOP") == 0
5834 || STRCMP(name + 1, "CONTAINED") == 0)
5835 {
5836 if (TOUPPER_ASC(**arg) != 'C')
5837 {
5838 EMSG2(_("E407: %s not allowed here"), name + 1);
5839 failed = TRUE;
5840 vim_free(name);
5841 break;
5842 }
5843 if (count != 0)
5844 {
5845 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5846 failed = TRUE;
5847 vim_free(name);
5848 break;
5849 }
5850 if (name[1] == 'A')
5851 id = SYNID_ALLBUT;
5852 else if (name[1] == 'T')
5853 id = SYNID_TOP;
5854 else
5855 id = SYNID_CONTAINED;
5856 id += current_syn_inc_tag;
5857 }
5858 else if (name[1] == '@')
5859 {
5860 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5861 }
5862 else
5863 {
5864 /*
5865 * Handle full group name.
5866 */
5867 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5868 id = syn_check_group(name + 1, (int)(end - p));
5869 else
5870 {
5871 /*
5872 * Handle match of regexp with group names.
5873 */
5874 *name = '^';
5875 STRCAT(name, "$");
5876 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5877 if (regmatch.regprog == NULL)
5878 {
5879 failed = TRUE;
5880 vim_free(name);
5881 break;
5882 }
5883
5884 regmatch.rm_ic = TRUE;
5885 id = 0;
5886 for (i = highlight_ga.ga_len; --i >= 0; )
5887 {
5888 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5889 (colnr_T)0))
5890 {
5891 if (round == 2)
5892 {
5893 /* Got more items than expected; can happen
5894 * when adding items that match:
5895 * "contains=a.*b,axb".
5896 * Go back to first round */
5897 if (count >= total_count)
5898 {
5899 vim_free(retval);
5900 round = 1;
5901 }
5902 else
5903 retval[count] = i + 1;
5904 }
5905 ++count;
5906 id = -1; /* remember that we found one */
5907 }
5908 }
5909 vim_free(regmatch.regprog);
5910 }
5911 }
5912 vim_free(name);
5913 if (id == 0)
5914 {
5915 EMSG2(_("E409: Unknown group name: %s"), p);
5916 failed = TRUE;
5917 break;
5918 }
5919 if (id > 0)
5920 {
5921 if (round == 2)
5922 {
5923 /* Got more items than expected, go back to first round */
5924 if (count >= total_count)
5925 {
5926 vim_free(retval);
5927 round = 1;
5928 }
5929 else
5930 retval[count] = id;
5931 }
5932 ++count;
5933 }
5934 p = skipwhite(end);
5935 if (*p != ',')
5936 break;
5937 p = skipwhite(p + 1); /* skip comma in between arguments */
5938 }
5939 if (failed)
5940 break;
5941 if (round == 1)
5942 {
5943 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5944 if (retval == NULL)
5945 break;
5946 retval[count] = 0; /* zero means end of the list */
5947 total_count = count;
5948 }
5949 }
5950
5951 *arg = p;
5952 if (failed || retval == NULL)
5953 {
5954 vim_free(retval);
5955 return FAIL;
5956 }
5957
5958 if (*list == NULL)
5959 *list = retval;
5960 else
5961 vim_free(retval); /* list already found, don't overwrite it */
5962
5963 return OK;
5964}
5965
5966/*
5967 * Make a copy of an ID list.
5968 */
5969 static short *
5970copy_id_list(list)
5971 short *list;
5972{
5973 int len;
5974 int count;
5975 short *retval;
5976
5977 if (list == NULL)
5978 return NULL;
5979
5980 for (count = 0; list[count]; ++count)
5981 ;
5982 len = (count + 1) * sizeof(short);
5983 retval = (short *)alloc((unsigned)len);
5984 if (retval != NULL)
5985 mch_memmove(retval, list, (size_t)len);
5986
5987 return retval;
5988}
5989
5990/*
5991 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5992 * "cur_si" can be NULL if not checking the "containedin" list.
5993 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5994 * the current item.
5995 * This function is called very often, keep it fast!!
5996 */
5997 static int
5998in_id_list(cur_si, list, ssp, contained)
5999 stateitem_T *cur_si; /* current item or NULL */
6000 short *list; /* id list */
6001 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6002 int contained; /* group id is contained */
6003{
6004 int retval;
6005 short *scl_list;
6006 short item;
6007 short id = ssp->id;
6008 static int depth = 0;
6009 int r;
6010
6011 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006012 if (cur_si != NULL && ssp->cont_in_list != NULL
6013 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006014 {
6015 /* Ignore transparent items without a contains argument. Double check
6016 * that we don't go back past the first one. */
6017 while ((cur_si->si_flags & HL_TRANS_CONT)
6018 && cur_si > (stateitem_T *)(current_state.ga_data))
6019 --cur_si;
6020 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6021 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006022 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6023 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006024 return TRUE;
6025 }
6026
6027 if (list == NULL)
6028 return FALSE;
6029
6030 /*
6031 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6032 * inside anything. Only allow not-contained groups.
6033 */
6034 if (list == ID_LIST_ALL)
6035 return !contained;
6036
6037 /*
6038 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6039 * contains list. We also require that "id" is at the same ":syn include"
6040 * level as the list.
6041 */
6042 item = *list;
6043 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6044 {
6045 if (item < SYNID_TOP)
6046 {
6047 /* ALL or ALLBUT: accept all groups in the same file */
6048 if (item - SYNID_ALLBUT != ssp->inc_tag)
6049 return FALSE;
6050 }
6051 else if (item < SYNID_CONTAINED)
6052 {
6053 /* TOP: accept all not-contained groups in the same file */
6054 if (item - SYNID_TOP != ssp->inc_tag || contained)
6055 return FALSE;
6056 }
6057 else
6058 {
6059 /* CONTAINED: accept all contained groups in the same file */
6060 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6061 return FALSE;
6062 }
6063 item = *++list;
6064 retval = FALSE;
6065 }
6066 else
6067 retval = TRUE;
6068
6069 /*
6070 * Return "retval" if id is in the contains list.
6071 */
6072 while (item != 0)
6073 {
6074 if (item == id)
6075 return retval;
6076 if (item >= SYNID_CLUSTER)
6077 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006078 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006079 /* restrict recursiveness to 30 to avoid an endless loop for a
6080 * cluster that includes itself (indirectly) */
6081 if (scl_list != NULL && depth < 30)
6082 {
6083 ++depth;
6084 r = in_id_list(NULL, scl_list, ssp, contained);
6085 --depth;
6086 if (r)
6087 return retval;
6088 }
6089 }
6090 item = *++list;
6091 }
6092 return !retval;
6093}
6094
6095struct subcommand
6096{
6097 char *name; /* subcommand name */
6098 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6099};
6100
6101static struct subcommand subcommands[] =
6102{
6103 {"case", syn_cmd_case},
6104 {"clear", syn_cmd_clear},
6105 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006106 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006107 {"enable", syn_cmd_enable},
6108 {"include", syn_cmd_include},
6109 {"keyword", syn_cmd_keyword},
6110 {"list", syn_cmd_list},
6111 {"manual", syn_cmd_manual},
6112 {"match", syn_cmd_match},
6113 {"on", syn_cmd_on},
6114 {"off", syn_cmd_off},
6115 {"region", syn_cmd_region},
6116 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006117 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006118 {"sync", syn_cmd_sync},
6119 {"", syn_cmd_list},
6120 {NULL, NULL}
6121};
6122
6123/*
6124 * ":syntax".
6125 * This searches the subcommands[] table for the subcommand name, and calls a
6126 * syntax_subcommand() function to do the rest.
6127 */
6128 void
6129ex_syntax(eap)
6130 exarg_T *eap;
6131{
6132 char_u *arg = eap->arg;
6133 char_u *subcmd_end;
6134 char_u *subcmd_name;
6135 int i;
6136
6137 syn_cmdlinep = eap->cmdlinep;
6138
6139 /* isolate subcommand name */
6140 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6141 ;
6142 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6143 if (subcmd_name != NULL)
6144 {
6145 if (eap->skip) /* skip error messages for all subcommands */
6146 ++emsg_skip;
6147 for (i = 0; ; ++i)
6148 {
6149 if (subcommands[i].name == NULL)
6150 {
6151 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6152 break;
6153 }
6154 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6155 {
6156 eap->arg = skipwhite(subcmd_end);
6157 (subcommands[i].func)(eap, FALSE);
6158 break;
6159 }
6160 }
6161 vim_free(subcmd_name);
6162 if (eap->skip)
6163 --emsg_skip;
6164 }
6165}
6166
Bram Moolenaar860cae12010-06-05 23:22:07 +02006167 void
6168ex_ownsyntax(eap)
6169 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006170{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006171 char_u *old_value;
6172 char_u *new_value;
6173
Bram Moolenaar860cae12010-06-05 23:22:07 +02006174 if (curwin->w_s == &curwin->w_buffer->b_s)
6175 {
6176 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6177 memset(curwin->w_s, 0, sizeof(synblock_T));
6178#ifdef FEAT_SPELL
6179 curwin->w_p_spell = FALSE; /* No spell checking */
6180 clear_string_option(&curwin->w_s->b_p_spc);
6181 clear_string_option(&curwin->w_s->b_p_spf);
6182 vim_free(curwin->w_s->b_cap_prog);
6183 curwin->w_s->b_cap_prog = NULL;
6184 clear_string_option(&curwin->w_s->b_p_spl);
6185#endif
6186 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006187
6188 /* save value of b:current_syntax */
6189 old_value = get_var_value((char_u *)"b:current_syntax");
6190 if (old_value != NULL)
6191 old_value = vim_strsave(old_value);
6192
6193 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6194 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006195 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006196
6197 /* move value of b:current_syntax to w:current_syntax */
6198 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006199 if (new_value != NULL)
6200 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006201
6202 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006203 if (old_value == NULL)
6204 do_unlet((char_u *)"b:current_syntax", TRUE);
6205 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006206 {
6207 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6208 vim_free(old_value);
6209 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006210}
6211
6212 int
6213syntax_present(win)
6214 win_T *win;
6215{
6216 return (win->w_s->b_syn_patterns.ga_len != 0
6217 || win->w_s->b_syn_clusters.ga_len != 0
6218 || win->w_s->b_keywtab.ht_used > 0
6219 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006220}
6221
6222#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6223
6224static enum
6225{
6226 EXP_SUBCMD, /* expand ":syn" sub-commands */
6227 EXP_CASE /* expand ":syn case" arguments */
6228} expand_what;
6229
Bram Moolenaar4f688582007-07-24 12:34:30 +00006230/*
6231 * Reset include_link, include_default, include_none to 0.
6232 * Called when we are done expanding.
6233 */
6234 void
6235reset_expand_highlight()
6236{
6237 include_link = include_default = include_none = 0;
6238}
6239
6240/*
6241 * Handle command line completion for :match and :echohl command: Add "None"
6242 * as highlight group.
6243 */
6244 void
6245set_context_in_echohl_cmd(xp, arg)
6246 expand_T *xp;
6247 char_u *arg;
6248{
6249 xp->xp_context = EXPAND_HIGHLIGHT;
6250 xp->xp_pattern = arg;
6251 include_none = 1;
6252}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006253
6254/*
6255 * Handle command line completion for :syntax command.
6256 */
6257 void
6258set_context_in_syntax_cmd(xp, arg)
6259 expand_T *xp;
6260 char_u *arg;
6261{
6262 char_u *p;
6263
6264 /* Default: expand subcommands */
6265 xp->xp_context = EXPAND_SYNTAX;
6266 expand_what = EXP_SUBCMD;
6267 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006268 include_link = 0;
6269 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006270
6271 /* (part of) subcommand already typed */
6272 if (*arg != NUL)
6273 {
6274 p = skiptowhite(arg);
6275 if (*p != NUL) /* past first word */
6276 {
6277 xp->xp_pattern = skipwhite(p);
6278 if (*skiptowhite(xp->xp_pattern) != NUL)
6279 xp->xp_context = EXPAND_NOTHING;
6280 else if (STRNICMP(arg, "case", p - arg) == 0)
6281 expand_what = EXP_CASE;
6282 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6283 || STRNICMP(arg, "region", p - arg) == 0
6284 || STRNICMP(arg, "match", p - arg) == 0
6285 || STRNICMP(arg, "list", p - arg) == 0)
6286 xp->xp_context = EXPAND_HIGHLIGHT;
6287 else
6288 xp->xp_context = EXPAND_NOTHING;
6289 }
6290 }
6291}
6292
6293static char *(case_args[]) = {"match", "ignore", NULL};
6294
6295/*
6296 * Function given to ExpandGeneric() to obtain the list syntax names for
6297 * expansion.
6298 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006299 char_u *
6300get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006301 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006302 int idx;
6303{
6304 if (expand_what == EXP_SUBCMD)
6305 return (char_u *)subcommands[idx].name;
6306 return (char_u *)case_args[idx];
6307}
6308
6309#endif /* FEAT_CMDL_COMPL */
6310
Bram Moolenaar071d4272004-06-13 20:20:40 +00006311/*
6312 * Function called for expression evaluation: get syntax ID at file position.
6313 */
6314 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006315syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006316 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006318 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006319 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006320 int *spellp; /* return: can do spell checking */
6321 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006322{
6323 /* When the position is not after the current position and in the same
6324 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006325 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006327 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006328 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006329
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006330 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331
6332 return (trans ? current_trans_id : current_id);
6333}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006334
Bram Moolenaar860cae12010-06-05 23:22:07 +02006335#if defined(FEAT_CONCEAL) || defined(PROTO)
6336/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006337 * Get extra information about the syntax item. Must be called right after
6338 * get_syntax_attr().
6339 * Stores the current item ID in "*idp".
6340 * Returns the current flags.
6341 */
6342 int
6343get_syntax_info(idp)
6344 int *idp;
6345{
6346 *idp = current_id;
6347 return current_flags;
6348}
6349
6350/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006351 * Return conceal substitution character
6352 */
6353 int
6354syn_get_sub_char()
6355{
6356 return current_sub_char;
6357}
6358#endif
6359
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006360#if defined(FEAT_EVAL) || defined(PROTO)
6361/*
6362 * Return the syntax ID at position "i" in the current stack.
6363 * The caller must have called syn_get_id() before to fill the stack.
6364 * Returns -1 when "i" is out of range.
6365 */
6366 int
6367syn_get_stack_item(i)
6368 int i;
6369{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006370 if (i >= current_state.ga_len)
6371 {
6372 /* Need to invalidate the state, because we didn't properly finish it
6373 * for the last character, "keep_state" was TRUE. */
6374 invalidate_current_state();
6375 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006376 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006377 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006378 return CUR_STATE(i).si_id;
6379}
6380#endif
6381
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382#if defined(FEAT_FOLDING) || defined(PROTO)
6383/*
6384 * Function called to get folding level for line "lnum" in window "wp".
6385 */
6386 int
6387syn_get_foldlevel(wp, lnum)
6388 win_T *wp;
6389 long lnum;
6390{
6391 int level = 0;
6392 int i;
6393
6394 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006395 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006396 {
6397 syntax_start(wp, lnum);
6398
6399 for (i = 0; i < current_state.ga_len; ++i)
6400 if (CUR_STATE(i).si_flags & HL_FOLD)
6401 ++level;
6402 }
6403 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006404 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006405 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006406 if (level < 0)
6407 level = 0;
6408 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409 return level;
6410}
6411#endif
6412
6413#endif /* FEAT_SYN_HL */
6414
6415
6416/**************************************
6417 * Highlighting stuff *
6418 **************************************/
6419
6420/*
6421 * The default highlight groups. These are compiled-in for fast startup and
6422 * they still work when the runtime files can't be found.
6423 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006424 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6425 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006426 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006427#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006428# define CENT(a, b) b
6429#else
6430# define CENT(a, b) a
6431#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006432static char *(highlight_init_both[]) =
6433 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006434 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6435 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6436 CENT("IncSearch term=reverse cterm=reverse",
6437 "IncSearch term=reverse cterm=reverse gui=reverse"),
6438 CENT("ModeMsg term=bold cterm=bold",
6439 "ModeMsg term=bold cterm=bold gui=bold"),
6440 CENT("NonText term=bold ctermfg=Blue",
6441 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6442 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6443 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6444 CENT("StatusLineNC term=reverse cterm=reverse",
6445 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006446#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006447 CENT("VertSplit term=reverse cterm=reverse",
6448 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006449#endif
6450#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006451 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6452 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006453#endif
6454#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006455 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6456 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006457#endif
6458#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006459 CENT("PmenuThumb cterm=reverse",
6460 "PmenuThumb cterm=reverse gui=reverse"),
6461 CENT("PmenuSbar ctermbg=Grey",
6462 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006463#endif
6464#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006465 CENT("TabLineSel term=bold cterm=bold",
6466 "TabLineSel term=bold cterm=bold gui=bold"),
6467 CENT("TabLineFill term=reverse cterm=reverse",
6468 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006469#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470#ifdef FEAT_GUI
6471 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006472 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006473#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006474 NULL
6475 };
6476
6477static char *(highlight_init_light[]) =
6478 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006479 CENT("Directory term=bold ctermfg=DarkBlue",
6480 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6481 CENT("LineNr term=underline ctermfg=Brown",
6482 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6483 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6484 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6485 CENT("Question term=standout ctermfg=DarkGreen",
6486 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6487 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6488 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006489#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006490 CENT("SpellBad term=reverse ctermbg=LightRed",
6491 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6492 CENT("SpellCap term=reverse ctermbg=LightBlue",
6493 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6494 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6495 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6496 CENT("SpellLocal term=underline ctermbg=Cyan",
6497 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006498#endif
6499#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006500 CENT("Pmenu ctermbg=LightMagenta",
6501 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6502 CENT("PmenuSel ctermbg=LightGrey",
6503 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006504#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006505 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6506 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6507 CENT("Title term=bold ctermfg=DarkMagenta",
6508 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6509 CENT("WarningMsg term=standout ctermfg=DarkRed",
6510 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006511#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006512 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6513 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006514#endif
6515#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006516 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6517 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6518 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6519 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006520#endif
6521#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006522 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6523 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006524#endif
6525#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006526 CENT("Visual term=reverse",
6527 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006528#endif
6529#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006530 CENT("DiffAdd term=bold ctermbg=LightBlue",
6531 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6532 CENT("DiffChange term=bold ctermbg=LightMagenta",
6533 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6534 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6535 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006536#endif
6537#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006538 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6539 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006540#endif
6541#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006542 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006543 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006544 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006545 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006546 CENT("ColorColumn term=reverse ctermbg=LightRed",
6547 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006548#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006549#ifdef FEAT_CONCEAL
6550 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6551 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6552#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006553#ifdef FEAT_AUTOCMD
6554 CENT("MatchParen term=reverse ctermbg=Cyan",
6555 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6556#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006557#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006558 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006559#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006560 NULL
6561 };
6562
6563static char *(highlight_init_dark[]) =
6564 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006565 CENT("Directory term=bold ctermfg=LightCyan",
6566 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6567 CENT("LineNr term=underline ctermfg=Yellow",
6568 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6569 CENT("MoreMsg term=bold ctermfg=LightGreen",
6570 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6571 CENT("Question term=standout ctermfg=LightGreen",
6572 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6573 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6574 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6575 CENT("SpecialKey term=bold ctermfg=LightBlue",
6576 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006577#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006578 CENT("SpellBad term=reverse ctermbg=Red",
6579 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6580 CENT("SpellCap term=reverse ctermbg=Blue",
6581 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6582 CENT("SpellRare term=reverse ctermbg=Magenta",
6583 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6584 CENT("SpellLocal term=underline ctermbg=Cyan",
6585 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006586#endif
6587#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006588 CENT("Pmenu ctermbg=Magenta",
6589 "Pmenu ctermbg=Magenta guibg=Magenta"),
6590 CENT("PmenuSel ctermbg=DarkGrey",
6591 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006592#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006593 CENT("Title term=bold ctermfg=LightMagenta",
6594 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6595 CENT("WarningMsg term=standout ctermfg=LightRed",
6596 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006597#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006598 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6599 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006600#endif
6601#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006602 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6603 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6604 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6605 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006606#endif
6607#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006608 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6609 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006610#endif
6611#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006612 CENT("Visual term=reverse",
6613 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006614#endif
6615#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006616 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6617 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6618 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6619 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6620 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6621 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006622#endif
6623#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006624 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6625 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006626#endif
6627#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006628 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006629 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006630 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006631 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006632 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6633 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006634#endif
6635#ifdef FEAT_AUTOCMD
6636 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6637 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006638#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006639#ifdef FEAT_CONCEAL
6640 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6641 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6642#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006643#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006644 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006645#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006646 NULL
6647 };
6648
6649 void
6650init_highlight(both, reset)
6651 int both; /* include groups where 'bg' doesn't matter */
6652 int reset; /* clear group first */
6653{
6654 int i;
6655 char **pp;
6656 static int had_both = FALSE;
6657#ifdef FEAT_EVAL
6658 char_u *p;
6659
6660 /*
6661 * Try finding the color scheme file. Used when a color file was loaded
6662 * and 'background' or 't_Co' is changed.
6663 */
6664 p = get_var_value((char_u *)"g:colors_name");
6665 if (p != NULL && load_colors(p) == OK)
6666 return;
6667#endif
6668
6669 /*
6670 * Didn't use a color file, use the compiled-in colors.
6671 */
6672 if (both)
6673 {
6674 had_both = TRUE;
6675 pp = highlight_init_both;
6676 for (i = 0; pp[i] != NULL; ++i)
6677 do_highlight((char_u *)pp[i], reset, TRUE);
6678 }
6679 else if (!had_both)
6680 /* Don't do anything before the call with both == TRUE from main().
6681 * Not everything has been setup then, and that call will overrule
6682 * everything anyway. */
6683 return;
6684
6685 if (*p_bg == 'l')
6686 pp = highlight_init_light;
6687 else
6688 pp = highlight_init_dark;
6689 for (i = 0; pp[i] != NULL; ++i)
6690 do_highlight((char_u *)pp[i], reset, TRUE);
6691
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006692 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006693 * depend on the number of colors available.
6694 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006695 * to avoid Statement highlighted text disappears.
6696 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006697 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006698 do_highlight((char_u *)(*p_bg == 'l'
6699 ? "Visual cterm=NONE ctermbg=LightGrey"
6700 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006701 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006702 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006703 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6704 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006705 if (*p_bg == 'l')
6706 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6707 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006708
Bram Moolenaar071d4272004-06-13 20:20:40 +00006709#ifdef FEAT_SYN_HL
6710 /*
6711 * If syntax highlighting is enabled load the highlighting for it.
6712 */
6713 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006714 {
6715 static int recursive = 0;
6716
6717 if (recursive >= 5)
6718 EMSG(_("E679: recursive loop loading syncolor.vim"));
6719 else
6720 {
6721 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006722 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006723 --recursive;
6724 }
6725 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006726#endif
6727}
6728
6729/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006730 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006731 * Return OK for success, FAIL for failure.
6732 */
6733 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006734load_colors(name)
6735 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006736{
6737 char_u *buf;
6738 int retval = FAIL;
6739 static int recursive = FALSE;
6740
6741 /* When being called recursively, this is probably because setting
6742 * 'background' caused the highlighting to be reloaded. This means it is
6743 * working, thus we should return OK. */
6744 if (recursive)
6745 return OK;
6746
6747 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006748 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006749 if (buf != NULL)
6750 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006751 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006752 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006753 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006754#ifdef FEAT_AUTOCMD
6755 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6756#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006757 }
6758 recursive = FALSE;
6759
6760 return retval;
6761}
6762
6763/*
6764 * Handle the ":highlight .." command.
6765 * When using ":hi clear" this is called recursively for each group with
6766 * "forceit" and "init" both TRUE.
6767 */
6768 void
6769do_highlight(line, forceit, init)
6770 char_u *line;
6771 int forceit;
6772 int init; /* TRUE when called for initializing */
6773{
6774 char_u *name_end;
6775 char_u *p;
6776 char_u *linep;
6777 char_u *key_start;
6778 char_u *arg_start;
6779 char_u *key = NULL, *arg = NULL;
6780 long i;
6781 int off;
6782 int len;
6783 int attr;
6784 int id;
6785 int idx;
6786 int dodefault = FALSE;
6787 int doclear = FALSE;
6788 int dolink = FALSE;
6789 int error = FALSE;
6790 int color;
6791 int is_normal_group = FALSE; /* "Normal" group */
6792#ifdef FEAT_GUI_X11
6793 int is_menu_group = FALSE; /* "Menu" group */
6794 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6795 int is_tooltip_group = FALSE; /* "Tooltip" group */
6796 int do_colors = FALSE; /* need to update colors? */
6797#else
6798# define is_menu_group 0
6799# define is_tooltip_group 0
6800#endif
6801
6802 /*
6803 * If no argument, list current highlighting.
6804 */
6805 if (ends_excmd(*line))
6806 {
6807 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6808 /* TODO: only call when the group has attributes set */
6809 highlight_list_one((int)i);
6810 return;
6811 }
6812
6813 /*
6814 * Isolate the name.
6815 */
6816 name_end = skiptowhite(line);
6817 linep = skipwhite(name_end);
6818
6819 /*
6820 * Check for "default" argument.
6821 */
6822 if (STRNCMP(line, "default", name_end - line) == 0)
6823 {
6824 dodefault = TRUE;
6825 line = linep;
6826 name_end = skiptowhite(line);
6827 linep = skipwhite(name_end);
6828 }
6829
6830 /*
6831 * Check for "clear" or "link" argument.
6832 */
6833 if (STRNCMP(line, "clear", name_end - line) == 0)
6834 doclear = TRUE;
6835 if (STRNCMP(line, "link", name_end - line) == 0)
6836 dolink = TRUE;
6837
6838 /*
6839 * ":highlight {group-name}": list highlighting for one group.
6840 */
6841 if (!doclear && !dolink && ends_excmd(*linep))
6842 {
6843 id = syn_namen2id(line, (int)(name_end - line));
6844 if (id == 0)
6845 EMSG2(_("E411: highlight group not found: %s"), line);
6846 else
6847 highlight_list_one(id);
6848 return;
6849 }
6850
6851 /*
6852 * Handle ":highlight link {from} {to}" command.
6853 */
6854 if (dolink)
6855 {
6856 char_u *from_start = linep;
6857 char_u *from_end;
6858 char_u *to_start;
6859 char_u *to_end;
6860 int from_id;
6861 int to_id;
6862
6863 from_end = skiptowhite(from_start);
6864 to_start = skipwhite(from_end);
6865 to_end = skiptowhite(to_start);
6866
6867 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6868 {
6869 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6870 from_start);
6871 return;
6872 }
6873
6874 if (!ends_excmd(*skipwhite(to_end)))
6875 {
6876 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6877 return;
6878 }
6879
6880 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6881 if (STRNCMP(to_start, "NONE", 4) == 0)
6882 to_id = 0;
6883 else
6884 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6885
6886 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6887 {
6888 /*
6889 * Don't allow a link when there already is some highlighting
6890 * for the group, unless '!' is used
6891 */
6892 if (to_id > 0 && !forceit && !init
6893 && hl_has_settings(from_id - 1, dodefault))
6894 {
6895 if (sourcing_name == NULL && !dodefault)
6896 EMSG(_("E414: group has settings, highlight link ignored"));
6897 }
6898 else
6899 {
6900 if (!init)
6901 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6902 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006903#ifdef FEAT_EVAL
6904 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6905#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006906 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006907 }
6908 }
6909
6910 /* Only call highlight_changed() once, after sourcing a syntax file */
6911 need_highlight_changed = TRUE;
6912
6913 return;
6914 }
6915
6916 if (doclear)
6917 {
6918 /*
6919 * ":highlight clear [group]" command.
6920 */
6921 line = linep;
6922 if (ends_excmd(*line))
6923 {
6924#ifdef FEAT_GUI
6925 /* First, we do not destroy the old values, but allocate the new
6926 * ones and update the display. THEN we destroy the old values.
6927 * If we destroy the old values first, then the old values
6928 * (such as GuiFont's or GuiFontset's) will still be displayed but
6929 * invalid because they were free'd.
6930 */
6931 if (gui.in_use)
6932 {
6933# ifdef FEAT_BEVAL_TIP
6934 gui_init_tooltip_font();
6935# endif
6936# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6937 gui_init_menu_font();
6938# endif
6939 }
6940# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6941 gui_mch_def_colors();
6942# endif
6943# ifdef FEAT_GUI_X11
6944# ifdef FEAT_MENU
6945
6946 /* This only needs to be done when there is no Menu highlight
6947 * group defined by default, which IS currently the case.
6948 */
6949 gui_mch_new_menu_colors();
6950# endif
6951 if (gui.in_use)
6952 {
6953 gui_new_scrollbar_colors();
6954# ifdef FEAT_BEVAL
6955 gui_mch_new_tooltip_colors();
6956# endif
6957# ifdef FEAT_MENU
6958 gui_mch_new_menu_font();
6959# endif
6960 }
6961# endif
6962
6963 /* Ok, we're done allocating the new default graphics items.
6964 * The screen should already be refreshed at this point.
6965 * It is now Ok to clear out the old data.
6966 */
6967#endif
6968#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006969 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006970#endif
6971 restore_cterm_colors();
6972
6973 /*
6974 * Clear all default highlight groups and load the defaults.
6975 */
6976 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6977 highlight_clear(idx);
6978 init_highlight(TRUE, TRUE);
6979#ifdef FEAT_GUI
6980 if (gui.in_use)
6981 highlight_gui_started();
6982#endif
6983 highlight_changed();
6984 redraw_later_clear();
6985 return;
6986 }
6987 name_end = skiptowhite(line);
6988 linep = skipwhite(name_end);
6989 }
6990
6991 /*
6992 * Find the group name in the table. If it does not exist yet, add it.
6993 */
6994 id = syn_check_group(line, (int)(name_end - line));
6995 if (id == 0) /* failed (out of memory) */
6996 return;
6997 idx = id - 1; /* index is ID minus one */
6998
6999 /* Return if "default" was used and the group already has settings. */
7000 if (dodefault && hl_has_settings(idx, TRUE))
7001 return;
7002
7003 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7004 is_normal_group = TRUE;
7005#ifdef FEAT_GUI_X11
7006 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7007 is_menu_group = TRUE;
7008 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7009 is_scrollbar_group = TRUE;
7010 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7011 is_tooltip_group = TRUE;
7012#endif
7013
7014 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7015 if (doclear || (forceit && init))
7016 {
7017 highlight_clear(idx);
7018 if (!doclear)
7019 HL_TABLE()[idx].sg_set = 0;
7020 }
7021
7022 if (!doclear)
7023 while (!ends_excmd(*linep))
7024 {
7025 key_start = linep;
7026 if (*linep == '=')
7027 {
7028 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7029 error = TRUE;
7030 break;
7031 }
7032
7033 /*
7034 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7035 * "guibg").
7036 */
7037 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7038 ++linep;
7039 vim_free(key);
7040 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7041 if (key == NULL)
7042 {
7043 error = TRUE;
7044 break;
7045 }
7046 linep = skipwhite(linep);
7047
7048 if (STRCMP(key, "NONE") == 0)
7049 {
7050 if (!init || HL_TABLE()[idx].sg_set == 0)
7051 {
7052 if (!init)
7053 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7054 highlight_clear(idx);
7055 }
7056 continue;
7057 }
7058
7059 /*
7060 * Check for the equal sign.
7061 */
7062 if (*linep != '=')
7063 {
7064 EMSG2(_("E416: missing equal sign: %s"), key_start);
7065 error = TRUE;
7066 break;
7067 }
7068 ++linep;
7069
7070 /*
7071 * Isolate the argument.
7072 */
7073 linep = skipwhite(linep);
7074 if (*linep == '\'') /* guifg='color name' */
7075 {
7076 arg_start = ++linep;
7077 linep = vim_strchr(linep, '\'');
7078 if (linep == NULL)
7079 {
7080 EMSG2(_(e_invarg2), key_start);
7081 error = TRUE;
7082 break;
7083 }
7084 }
7085 else
7086 {
7087 arg_start = linep;
7088 linep = skiptowhite(linep);
7089 }
7090 if (linep == arg_start)
7091 {
7092 EMSG2(_("E417: missing argument: %s"), key_start);
7093 error = TRUE;
7094 break;
7095 }
7096 vim_free(arg);
7097 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7098 if (arg == NULL)
7099 {
7100 error = TRUE;
7101 break;
7102 }
7103 if (*linep == '\'')
7104 ++linep;
7105
7106 /*
7107 * Store the argument.
7108 */
7109 if ( STRCMP(key, "TERM") == 0
7110 || STRCMP(key, "CTERM") == 0
7111 || STRCMP(key, "GUI") == 0)
7112 {
7113 attr = 0;
7114 off = 0;
7115 while (arg[off] != NUL)
7116 {
7117 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7118 {
7119 len = (int)STRLEN(hl_name_table[i]);
7120 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7121 {
7122 attr |= hl_attr_table[i];
7123 off += len;
7124 break;
7125 }
7126 }
7127 if (i < 0)
7128 {
7129 EMSG2(_("E418: Illegal value: %s"), arg);
7130 error = TRUE;
7131 break;
7132 }
7133 if (arg[off] == ',') /* another one follows */
7134 ++off;
7135 }
7136 if (error)
7137 break;
7138 if (*key == 'T')
7139 {
7140 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7141 {
7142 if (!init)
7143 HL_TABLE()[idx].sg_set |= SG_TERM;
7144 HL_TABLE()[idx].sg_term = attr;
7145 }
7146 }
7147 else if (*key == 'C')
7148 {
7149 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7150 {
7151 if (!init)
7152 HL_TABLE()[idx].sg_set |= SG_CTERM;
7153 HL_TABLE()[idx].sg_cterm = attr;
7154 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7155 }
7156 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007157#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007158 else
7159 {
7160 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7161 {
7162 if (!init)
7163 HL_TABLE()[idx].sg_set |= SG_GUI;
7164 HL_TABLE()[idx].sg_gui = attr;
7165 }
7166 }
7167#endif
7168 }
7169 else if (STRCMP(key, "FONT") == 0)
7170 {
7171 /* in non-GUI fonts are simply ignored */
7172#ifdef FEAT_GUI
7173 if (!gui.shell_created)
7174 {
7175 /* GUI not started yet, always accept the name. */
7176 vim_free(HL_TABLE()[idx].sg_font_name);
7177 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7178 }
7179 else
7180 {
7181 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7182# ifdef FEAT_XFONTSET
7183 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7184# endif
7185 /* First, save the current font/fontset.
7186 * Then try to allocate the font/fontset.
7187 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7188 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7189 */
7190
7191 HL_TABLE()[idx].sg_font = NOFONT;
7192# ifdef FEAT_XFONTSET
7193 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7194# endif
7195 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7196 is_tooltip_group);
7197
7198# ifdef FEAT_XFONTSET
7199 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7200 {
7201 /* New fontset was accepted. Free the old one, if there was
7202 * one.
7203 */
7204 gui_mch_free_fontset(temp_sg_fontset);
7205 vim_free(HL_TABLE()[idx].sg_font_name);
7206 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7207 }
7208 else
7209 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7210# endif
7211 if (HL_TABLE()[idx].sg_font != NOFONT)
7212 {
7213 /* New font was accepted. Free the old one, if there was
7214 * one.
7215 */
7216 gui_mch_free_font(temp_sg_font);
7217 vim_free(HL_TABLE()[idx].sg_font_name);
7218 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7219 }
7220 else
7221 HL_TABLE()[idx].sg_font = temp_sg_font;
7222 }
7223#endif
7224 }
7225 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7226 {
7227 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7228 {
7229 if (!init)
7230 HL_TABLE()[idx].sg_set |= SG_CTERM;
7231
7232 /* When setting the foreground color, and previously the "bold"
7233 * flag was set for a light color, reset it now */
7234 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7235 {
7236 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7237 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7238 }
7239
7240 if (VIM_ISDIGIT(*arg))
7241 color = atoi((char *)arg);
7242 else if (STRICMP(arg, "fg") == 0)
7243 {
7244 if (cterm_normal_fg_color)
7245 color = cterm_normal_fg_color - 1;
7246 else
7247 {
7248 EMSG(_("E419: FG color unknown"));
7249 error = TRUE;
7250 break;
7251 }
7252 }
7253 else if (STRICMP(arg, "bg") == 0)
7254 {
7255 if (cterm_normal_bg_color > 0)
7256 color = cterm_normal_bg_color - 1;
7257 else
7258 {
7259 EMSG(_("E420: BG color unknown"));
7260 error = TRUE;
7261 break;
7262 }
7263 }
7264 else
7265 {
7266 static char *(color_names[28]) = {
7267 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7268 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7269 "Gray", "Grey",
7270 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7271 "Blue", "LightBlue", "Green", "LightGreen",
7272 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7273 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7274 static int color_numbers_16[28] = {0, 1, 2, 3,
7275 4, 5, 6, 6,
7276 7, 7,
7277 7, 7, 8, 8,
7278 9, 9, 10, 10,
7279 11, 11, 12, 12, 13,
7280 13, 14, 14, 15, -1};
7281 /* for xterm with 88 colors... */
7282 static int color_numbers_88[28] = {0, 4, 2, 6,
7283 1, 5, 32, 72,
7284 84, 84,
7285 7, 7, 82, 82,
7286 12, 43, 10, 61,
7287 14, 63, 9, 74, 13,
7288 75, 11, 78, 15, -1};
7289 /* for xterm with 256 colors... */
7290 static int color_numbers_256[28] = {0, 4, 2, 6,
7291 1, 5, 130, 130,
7292 248, 248,
7293 7, 7, 242, 242,
7294 12, 81, 10, 121,
7295 14, 159, 9, 224, 13,
7296 225, 11, 229, 15, -1};
7297 /* for terminals with less than 16 colors... */
7298 static int color_numbers_8[28] = {0, 4, 2, 6,
7299 1, 5, 3, 3,
7300 7, 7,
7301 7, 7, 0+8, 0+8,
7302 4+8, 4+8, 2+8, 2+8,
7303 6+8, 6+8, 1+8, 1+8, 5+8,
7304 5+8, 3+8, 3+8, 7+8, -1};
7305#if defined(__QNXNTO__)
7306 static int *color_numbers_8_qansi = color_numbers_8;
7307 /* On qnx, the 8 & 16 color arrays are the same */
7308 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7309 color_numbers_8_qansi = color_numbers_16;
7310#endif
7311
7312 /* reduce calls to STRICMP a bit, it can be slow */
7313 off = TOUPPER_ASC(*arg);
7314 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7315 if (off == color_names[i][0]
7316 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7317 break;
7318 if (i < 0)
7319 {
7320 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7321 error = TRUE;
7322 break;
7323 }
7324
7325 /* Use the _16 table to check if its a valid color name. */
7326 color = color_numbers_16[i];
7327 if (color >= 0)
7328 {
7329 if (t_colors == 8)
7330 {
7331 /* t_Co is 8: use the 8 colors table */
7332#if defined(__QNXNTO__)
7333 color = color_numbers_8_qansi[i];
7334#else
7335 color = color_numbers_8[i];
7336#endif
7337 if (key[5] == 'F')
7338 {
7339 /* set/reset bold attribute to get light foreground
7340 * colors (on some terminals, e.g. "linux") */
7341 if (color & 8)
7342 {
7343 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7344 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7345 }
7346 else
7347 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7348 }
7349 color &= 7; /* truncate to 8 colors */
7350 }
7351 else if (t_colors == 16 || t_colors == 88
7352 || t_colors == 256)
7353 {
7354 /*
7355 * Guess: if the termcap entry ends in 'm', it is
7356 * probably an xterm-like terminal. Use the changed
7357 * order for colors.
7358 */
7359 if (*T_CAF != NUL)
7360 p = T_CAF;
7361 else
7362 p = T_CSF;
7363 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7364 switch (t_colors)
7365 {
7366 case 16:
7367 color = color_numbers_8[i];
7368 break;
7369 case 88:
7370 color = color_numbers_88[i];
7371 break;
7372 case 256:
7373 color = color_numbers_256[i];
7374 break;
7375 }
7376 }
7377 }
7378 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007379 /* Add one to the argument, to avoid zero. Zero is used for
7380 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007381 if (key[5] == 'F')
7382 {
7383 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7384 if (is_normal_group)
7385 {
7386 cterm_normal_fg_color = color + 1;
7387 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7388#ifdef FEAT_GUI
7389 /* Don't do this if the GUI is used. */
7390 if (!gui.in_use && !gui.starting)
7391#endif
7392 {
7393 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007394 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007395 term_fg_color(color);
7396 }
7397 }
7398 }
7399 else
7400 {
7401 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7402 if (is_normal_group)
7403 {
7404 cterm_normal_bg_color = color + 1;
7405#ifdef FEAT_GUI
7406 /* Don't mess with 'background' if the GUI is used. */
7407 if (!gui.in_use && !gui.starting)
7408#endif
7409 {
7410 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007411 if (color >= 0)
7412 {
7413 if (termcap_active)
7414 term_bg_color(color);
7415 if (t_colors < 16)
7416 i = (color == 0 || color == 4);
7417 else
7418 i = (color < 7 || color == 8);
7419 /* Set the 'background' option if the value is
7420 * wrong. */
7421 if (i != (*p_bg == 'd'))
7422 set_option_value((char_u *)"bg", 0L,
7423 i ? (char_u *)"dark"
7424 : (char_u *)"light", 0);
7425 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007426 }
7427 }
7428 }
7429 }
7430 }
7431 else if (STRCMP(key, "GUIFG") == 0)
7432 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007433#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007434 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007435 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007436 if (!init)
7437 HL_TABLE()[idx].sg_set |= SG_GUI;
7438
Bram Moolenaar61623362010-07-14 22:04:22 +02007439# ifdef FEAT_GUI
7440 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007441 i = color_name2handle(arg);
7442 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7443 {
7444 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007445# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007446 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7447 if (STRCMP(arg, "NONE"))
7448 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7449 else
7450 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007451# ifdef FEAT_GUI
7452# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007453 if (is_menu_group)
7454 gui.menu_fg_pixel = i;
7455 if (is_scrollbar_group)
7456 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007457# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007458 if (is_tooltip_group)
7459 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007460# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007461 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007462# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007463 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007464# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007465 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007466#endif
7467 }
7468 else if (STRCMP(key, "GUIBG") == 0)
7469 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007470#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007471 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007472 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007473 if (!init)
7474 HL_TABLE()[idx].sg_set |= SG_GUI;
7475
Bram Moolenaar61623362010-07-14 22:04:22 +02007476# ifdef FEAT_GUI
7477 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007478 i = color_name2handle(arg);
7479 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7480 {
7481 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007482# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007483 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7484 if (STRCMP(arg, "NONE") != 0)
7485 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7486 else
7487 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007488# ifdef FEAT_GUI
7489# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007490 if (is_menu_group)
7491 gui.menu_bg_pixel = i;
7492 if (is_scrollbar_group)
7493 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007494# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007495 if (is_tooltip_group)
7496 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007497# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007498 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007499# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007500 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007501# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007502 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007503#endif
7504 }
7505 else if (STRCMP(key, "GUISP") == 0)
7506 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007507#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007508 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7509 {
7510 if (!init)
7511 HL_TABLE()[idx].sg_set |= SG_GUI;
7512
Bram Moolenaar61623362010-07-14 22:04:22 +02007513# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007514 i = color_name2handle(arg);
7515 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7516 {
7517 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007518# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007519 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7520 if (STRCMP(arg, "NONE") != 0)
7521 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7522 else
7523 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007524# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007525 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007526# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007527 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007528#endif
7529 }
7530 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7531 {
7532 char_u buf[100];
7533 char_u *tname;
7534
7535 if (!init)
7536 HL_TABLE()[idx].sg_set |= SG_TERM;
7537
7538 /*
7539 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007540 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007541 */
7542 if (STRNCMP(arg, "t_", 2) == 0)
7543 {
7544 off = 0;
7545 buf[0] = 0;
7546 while (arg[off] != NUL)
7547 {
7548 /* Isolate one termcap name */
7549 for (len = 0; arg[off + len] &&
7550 arg[off + len] != ','; ++len)
7551 ;
7552 tname = vim_strnsave(arg + off, len);
7553 if (tname == NULL) /* out of memory */
7554 {
7555 error = TRUE;
7556 break;
7557 }
7558 /* lookup the escape sequence for the item */
7559 p = get_term_code(tname);
7560 vim_free(tname);
7561 if (p == NULL) /* ignore non-existing things */
7562 p = (char_u *)"";
7563
7564 /* Append it to the already found stuff */
7565 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7566 {
7567 EMSG2(_("E422: terminal code too long: %s"), arg);
7568 error = TRUE;
7569 break;
7570 }
7571 STRCAT(buf, p);
7572
7573 /* Advance to the next item */
7574 off += len;
7575 if (arg[off] == ',') /* another one follows */
7576 ++off;
7577 }
7578 }
7579 else
7580 {
7581 /*
7582 * Copy characters from arg[] to buf[], translating <> codes.
7583 */
7584 for (p = arg, off = 0; off < 100 && *p; )
7585 {
7586 len = trans_special(&p, buf + off, FALSE);
7587 if (len) /* recognized special char */
7588 off += len;
7589 else /* copy as normal char */
7590 buf[off++] = *p++;
7591 }
7592 buf[off] = NUL;
7593 }
7594 if (error)
7595 break;
7596
7597 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7598 p = NULL;
7599 else
7600 p = vim_strsave(buf);
7601 if (key[2] == 'A')
7602 {
7603 vim_free(HL_TABLE()[idx].sg_start);
7604 HL_TABLE()[idx].sg_start = p;
7605 }
7606 else
7607 {
7608 vim_free(HL_TABLE()[idx].sg_stop);
7609 HL_TABLE()[idx].sg_stop = p;
7610 }
7611 }
7612 else
7613 {
7614 EMSG2(_("E423: Illegal argument: %s"), key_start);
7615 error = TRUE;
7616 break;
7617 }
7618
7619 /*
7620 * When highlighting has been given for a group, don't link it.
7621 */
7622 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7623 HL_TABLE()[idx].sg_link = 0;
7624
7625 /*
7626 * Continue with next argument.
7627 */
7628 linep = skipwhite(linep);
7629 }
7630
7631 /*
7632 * If there is an error, and it's a new entry, remove it from the table.
7633 */
7634 if (error && idx == highlight_ga.ga_len)
7635 syn_unadd_group();
7636 else
7637 {
7638 if (is_normal_group)
7639 {
7640 HL_TABLE()[idx].sg_term_attr = 0;
7641 HL_TABLE()[idx].sg_cterm_attr = 0;
7642#ifdef FEAT_GUI
7643 HL_TABLE()[idx].sg_gui_attr = 0;
7644 /*
7645 * Need to update all groups, because they might be using "bg"
7646 * and/or "fg", which have been changed now.
7647 */
7648 if (gui.in_use)
7649 highlight_gui_started();
7650#endif
7651 }
7652#ifdef FEAT_GUI_X11
7653# ifdef FEAT_MENU
7654 else if (is_menu_group)
7655 {
7656 if (gui.in_use && do_colors)
7657 gui_mch_new_menu_colors();
7658 }
7659# endif
7660 else if (is_scrollbar_group)
7661 {
7662 if (gui.in_use && do_colors)
7663 gui_new_scrollbar_colors();
7664 }
7665# ifdef FEAT_BEVAL
7666 else if (is_tooltip_group)
7667 {
7668 if (gui.in_use && do_colors)
7669 gui_mch_new_tooltip_colors();
7670 }
7671# endif
7672#endif
7673 else
7674 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007675#ifdef FEAT_EVAL
7676 HL_TABLE()[idx].sg_scriptID = current_SID;
7677#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007678 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007679 }
7680 vim_free(key);
7681 vim_free(arg);
7682
7683 /* Only call highlight_changed() once, after sourcing a syntax file */
7684 need_highlight_changed = TRUE;
7685}
7686
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007687#if defined(EXITFREE) || defined(PROTO)
7688 void
7689free_highlight()
7690{
7691 int i;
7692
7693 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007694 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007695 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007696 vim_free(HL_TABLE()[i].sg_name);
7697 vim_free(HL_TABLE()[i].sg_name_u);
7698 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007699 ga_clear(&highlight_ga);
7700}
7701#endif
7702
Bram Moolenaar071d4272004-06-13 20:20:40 +00007703/*
7704 * Reset the cterm colors to what they were before Vim was started, if
7705 * possible. Otherwise reset them to zero.
7706 */
7707 void
7708restore_cterm_colors()
7709{
7710#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7711 /* Since t_me has been set, this probably means that the user
7712 * wants to use this as default colors. Need to reset default
7713 * background/foreground colors. */
7714 mch_set_normal_colors();
7715#else
7716 cterm_normal_fg_color = 0;
7717 cterm_normal_fg_bold = 0;
7718 cterm_normal_bg_color = 0;
7719#endif
7720}
7721
7722/*
7723 * Return TRUE if highlight group "idx" has any settings.
7724 * When "check_link" is TRUE also check for an existing link.
7725 */
7726 static int
7727hl_has_settings(idx, check_link)
7728 int idx;
7729 int check_link;
7730{
7731 return ( HL_TABLE()[idx].sg_term_attr != 0
7732 || HL_TABLE()[idx].sg_cterm_attr != 0
7733#ifdef FEAT_GUI
7734 || HL_TABLE()[idx].sg_gui_attr != 0
7735#endif
7736 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7737}
7738
7739/*
7740 * Clear highlighting for one group.
7741 */
7742 static void
7743highlight_clear(idx)
7744 int idx;
7745{
7746 HL_TABLE()[idx].sg_term = 0;
7747 vim_free(HL_TABLE()[idx].sg_start);
7748 HL_TABLE()[idx].sg_start = NULL;
7749 vim_free(HL_TABLE()[idx].sg_stop);
7750 HL_TABLE()[idx].sg_stop = NULL;
7751 HL_TABLE()[idx].sg_term_attr = 0;
7752 HL_TABLE()[idx].sg_cterm = 0;
7753 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7754 HL_TABLE()[idx].sg_cterm_fg = 0;
7755 HL_TABLE()[idx].sg_cterm_bg = 0;
7756 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007757#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007758 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007759 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7760 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007761 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7762 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007763 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7764 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007765#endif
7766#ifdef FEAT_GUI
7767 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7768 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7769 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007770 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7771 HL_TABLE()[idx].sg_font = NOFONT;
7772# ifdef FEAT_XFONTSET
7773 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7774 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7775# endif
7776 vim_free(HL_TABLE()[idx].sg_font_name);
7777 HL_TABLE()[idx].sg_font_name = NULL;
7778 HL_TABLE()[idx].sg_gui_attr = 0;
7779#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007780#ifdef FEAT_EVAL
7781 /* Clear the script ID only when there is no link, since that is not
7782 * cleared. */
7783 if (HL_TABLE()[idx].sg_link == 0)
7784 HL_TABLE()[idx].sg_scriptID = 0;
7785#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007786}
7787
7788#if defined(FEAT_GUI) || defined(PROTO)
7789/*
7790 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007791 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007792 * "Tooltip" colors.
7793 */
7794 void
7795set_normal_colors()
7796{
7797 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007798 &gui.norm_pixel, &gui.back_pixel,
7799 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800 {
7801 gui_mch_new_colors();
7802 must_redraw = CLEAR;
7803 }
7804#ifdef FEAT_GUI_X11
7805 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007806 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7807 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 {
7809# ifdef FEAT_MENU
7810 gui_mch_new_menu_colors();
7811# endif
7812 must_redraw = CLEAR;
7813 }
7814# ifdef FEAT_BEVAL
7815 if (set_group_colors((char_u *)"Tooltip",
7816 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7817 FALSE, FALSE, TRUE))
7818 {
7819# ifdef FEAT_TOOLBAR
7820 gui_mch_new_tooltip_colors();
7821# endif
7822 must_redraw = CLEAR;
7823 }
7824#endif
7825 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007826 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7827 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828 {
7829 gui_new_scrollbar_colors();
7830 must_redraw = CLEAR;
7831 }
7832#endif
7833}
7834
7835/*
7836 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7837 */
7838 static int
7839set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7840 char_u *name;
7841 guicolor_T *fgp;
7842 guicolor_T *bgp;
7843 int do_menu;
7844 int use_norm;
7845 int do_tooltip;
7846{
7847 int idx;
7848
7849 idx = syn_name2id(name) - 1;
7850 if (idx >= 0)
7851 {
7852 gui_do_one_color(idx, do_menu, do_tooltip);
7853
7854 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7855 *fgp = HL_TABLE()[idx].sg_gui_fg;
7856 else if (use_norm)
7857 *fgp = gui.def_norm_pixel;
7858 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7859 *bgp = HL_TABLE()[idx].sg_gui_bg;
7860 else if (use_norm)
7861 *bgp = gui.def_back_pixel;
7862 return TRUE;
7863 }
7864 return FALSE;
7865}
7866
7867/*
7868 * Get the font of the "Normal" group.
7869 * Returns "" when it's not found or not set.
7870 */
7871 char_u *
7872hl_get_font_name()
7873{
7874 int id;
7875 char_u *s;
7876
7877 id = syn_name2id((char_u *)"Normal");
7878 if (id > 0)
7879 {
7880 s = HL_TABLE()[id - 1].sg_font_name;
7881 if (s != NULL)
7882 return s;
7883 }
7884 return (char_u *)"";
7885}
7886
7887/*
7888 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7889 * actually chosen to be used.
7890 */
7891 void
7892hl_set_font_name(font_name)
7893 char_u *font_name;
7894{
7895 int id;
7896
7897 id = syn_name2id((char_u *)"Normal");
7898 if (id > 0)
7899 {
7900 vim_free(HL_TABLE()[id - 1].sg_font_name);
7901 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7902 }
7903}
7904
7905/*
7906 * Set background color for "Normal" group. Called by gui_set_bg_color()
7907 * when the color is known.
7908 */
7909 void
7910hl_set_bg_color_name(name)
7911 char_u *name; /* must have been allocated */
7912{
7913 int id;
7914
7915 if (name != NULL)
7916 {
7917 id = syn_name2id((char_u *)"Normal");
7918 if (id > 0)
7919 {
7920 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7921 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7922 }
7923 }
7924}
7925
7926/*
7927 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7928 * when the color is known.
7929 */
7930 void
7931hl_set_fg_color_name(name)
7932 char_u *name; /* must have been allocated */
7933{
7934 int id;
7935
7936 if (name != NULL)
7937 {
7938 id = syn_name2id((char_u *)"Normal");
7939 if (id > 0)
7940 {
7941 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7942 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7943 }
7944 }
7945}
7946
7947/*
7948 * Return the handle for a color name.
7949 * Returns INVALCOLOR when failed.
7950 */
7951 static guicolor_T
7952color_name2handle(name)
7953 char_u *name;
7954{
7955 if (STRCMP(name, "NONE") == 0)
7956 return INVALCOLOR;
7957
7958 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7959 return gui.norm_pixel;
7960 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7961 return gui.back_pixel;
7962
7963 return gui_get_color(name);
7964}
7965
7966/*
7967 * Return the handle for a font name.
7968 * Returns NOFONT when failed.
7969 */
7970 static GuiFont
7971font_name2handle(name)
7972 char_u *name;
7973{
7974 if (STRCMP(name, "NONE") == 0)
7975 return NOFONT;
7976
7977 return gui_mch_get_font(name, TRUE);
7978}
7979
7980# ifdef FEAT_XFONTSET
7981/*
7982 * Return the handle for a fontset name.
7983 * Returns NOFONTSET when failed.
7984 */
7985 static GuiFontset
7986fontset_name2handle(name, fixed_width)
7987 char_u *name;
7988 int fixed_width;
7989{
7990 if (STRCMP(name, "NONE") == 0)
7991 return NOFONTSET;
7992
7993 return gui_mch_get_fontset(name, TRUE, fixed_width);
7994}
7995# endif
7996
7997/*
7998 * Get the font or fontset for one highlight group.
7999 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008000 static void
8001hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8002 int idx;
8003 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008004 int do_normal; /* set normal font */
8005 int do_menu UNUSED; /* set menu font */
8006 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008007{
8008# ifdef FEAT_XFONTSET
8009 /* If 'guifontset' is not empty, first try using the name as a
8010 * fontset. If that doesn't work, use it as a font name. */
8011 if (*p_guifontset != NUL
8012# ifdef FONTSET_ALWAYS
8013 || do_menu
8014# endif
8015# ifdef FEAT_BEVAL_TIP
8016 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8017 || do_tooltip
8018# endif
8019 )
8020 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8021# ifdef FONTSET_ALWAYS
8022 || do_menu
8023# endif
8024# ifdef FEAT_BEVAL_TIP
8025 || do_tooltip
8026# endif
8027 );
8028 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8029 {
8030 /* If it worked and it's the Normal group, use it as the
8031 * normal fontset. Same for the Menu group. */
8032 if (do_normal)
8033 gui_init_font(arg, TRUE);
8034# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8035 if (do_menu)
8036 {
8037# ifdef FONTSET_ALWAYS
8038 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8039# else
8040 /* YIKES! This is a bug waiting to crash the program */
8041 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8042# endif
8043 gui_mch_new_menu_font();
8044 }
8045# ifdef FEAT_BEVAL
8046 if (do_tooltip)
8047 {
8048 /* The Athena widget set cannot currently handle switching between
8049 * displaying a single font and a fontset.
8050 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008051 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008052 * XFontStruct is used.
8053 */
8054 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8055 gui_mch_new_tooltip_font();
8056 }
8057# endif
8058# endif
8059 }
8060 else
8061# endif
8062 {
8063 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8064 /* If it worked and it's the Normal group, use it as the
8065 * normal font. Same for the Menu group. */
8066 if (HL_TABLE()[idx].sg_font != NOFONT)
8067 {
8068 if (do_normal)
8069 gui_init_font(arg, FALSE);
8070#ifndef FONTSET_ALWAYS
8071# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8072 if (do_menu)
8073 {
8074 gui.menu_font = HL_TABLE()[idx].sg_font;
8075 gui_mch_new_menu_font();
8076 }
8077# endif
8078#endif
8079 }
8080 }
8081}
8082
8083#endif /* FEAT_GUI */
8084
8085/*
8086 * Table with the specifications for an attribute number.
8087 * Note that this table is used by ALL buffers. This is required because the
8088 * GUI can redraw at any time for any buffer.
8089 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008090static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008091
8092#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8093
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008094static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008095
8096#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8097
8098#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008099static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008100
8101#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8102#endif
8103
8104/*
8105 * Return the attr number for a set of colors and font.
8106 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8107 * if the combination is new.
8108 * Return 0 for error (no more room).
8109 */
8110 static int
8111get_attr_entry(table, aep)
8112 garray_T *table;
8113 attrentry_T *aep;
8114{
8115 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008116 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008117 static int recursive = FALSE;
8118
8119 /*
8120 * Init the table, in case it wasn't done yet.
8121 */
8122 table->ga_itemsize = sizeof(attrentry_T);
8123 table->ga_growsize = 7;
8124
8125 /*
8126 * Try to find an entry with the same specifications.
8127 */
8128 for (i = 0; i < table->ga_len; ++i)
8129 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008130 taep = &(((attrentry_T *)table->ga_data)[i]);
8131 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008132 && (
8133#ifdef FEAT_GUI
8134 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008135 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8136 && aep->ae_u.gui.bg_color
8137 == taep->ae_u.gui.bg_color
8138 && aep->ae_u.gui.sp_color
8139 == taep->ae_u.gui.sp_color
8140 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008142 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008143# endif
8144 ))
8145 ||
8146#endif
8147 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008148 && (aep->ae_u.term.start == NULL)
8149 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008150 && (aep->ae_u.term.start == NULL
8151 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008152 taep->ae_u.term.start) == 0)
8153 && (aep->ae_u.term.stop == NULL)
8154 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008155 && (aep->ae_u.term.stop == NULL
8156 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008157 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008158 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008159 && aep->ae_u.cterm.fg_color
8160 == taep->ae_u.cterm.fg_color
8161 && aep->ae_u.cterm.bg_color
8162 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008163 ))
8164
8165 return i + ATTR_OFF;
8166 }
8167
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008168 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169 {
8170 /*
8171 * Running out of attribute entries! remove all attributes, and
8172 * compute new ones for all groups.
8173 * When called recursively, we are really out of numbers.
8174 */
8175 if (recursive)
8176 {
8177 EMSG(_("E424: Too many different highlighting attributes in use"));
8178 return 0;
8179 }
8180 recursive = TRUE;
8181
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008182 clear_hl_tables();
8183
Bram Moolenaar071d4272004-06-13 20:20:40 +00008184 must_redraw = CLEAR;
8185
8186 for (i = 0; i < highlight_ga.ga_len; ++i)
8187 set_hl_attr(i);
8188
8189 recursive = FALSE;
8190 }
8191
8192 /*
8193 * This is a new combination of colors and font, add an entry.
8194 */
8195 if (ga_grow(table, 1) == FAIL)
8196 return 0;
8197
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008198 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8199 vim_memset(taep, 0, sizeof(attrentry_T));
8200 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008201#ifdef FEAT_GUI
8202 if (table == &gui_attr_table)
8203 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008204 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8205 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8206 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8207 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008209 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210# endif
8211 }
8212#endif
8213 if (table == &term_attr_table)
8214 {
8215 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008216 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008218 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008220 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008221 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008222 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223 }
8224 else if (table == &cterm_attr_table)
8225 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008226 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8227 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008228 }
8229 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008230 return (table->ga_len - 1 + ATTR_OFF);
8231}
8232
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008233/*
8234 * Clear all highlight tables.
8235 */
8236 void
8237clear_hl_tables()
8238{
8239 int i;
8240 attrentry_T *taep;
8241
8242#ifdef FEAT_GUI
8243 ga_clear(&gui_attr_table);
8244#endif
8245 for (i = 0; i < term_attr_table.ga_len; ++i)
8246 {
8247 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8248 vim_free(taep->ae_u.term.start);
8249 vim_free(taep->ae_u.term.stop);
8250 }
8251 ga_clear(&term_attr_table);
8252 ga_clear(&cterm_attr_table);
8253}
8254
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008255#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008256/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008257 * Combine special attributes (e.g., for spelling) with other attributes
8258 * (e.g., for syntax highlighting).
8259 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008260 * This creates a new group when required.
8261 * Since we expect there to be few spelling mistakes we don't cache the
8262 * result.
8263 * Return the resulting attributes.
8264 */
8265 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008266hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008267 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008268 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008269{
8270 attrentry_T *char_aep = NULL;
8271 attrentry_T *spell_aep;
8272 attrentry_T new_en;
8273
8274 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008275 return prim_attr;
8276 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8277 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008278#ifdef FEAT_GUI
8279 if (gui.in_use)
8280 {
8281 if (char_attr > HL_ALL)
8282 char_aep = syn_gui_attr2entry(char_attr);
8283 if (char_aep != NULL)
8284 new_en = *char_aep;
8285 else
8286 {
8287 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008288 new_en.ae_u.gui.fg_color = INVALCOLOR;
8289 new_en.ae_u.gui.bg_color = INVALCOLOR;
8290 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008291 if (char_attr <= HL_ALL)
8292 new_en.ae_attr = char_attr;
8293 }
8294
Bram Moolenaar30abd282005-06-22 22:35:10 +00008295 if (prim_attr <= HL_ALL)
8296 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008297 else
8298 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008299 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008300 if (spell_aep != NULL)
8301 {
8302 new_en.ae_attr |= spell_aep->ae_attr;
8303 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8304 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8305 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8306 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8307 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8308 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8309 if (spell_aep->ae_u.gui.font != NOFONT)
8310 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8311# ifdef FEAT_XFONTSET
8312 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8313 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8314# endif
8315 }
8316 }
8317 return get_attr_entry(&gui_attr_table, &new_en);
8318 }
8319#endif
8320
8321 if (t_colors > 1)
8322 {
8323 if (char_attr > HL_ALL)
8324 char_aep = syn_cterm_attr2entry(char_attr);
8325 if (char_aep != NULL)
8326 new_en = *char_aep;
8327 else
8328 {
8329 vim_memset(&new_en, 0, sizeof(new_en));
8330 if (char_attr <= HL_ALL)
8331 new_en.ae_attr = char_attr;
8332 }
8333
Bram Moolenaar30abd282005-06-22 22:35:10 +00008334 if (prim_attr <= HL_ALL)
8335 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008336 else
8337 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008338 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008339 if (spell_aep != NULL)
8340 {
8341 new_en.ae_attr |= spell_aep->ae_attr;
8342 if (spell_aep->ae_u.cterm.fg_color > 0)
8343 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8344 if (spell_aep->ae_u.cterm.bg_color > 0)
8345 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8346 }
8347 }
8348 return get_attr_entry(&cterm_attr_table, &new_en);
8349 }
8350
8351 if (char_attr > HL_ALL)
8352 char_aep = syn_term_attr2entry(char_attr);
8353 if (char_aep != NULL)
8354 new_en = *char_aep;
8355 else
8356 {
8357 vim_memset(&new_en, 0, sizeof(new_en));
8358 if (char_attr <= HL_ALL)
8359 new_en.ae_attr = char_attr;
8360 }
8361
Bram Moolenaar30abd282005-06-22 22:35:10 +00008362 if (prim_attr <= HL_ALL)
8363 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008364 else
8365 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008366 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008367 if (spell_aep != NULL)
8368 {
8369 new_en.ae_attr |= spell_aep->ae_attr;
8370 if (spell_aep->ae_u.term.start != NULL)
8371 {
8372 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8373 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8374 }
8375 }
8376 }
8377 return get_attr_entry(&term_attr_table, &new_en);
8378}
8379#endif
8380
Bram Moolenaar071d4272004-06-13 20:20:40 +00008381#ifdef FEAT_GUI
8382
8383 attrentry_T *
8384syn_gui_attr2entry(attr)
8385 int attr;
8386{
8387 attr -= ATTR_OFF;
8388 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8389 return NULL;
8390 return &(GUI_ATTR_ENTRY(attr));
8391}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008392#endif /* FEAT_GUI */
8393
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008394/*
8395 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8396 * Only to be used when "attr" > HL_ALL.
8397 */
8398 int
8399syn_attr2attr(attr)
8400 int attr;
8401{
8402 attrentry_T *aep;
8403
8404#ifdef FEAT_GUI
8405 if (gui.in_use)
8406 aep = syn_gui_attr2entry(attr);
8407 else
8408#endif
8409 if (t_colors > 1)
8410 aep = syn_cterm_attr2entry(attr);
8411 else
8412 aep = syn_term_attr2entry(attr);
8413
8414 if (aep == NULL) /* highlighting not set */
8415 return 0;
8416 return aep->ae_attr;
8417}
8418
8419
Bram Moolenaar071d4272004-06-13 20:20:40 +00008420 attrentry_T *
8421syn_term_attr2entry(attr)
8422 int attr;
8423{
8424 attr -= ATTR_OFF;
8425 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8426 return NULL;
8427 return &(TERM_ATTR_ENTRY(attr));
8428}
8429
8430 attrentry_T *
8431syn_cterm_attr2entry(attr)
8432 int attr;
8433{
8434 attr -= ATTR_OFF;
8435 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8436 return NULL;
8437 return &(CTERM_ATTR_ENTRY(attr));
8438}
8439
8440#define LIST_ATTR 1
8441#define LIST_STRING 2
8442#define LIST_INT 3
8443
8444 static void
8445highlight_list_one(id)
8446 int id;
8447{
8448 struct hl_group *sgp;
8449 int didh = FALSE;
8450
8451 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8452
8453 didh = highlight_list_arg(id, didh, LIST_ATTR,
8454 sgp->sg_term, NULL, "term");
8455 didh = highlight_list_arg(id, didh, LIST_STRING,
8456 0, sgp->sg_start, "start");
8457 didh = highlight_list_arg(id, didh, LIST_STRING,
8458 0, sgp->sg_stop, "stop");
8459
8460 didh = highlight_list_arg(id, didh, LIST_ATTR,
8461 sgp->sg_cterm, NULL, "cterm");
8462 didh = highlight_list_arg(id, didh, LIST_INT,
8463 sgp->sg_cterm_fg, NULL, "ctermfg");
8464 didh = highlight_list_arg(id, didh, LIST_INT,
8465 sgp->sg_cterm_bg, NULL, "ctermbg");
8466
Bram Moolenaar61623362010-07-14 22:04:22 +02008467#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008468 didh = highlight_list_arg(id, didh, LIST_ATTR,
8469 sgp->sg_gui, NULL, "gui");
8470 didh = highlight_list_arg(id, didh, LIST_STRING,
8471 0, sgp->sg_gui_fg_name, "guifg");
8472 didh = highlight_list_arg(id, didh, LIST_STRING,
8473 0, sgp->sg_gui_bg_name, "guibg");
8474 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008475 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008476#endif
8477#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008478 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008479 0, sgp->sg_font_name, "font");
8480#endif
8481
Bram Moolenaar661b1822005-07-28 22:36:45 +00008482 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483 {
8484 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008485 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008486 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8487 msg_putchar(' ');
8488 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8489 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008490
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008491 if (!didh)
8492 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008493#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008494 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008495 last_set_msg(sgp->sg_scriptID);
8496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008497}
8498
8499 static int
8500highlight_list_arg(id, didh, type, iarg, sarg, name)
8501 int id;
8502 int didh;
8503 int type;
8504 int iarg;
8505 char_u *sarg;
8506 char *name;
8507{
8508 char_u buf[100];
8509 char_u *ts;
8510 int i;
8511
Bram Moolenaar661b1822005-07-28 22:36:45 +00008512 if (got_int)
8513 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008514 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8515 {
8516 ts = buf;
8517 if (type == LIST_INT)
8518 sprintf((char *)buf, "%d", iarg - 1);
8519 else if (type == LIST_STRING)
8520 ts = sarg;
8521 else /* type == LIST_ATTR */
8522 {
8523 buf[0] = NUL;
8524 for (i = 0; hl_attr_table[i] != 0; ++i)
8525 {
8526 if (iarg & hl_attr_table[i])
8527 {
8528 if (buf[0] != NUL)
8529 STRCAT(buf, ",");
8530 STRCAT(buf, hl_name_table[i]);
8531 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8532 }
8533 }
8534 }
8535
8536 (void)syn_list_header(didh,
8537 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8538 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008539 if (!got_int)
8540 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008541 if (*name != NUL)
8542 {
8543 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8544 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8545 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008546 msg_outtrans(ts);
8547 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008548 }
8549 return didh;
8550}
8551
8552#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8553/*
8554 * Return "1" if highlight group "id" has attribute "flag".
8555 * Return NULL otherwise.
8556 */
8557 char_u *
8558highlight_has_attr(id, flag, modec)
8559 int id;
8560 int flag;
8561 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8562{
8563 int attr;
8564
8565 if (id <= 0 || id > highlight_ga.ga_len)
8566 return NULL;
8567
Bram Moolenaar61623362010-07-14 22:04:22 +02008568#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008569 if (modec == 'g')
8570 attr = HL_TABLE()[id - 1].sg_gui;
8571 else
8572#endif
8573 if (modec == 'c')
8574 attr = HL_TABLE()[id - 1].sg_cterm;
8575 else
8576 attr = HL_TABLE()[id - 1].sg_term;
8577
8578 if (attr & flag)
8579 return (char_u *)"1";
8580 return NULL;
8581}
8582#endif
8583
8584#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8585/*
8586 * Return color name of highlight group "id".
8587 */
8588 char_u *
8589highlight_color(id, what, modec)
8590 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008591 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008592 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8593{
8594 static char_u name[20];
8595 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008596 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008597 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008598 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008599
8600 if (id <= 0 || id > highlight_ga.ga_len)
8601 return NULL;
8602
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008603 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008604 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008605 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008606 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008607 font = TRUE;
8608 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008609 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008610 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8611 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008612 if (modec == 'g')
8613 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008614# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008615 /* return font name */
8616 if (font)
8617 return HL_TABLE()[id - 1].sg_font_name;
8618
Bram Moolenaar071d4272004-06-13 20:20:40 +00008619 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008620 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008621 {
8622 guicolor_T color;
8623 long_u rgb;
8624 static char_u buf[10];
8625
8626 if (fg)
8627 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008628 else if (sp)
8629 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008630 else
8631 color = HL_TABLE()[id - 1].sg_gui_bg;
8632 if (color == INVALCOLOR)
8633 return NULL;
8634 rgb = gui_mch_get_rgb(color);
8635 sprintf((char *)buf, "#%02x%02x%02x",
8636 (unsigned)(rgb >> 16),
8637 (unsigned)(rgb >> 8) & 255,
8638 (unsigned)rgb & 255);
8639 return buf;
8640 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008641#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008642 if (fg)
8643 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008644 if (sp)
8645 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008646 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8647 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008648 if (font || sp)
8649 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008650 if (modec == 'c')
8651 {
8652 if (fg)
8653 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8654 else
8655 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8656 sprintf((char *)name, "%d", n);
8657 return name;
8658 }
8659 /* term doesn't have color */
8660 return NULL;
8661}
8662#endif
8663
8664#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8665 || defined(PROTO)
8666/*
8667 * Return color name of highlight group "id" as RGB value.
8668 */
8669 long_u
8670highlight_gui_color_rgb(id, fg)
8671 int id;
8672 int fg; /* TRUE = fg, FALSE = bg */
8673{
8674 guicolor_T color;
8675
8676 if (id <= 0 || id > highlight_ga.ga_len)
8677 return 0L;
8678
8679 if (fg)
8680 color = HL_TABLE()[id - 1].sg_gui_fg;
8681 else
8682 color = HL_TABLE()[id - 1].sg_gui_bg;
8683
8684 if (color == INVALCOLOR)
8685 return 0L;
8686
8687 return gui_mch_get_rgb(color);
8688}
8689#endif
8690
8691/*
8692 * Output the syntax list header.
8693 * Return TRUE when started a new line.
8694 */
8695 static int
8696syn_list_header(did_header, outlen, id)
8697 int did_header; /* did header already */
8698 int outlen; /* length of string that comes */
8699 int id; /* highlight group id */
8700{
8701 int endcol = 19;
8702 int newline = TRUE;
8703
8704 if (!did_header)
8705 {
8706 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008707 if (got_int)
8708 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008709 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8710 endcol = 15;
8711 }
8712 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008713 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008714 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008715 if (got_int)
8716 return TRUE;
8717 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008718 else
8719 {
8720 if (msg_col >= endcol) /* wrap around is like starting a new line */
8721 newline = FALSE;
8722 }
8723
8724 if (msg_col >= endcol) /* output at least one space */
8725 endcol = msg_col + 1;
8726 if (Columns <= endcol) /* avoid hang for tiny window */
8727 endcol = Columns - 1;
8728
8729 msg_advance(endcol);
8730
8731 /* Show "xxx" with the attributes. */
8732 if (!did_header)
8733 {
8734 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8735 msg_putchar(' ');
8736 }
8737
8738 return newline;
8739}
8740
8741/*
8742 * Set the attribute numbers for a highlight group.
8743 * Called after one of the attributes has changed.
8744 */
8745 static void
8746set_hl_attr(idx)
8747 int idx; /* index in array */
8748{
8749 attrentry_T at_en;
8750 struct hl_group *sgp = HL_TABLE() + idx;
8751
8752 /* The "Normal" group doesn't need an attribute number */
8753 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8754 return;
8755
8756#ifdef FEAT_GUI
8757 /*
8758 * For the GUI mode: If there are other than "normal" highlighting
8759 * attributes, need to allocate an attr number.
8760 */
8761 if (sgp->sg_gui_fg == INVALCOLOR
8762 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008763 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008764 && sgp->sg_font == NOFONT
8765# ifdef FEAT_XFONTSET
8766 && sgp->sg_fontset == NOFONTSET
8767# endif
8768 )
8769 {
8770 sgp->sg_gui_attr = sgp->sg_gui;
8771 }
8772 else
8773 {
8774 at_en.ae_attr = sgp->sg_gui;
8775 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8776 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008777 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008778 at_en.ae_u.gui.font = sgp->sg_font;
8779# ifdef FEAT_XFONTSET
8780 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8781# endif
8782 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8783 }
8784#endif
8785 /*
8786 * For the term mode: If there are other than "normal" highlighting
8787 * attributes, need to allocate an attr number.
8788 */
8789 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8790 sgp->sg_term_attr = sgp->sg_term;
8791 else
8792 {
8793 at_en.ae_attr = sgp->sg_term;
8794 at_en.ae_u.term.start = sgp->sg_start;
8795 at_en.ae_u.term.stop = sgp->sg_stop;
8796 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8797 }
8798
8799 /*
8800 * For the color term mode: If there are other than "normal"
8801 * highlighting attributes, need to allocate an attr number.
8802 */
8803 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8804 sgp->sg_cterm_attr = sgp->sg_cterm;
8805 else
8806 {
8807 at_en.ae_attr = sgp->sg_cterm;
8808 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8809 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8810 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8811 }
8812}
8813
8814/*
8815 * Lookup a highlight group name and return it's ID.
8816 * If it is not found, 0 is returned.
8817 */
8818 int
8819syn_name2id(name)
8820 char_u *name;
8821{
8822 int i;
8823 char_u name_u[200];
8824
8825 /* Avoid using stricmp() too much, it's slow on some systems */
8826 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8827 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008828 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008829 vim_strup(name_u);
8830 for (i = highlight_ga.ga_len; --i >= 0; )
8831 if (HL_TABLE()[i].sg_name_u != NULL
8832 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8833 break;
8834 return i + 1;
8835}
8836
8837#if defined(FEAT_EVAL) || defined(PROTO)
8838/*
8839 * Return TRUE if highlight group "name" exists.
8840 */
8841 int
8842highlight_exists(name)
8843 char_u *name;
8844{
8845 return (syn_name2id(name) > 0);
8846}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008847
8848# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8849/*
8850 * Return the name of highlight group "id".
8851 * When not a valid ID return an empty string.
8852 */
8853 char_u *
8854syn_id2name(id)
8855 int id;
8856{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008857 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008858 return (char_u *)"";
8859 return HL_TABLE()[id - 1].sg_name;
8860}
8861# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008862#endif
8863
8864/*
8865 * Like syn_name2id(), but take a pointer + length argument.
8866 */
8867 int
8868syn_namen2id(linep, len)
8869 char_u *linep;
8870 int len;
8871{
8872 char_u *name;
8873 int id = 0;
8874
8875 name = vim_strnsave(linep, len);
8876 if (name != NULL)
8877 {
8878 id = syn_name2id(name);
8879 vim_free(name);
8880 }
8881 return id;
8882}
8883
8884/*
8885 * Find highlight group name in the table and return it's ID.
8886 * The argument is a pointer to the name and the length of the name.
8887 * If it doesn't exist yet, a new entry is created.
8888 * Return 0 for failure.
8889 */
8890 int
8891syn_check_group(pp, len)
8892 char_u *pp;
8893 int len;
8894{
8895 int id;
8896 char_u *name;
8897
8898 name = vim_strnsave(pp, len);
8899 if (name == NULL)
8900 return 0;
8901
8902 id = syn_name2id(name);
8903 if (id == 0) /* doesn't exist yet */
8904 id = syn_add_group(name);
8905 else
8906 vim_free(name);
8907 return id;
8908}
8909
8910/*
8911 * Add new highlight group and return it's ID.
8912 * "name" must be an allocated string, it will be consumed.
8913 * Return 0 for failure.
8914 */
8915 static int
8916syn_add_group(name)
8917 char_u *name;
8918{
8919 char_u *p;
8920
8921 /* Check that the name is ASCII letters, digits and underscore. */
8922 for (p = name; *p != NUL; ++p)
8923 {
8924 if (!vim_isprintc(*p))
8925 {
8926 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008927 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008928 return 0;
8929 }
8930 else if (!ASCII_ISALNUM(*p) && *p != '_')
8931 {
8932 /* This is an error, but since there previously was no check only
8933 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008934 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008935 MSG(_("W18: Invalid character in group name"));
8936 break;
8937 }
8938 }
8939
8940 /*
8941 * First call for this growarray: init growing array.
8942 */
8943 if (highlight_ga.ga_data == NULL)
8944 {
8945 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8946 highlight_ga.ga_growsize = 10;
8947 }
8948
8949 /*
8950 * Make room for at least one other syntax_highlight entry.
8951 */
8952 if (ga_grow(&highlight_ga, 1) == FAIL)
8953 {
8954 vim_free(name);
8955 return 0;
8956 }
8957
8958 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8959 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8960 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8961#ifdef FEAT_GUI
8962 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8963 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008964 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008965#endif
8966 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008967
8968 return highlight_ga.ga_len; /* ID is index plus one */
8969}
8970
8971/*
8972 * When, just after calling syn_add_group(), an error is discovered, this
8973 * function deletes the new name.
8974 */
8975 static void
8976syn_unadd_group()
8977{
8978 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008979 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8980 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8981}
8982
8983/*
8984 * Translate a group ID to highlight attributes.
8985 */
8986 int
8987syn_id2attr(hl_id)
8988 int hl_id;
8989{
8990 int attr;
8991 struct hl_group *sgp;
8992
8993 hl_id = syn_get_final_id(hl_id);
8994 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8995
8996#ifdef FEAT_GUI
8997 /*
8998 * Only use GUI attr when the GUI is being used.
8999 */
9000 if (gui.in_use)
9001 attr = sgp->sg_gui_attr;
9002 else
9003#endif
9004 if (t_colors > 1)
9005 attr = sgp->sg_cterm_attr;
9006 else
9007 attr = sgp->sg_term_attr;
9008
9009 return attr;
9010}
9011
9012#ifdef FEAT_GUI
9013/*
9014 * Get the GUI colors and attributes for a group ID.
9015 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9016 */
9017 int
9018syn_id2colors(hl_id, fgp, bgp)
9019 int hl_id;
9020 guicolor_T *fgp;
9021 guicolor_T *bgp;
9022{
9023 struct hl_group *sgp;
9024
9025 hl_id = syn_get_final_id(hl_id);
9026 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9027
9028 *fgp = sgp->sg_gui_fg;
9029 *bgp = sgp->sg_gui_bg;
9030 return sgp->sg_gui;
9031}
9032#endif
9033
9034/*
9035 * Translate a group ID to the final group ID (following links).
9036 */
9037 int
9038syn_get_final_id(hl_id)
9039 int hl_id;
9040{
9041 int count;
9042 struct hl_group *sgp;
9043
9044 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9045 return 0; /* Can be called from eval!! */
9046
9047 /*
9048 * Follow links until there is no more.
9049 * Look out for loops! Break after 100 links.
9050 */
9051 for (count = 100; --count >= 0; )
9052 {
9053 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9054 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9055 break;
9056 hl_id = sgp->sg_link;
9057 }
9058
9059 return hl_id;
9060}
9061
9062#ifdef FEAT_GUI
9063/*
9064 * Call this function just after the GUI has started.
9065 * It finds the font and color handles for the highlighting groups.
9066 */
9067 void
9068highlight_gui_started()
9069{
9070 int idx;
9071
9072 /* First get the colors from the "Normal" and "Menu" group, if set */
9073 set_normal_colors();
9074
9075 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9076 gui_do_one_color(idx, FALSE, FALSE);
9077
9078 highlight_changed();
9079}
9080
9081 static void
9082gui_do_one_color(idx, do_menu, do_tooltip)
9083 int idx;
9084 int do_menu; /* TRUE: might set the menu font */
9085 int do_tooltip; /* TRUE: might set the tooltip font */
9086{
9087 int didit = FALSE;
9088
9089 if (HL_TABLE()[idx].sg_font_name != NULL)
9090 {
9091 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9092 do_tooltip);
9093 didit = TRUE;
9094 }
9095 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9096 {
9097 HL_TABLE()[idx].sg_gui_fg =
9098 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9099 didit = TRUE;
9100 }
9101 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9102 {
9103 HL_TABLE()[idx].sg_gui_bg =
9104 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9105 didit = TRUE;
9106 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009107 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9108 {
9109 HL_TABLE()[idx].sg_gui_sp =
9110 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9111 didit = TRUE;
9112 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009113 if (didit) /* need to get a new attr number */
9114 set_hl_attr(idx);
9115}
9116
9117#endif
9118
9119/*
9120 * Translate the 'highlight' option into attributes in highlight_attr[] and
9121 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9122 * corresponding highlights to use on top of HLF_SNC is computed.
9123 * Called only when the 'highlight' option has been changed and upon first
9124 * screen redraw after any :highlight command.
9125 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9126 */
9127 int
9128highlight_changed()
9129{
9130 int hlf;
9131 int i;
9132 char_u *p;
9133 int attr;
9134 char_u *end;
9135 int id;
9136#ifdef USER_HIGHLIGHT
9137 char_u userhl[10];
9138# ifdef FEAT_STL_OPT
9139 int id_SNC = -1;
9140 int id_S = -1;
9141 int hlcnt;
9142# endif
9143#endif
9144 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9145
9146 need_highlight_changed = FALSE;
9147
9148 /*
9149 * Clear all attributes.
9150 */
9151 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9152 highlight_attr[hlf] = 0;
9153
9154 /*
9155 * First set all attributes to their default value.
9156 * Then use the attributes from the 'highlight' option.
9157 */
9158 for (i = 0; i < 2; ++i)
9159 {
9160 if (i)
9161 p = p_hl;
9162 else
9163 p = get_highlight_default();
9164 if (p == NULL) /* just in case */
9165 continue;
9166
9167 while (*p)
9168 {
9169 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9170 if (hl_flags[hlf] == *p)
9171 break;
9172 ++p;
9173 if (hlf == (int)HLF_COUNT || *p == NUL)
9174 return FAIL;
9175
9176 /*
9177 * Allow several hl_flags to be combined, like "bu" for
9178 * bold-underlined.
9179 */
9180 attr = 0;
9181 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9182 {
9183 if (vim_iswhite(*p)) /* ignore white space */
9184 continue;
9185
9186 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9187 return FAIL;
9188
9189 switch (*p)
9190 {
9191 case 'b': attr |= HL_BOLD;
9192 break;
9193 case 'i': attr |= HL_ITALIC;
9194 break;
9195 case '-':
9196 case 'n': /* no highlighting */
9197 break;
9198 case 'r': attr |= HL_INVERSE;
9199 break;
9200 case 's': attr |= HL_STANDOUT;
9201 break;
9202 case 'u': attr |= HL_UNDERLINE;
9203 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009204 case 'c': attr |= HL_UNDERCURL;
9205 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009206 case ':': ++p; /* highlight group name */
9207 if (attr || *p == NUL) /* no combinations */
9208 return FAIL;
9209 end = vim_strchr(p, ',');
9210 if (end == NULL)
9211 end = p + STRLEN(p);
9212 id = syn_check_group(p, (int)(end - p));
9213 if (id == 0)
9214 return FAIL;
9215 attr = syn_id2attr(id);
9216 p = end - 1;
9217#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9218 if (hlf == (int)HLF_SNC)
9219 id_SNC = syn_get_final_id(id);
9220 else if (hlf == (int)HLF_S)
9221 id_S = syn_get_final_id(id);
9222#endif
9223 break;
9224 default: return FAIL;
9225 }
9226 }
9227 highlight_attr[hlf] = attr;
9228
9229 p = skip_to_option_part(p); /* skip comma and spaces */
9230 }
9231 }
9232
9233#ifdef USER_HIGHLIGHT
9234 /* Setup the user highlights
9235 *
9236 * Temporarily utilize 10 more hl entries. Have to be in there
9237 * simultaneously in case of table overflows in get_attr_entry()
9238 */
9239# ifdef FEAT_STL_OPT
9240 if (ga_grow(&highlight_ga, 10) == FAIL)
9241 return FAIL;
9242 hlcnt = highlight_ga.ga_len;
9243 if (id_S == 0)
9244 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009245 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009246 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9247 id_S = hlcnt + 10;
9248 }
9249# endif
9250 for (i = 0; i < 9; i++)
9251 {
9252 sprintf((char *)userhl, "User%d", i + 1);
9253 id = syn_name2id(userhl);
9254 if (id == 0)
9255 {
9256 highlight_user[i] = 0;
9257# ifdef FEAT_STL_OPT
9258 highlight_stlnc[i] = 0;
9259# endif
9260 }
9261 else
9262 {
9263# ifdef FEAT_STL_OPT
9264 struct hl_group *hlt = HL_TABLE();
9265# endif
9266
9267 highlight_user[i] = syn_id2attr(id);
9268# ifdef FEAT_STL_OPT
9269 if (id_SNC == 0)
9270 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009271 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009272 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9273 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009274# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009275 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9276# endif
9277 }
9278 else
9279 mch_memmove(&hlt[hlcnt + i],
9280 &hlt[id_SNC - 1],
9281 sizeof(struct hl_group));
9282 hlt[hlcnt + i].sg_link = 0;
9283
9284 /* Apply difference between UserX and HLF_S to HLF_SNC */
9285 hlt[hlcnt + i].sg_term ^=
9286 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9287 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9288 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9289 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9290 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9291 hlt[hlcnt + i].sg_cterm ^=
9292 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9293 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9294 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9295 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9296 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009297# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009298 hlt[hlcnt + i].sg_gui ^=
9299 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009300# endif
9301# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009302 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9303 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9304 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9305 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009306 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9307 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009308 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9309 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9310# ifdef FEAT_XFONTSET
9311 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9312 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9313# endif
9314# endif
9315 highlight_ga.ga_len = hlcnt + i + 1;
9316 set_hl_attr(hlcnt + i); /* At long last we can apply */
9317 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9318# endif
9319 }
9320 }
9321# ifdef FEAT_STL_OPT
9322 highlight_ga.ga_len = hlcnt;
9323# endif
9324
9325#endif /* USER_HIGHLIGHT */
9326
9327 return OK;
9328}
9329
Bram Moolenaar4f688582007-07-24 12:34:30 +00009330#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009331
9332static void highlight_list __ARGS((void));
9333static void highlight_list_two __ARGS((int cnt, int attr));
9334
9335/*
9336 * Handle command line completion for :highlight command.
9337 */
9338 void
9339set_context_in_highlight_cmd(xp, arg)
9340 expand_T *xp;
9341 char_u *arg;
9342{
9343 char_u *p;
9344
9345 /* Default: expand group names */
9346 xp->xp_context = EXPAND_HIGHLIGHT;
9347 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009348 include_link = 2;
9349 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009350
9351 /* (part of) subcommand already typed */
9352 if (*arg != NUL)
9353 {
9354 p = skiptowhite(arg);
9355 if (*p != NUL) /* past "default" or group name */
9356 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009357 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009358 if (STRNCMP("default", arg, p - arg) == 0)
9359 {
9360 arg = skipwhite(p);
9361 xp->xp_pattern = arg;
9362 p = skiptowhite(arg);
9363 }
9364 if (*p != NUL) /* past group name */
9365 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009366 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009367 if (arg[1] == 'i' && arg[0] == 'N')
9368 highlight_list();
9369 if (STRNCMP("link", arg, p - arg) == 0
9370 || STRNCMP("clear", arg, p - arg) == 0)
9371 {
9372 xp->xp_pattern = skipwhite(p);
9373 p = skiptowhite(xp->xp_pattern);
9374 if (*p != NUL) /* past first group name */
9375 {
9376 xp->xp_pattern = skipwhite(p);
9377 p = skiptowhite(xp->xp_pattern);
9378 }
9379 }
9380 if (*p != NUL) /* past group name(s) */
9381 xp->xp_context = EXPAND_NOTHING;
9382 }
9383 }
9384 }
9385}
9386
9387/*
9388 * List highlighting matches in a nice way.
9389 */
9390 static void
9391highlight_list()
9392{
9393 int i;
9394
9395 for (i = 10; --i >= 0; )
9396 highlight_list_two(i, hl_attr(HLF_D));
9397 for (i = 40; --i >= 0; )
9398 highlight_list_two(99, 0);
9399}
9400
9401 static void
9402highlight_list_two(cnt, attr)
9403 int cnt;
9404 int attr;
9405{
9406 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9407 msg_clr_eos();
9408 out_flush();
9409 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9410}
9411
9412#endif /* FEAT_CMDL_COMPL */
9413
9414#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9415 || defined(FEAT_SIGNS) || defined(PROTO)
9416/*
9417 * Function given to ExpandGeneric() to obtain the list of group names.
9418 * Also used for synIDattr() function.
9419 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009420 char_u *
9421get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009422 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009423 int idx;
9424{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009425#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009426 if (idx == highlight_ga.ga_len && include_none != 0)
9427 return (char_u *)"none";
9428 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009429 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009430 if (idx == highlight_ga.ga_len + include_none + include_default
9431 && include_link != 0)
9432 return (char_u *)"link";
9433 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9434 && include_link != 0)
9435 return (char_u *)"clear";
9436#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009437 if (idx < 0 || idx >= highlight_ga.ga_len)
9438 return NULL;
9439 return HL_TABLE()[idx].sg_name;
9440}
9441#endif
9442
Bram Moolenaar4f688582007-07-24 12:34:30 +00009443#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009444/*
9445 * Free all the highlight group fonts.
9446 * Used when quitting for systems which need it.
9447 */
9448 void
9449free_highlight_fonts()
9450{
9451 int idx;
9452
9453 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9454 {
9455 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9456 HL_TABLE()[idx].sg_font = NOFONT;
9457# ifdef FEAT_XFONTSET
9458 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9459 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9460# endif
9461 }
9462
9463 gui_mch_free_font(gui.norm_font);
9464# ifdef FEAT_XFONTSET
9465 gui_mch_free_fontset(gui.fontset);
9466# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009467# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009468 gui_mch_free_font(gui.bold_font);
9469 gui_mch_free_font(gui.ital_font);
9470 gui_mch_free_font(gui.boldital_font);
9471# endif
9472}
9473#endif
9474
9475/**************************************
9476 * End of Highlighting stuff *
9477 **************************************/