blob: 0e113f0fd3fffac416b2a56de08592bad011fcd7 [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 */
38 int sg_gui; /* "gui=" highlighting attributes */
39 guicolor_T sg_gui_fg; /* GUI foreground color handle */
40 char_u *sg_gui_fg_name;/* GUI foreground color name */
41 guicolor_T sg_gui_bg; /* GUI background color handle */
42 char_u *sg_gui_bg_name;/* GUI background color name */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
44 char_u *sg_gui_sp_name;/* GUI special color name */
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 GuiFont sg_font; /* GUI font handle */
46#ifdef FEAT_XFONTSET
47 GuiFontset sg_fontset; /* GUI fontset handle */
48#endif
49 char_u *sg_font_name; /* GUI font or fontset name */
50 int sg_gui_attr; /* Screen attr for GUI mode */
51#endif
52 int sg_link; /* link to this highlight group ID */
53 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000054#ifdef FEAT_EVAL
55 scid_T sg_scriptID; /* script in which the group was last set */
56#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000057};
58
59#define SG_TERM 1 /* term has been set */
60#define SG_CTERM 2 /* cterm has been set */
61#define SG_GUI 4 /* gui has been set */
62#define SG_LINK 8 /* link has been set */
63
64static garray_T highlight_ga; /* highlight groups for 'highlight' option */
65
66#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
67
68#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000069/* Flags to indicate an additional string for highlight name completion. */
70static int include_none = 0; /* when 1 include "None" */
71static int include_default = 0; /* when 1 include "default" */
72static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000073#endif
74
75/*
76 * The "term", "cterm" and "gui" arguments can be any combination of the
77 * following names, separated by commas (but no spaces!).
78 */
79static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000080 {"bold", "standout", "underline", "undercurl",
81 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000082static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000083 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000084
85static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
86static void syn_unadd_group __ARGS((void));
87static void set_hl_attr __ARGS((int idx));
88static void highlight_list_one __ARGS((int id));
89static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
90static int syn_add_group __ARGS((char_u *name));
91static int syn_list_header __ARGS((int did_header, int outlen, int id));
92static int hl_has_settings __ARGS((int idx, int check_link));
93static void highlight_clear __ARGS((int idx));
94
95#ifdef FEAT_GUI
96static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
97static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
98static guicolor_T color_name2handle __ARGS((char_u *name));
99static GuiFont font_name2handle __ARGS((char_u *name));
100# ifdef FEAT_XFONTSET
101static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
102# endif
103static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
104#endif
105
106/*
107 * An attribute number is the index in attr_table plus ATTR_OFF.
108 */
109#define ATTR_OFF (HL_ALL + 1)
110
111#if defined(FEAT_SYN_HL) || defined(PROTO)
112
113#define SYN_NAMELEN 50 /* maximum length of a syntax name */
114
115/* different types of offsets that are possible */
116#define SPO_MS_OFF 0 /* match start offset */
117#define SPO_ME_OFF 1 /* match end offset */
118#define SPO_HS_OFF 2 /* highl. start offset */
119#define SPO_HE_OFF 3 /* highl. end offset */
120#define SPO_RS_OFF 4 /* region start offset */
121#define SPO_RE_OFF 5 /* region end offset */
122#define SPO_LC_OFF 6 /* leading context offset */
123#define SPO_COUNT 7
124
125static char *(spo_name_tab[SPO_COUNT]) =
126 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
127
128/*
129 * The patterns that are being searched for are stored in a syn_pattern.
130 * A match item consists of one pattern.
131 * A start/end item consists of n start patterns and m end patterns.
132 * A start/skip/end item consists of n start patterns, one skip pattern and m
133 * end patterns.
134 * For the latter two, the patterns are always consecutive: start-skip-end.
135 *
136 * A character offset can be given for the matched text (_m_start and _m_end)
137 * and for the actually highlighted text (_h_start and _h_end).
138 */
139typedef struct syn_pattern
140{
141 char sp_type; /* see SPTYPE_ defines below */
142 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200143 int sp_flags; /* see HL_ defines below */
144#ifdef FEAT_CONCEAL
145 int sp_char; /* conceal substitute character */
146#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000147 struct sp_syn sp_syn; /* struct passed to in_id_list() */
148 short sp_syn_match_id; /* highlight group ID of pattern */
149 char_u *sp_pattern; /* regexp to match, pattern */
150 regprog_T *sp_prog; /* regexp to match, program */
151 int sp_ic; /* ignore-case flag for sp_prog */
152 short sp_off_flags; /* see below */
153 int sp_offsets[SPO_COUNT]; /* offsets */
154 short *sp_cont_list; /* cont. group IDs, if non-zero */
155 short *sp_next_list; /* next group IDs, if non-zero */
156 int sp_sync_idx; /* sync item index (syncing only) */
157 int sp_line_id; /* ID of last line where tried */
158 int sp_startcol; /* next match in sp_line_id line */
159} synpat_T;
160
161/* The sp_off_flags are computed like this:
162 * offset from the start of the matched text: (1 << SPO_XX_OFF)
163 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
164 * When both are present, only one is used.
165 */
166
167#define SPTYPE_MATCH 1 /* match keyword with this group ID */
168#define SPTYPE_START 2 /* match a regexp, start of item */
169#define SPTYPE_END 3 /* match a regexp, end of item */
170#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
171
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172
173#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
174
175#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
176
177/*
178 * Flags for b_syn_sync_flags:
179 */
180#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
181#define SF_MATCH 0x02 /* sync by matching a pattern */
182
183#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
184
Bram Moolenaar071d4272004-06-13 20:20:40 +0000185#define MAXKEYWLEN 80 /* maximum length of a keyword */
186
187/*
188 * The attributes of the syntax item that has been recognized.
189 */
190static int current_attr = 0; /* attr of current syntax word */
191#ifdef FEAT_EVAL
192static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000193static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200195#ifdef FEAT_CONCEAL
196static int current_flags = 0;
197static int current_sub_char = 0;
198#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199
Bram Moolenaar217ad922005-03-20 22:37:15 +0000200typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000201{
202 char_u *scl_name; /* syntax cluster name */
203 char_u *scl_name_u; /* uppercase of scl_name */
204 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000205} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206
207/*
208 * Methods of combining two clusters
209 */
210#define CLUSTER_REPLACE 1 /* replace first list with second */
211#define CLUSTER_ADD 2 /* add second list to first */
212#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
213
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215
216/*
217 * Syntax group IDs have different types:
218 * 0 - 9999 normal syntax groups
219 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
220 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
221 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
222 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
223 */
224#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
225#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
226#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
227#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
228
229/*
230 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
231 * expand_filename(). Most of the other syntax commands don't need it, so
232 * instead of passing it to them, we stow it here.
233 */
234static char_u **syn_cmdlinep;
235
236/*
237 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200238 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239 * rules in each ":syn include"'d file.
240 */
241static int current_syn_inc_tag = 0;
242static int running_syn_inc_tag = 0;
243
244/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000245 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
246 * This avoids adding a pointer to the hashtable item.
247 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
248 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
249 * HI2KE() converts a hashitem pointer to a var pointer.
250 */
251static keyentry_T dumkey;
252#define KE2HIKEY(kp) ((kp)->keyword)
253#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
254#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
255
256/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000257 * To reduce the time spent in keepend(), remember at which level in the state
258 * stack the first item with "keepend" is present. When "-1", there is no
259 * "keepend" on the stack.
260 */
261static int keepend_level = -1;
262
263/*
264 * For the current state we need to remember more than just the idx.
265 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
266 * (The end positions have the column number of the next char)
267 */
268typedef struct state_item
269{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000270 int si_idx; /* index of syntax pattern or
271 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000273 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 int si_m_lnum; /* lnum of the match */
275 int si_m_startcol; /* starting column of the match */
276 lpos_T si_m_endpos; /* just after end posn of the match */
277 lpos_T si_h_startpos; /* start position of the highlighting */
278 lpos_T si_h_endpos; /* end position of the highlighting */
279 lpos_T si_eoe_pos; /* end position of end pattern */
280 int si_end_idx; /* group ID for end pattern or zero */
281 int si_ends; /* if match ends before si_m_endpos */
282 int si_attr; /* attributes in this state */
283 long si_flags; /* HL_HAS_EOL flag in this state, and
284 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200285#ifdef FEAT_CONCEAL
286 int si_char; /* substitution character for conceal */
287#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288 short *si_cont_list; /* list of contained groups */
289 short *si_next_list; /* nextgroup IDs after this item ends */
290 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
291 * pattern */
292} stateitem_T;
293
294#define KEYWORD_IDX -1 /* value of si_idx for keywords */
295#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
296 but contained groups */
297
298/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000299 * Struct to reduce the number of arguments to get_syn_options(), it's used
300 * very often.
301 */
302typedef struct
303{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000304 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000305 int keyword; /* TRUE for ":syn keyword" */
306 int *sync_idx; /* syntax item for "grouphere" argument, NULL
307 if not allowed */
308 char has_cont_list; /* TRUE if "cont_list" can be used */
309 short *cont_list; /* group IDs for "contains" argument */
310 short *cont_in_list; /* group IDs for "containedin" argument */
311 short *next_list; /* group IDs for "nextgroup" argument */
312} syn_opt_arg_T;
313
314/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315 * The next possible match in the current line for any pattern is remembered,
316 * to avoid having to try for a match in each column.
317 * If next_match_idx == -1, not tried (in this line) yet.
318 * If next_match_col == MAXCOL, no match found in this line.
319 * (All end positions have the column of the char after the end)
320 */
321static int next_match_col; /* column for start of next match */
322static lpos_T next_match_m_endpos; /* position for end of next match */
323static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
324static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
325static int next_match_idx; /* index of matched item */
326static long next_match_flags; /* flags for next match */
327static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
328static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
329static int next_match_end_idx; /* ID of group for end pattn or zero */
330static reg_extmatch_T *next_match_extmatch = NULL;
331
332/*
333 * A state stack is an array of integers or stateitem_T, stored in a
334 * garray_T. A state stack is invalid if it's itemsize entry is zero.
335 */
336#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
337#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
338
339/*
340 * The current state (within the line) of the recognition engine.
341 * When current_state.ga_itemsize is 0 the current state is invalid.
342 */
343static win_T *syn_win; /* current window for highlighting */
344static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200345static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346static linenr_T current_lnum = 0; /* lnum of current state */
347static colnr_T current_col = 0; /* column of current state */
348static int current_state_stored = 0; /* TRUE if stored current state
349 * after setting current_finished */
350static int current_finished = 0; /* current line has been finished */
351static garray_T current_state /* current stack of state_items */
352 = {0, 0, 0, 0, NULL};
353static short *current_next_list = NULL; /* when non-zero, nextgroup list */
354static int current_next_flags = 0; /* flags for current_next_list */
355static int current_line_id = 0; /* unique number for current line */
356
357#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
358
359static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
360static int syn_match_linecont __ARGS((linenr_T lnum));
361static void syn_start_line __ARGS((void));
362static void syn_update_ends __ARGS((int startofline));
363static void syn_stack_alloc __ARGS((void));
364static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200365static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000366static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000367static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000368static void load_current_state __ARGS((synstate_T *from));
369static void invalidate_current_state __ARGS((void));
370static int syn_stack_equal __ARGS((synstate_T *sp));
371static void validate_current_state __ARGS((void));
372static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000373static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000374static int did_match_already __ARGS((int idx, garray_T *gap));
375static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
376static void check_state_ends __ARGS((void));
377static void update_si_attr __ARGS((int idx));
378static void check_keepend __ARGS((void));
379static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
380static short *copy_id_list __ARGS((short *list));
381static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
382static int push_current_state __ARGS((int idx));
383static void pop_current_state __ARGS((void));
384
Bram Moolenaar860cae12010-06-05 23:22:07 +0200385static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386static 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));
387static void clear_syn_state __ARGS((synstate_T *p));
388static void clear_current_state __ARGS((void));
389
390static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
391static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
392static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
393static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
394static char_u *syn_getcurline __ARGS((void));
395static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200396static 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 +0000397static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000398static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000399static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200400static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
401static void syn_clear_pattern __ARGS((synblock_T *block, int i));
402static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200404static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405static void syn_clear_one __ARGS((int id, int syncing));
406static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
407static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
408static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
409static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
410static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
411static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
412static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
413static void syn_lines_msg __ARGS((void));
414static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200415static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000416static void syn_list_one __ARGS((int id, int syncing, int link_only));
417static void syn_list_cluster __ARGS((int id));
418static void put_id_list __ARGS((char_u *name, short *list, int attr));
419static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000420static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
421static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
422static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200423static 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 +0000424static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200425static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
427static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
428static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
429static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
430#ifdef __BORLANDC__
431static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
432#else
433static int syn_compare_stub __ARGS((const void *v1, const void *v2));
434#endif
435static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
436static int syn_scl_name2id __ARGS((char_u *name));
437static int syn_scl_namen2id __ARGS((char_u *linep, int len));
438static int syn_check_cluster __ARGS((char_u *pp, int len));
439static int syn_add_cluster __ARGS((char_u *name));
440static void init_syn_patterns __ARGS((void));
441static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
442static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
443static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
444static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
445static void syn_incl_toplevel __ARGS((int id, int *flagsp));
446
447/*
448 * Start the syntax recognition for a line. This function is normally called
449 * from the screen updating, once for each displayed line.
450 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
451 * it. Careful: curbuf and curwin are likely to point to another buffer and
452 * window.
453 */
454 void
455syntax_start(wp, lnum)
456 win_T *wp;
457 linenr_T lnum;
458{
459 synstate_T *p;
460 synstate_T *last_valid = NULL;
461 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000462 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000463 linenr_T parsed_lnum;
464 linenr_T first_stored;
465 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000466 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000467
Bram Moolenaar071d4272004-06-13 20:20:40 +0000468 /*
469 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000470 * Also do this when a change was made, the current state may be invalid
471 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200473 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 {
475 invalidate_current_state();
476 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200477 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000478 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000479 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480 syn_win = wp;
481
482 /*
483 * Allocate syntax stack when needed.
484 */
485 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200486 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000487 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200488 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489
490 /*
491 * If the state of the end of the previous line is useful, store it.
492 */
493 if (VALID_STATE(&current_state)
494 && current_lnum < lnum
495 && current_lnum < syn_buf->b_ml.ml_line_count)
496 {
497 (void)syn_finish_line(FALSE);
498 if (!current_state_stored)
499 {
500 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000501 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000502 }
503
504 /*
505 * If the current_lnum is now the same as "lnum", keep the current
506 * state (this happens very often!). Otherwise invalidate
507 * current_state and figure it out below.
508 */
509 if (current_lnum != lnum)
510 invalidate_current_state();
511 }
512 else
513 invalidate_current_state();
514
515 /*
516 * Try to synchronize from a saved state in b_sst_array[].
517 * Only do this if lnum is not before and not to far beyond a saved state.
518 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200519 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 {
521 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200522 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 {
524 if (p->sst_lnum > lnum)
525 break;
526 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
527 {
528 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200529 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 last_min_valid = p;
531 }
532 }
533 if (last_min_valid != NULL)
534 load_current_state(last_min_valid);
535 }
536
537 /*
538 * If "lnum" is before or far beyond a line with a saved state, need to
539 * re-synchronize.
540 */
541 if (INVALID_STATE(&current_state))
542 {
543 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200544 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000545 }
546 else
547 first_stored = current_lnum;
548
549 /*
550 * Advance from the sync point or saved state until the current line.
551 * Save some entries for syncing with later on.
552 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200553 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000554 dist = 999999;
555 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200556 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557 while (current_lnum < lnum)
558 {
559 syn_start_line();
560 (void)syn_finish_line(FALSE);
561 ++current_lnum;
562
563 /* If we parsed at least "minlines" lines or started at a valid
564 * state, the current state is considered valid. */
565 if (current_lnum >= first_stored)
566 {
567 /* Check if the saved state entry is for the current line and is
568 * equal to the current state. If so, then validate all saved
569 * states that depended on a change before the parsed line. */
570 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000571 prev = syn_stack_find_entry(current_lnum - 1);
572 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200573 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000575 sp = prev;
576 while (sp != NULL && sp->sst_lnum < current_lnum)
577 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 if (sp != NULL
579 && sp->sst_lnum == current_lnum
580 && syn_stack_equal(sp))
581 {
582 parsed_lnum = current_lnum;
583 prev = sp;
584 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
585 {
586 if (sp->sst_lnum <= lnum)
587 /* valid state before desired line, use this one */
588 prev = sp;
589 else if (sp->sst_change_lnum == 0)
590 /* past saved states depending on change, break here. */
591 break;
592 sp->sst_change_lnum = 0;
593 sp = sp->sst_next;
594 }
595 load_current_state(prev);
596 }
597 /* Store the state at this line when it's the first one, the line
598 * where we start parsing, or some distance from the previously
599 * saved state. But only when parsed at least 'minlines'. */
600 else if (prev == NULL
601 || current_lnum == lnum
602 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000603 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000604 }
605
606 /* This can take a long time: break when CTRL-C pressed. The current
607 * state will be wrong then. */
608 line_breakcheck();
609 if (got_int)
610 {
611 current_lnum = lnum;
612 break;
613 }
614 }
615
616 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617}
618
619/*
620 * We cannot simply discard growarrays full of state_items or buf_states; we
621 * have to manually release their extmatch pointers first.
622 */
623 static void
624clear_syn_state(p)
625 synstate_T *p;
626{
627 int i;
628 garray_T *gap;
629
630 if (p->sst_stacksize > SST_FIX_STATES)
631 {
632 gap = &(p->sst_union.sst_ga);
633 for (i = 0; i < gap->ga_len; i++)
634 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
635 ga_clear(gap);
636 }
637 else
638 {
639 for (i = 0; i < p->sst_stacksize; i++)
640 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
641 }
642}
643
644/*
645 * Cleanup the current_state stack.
646 */
647 static void
648clear_current_state()
649{
650 int i;
651 stateitem_T *sip;
652
653 sip = (stateitem_T *)(current_state.ga_data);
654 for (i = 0; i < current_state.ga_len; i++)
655 unref_extmatch(sip[i].si_extmatch);
656 ga_clear(&current_state);
657}
658
659/*
660 * Try to find a synchronisation point for line "lnum".
661 *
662 * This sets current_lnum and the current state. One of three methods is
663 * used:
664 * 1. Search backwards for the end of a C-comment.
665 * 2. Search backwards for given sync patterns.
666 * 3. Simply start on a given number of lines above "lnum".
667 */
668 static void
669syn_sync(wp, start_lnum, last_valid)
670 win_T *wp;
671 linenr_T start_lnum;
672 synstate_T *last_valid;
673{
674 buf_T *curbuf_save;
675 win_T *curwin_save;
676 pos_T cursor_save;
677 int idx;
678 linenr_T lnum;
679 linenr_T end_lnum;
680 linenr_T break_lnum;
681 int had_sync_point;
682 stateitem_T *cur_si;
683 synpat_T *spp;
684 char_u *line;
685 int found_flags = 0;
686 int found_match_idx = 0;
687 linenr_T found_current_lnum = 0;
688 int found_current_col= 0;
689 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000690 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691
692 /*
693 * Clear any current state that might be hanging around.
694 */
695 invalidate_current_state();
696
697 /*
698 * Start at least "minlines" back. Default starting point for parsing is
699 * there.
700 * Start further back, to avoid that scrolling backwards will result in
701 * resyncing for every line. Now it resyncs only one out of N lines,
702 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
703 * Watch out for overflow when minlines is MAXLNUM.
704 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200705 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706 start_lnum = 1;
707 else
708 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200709 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200711 else if (syn_block->b_syn_sync_minlines < 10)
712 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200714 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
715 if (syn_block->b_syn_sync_maxlines != 0
716 && lnum > syn_block->b_syn_sync_maxlines)
717 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000718 if (lnum >= start_lnum)
719 start_lnum = 1;
720 else
721 start_lnum -= lnum;
722 }
723 current_lnum = start_lnum;
724
725 /*
726 * 1. Search backwards for the end of a C-style comment.
727 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200728 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729 {
730 /* Need to make syn_buf the current buffer for a moment, to be able to
731 * use find_start_comment(). */
732 curwin_save = curwin;
733 curwin = wp;
734 curbuf_save = curbuf;
735 curbuf = syn_buf;
736
737 /*
738 * Skip lines that end in a backslash.
739 */
740 for ( ; start_lnum > 1; --start_lnum)
741 {
742 line = ml_get(start_lnum - 1);
743 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
744 break;
745 }
746 current_lnum = start_lnum;
747
748 /* set cursor to start of search */
749 cursor_save = wp->w_cursor;
750 wp->w_cursor.lnum = start_lnum;
751 wp->w_cursor.col = 0;
752
753 /*
754 * If the line is inside a comment, need to find the syntax item that
755 * defines the comment.
756 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
757 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200758 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200760 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
761 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
762 == syn_block->b_syn_sync_id
763 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 {
765 validate_current_state();
766 if (push_current_state(idx) == OK)
767 update_si_attr(current_state.ga_len - 1);
768 break;
769 }
770 }
771
772 /* restore cursor and buffer */
773 wp->w_cursor = cursor_save;
774 curwin = curwin_save;
775 curbuf = curbuf_save;
776 }
777
778 /*
779 * 2. Search backwards for given sync patterns.
780 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200781 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200783 if (syn_block->b_syn_sync_maxlines != 0
784 && start_lnum > syn_block->b_syn_sync_maxlines)
785 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 else
787 break_lnum = 0;
788
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000789 found_m_endpos.lnum = 0;
790 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000791 end_lnum = start_lnum;
792 lnum = start_lnum;
793 while (--lnum > break_lnum)
794 {
795 /* This can take a long time: break when CTRL-C pressed. */
796 line_breakcheck();
797 if (got_int)
798 {
799 invalidate_current_state();
800 current_lnum = start_lnum;
801 break;
802 }
803
804 /* Check if we have run into a valid saved state stack now. */
805 if (last_valid != NULL && lnum == last_valid->sst_lnum)
806 {
807 load_current_state(last_valid);
808 break;
809 }
810
811 /*
812 * Check if the previous line has the line-continuation pattern.
813 */
814 if (lnum > 1 && syn_match_linecont(lnum - 1))
815 continue;
816
817 /*
818 * Start with nothing on the state stack
819 */
820 validate_current_state();
821
822 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
823 {
824 syn_start_line();
825 for (;;)
826 {
827 had_sync_point = syn_finish_line(TRUE);
828 /*
829 * When a sync point has been found, remember where, and
830 * continue to look for another one, further on in the line.
831 */
832 if (had_sync_point && current_state.ga_len)
833 {
834 cur_si = &CUR_STATE(current_state.ga_len - 1);
835 if (cur_si->si_m_endpos.lnum > start_lnum)
836 {
837 /* ignore match that goes to after where started */
838 current_lnum = end_lnum;
839 break;
840 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000841 if (cur_si->si_idx < 0)
842 {
843 /* Cannot happen? */
844 found_flags = 0;
845 found_match_idx = KEYWORD_IDX;
846 }
847 else
848 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200849 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000850 found_flags = spp->sp_flags;
851 found_match_idx = spp->sp_sync_idx;
852 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853 found_current_lnum = current_lnum;
854 found_current_col = current_col;
855 found_m_endpos = cur_si->si_m_endpos;
856 /*
857 * Continue after the match (be aware of a zero-length
858 * match).
859 */
860 if (found_m_endpos.lnum > current_lnum)
861 {
862 current_lnum = found_m_endpos.lnum;
863 current_col = found_m_endpos.col;
864 if (current_lnum >= end_lnum)
865 break;
866 }
867 else if (found_m_endpos.col > current_col)
868 current_col = found_m_endpos.col;
869 else
870 ++current_col;
871
872 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000873 * an item that ends here, need to do that now. Be
874 * careful not to go past the NUL. */
875 prev_current_col = current_col;
876 if (syn_getcurline()[current_col] != NUL)
877 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000879 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880 }
881 else
882 break;
883 }
884 }
885
886 /*
887 * If a sync point was encountered, break here.
888 */
889 if (found_flags)
890 {
891 /*
892 * Put the item that was specified by the sync point on the
893 * state stack. If there was no item specified, make the
894 * state stack empty.
895 */
896 clear_current_state();
897 if (found_match_idx >= 0
898 && push_current_state(found_match_idx) == OK)
899 update_si_attr(current_state.ga_len - 1);
900
901 /*
902 * When using "grouphere", continue from the sync point
903 * match, until the end of the line. Parsing starts at
904 * the next line.
905 * For "groupthere" the parsing starts at start_lnum.
906 */
907 if (found_flags & HL_SYNC_HERE)
908 {
909 if (current_state.ga_len)
910 {
911 cur_si = &CUR_STATE(current_state.ga_len - 1);
912 cur_si->si_h_startpos.lnum = found_current_lnum;
913 cur_si->si_h_startpos.col = found_current_col;
914 update_si_end(cur_si, (int)current_col, TRUE);
915 check_keepend();
916 }
917 current_col = found_m_endpos.col;
918 current_lnum = found_m_endpos.lnum;
919 (void)syn_finish_line(FALSE);
920 ++current_lnum;
921 }
922 else
923 current_lnum = start_lnum;
924
925 break;
926 }
927
928 end_lnum = lnum;
929 invalidate_current_state();
930 }
931
932 /* Ran into start of the file or exceeded maximum number of lines */
933 if (lnum <= break_lnum)
934 {
935 invalidate_current_state();
936 current_lnum = break_lnum + 1;
937 }
938 }
939
940 validate_current_state();
941}
942
943/*
944 * Return TRUE if the line-continuation pattern matches in line "lnum".
945 */
946 static int
947syn_match_linecont(lnum)
948 linenr_T lnum;
949{
950 regmmatch_T regmatch;
951
Bram Moolenaar860cae12010-06-05 23:22:07 +0200952 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000953 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200954 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
955 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956 return syn_regexec(&regmatch, lnum, (colnr_T)0);
957 }
958 return FALSE;
959}
960
961/*
962 * Prepare the current state for the start of a line.
963 */
964 static void
965syn_start_line()
966{
967 current_finished = FALSE;
968 current_col = 0;
969
970 /*
971 * Need to update the end of a start/skip/end that continues from the
972 * previous line and regions that have "keepend".
973 */
974 if (current_state.ga_len > 0)
975 syn_update_ends(TRUE);
976
977 next_match_idx = -1;
978 ++current_line_id;
979}
980
981/*
982 * Check for items in the stack that need their end updated.
983 * When "startofline" is TRUE the last item is always updated.
984 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
985 */
986 static void
987syn_update_ends(startofline)
988 int startofline;
989{
990 stateitem_T *cur_si;
991 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000992 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000993
994 if (startofline)
995 {
996 /* Check for a match carried over from a previous line with a
997 * contained region. The match ends as soon as the region ends. */
998 for (i = 0; i < current_state.ga_len; ++i)
999 {
1000 cur_si = &CUR_STATE(i);
1001 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001002 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003 == SPTYPE_MATCH
1004 && cur_si->si_m_endpos.lnum < current_lnum)
1005 {
1006 cur_si->si_flags |= HL_MATCHCONT;
1007 cur_si->si_m_endpos.lnum = 0;
1008 cur_si->si_m_endpos.col = 0;
1009 cur_si->si_h_endpos = cur_si->si_m_endpos;
1010 cur_si->si_ends = TRUE;
1011 }
1012 }
1013 }
1014
1015 /*
1016 * Need to update the end of a start/skip/end that continues from the
1017 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001018 * influence contained items. If we've just removed "extend"
1019 * (startofline == 0) then we should update ends of normal regions
1020 * contained inside "keepend" because "extend" could have extended
1021 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022 * Then check for items ending in column 0.
1023 */
1024 i = current_state.ga_len - 1;
1025 if (keepend_level >= 0)
1026 for ( ; i > keepend_level; --i)
1027 if (CUR_STATE(i).si_flags & HL_EXTEND)
1028 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001029
1030 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001031 for ( ; i < current_state.ga_len; ++i)
1032 {
1033 cur_si = &CUR_STATE(i);
1034 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001035 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036 || (i == current_state.ga_len - 1 && startofline))
1037 {
1038 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1039 cur_si->si_h_startpos.lnum = current_lnum;
1040
1041 if (!(cur_si->si_flags & HL_MATCHCONT))
1042 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001043
1044 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1045 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046 }
1047 }
1048 check_keepend();
1049 check_state_ends();
1050}
1051
1052/****************************************
1053 * Handling of the state stack cache.
1054 */
1055
1056/*
1057 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1058 *
1059 * To speed up syntax highlighting, the state stack for the start of some
1060 * lines is cached. These entries can be used to start parsing at that point.
1061 *
1062 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1063 * valid entries. b_sst_first points to the first one, then follow sst_next.
1064 * The entries are sorted on line number. The first entry is often for line 2
1065 * (line 1 always starts with an empty stack).
1066 * There is also a list for free entries. This construction is used to avoid
1067 * having to allocate and free memory blocks too often.
1068 *
1069 * When making changes to the buffer, this is logged in b_mod_*. When calling
1070 * update_screen() to update the display, it will call
1071 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1072 * entries. The entries which are inside the changed area are removed,
1073 * because they must be recomputed. Entries below the changed have their line
1074 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1075 * set to indicate that a check must be made if the changed lines would change
1076 * the cached entry.
1077 *
1078 * When later displaying lines, an entry is stored for each line. Displayed
1079 * lines are likely to be displayed again, in which case the state at the
1080 * start of the line is needed.
1081 * For not displayed lines, an entry is stored for every so many lines. These
1082 * entries will be used e.g., when scrolling backwards. The distance between
1083 * entries depends on the number of lines in the buffer. For small buffers
1084 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1085 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1086 */
1087
Bram Moolenaar860cae12010-06-05 23:22:07 +02001088 static void
1089syn_stack_free_block(block)
1090 synblock_T *block;
1091{
1092 synstate_T *p;
1093
1094 if (block->b_sst_array != NULL)
1095 {
1096 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1097 clear_syn_state(p);
1098 vim_free(block->b_sst_array);
1099 block->b_sst_array = NULL;
1100 block->b_sst_len = 0;
1101 }
1102}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001103/*
1104 * Free b_sst_array[] for buffer "buf".
1105 * Used when syntax items changed to force resyncing everywhere.
1106 */
1107 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001108syn_stack_free_all(block)
1109 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001111 win_T *wp;
1112
Bram Moolenaar860cae12010-06-05 23:22:07 +02001113 syn_stack_free_block(block);
1114
1115
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116#ifdef FEAT_FOLDING
1117 /* When using "syntax" fold method, must update all folds. */
1118 FOR_ALL_WINDOWS(wp)
1119 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001120 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 foldUpdateAll(wp);
1122 }
1123#endif
1124}
1125
1126/*
1127 * Allocate the syntax state stack for syn_buf when needed.
1128 * If the number of entries in b_sst_array[] is much too big or a bit too
1129 * small, reallocate it.
1130 * Also used to allocate b_sst_array[] for the first time.
1131 */
1132 static void
1133syn_stack_alloc()
1134{
1135 long len;
1136 synstate_T *to, *from;
1137 synstate_T *sstp;
1138
1139 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1140 if (len < SST_MIN_ENTRIES)
1141 len = SST_MIN_ENTRIES;
1142 else if (len > SST_MAX_ENTRIES)
1143 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001144 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001145 {
1146 /* Allocate 50% too much, to avoid reallocating too often. */
1147 len = syn_buf->b_ml.ml_line_count;
1148 len = (len + len / 2) / SST_DIST + Rows * 2;
1149 if (len < SST_MIN_ENTRIES)
1150 len = SST_MIN_ENTRIES;
1151 else if (len > SST_MAX_ENTRIES)
1152 len = SST_MAX_ENTRIES;
1153
Bram Moolenaar860cae12010-06-05 23:22:07 +02001154 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001155 {
1156 /* When shrinking the array, cleanup the existing stack.
1157 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001158 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001159 && syn_stack_cleanup())
1160 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001161 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1162 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163 }
1164
1165 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1166 if (sstp == NULL) /* out of memory! */
1167 return;
1168
1169 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001170 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 {
1172 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001173 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 from = from->sst_next)
1175 {
1176 ++to;
1177 *to = *from;
1178 to->sst_next = to + 1;
1179 }
1180 }
1181 if (to != sstp - 1)
1182 {
1183 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001184 syn_block->b_sst_first = sstp;
1185 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186 }
1187 else
1188 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001189 syn_block->b_sst_first = NULL;
1190 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 }
1192
1193 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001194 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 while (++to < sstp + len)
1196 to->sst_next = to + 1;
1197 (sstp + len - 1)->sst_next = NULL;
1198
Bram Moolenaar860cae12010-06-05 23:22:07 +02001199 vim_free(syn_block->b_sst_array);
1200 syn_block->b_sst_array = sstp;
1201 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 }
1203}
1204
1205/*
1206 * Check for changes in a buffer to affect stored syntax states. Uses the
1207 * b_mod_* fields.
1208 * Called from update_screen(), before screen is being updated, once for each
1209 * displayed buffer.
1210 */
1211 void
1212syn_stack_apply_changes(buf)
1213 buf_T *buf;
1214{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001215 win_T *wp;
1216
1217 syn_stack_apply_changes_block(&buf->b_s, buf);
1218
1219 FOR_ALL_WINDOWS(wp)
1220 {
1221 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1222 syn_stack_apply_changes_block(wp->w_s, buf);
1223 }
1224}
1225
1226 static void
1227syn_stack_apply_changes_block(block, buf)
1228 synblock_T *block;
1229 buf_T *buf;
1230{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 synstate_T *p, *prev, *np;
1232 linenr_T n;
1233
Bram Moolenaar860cae12010-06-05 23:22:07 +02001234 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 return;
1236
1237 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001238 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001239 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001240 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 {
1242 n = p->sst_lnum + buf->b_mod_xlines;
1243 if (n <= buf->b_mod_bot)
1244 {
1245 /* this state is inside the changed area, remove it */
1246 np = p->sst_next;
1247 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001248 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 else
1250 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001251 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 p = np;
1253 continue;
1254 }
1255 /* This state is below the changed area. Remember the line
1256 * that needs to be parsed before this entry can be made valid
1257 * again. */
1258 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1259 {
1260 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1261 p->sst_change_lnum += buf->b_mod_xlines;
1262 else
1263 p->sst_change_lnum = buf->b_mod_top;
1264 }
1265 if (p->sst_change_lnum == 0
1266 || p->sst_change_lnum < buf->b_mod_bot)
1267 p->sst_change_lnum = buf->b_mod_bot;
1268
1269 p->sst_lnum = n;
1270 }
1271 prev = p;
1272 p = p->sst_next;
1273 }
1274}
1275
1276/*
1277 * Reduce the number of entries in the state stack for syn_buf.
1278 * Returns TRUE if at least one entry was freed.
1279 */
1280 static int
1281syn_stack_cleanup()
1282{
1283 synstate_T *p, *prev;
1284 disptick_T tick;
1285 int above;
1286 int dist;
1287 int retval = FALSE;
1288
Bram Moolenaar860cae12010-06-05 23:22:07 +02001289 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290 return retval;
1291
1292 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001293 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001294 dist = 999999;
1295 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001296 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297
1298 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001299 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 * be removed. Set "above" when the "tick" for the oldest entry is above
1301 * "b_sst_lasttick" (the display tick wraps around).
1302 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001303 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001305 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1307 {
1308 if (prev->sst_lnum + dist > p->sst_lnum)
1309 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001310 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311 {
1312 if (!above || p->sst_tick < tick)
1313 tick = p->sst_tick;
1314 above = TRUE;
1315 }
1316 else if (!above && p->sst_tick < tick)
1317 tick = p->sst_tick;
1318 }
1319 }
1320
1321 /*
1322 * Go through the list to make the entries for the oldest tick at an
1323 * interval of several lines.
1324 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001325 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1327 {
1328 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1329 {
1330 /* Move this entry from used list to free list */
1331 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001332 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001333 p = prev;
1334 retval = TRUE;
1335 }
1336 }
1337 return retval;
1338}
1339
1340/*
1341 * Free the allocated memory for a syn_state item.
1342 * Move the entry into the free list.
1343 */
1344 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001345syn_stack_free_entry(block, p)
1346 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347 synstate_T *p;
1348{
1349 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 p->sst_next = block->b_sst_firstfree;
1351 block->b_sst_firstfree = p;
1352 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353}
1354
1355/*
1356 * Find an entry in the list of state stacks at or before "lnum".
1357 * Returns NULL when there is no entry or the first entry is after "lnum".
1358 */
1359 static synstate_T *
1360syn_stack_find_entry(lnum)
1361 linenr_T lnum;
1362{
1363 synstate_T *p, *prev;
1364
1365 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001366 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001367 {
1368 if (p->sst_lnum == lnum)
1369 return p;
1370 if (p->sst_lnum > lnum)
1371 break;
1372 }
1373 return prev;
1374}
1375
1376/*
1377 * Try saving the current state in b_sst_array[].
1378 * The current state must be valid for the start of the current_lnum line!
1379 */
1380 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001381store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382{
1383 int i;
1384 synstate_T *p;
1385 bufstate_T *bp;
1386 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001387 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001388
1389 /*
1390 * If the current state contains a start or end pattern that continues
1391 * from the previous line, we can't use it. Don't store it then.
1392 */
1393 for (i = current_state.ga_len - 1; i >= 0; --i)
1394 {
1395 cur_si = &CUR_STATE(i);
1396 if (cur_si->si_h_startpos.lnum >= current_lnum
1397 || cur_si->si_m_endpos.lnum >= current_lnum
1398 || cur_si->si_h_endpos.lnum >= current_lnum
1399 || (cur_si->si_end_idx
1400 && cur_si->si_eoe_pos.lnum >= current_lnum))
1401 break;
1402 }
1403 if (i >= 0)
1404 {
1405 if (sp != NULL)
1406 {
1407 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001408 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001409 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001410 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411 else
1412 {
1413 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001414 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415 if (p->sst_next == sp)
1416 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001417 if (p != NULL) /* just in case */
1418 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001419 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001420 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 sp = NULL;
1422 }
1423 }
1424 else if (sp == NULL || sp->sst_lnum != current_lnum)
1425 {
1426 /*
1427 * Add a new entry
1428 */
1429 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001430 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431 {
1432 (void)syn_stack_cleanup();
1433 /* "sp" may have been moved to the freelist now */
1434 sp = syn_stack_find_entry(current_lnum);
1435 }
1436 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001437 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438 sp = NULL;
1439 else
1440 {
1441 /* Take the first item from the free list and put it in the used
1442 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001443 p = syn_block->b_sst_firstfree;
1444 syn_block->b_sst_firstfree = p->sst_next;
1445 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446 if (sp == NULL)
1447 {
1448 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001449 p->sst_next = syn_block->b_sst_first;
1450 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 }
1452 else
1453 {
1454 /* insert in list after *sp */
1455 p->sst_next = sp->sst_next;
1456 sp->sst_next = p;
1457 }
1458 sp = p;
1459 sp->sst_stacksize = 0;
1460 sp->sst_lnum = current_lnum;
1461 }
1462 }
1463 if (sp != NULL)
1464 {
1465 /* When overwriting an existing state stack, clear it first */
1466 clear_syn_state(sp);
1467 sp->sst_stacksize = current_state.ga_len;
1468 if (current_state.ga_len > SST_FIX_STATES)
1469 {
1470 /* Need to clear it, might be something remaining from when the
1471 * length was less than SST_FIX_STATES. */
1472 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1473 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1474 sp->sst_stacksize = 0;
1475 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001476 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1478 }
1479 else
1480 bp = sp->sst_union.sst_stack;
1481 for (i = 0; i < sp->sst_stacksize; ++i)
1482 {
1483 bp[i].bs_idx = CUR_STATE(i).si_idx;
1484 bp[i].bs_flags = CUR_STATE(i).si_flags;
1485 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1486 }
1487 sp->sst_next_flags = current_next_flags;
1488 sp->sst_next_list = current_next_list;
1489 sp->sst_tick = display_tick;
1490 sp->sst_change_lnum = 0;
1491 }
1492 current_state_stored = TRUE;
1493 return sp;
1494}
1495
1496/*
1497 * Copy a state stack from "from" in b_sst_array[] to current_state;
1498 */
1499 static void
1500load_current_state(from)
1501 synstate_T *from;
1502{
1503 int i;
1504 bufstate_T *bp;
1505
1506 clear_current_state();
1507 validate_current_state();
1508 keepend_level = -1;
1509 if (from->sst_stacksize
1510 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1511 {
1512 if (from->sst_stacksize > SST_FIX_STATES)
1513 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1514 else
1515 bp = from->sst_union.sst_stack;
1516 for (i = 0; i < from->sst_stacksize; ++i)
1517 {
1518 CUR_STATE(i).si_idx = bp[i].bs_idx;
1519 CUR_STATE(i).si_flags = bp[i].bs_flags;
1520 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1521 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1522 keepend_level = i;
1523 CUR_STATE(i).si_ends = FALSE;
1524 CUR_STATE(i).si_m_lnum = 0;
1525 if (CUR_STATE(i).si_idx >= 0)
1526 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001527 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001528 else
1529 CUR_STATE(i).si_next_list = NULL;
1530 update_si_attr(i);
1531 }
1532 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001533 }
1534 current_next_list = from->sst_next_list;
1535 current_next_flags = from->sst_next_flags;
1536 current_lnum = from->sst_lnum;
1537}
1538
1539/*
1540 * Compare saved state stack "*sp" with the current state.
1541 * Return TRUE when they are equal.
1542 */
1543 static int
1544syn_stack_equal(sp)
1545 synstate_T *sp;
1546{
1547 int i, j;
1548 bufstate_T *bp;
1549 reg_extmatch_T *six, *bsx;
1550
1551 /* First a quick check if the stacks have the same size end nextlist. */
1552 if (sp->sst_stacksize == current_state.ga_len
1553 && sp->sst_next_list == current_next_list)
1554 {
1555 /* Need to compare all states on both stacks. */
1556 if (sp->sst_stacksize > SST_FIX_STATES)
1557 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1558 else
1559 bp = sp->sst_union.sst_stack;
1560
1561 for (i = current_state.ga_len; --i >= 0; )
1562 {
1563 /* If the item has another index the state is different. */
1564 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1565 break;
1566 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1567 {
1568 /* When the extmatch pointers are different, the strings in
1569 * them can still be the same. Check if the extmatch
1570 * references are equal. */
1571 bsx = bp[i].bs_extmatch;
1572 six = CUR_STATE(i).si_extmatch;
1573 /* If one of the extmatch pointers is NULL the states are
1574 * different. */
1575 if (bsx == NULL || six == NULL)
1576 break;
1577 for (j = 0; j < NSUBEXP; ++j)
1578 {
1579 /* Check each referenced match string. They must all be
1580 * equal. */
1581 if (bsx->matches[j] != six->matches[j])
1582 {
1583 /* If the pointer is different it can still be the
1584 * same text. Compare the strings, ignore case when
1585 * the start item has the sp_ic flag set. */
1586 if (bsx->matches[j] == NULL
1587 || six->matches[j] == NULL)
1588 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001589 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001590 ? MB_STRICMP(bsx->matches[j],
1591 six->matches[j]) != 0
1592 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1593 break;
1594 }
1595 }
1596 if (j != NSUBEXP)
1597 break;
1598 }
1599 }
1600 if (i < 0)
1601 return TRUE;
1602 }
1603 return FALSE;
1604}
1605
1606/*
1607 * We stop parsing syntax above line "lnum". If the stored state at or below
1608 * this line depended on a change before it, it now depends on the line below
1609 * the last parsed line.
1610 * The window looks like this:
1611 * line which changed
1612 * displayed line
1613 * displayed line
1614 * lnum -> line below window
1615 */
1616 void
1617syntax_end_parsing(lnum)
1618 linenr_T lnum;
1619{
1620 synstate_T *sp;
1621
1622 sp = syn_stack_find_entry(lnum);
1623 if (sp != NULL && sp->sst_lnum < lnum)
1624 sp = sp->sst_next;
1625
1626 if (sp != NULL && sp->sst_change_lnum != 0)
1627 sp->sst_change_lnum = lnum;
1628}
1629
1630/*
1631 * End of handling of the state stack.
1632 ****************************************/
1633
1634 static void
1635invalidate_current_state()
1636{
1637 clear_current_state();
1638 current_state.ga_itemsize = 0; /* mark current_state invalid */
1639 current_next_list = NULL;
1640 keepend_level = -1;
1641}
1642
1643 static void
1644validate_current_state()
1645{
1646 current_state.ga_itemsize = sizeof(stateitem_T);
1647 current_state.ga_growsize = 3;
1648}
1649
1650/*
1651 * Return TRUE if the syntax at start of lnum changed since last time.
1652 * This will only be called just after get_syntax_attr() for the previous
1653 * line, to check if the next line needs to be redrawn too.
1654 */
1655 int
1656syntax_check_changed(lnum)
1657 linenr_T lnum;
1658{
1659 int retval = TRUE;
1660 synstate_T *sp;
1661
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 /*
1663 * Check the state stack when:
1664 * - lnum is just below the previously syntaxed line.
1665 * - lnum is not before the lines with saved states.
1666 * - lnum is not past the lines with saved states.
1667 * - lnum is at or before the last changed line.
1668 */
1669 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1670 {
1671 sp = syn_stack_find_entry(lnum);
1672 if (sp != NULL && sp->sst_lnum == lnum)
1673 {
1674 /*
1675 * finish the previous line (needed when not all of the line was
1676 * drawn)
1677 */
1678 (void)syn_finish_line(FALSE);
1679
1680 /*
1681 * Compare the current state with the previously saved state of
1682 * the line.
1683 */
1684 if (syn_stack_equal(sp))
1685 retval = FALSE;
1686
1687 /*
1688 * Store the current state in b_sst_array[] for later use.
1689 */
1690 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001691 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692 }
1693 }
1694
Bram Moolenaar071d4272004-06-13 20:20:40 +00001695 return retval;
1696}
1697
1698/*
1699 * Finish the current line.
1700 * This doesn't return any attributes, it only gets the state at the end of
1701 * the line. It can start anywhere in the line, as long as the current state
1702 * is valid.
1703 */
1704 static int
1705syn_finish_line(syncing)
1706 int syncing; /* called for syncing */
1707{
1708 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001709 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001710
1711 if (!current_finished)
1712 {
1713 while (!current_finished)
1714 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001715 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001716 /*
1717 * When syncing, and found some item, need to check the item.
1718 */
1719 if (syncing && current_state.ga_len)
1720 {
1721 /*
1722 * Check for match with sync item.
1723 */
1724 cur_si = &CUR_STATE(current_state.ga_len - 1);
1725 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001726 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1728 return TRUE;
1729
1730 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001731 * that ends here, need to do that now. Be careful not to go
1732 * past the NUL. */
1733 prev_current_col = current_col;
1734 if (syn_getcurline()[current_col] != NUL)
1735 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001737 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738 }
1739 ++current_col;
1740 }
1741 }
1742 return FALSE;
1743}
1744
1745/*
1746 * Return highlight attributes for next character.
1747 * Must first call syntax_start() once for the line.
1748 * "col" is normally 0 for the first use in a line, and increments by one each
1749 * time. It's allowed to skip characters and to stop before the end of the
1750 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001751 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1752 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 */
1754 int
Bram Moolenaar860cae12010-06-05 23:22:07 +02001755get_syntax_attr(col, p_flags, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 colnr_T col;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001757 int *p_flags UNUSED;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001758 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001759 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001760{
1761 int attr = 0;
1762
Bram Moolenaar349955a2007-08-14 21:07:36 +00001763 if (can_spell != NULL)
1764 /* Default: Only do spelling when there is no @Spell cluster or when
1765 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001766 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1767 ? (syn_block->b_spell_cluster_id == 0)
1768 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001769
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001771 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772 return 0;
1773
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001774 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001775 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001776 {
1777 clear_current_state();
1778#ifdef FEAT_EVAL
1779 current_id = 0;
1780 current_trans_id = 0;
1781#endif
1782 return 0;
1783 }
1784
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 /* Make sure current_state is valid */
1786 if (INVALID_STATE(&current_state))
1787 validate_current_state();
1788
1789 /*
1790 * Skip from the current column to "col", get the attributes for "col".
1791 */
1792 while (current_col <= col)
1793 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001794 attr = syn_current_attr(FALSE, TRUE, can_spell,
1795 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 ++current_col;
1797 }
1798
Bram Moolenaar860cae12010-06-05 23:22:07 +02001799#ifdef FEAT_CONCEAL
1800 if (p_flags != NULL)
1801 *p_flags = current_flags;
1802#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 return attr;
1804}
1805
1806/*
1807 * Get syntax attributes for current_lnum, current_col.
1808 */
1809 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001810syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 int syncing; /* When 1: called for syncing */
1812 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001813 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001814 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815{
1816 int syn_id;
1817 lpos_T endpos; /* was: char_u *endp; */
1818 lpos_T hl_startpos; /* was: int hl_startcol; */
1819 lpos_T hl_endpos;
1820 lpos_T eos_pos; /* end-of-start match (start region) */
1821 lpos_T eoe_pos; /* end-of-end pattern */
1822 int end_idx; /* group ID for end pattern */
1823 int idx;
1824 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001825 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826 int startcol;
1827 int endcol;
1828 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001829 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001830 short *next_list;
1831 int found_match; /* found usable match */
1832 static int try_next_column = FALSE; /* must try in next col */
1833 int do_keywords;
1834 regmmatch_T regmatch;
1835 lpos_T pos;
1836 int lc_col;
1837 reg_extmatch_T *cur_extmatch = NULL;
1838 char_u *line; /* current line. NOTE: becomes invalid after
1839 looking for a pattern match! */
1840
1841 /* variables for zero-width matches that have a "nextgroup" argument */
1842 int keep_next_list;
1843 int zero_width_next_list = FALSE;
1844 garray_T zero_width_next_ga;
1845
1846 /*
1847 * No character, no attributes! Past end of line?
1848 * Do try matching with an empty line (could be the start of a region).
1849 */
1850 line = syn_getcurline();
1851 if (line[current_col] == NUL && current_col != 0)
1852 {
1853 /*
1854 * If we found a match after the last column, use it.
1855 */
1856 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1857 && next_match_col != MAXCOL)
1858 (void)push_next_match(NULL);
1859
1860 current_finished = TRUE;
1861 current_state_stored = FALSE;
1862 return 0;
1863 }
1864
1865 /* if the current or next character is NUL, we will finish the line now */
1866 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1867 {
1868 current_finished = TRUE;
1869 current_state_stored = FALSE;
1870 }
1871
1872 /*
1873 * When in the previous column there was a match but it could not be used
1874 * (empty match or already matched in this column) need to try again in
1875 * the next column.
1876 */
1877 if (try_next_column)
1878 {
1879 next_match_idx = -1;
1880 try_next_column = FALSE;
1881 }
1882
1883 /* Only check for keywords when not syncing and there are some. */
1884 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001885 && (syn_block->b_keywtab.ht_used > 0
1886 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887
1888 /* Init the list of zero-width matches with a nextlist. This is used to
1889 * avoid matching the same item in the same position twice. */
1890 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1891
1892 /*
1893 * Repeat matching keywords and patterns, to find contained items at the
1894 * same column. This stops when there are no extra matches at the current
1895 * column.
1896 */
1897 do
1898 {
1899 found_match = FALSE;
1900 keep_next_list = FALSE;
1901 syn_id = 0;
1902
1903 /*
1904 * 1. Check for a current state.
1905 * Only when there is no current state, or if the current state may
1906 * contain other things, we need to check for keywords and patterns.
1907 * Always need to check for contained items if some item has the
1908 * "containedin" argument (takes extra time!).
1909 */
1910 if (current_state.ga_len)
1911 cur_si = &CUR_STATE(current_state.ga_len - 1);
1912 else
1913 cur_si = NULL;
1914
Bram Moolenaar860cae12010-06-05 23:22:07 +02001915 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001916 || cur_si->si_cont_list != NULL)
1917 {
1918 /*
1919 * 2. Check for keywords, if on a keyword char after a non-keyword
1920 * char. Don't do this when syncing.
1921 */
1922 if (do_keywords)
1923 {
1924 line = syn_getcurline();
1925 if (vim_iswordc_buf(line + current_col, syn_buf)
1926 && (current_col == 0
1927 || !vim_iswordc_buf(line + current_col - 1
1928#ifdef FEAT_MBYTE
1929 - (has_mbyte
1930 ? (*mb_head_off)(line, line + current_col - 1)
1931 : 0)
1932#endif
1933 , syn_buf)))
1934 {
1935 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001936 &endcol, &flags, &next_list, cur_si,
1937 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001938 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939 {
1940 if (push_current_state(KEYWORD_IDX) == OK)
1941 {
1942 cur_si = &CUR_STATE(current_state.ga_len - 1);
1943 cur_si->si_m_startcol = current_col;
1944 cur_si->si_h_startpos.lnum = current_lnum;
1945 cur_si->si_h_startpos.col = 0; /* starts right away */
1946 cur_si->si_m_endpos.lnum = current_lnum;
1947 cur_si->si_m_endpos.col = endcol;
1948 cur_si->si_h_endpos.lnum = current_lnum;
1949 cur_si->si_h_endpos.col = endcol;
1950 cur_si->si_ends = TRUE;
1951 cur_si->si_end_idx = 0;
1952 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001953#ifdef FEAT_CONCEAL
1954 cur_si->si_char = cchar;
1955 if (current_state.ga_len > 1)
1956 cur_si->si_flags |=
1957 CUR_STATE(current_state.ga_len - 2).si_flags
1958 & HL_CONCEAL;
1959#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001960 cur_si->si_id = syn_id;
1961 cur_si->si_trans_id = syn_id;
1962 if (flags & HL_TRANSP)
1963 {
1964 if (current_state.ga_len < 2)
1965 {
1966 cur_si->si_attr = 0;
1967 cur_si->si_trans_id = 0;
1968 }
1969 else
1970 {
1971 cur_si->si_attr = CUR_STATE(
1972 current_state.ga_len - 2).si_attr;
1973 cur_si->si_trans_id = CUR_STATE(
1974 current_state.ga_len - 2).si_trans_id;
1975 }
1976 }
1977 else
1978 cur_si->si_attr = syn_id2attr(syn_id);
1979 cur_si->si_cont_list = NULL;
1980 cur_si->si_next_list = next_list;
1981 check_keepend();
1982 }
1983 else
1984 vim_free(next_list);
1985 }
1986 }
1987 }
1988
1989 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001990 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001991 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001992 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001993 {
1994 /*
1995 * If we didn't check for a match yet, or we are past it, check
1996 * for any match with a pattern.
1997 */
1998 if (next_match_idx < 0 || next_match_col < (int)current_col)
1999 {
2000 /*
2001 * Check all relevant patterns for a match at this
2002 * position. This is complicated, because matching with a
2003 * pattern takes quite a bit of time, thus we want to
2004 * avoid doing it when it's not needed.
2005 */
2006 next_match_idx = 0; /* no match in this line yet */
2007 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002008 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002010 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011 if ( spp->sp_syncing == syncing
2012 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2013 && (spp->sp_type == SPTYPE_MATCH
2014 || spp->sp_type == SPTYPE_START)
2015 && (current_next_list != NULL
2016 ? in_id_list(NULL, current_next_list,
2017 &spp->sp_syn, 0)
2018 : (cur_si == NULL
2019 ? !(spp->sp_flags & HL_CONTAINED)
2020 : in_id_list(cur_si,
2021 cur_si->si_cont_list, &spp->sp_syn,
2022 spp->sp_flags & HL_CONTAINED))))
2023 {
2024 /* If we already tried matching in this line, and
2025 * there isn't a match before next_match_col, skip
2026 * this item. */
2027 if (spp->sp_line_id == current_line_id
2028 && spp->sp_startcol >= next_match_col)
2029 continue;
2030 spp->sp_line_id = current_line_id;
2031
2032 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2033 if (lc_col < 0)
2034 lc_col = 0;
2035
2036 regmatch.rmm_ic = spp->sp_ic;
2037 regmatch.regprog = spp->sp_prog;
2038 if (!syn_regexec(&regmatch, current_lnum,
2039 (colnr_T)lc_col))
2040 {
2041 /* no match in this line, try another one */
2042 spp->sp_startcol = MAXCOL;
2043 continue;
2044 }
2045
2046 /*
2047 * Compute the first column of the match.
2048 */
2049 syn_add_start_off(&pos, &regmatch,
2050 spp, SPO_MS_OFF, -1);
2051 if (pos.lnum > current_lnum)
2052 {
2053 /* must have used end of match in a next line,
2054 * we can't handle that */
2055 spp->sp_startcol = MAXCOL;
2056 continue;
2057 }
2058 startcol = pos.col;
2059
2060 /* remember the next column where this pattern
2061 * matches in the current line */
2062 spp->sp_startcol = startcol;
2063
2064 /*
2065 * If a previously found match starts at a lower
2066 * column number, don't use this one.
2067 */
2068 if (startcol >= next_match_col)
2069 continue;
2070
2071 /*
2072 * If we matched this pattern at this position
2073 * before, skip it. Must retry in the next
2074 * column, because it may match from there.
2075 */
2076 if (did_match_already(idx, &zero_width_next_ga))
2077 {
2078 try_next_column = TRUE;
2079 continue;
2080 }
2081
2082 endpos.lnum = regmatch.endpos[0].lnum;
2083 endpos.col = regmatch.endpos[0].col;
2084
2085 /* Compute the highlight start. */
2086 syn_add_start_off(&hl_startpos, &regmatch,
2087 spp, SPO_HS_OFF, -1);
2088
2089 /* Compute the region start. */
2090 /* Default is to use the end of the match. */
2091 syn_add_end_off(&eos_pos, &regmatch,
2092 spp, SPO_RS_OFF, 0);
2093
2094 /*
2095 * Grab the external submatches before they get
2096 * overwritten. Reference count doesn't change.
2097 */
2098 unref_extmatch(cur_extmatch);
2099 cur_extmatch = re_extmatch_out;
2100 re_extmatch_out = NULL;
2101
2102 flags = 0;
2103 eoe_pos.lnum = 0; /* avoid warning */
2104 eoe_pos.col = 0;
2105 end_idx = 0;
2106 hl_endpos.lnum = 0;
2107
2108 /*
2109 * For a "oneline" the end must be found in the
2110 * same line too. Search for it after the end of
2111 * the match with the start pattern. Set the
2112 * resulting end positions at the same time.
2113 */
2114 if (spp->sp_type == SPTYPE_START
2115 && (spp->sp_flags & HL_ONELINE))
2116 {
2117 lpos_T startpos;
2118
2119 startpos = endpos;
2120 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2121 &flags, &eoe_pos, &end_idx, cur_extmatch);
2122 if (endpos.lnum == 0)
2123 continue; /* not found */
2124 }
2125
2126 /*
2127 * For a "match" the size must be > 0 after the
2128 * end offset needs has been added. Except when
2129 * syncing.
2130 */
2131 else if (spp->sp_type == SPTYPE_MATCH)
2132 {
2133 syn_add_end_off(&hl_endpos, &regmatch, spp,
2134 SPO_HE_OFF, 0);
2135 syn_add_end_off(&endpos, &regmatch, spp,
2136 SPO_ME_OFF, 0);
2137 if (endpos.lnum == current_lnum
2138 && (int)endpos.col + syncing < startcol)
2139 {
2140 /*
2141 * If an empty string is matched, may need
2142 * to try matching again at next column.
2143 */
2144 if (regmatch.startpos[0].col
2145 == regmatch.endpos[0].col)
2146 try_next_column = TRUE;
2147 continue;
2148 }
2149 }
2150
2151 /*
2152 * keep the best match so far in next_match_*
2153 */
2154 /* Highlighting must start after startpos and end
2155 * before endpos. */
2156 if (hl_startpos.lnum == current_lnum
2157 && (int)hl_startpos.col < startcol)
2158 hl_startpos.col = startcol;
2159 limit_pos_zero(&hl_endpos, &endpos);
2160
2161 next_match_idx = idx;
2162 next_match_col = startcol;
2163 next_match_m_endpos = endpos;
2164 next_match_h_endpos = hl_endpos;
2165 next_match_h_startpos = hl_startpos;
2166 next_match_flags = flags;
2167 next_match_eos_pos = eos_pos;
2168 next_match_eoe_pos = eoe_pos;
2169 next_match_end_idx = end_idx;
2170 unref_extmatch(next_match_extmatch);
2171 next_match_extmatch = cur_extmatch;
2172 cur_extmatch = NULL;
2173 }
2174 }
2175 }
2176
2177 /*
2178 * If we found a match at the current column, use it.
2179 */
2180 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2181 {
2182 synpat_T *lspp;
2183
2184 /* When a zero-width item matched which has a nextgroup,
2185 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002186 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002187 if (next_match_m_endpos.lnum == current_lnum
2188 && next_match_m_endpos.col == current_col
2189 && lspp->sp_next_list != NULL)
2190 {
2191 current_next_list = lspp->sp_next_list;
2192 current_next_flags = lspp->sp_flags;
2193 keep_next_list = TRUE;
2194 zero_width_next_list = TRUE;
2195
2196 /* Add the index to a list, so that we can check
2197 * later that we don't match it again (and cause an
2198 * endless loop). */
2199 if (ga_grow(&zero_width_next_ga, 1) == OK)
2200 {
2201 ((int *)(zero_width_next_ga.ga_data))
2202 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002203 }
2204 next_match_idx = -1;
2205 }
2206 else
2207 cur_si = push_next_match(cur_si);
2208 found_match = TRUE;
2209 }
2210 }
2211 }
2212
2213 /*
2214 * Handle searching for nextgroup match.
2215 */
2216 if (current_next_list != NULL && !keep_next_list)
2217 {
2218 /*
2219 * If a nextgroup was not found, continue looking for one if:
2220 * - this is an empty line and the "skipempty" option was given
2221 * - we are on white space and the "skipwhite" option was given
2222 */
2223 if (!found_match)
2224 {
2225 line = syn_getcurline();
2226 if (((current_next_flags & HL_SKIPWHITE)
2227 && vim_iswhite(line[current_col]))
2228 || ((current_next_flags & HL_SKIPEMPTY)
2229 && *line == NUL))
2230 break;
2231 }
2232
2233 /*
2234 * If a nextgroup was found: Use it, and continue looking for
2235 * contained matches.
2236 * If a nextgroup was not found: Continue looking for a normal
2237 * match.
2238 * When did set current_next_list for a zero-width item and no
2239 * match was found don't loop (would get stuck).
2240 */
2241 current_next_list = NULL;
2242 next_match_idx = -1;
2243 if (!zero_width_next_list)
2244 found_match = TRUE;
2245 }
2246
2247 } while (found_match);
2248
2249 /*
2250 * Use attributes from the current state, if within its highlighting.
2251 * If not, use attributes from the current-but-one state, etc.
2252 */
2253 current_attr = 0;
2254#ifdef FEAT_EVAL
2255 current_id = 0;
2256 current_trans_id = 0;
2257#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002258#ifdef FEAT_CONCEAL
2259 current_flags = 0;
2260#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002261 if (cur_si != NULL)
2262 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002263#ifndef FEAT_EVAL
2264 int current_trans_id = 0;
2265#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2267 {
2268 sip = &CUR_STATE(idx);
2269 if ((current_lnum > sip->si_h_startpos.lnum
2270 || (current_lnum == sip->si_h_startpos.lnum
2271 && current_col >= sip->si_h_startpos.col))
2272 && (sip->si_h_endpos.lnum == 0
2273 || current_lnum < sip->si_h_endpos.lnum
2274 || (current_lnum == sip->si_h_endpos.lnum
2275 && current_col < sip->si_h_endpos.col)))
2276 {
2277 current_attr = sip->si_attr;
2278#ifdef FEAT_EVAL
2279 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002281 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002282#ifdef FEAT_CONCEAL
2283 current_flags = sip->si_flags;
2284 current_sub_char = sip->si_char;
2285#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 break;
2287 }
2288 }
2289
Bram Moolenaar217ad922005-03-20 22:37:15 +00002290 if (can_spell != NULL)
2291 {
2292 struct sp_syn sps;
2293
2294 /*
2295 * set "can_spell" to TRUE if spell checking is supposed to be
2296 * done in the current item.
2297 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002298 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002299 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002300 /* There is no @Spell cluster: Do spelling for items without
2301 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002302 if (syn_block->b_nospell_cluster_id == 0
2303 || current_trans_id == 0)
2304 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002305 else
2306 {
2307 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002308 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002309 sps.cont_in_list = NULL;
2310 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2311 }
2312 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002313 else
2314 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002315 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002316 * the @Spell cluster. But not when @NoSpell is also there.
2317 * At the toplevel only spell check when ":syn spell toplevel"
2318 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002319 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002320 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002321 else
2322 {
2323 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002324 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002325 sps.cont_in_list = NULL;
2326 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2327
Bram Moolenaar860cae12010-06-05 23:22:07 +02002328 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002329 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002330 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002331 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2332 *can_spell = FALSE;
2333 }
2334 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002335 }
2336 }
2337
2338
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 /*
2340 * Check for end of current state (and the states before it) at the
2341 * next column. Don't do this for syncing, because we would miss a
2342 * single character match.
2343 * First check if the current state ends at the current column. It
2344 * may be for an empty match and a containing item might end in the
2345 * current column.
2346 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002347 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002348 {
2349 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002350 if (current_state.ga_len > 0
2351 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 {
2353 ++current_col;
2354 check_state_ends();
2355 --current_col;
2356 }
2357 }
2358 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002359 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002360 /* Default: Only do spelling when there is no @Spell cluster or when
2361 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2363 ? (syn_block->b_spell_cluster_id == 0)
2364 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002366 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 if (current_next_list != NULL
2368 && syn_getcurline()[current_col + 1] == NUL
2369 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2370 current_next_list = NULL;
2371
2372 if (zero_width_next_ga.ga_len > 0)
2373 ga_clear(&zero_width_next_ga);
2374
2375 /* No longer need external matches. But keep next_match_extmatch. */
2376 unref_extmatch(re_extmatch_out);
2377 re_extmatch_out = NULL;
2378 unref_extmatch(cur_extmatch);
2379
2380 return current_attr;
2381}
2382
2383
2384/*
2385 * Check if we already matched pattern "idx" at the current column.
2386 */
2387 static int
2388did_match_already(idx, gap)
2389 int idx;
2390 garray_T *gap;
2391{
2392 int i;
2393
2394 for (i = current_state.ga_len; --i >= 0; )
2395 if (CUR_STATE(i).si_m_startcol == (int)current_col
2396 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2397 && CUR_STATE(i).si_idx == idx)
2398 return TRUE;
2399
2400 /* Zero-width matches with a nextgroup argument are not put on the syntax
2401 * stack, and can only be matched once anyway. */
2402 for (i = gap->ga_len; --i >= 0; )
2403 if (((int *)(gap->ga_data))[i] == idx)
2404 return TRUE;
2405
2406 return FALSE;
2407}
2408
2409/*
2410 * Push the next match onto the stack.
2411 */
2412 static stateitem_T *
2413push_next_match(cur_si)
2414 stateitem_T *cur_si;
2415{
2416 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002417#ifdef FEAT_CONCEAL
2418 int save_flags;
2419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002420
Bram Moolenaar860cae12010-06-05 23:22:07 +02002421 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422
2423 /*
2424 * Push the item in current_state stack;
2425 */
2426 if (push_current_state(next_match_idx) == OK)
2427 {
2428 /*
2429 * If it's a start-skip-end type that crosses lines, figure out how
2430 * much it continues in this line. Otherwise just fill in the length.
2431 */
2432 cur_si = &CUR_STATE(current_state.ga_len - 1);
2433 cur_si->si_h_startpos = next_match_h_startpos;
2434 cur_si->si_m_startcol = current_col;
2435 cur_si->si_m_lnum = current_lnum;
2436 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002437#ifdef FEAT_CONCEAL
2438 cur_si->si_char = spp->sp_char;
2439 if (current_state.ga_len > 1)
2440 cur_si->si_flags |=
2441 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2442#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002443 cur_si->si_next_list = spp->sp_next_list;
2444 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2445 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2446 {
2447 /* Try to find the end pattern in the current line */
2448 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2449 check_keepend();
2450 }
2451 else
2452 {
2453 cur_si->si_m_endpos = next_match_m_endpos;
2454 cur_si->si_h_endpos = next_match_h_endpos;
2455 cur_si->si_ends = TRUE;
2456 cur_si->si_flags |= next_match_flags;
2457 cur_si->si_eoe_pos = next_match_eoe_pos;
2458 cur_si->si_end_idx = next_match_end_idx;
2459 }
2460 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2461 keepend_level = current_state.ga_len - 1;
2462 check_keepend();
2463 update_si_attr(current_state.ga_len - 1);
2464
Bram Moolenaar860cae12010-06-05 23:22:07 +02002465#ifdef FEAT_CONCEAL
2466 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2467#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002468 /*
2469 * If the start pattern has another highlight group, push another item
2470 * on the stack for the start pattern.
2471 */
2472 if ( spp->sp_type == SPTYPE_START
2473 && spp->sp_syn_match_id != 0
2474 && push_current_state(next_match_idx) == OK)
2475 {
2476 cur_si = &CUR_STATE(current_state.ga_len - 1);
2477 cur_si->si_h_startpos = next_match_h_startpos;
2478 cur_si->si_m_startcol = current_col;
2479 cur_si->si_m_lnum = current_lnum;
2480 cur_si->si_m_endpos = next_match_eos_pos;
2481 cur_si->si_h_endpos = next_match_eos_pos;
2482 cur_si->si_ends = TRUE;
2483 cur_si->si_end_idx = 0;
2484 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002485#ifdef FEAT_CONCEAL
2486 cur_si->si_flags |= save_flags;
2487 if (cur_si->si_flags & HL_CONCEALENDS)
2488 cur_si->si_flags |= HL_CONCEAL;
2489#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002490 cur_si->si_next_list = NULL;
2491 check_keepend();
2492 update_si_attr(current_state.ga_len - 1);
2493 }
2494 }
2495
2496 next_match_idx = -1; /* try other match next time */
2497
2498 return cur_si;
2499}
2500
2501/*
2502 * Check for end of current state (and the states before it).
2503 */
2504 static void
2505check_state_ends()
2506{
2507 stateitem_T *cur_si;
2508 int had_extend = FALSE;
2509
2510 cur_si = &CUR_STATE(current_state.ga_len - 1);
2511 for (;;)
2512 {
2513 if (cur_si->si_ends
2514 && (cur_si->si_m_endpos.lnum < current_lnum
2515 || (cur_si->si_m_endpos.lnum == current_lnum
2516 && cur_si->si_m_endpos.col <= current_col)))
2517 {
2518 /*
2519 * If there is an end pattern group ID, highlight the end pattern
2520 * now. No need to pop the current item from the stack.
2521 * Only do this if the end pattern continues beyond the current
2522 * position.
2523 */
2524 if (cur_si->si_end_idx
2525 && (cur_si->si_eoe_pos.lnum > current_lnum
2526 || (cur_si->si_eoe_pos.lnum == current_lnum
2527 && cur_si->si_eoe_pos.col > current_col)))
2528 {
2529 cur_si->si_idx = cur_si->si_end_idx;
2530 cur_si->si_end_idx = 0;
2531 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2532 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2533 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002534#ifdef FEAT_CONCEAL
2535 if (cur_si->si_flags & HL_CONCEALENDS)
2536 cur_si->si_flags |= HL_CONCEAL;
2537#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002539
2540 /* what matches next may be different now, clear it */
2541 next_match_idx = 0;
2542 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543 break;
2544 }
2545 else
2546 {
2547 /* handle next_list, unless at end of line and no "skipnl" or
2548 * "skipempty" */
2549 current_next_list = cur_si->si_next_list;
2550 current_next_flags = cur_si->si_flags;
2551 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2552 && syn_getcurline()[current_col] == NUL)
2553 current_next_list = NULL;
2554
2555 /* When the ended item has "extend", another item with
2556 * "keepend" now needs to check for its end. */
2557 if (cur_si->si_flags & HL_EXTEND)
2558 had_extend = TRUE;
2559
2560 pop_current_state();
2561
2562 if (current_state.ga_len == 0)
2563 break;
2564
Bram Moolenaar81993f42008-01-11 20:27:45 +00002565 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002566 {
2567 syn_update_ends(FALSE);
2568 if (current_state.ga_len == 0)
2569 break;
2570 }
2571
2572 cur_si = &CUR_STATE(current_state.ga_len - 1);
2573
2574 /*
2575 * Only for a region the search for the end continues after
2576 * the end of the contained item. If the contained match
2577 * included the end-of-line, break here, the region continues.
2578 * Don't do this when:
2579 * - "keepend" is used for the contained item
2580 * - not at the end of the line (could be end="x$"me=e-1).
2581 * - "excludenl" is used (HL_HAS_EOL won't be set)
2582 */
2583 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002584 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 == SPTYPE_START
2586 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2587 {
2588 update_si_end(cur_si, (int)current_col, TRUE);
2589 check_keepend();
2590 if ((current_next_flags & HL_HAS_EOL)
2591 && keepend_level < 0
2592 && syn_getcurline()[current_col] == NUL)
2593 break;
2594 }
2595 }
2596 }
2597 else
2598 break;
2599 }
2600}
2601
2602/*
2603 * Update an entry in the current_state stack for a match or region. This
2604 * fills in si_attr, si_next_list and si_cont_list.
2605 */
2606 static void
2607update_si_attr(idx)
2608 int idx;
2609{
2610 stateitem_T *sip = &CUR_STATE(idx);
2611 synpat_T *spp;
2612
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002613 /* This should not happen... */
2614 if (sip->si_idx < 0)
2615 return;
2616
Bram Moolenaar860cae12010-06-05 23:22:07 +02002617 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 if (sip->si_flags & HL_MATCH)
2619 sip->si_id = spp->sp_syn_match_id;
2620 else
2621 sip->si_id = spp->sp_syn.id;
2622 sip->si_attr = syn_id2attr(sip->si_id);
2623 sip->si_trans_id = sip->si_id;
2624 if (sip->si_flags & HL_MATCH)
2625 sip->si_cont_list = NULL;
2626 else
2627 sip->si_cont_list = spp->sp_cont_list;
2628
2629 /*
2630 * For transparent items, take attr from outer item.
2631 * Also take cont_list, if there is none.
2632 * Don't do this for the matchgroup of a start or end pattern.
2633 */
2634 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2635 {
2636 if (idx == 0)
2637 {
2638 sip->si_attr = 0;
2639 sip->si_trans_id = 0;
2640 if (sip->si_cont_list == NULL)
2641 sip->si_cont_list = ID_LIST_ALL;
2642 }
2643 else
2644 {
2645 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2646 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002647 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2648 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 if (sip->si_cont_list == NULL)
2650 {
2651 sip->si_flags |= HL_TRANS_CONT;
2652 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2653 }
2654 }
2655 }
2656}
2657
2658/*
2659 * Check the current stack for patterns with "keepend" flag.
2660 * Propagate the match-end to contained items, until a "skipend" item is found.
2661 */
2662 static void
2663check_keepend()
2664{
2665 int i;
2666 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002667 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668 stateitem_T *sip;
2669
2670 /*
2671 * This check can consume a lot of time; only do it from the level where
2672 * there really is a keepend.
2673 */
2674 if (keepend_level < 0)
2675 return;
2676
2677 /*
2678 * Find the last index of an "extend" item. "keepend" items before that
2679 * won't do anything. If there is no "extend" item "i" will be
2680 * "keepend_level" and all "keepend" items will work normally.
2681 */
2682 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2683 if (CUR_STATE(i).si_flags & HL_EXTEND)
2684 break;
2685
2686 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002687 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002688 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002689 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690 for ( ; i < current_state.ga_len; ++i)
2691 {
2692 sip = &CUR_STATE(i);
2693 if (maxpos.lnum != 0)
2694 {
2695 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002696 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2698 sip->si_ends = TRUE;
2699 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002700 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2701 {
2702 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 || maxpos.lnum > sip->si_m_endpos.lnum
2704 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002705 && maxpos.col > sip->si_m_endpos.col))
2706 maxpos = sip->si_m_endpos;
2707 if (maxpos_h.lnum == 0
2708 || maxpos_h.lnum > sip->si_h_endpos.lnum
2709 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2710 && maxpos_h.col > sip->si_h_endpos.col))
2711 maxpos_h = sip->si_h_endpos;
2712 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002713 }
2714}
2715
2716/*
2717 * Update an entry in the current_state stack for a start-skip-end pattern.
2718 * This finds the end of the current item, if it's in the current line.
2719 *
2720 * Return the flags for the matched END.
2721 */
2722 static void
2723update_si_end(sip, startcol, force)
2724 stateitem_T *sip;
2725 int startcol; /* where to start searching for the end */
2726 int force; /* when TRUE overrule a previous end */
2727{
2728 lpos_T startpos;
2729 lpos_T endpos;
2730 lpos_T hl_endpos;
2731 lpos_T end_endpos;
2732 int end_idx;
2733
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002734 /* return quickly for a keyword */
2735 if (sip->si_idx < 0)
2736 return;
2737
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738 /* Don't update when it's already done. Can be a match of an end pattern
2739 * that started in a previous line. Watch out: can also be a "keepend"
2740 * from a containing item. */
2741 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2742 return;
2743
2744 /*
2745 * We need to find the end of the region. It may continue in the next
2746 * line.
2747 */
2748 end_idx = 0;
2749 startpos.lnum = current_lnum;
2750 startpos.col = startcol;
2751 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2752 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2753
2754 if (endpos.lnum == 0)
2755 {
2756 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002757 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758 {
2759 /* a "oneline" never continues in the next line */
2760 sip->si_ends = TRUE;
2761 sip->si_m_endpos.lnum = current_lnum;
2762 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2763 }
2764 else
2765 {
2766 /* continues in the next line */
2767 sip->si_ends = FALSE;
2768 sip->si_m_endpos.lnum = 0;
2769 }
2770 sip->si_h_endpos = sip->si_m_endpos;
2771 }
2772 else
2773 {
2774 /* match within this line */
2775 sip->si_m_endpos = endpos;
2776 sip->si_h_endpos = hl_endpos;
2777 sip->si_eoe_pos = end_endpos;
2778 sip->si_ends = TRUE;
2779 sip->si_end_idx = end_idx;
2780 }
2781}
2782
2783/*
2784 * Add a new state to the current state stack.
2785 * It is cleared and the index set to "idx".
2786 * Return FAIL if it's not possible (out of memory).
2787 */
2788 static int
2789push_current_state(idx)
2790 int idx;
2791{
2792 if (ga_grow(&current_state, 1) == FAIL)
2793 return FAIL;
2794 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2795 CUR_STATE(current_state.ga_len).si_idx = idx;
2796 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 return OK;
2798}
2799
2800/*
2801 * Remove a state from the current_state stack.
2802 */
2803 static void
2804pop_current_state()
2805{
2806 if (current_state.ga_len)
2807 {
2808 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2809 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002810 }
2811 /* after the end of a pattern, try matching a keyword or pattern */
2812 next_match_idx = -1;
2813
2814 /* if first state with "keepend" is popped, reset keepend_level */
2815 if (keepend_level >= current_state.ga_len)
2816 keepend_level = -1;
2817}
2818
2819/*
2820 * Find the end of a start/skip/end syntax region after "startpos".
2821 * Only checks one line.
2822 * Also handles a match item that continued from a previous line.
2823 * If not found, the syntax item continues in the next line. m_endpos->lnum
2824 * will be 0.
2825 * If found, the end of the region and the end of the highlighting is
2826 * computed.
2827 */
2828 static void
2829find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2830 end_idx, start_ext)
2831 int idx; /* index of the pattern */
2832 lpos_T *startpos; /* where to start looking for an END match */
2833 lpos_T *m_endpos; /* return: end of match */
2834 lpos_T *hl_endpos; /* return: end of highlighting */
2835 long *flagsp; /* return: flags of matching END */
2836 lpos_T *end_endpos; /* return: end of end pattern match */
2837 int *end_idx; /* return: group ID for end pat. match, or 0 */
2838 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2839{
2840 colnr_T matchcol;
2841 synpat_T *spp, *spp_skip;
2842 int start_idx;
2843 int best_idx;
2844 regmmatch_T regmatch;
2845 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2846 lpos_T pos;
2847 char_u *line;
2848 int had_match = FALSE;
2849
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002850 /* just in case we are invoked for a keyword */
2851 if (idx < 0)
2852 return;
2853
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 /*
2855 * Check for being called with a START pattern.
2856 * Can happen with a match that continues to the next line, because it
2857 * contained a region.
2858 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002859 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002860 if (spp->sp_type != SPTYPE_START)
2861 {
2862 *hl_endpos = *startpos;
2863 return;
2864 }
2865
2866 /*
2867 * Find the SKIP or first END pattern after the last START pattern.
2868 */
2869 for (;;)
2870 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002871 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002872 if (spp->sp_type != SPTYPE_START)
2873 break;
2874 ++idx;
2875 }
2876
2877 /*
2878 * Lookup the SKIP pattern (if present)
2879 */
2880 if (spp->sp_type == SPTYPE_SKIP)
2881 {
2882 spp_skip = spp;
2883 ++idx;
2884 }
2885 else
2886 spp_skip = NULL;
2887
2888 /* Setup external matches for syn_regexec(). */
2889 unref_extmatch(re_extmatch_in);
2890 re_extmatch_in = ref_extmatch(start_ext);
2891
2892 matchcol = startpos->col; /* start looking for a match at sstart */
2893 start_idx = idx; /* remember the first END pattern. */
2894 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2895 for (;;)
2896 {
2897 /*
2898 * Find end pattern that matches first after "matchcol".
2899 */
2900 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002901 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902 {
2903 int lc_col = matchcol;
2904
Bram Moolenaar860cae12010-06-05 23:22:07 +02002905 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002906 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2907 break;
2908 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2909 if (lc_col < 0)
2910 lc_col = 0;
2911
2912 regmatch.rmm_ic = spp->sp_ic;
2913 regmatch.regprog = spp->sp_prog;
2914 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2915 {
2916 if (best_idx == -1 || regmatch.startpos[0].col
2917 < best_regmatch.startpos[0].col)
2918 {
2919 best_idx = idx;
2920 best_regmatch.startpos[0] = regmatch.startpos[0];
2921 best_regmatch.endpos[0] = regmatch.endpos[0];
2922 }
2923 }
2924 }
2925
2926 /*
2927 * If all end patterns have been tried, and there is no match, the
2928 * item continues until end-of-line.
2929 */
2930 if (best_idx == -1)
2931 break;
2932
2933 /*
2934 * If the skip pattern matches before the end pattern,
2935 * continue searching after the skip pattern.
2936 */
2937 if (spp_skip != NULL)
2938 {
2939 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2940
2941 if (lc_col < 0)
2942 lc_col = 0;
2943 regmatch.rmm_ic = spp_skip->sp_ic;
2944 regmatch.regprog = spp_skip->sp_prog;
2945 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2946 && regmatch.startpos[0].col
2947 <= best_regmatch.startpos[0].col)
2948 {
2949 /* Add offset to skip pattern match */
2950 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2951
2952 /* If the skip pattern goes on to the next line, there is no
2953 * match with an end pattern in this line. */
2954 if (pos.lnum > startpos->lnum)
2955 break;
2956
2957 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2958
2959 /* take care of an empty match or negative offset */
2960 if (pos.col <= matchcol)
2961 ++matchcol;
2962 else if (pos.col <= regmatch.endpos[0].col)
2963 matchcol = pos.col;
2964 else
2965 /* Be careful not to jump over the NUL at the end-of-line */
2966 for (matchcol = regmatch.endpos[0].col;
2967 line[matchcol] != NUL && matchcol < pos.col;
2968 ++matchcol)
2969 ;
2970
2971 /* if the skip pattern includes end-of-line, break here */
2972 if (line[matchcol] == NUL)
2973 break;
2974
2975 continue; /* start with first end pattern again */
2976 }
2977 }
2978
2979 /*
2980 * Match from start pattern to end pattern.
2981 * Correct for match and highlight offset of end pattern.
2982 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002983 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2985 /* can't end before the start */
2986 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2987 m_endpos->col = startpos->col;
2988
2989 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2990 /* can't end before the start */
2991 if (end_endpos->lnum == startpos->lnum
2992 && end_endpos->col < startpos->col)
2993 end_endpos->col = startpos->col;
2994 /* can't end after the match */
2995 limit_pos(end_endpos, m_endpos);
2996
2997 /*
2998 * If the end group is highlighted differently, adjust the pointers.
2999 */
3000 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3001 {
3002 *end_idx = best_idx;
3003 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3004 {
3005 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3006 hl_endpos->col = best_regmatch.endpos[0].col;
3007 }
3008 else
3009 {
3010 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3011 hl_endpos->col = best_regmatch.startpos[0].col;
3012 }
3013 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3014
3015 /* can't end before the start */
3016 if (hl_endpos->lnum == startpos->lnum
3017 && hl_endpos->col < startpos->col)
3018 hl_endpos->col = startpos->col;
3019 limit_pos(hl_endpos, m_endpos);
3020
3021 /* now the match ends where the highlighting ends, it is turned
3022 * into the matchgroup for the end */
3023 *m_endpos = *hl_endpos;
3024 }
3025 else
3026 {
3027 *end_idx = 0;
3028 *hl_endpos = *end_endpos;
3029 }
3030
3031 *flagsp = spp->sp_flags;
3032
3033 had_match = TRUE;
3034 break;
3035 }
3036
3037 /* no match for an END pattern in this line */
3038 if (!had_match)
3039 m_endpos->lnum = 0;
3040
3041 /* Remove external matches. */
3042 unref_extmatch(re_extmatch_in);
3043 re_extmatch_in = NULL;
3044}
3045
3046/*
3047 * Limit "pos" not to be after "limit".
3048 */
3049 static void
3050limit_pos(pos, limit)
3051 lpos_T *pos;
3052 lpos_T *limit;
3053{
3054 if (pos->lnum > limit->lnum)
3055 *pos = *limit;
3056 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3057 pos->col = limit->col;
3058}
3059
3060/*
3061 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3062 */
3063 static void
3064limit_pos_zero(pos, limit)
3065 lpos_T *pos;
3066 lpos_T *limit;
3067{
3068 if (pos->lnum == 0)
3069 *pos = *limit;
3070 else
3071 limit_pos(pos, limit);
3072}
3073
3074/*
3075 * Add offset to matched text for end of match or highlight.
3076 */
3077 static void
3078syn_add_end_off(result, regmatch, spp, idx, extra)
3079 lpos_T *result; /* returned position */
3080 regmmatch_T *regmatch; /* start/end of match */
3081 synpat_T *spp; /* matched pattern */
3082 int idx; /* index of offset */
3083 int extra; /* extra chars for offset to start */
3084{
3085 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003086 int off;
3087 char_u *base;
3088 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003089
3090 if (spp->sp_off_flags & (1 << idx))
3091 {
3092 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003093 col = regmatch->startpos[0].col;
3094 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003095 }
3096 else
3097 {
3098 result->lnum = regmatch->endpos[0].lnum;
3099 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003100 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003101 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003102 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3103 * is a matchgroup. Watch out for match with last NL in the buffer. */
3104 if (result->lnum > syn_buf->b_ml.ml_line_count)
3105 col = 0;
3106 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003107 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003108 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3109 p = base + col;
3110 if (off > 0)
3111 {
3112 while (off-- > 0 && *p != NUL)
3113 mb_ptr_adv(p);
3114 }
3115 else if (off < 0)
3116 {
3117 while (off++ < 0 && base < p)
3118 mb_ptr_back(base, p);
3119 }
3120 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003121 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003122 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003123}
3124
3125/*
3126 * Add offset to matched text for start of match or highlight.
3127 * Avoid resulting column to become negative.
3128 */
3129 static void
3130syn_add_start_off(result, regmatch, spp, idx, extra)
3131 lpos_T *result; /* returned position */
3132 regmmatch_T *regmatch; /* start/end of match */
3133 synpat_T *spp;
3134 int idx;
3135 int extra; /* extra chars for offset to end */
3136{
3137 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003138 int off;
3139 char_u *base;
3140 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003141
3142 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3143 {
3144 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003145 col = regmatch->endpos[0].col;
3146 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003147 }
3148 else
3149 {
3150 result->lnum = regmatch->startpos[0].lnum;
3151 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003152 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003154 if (result->lnum > syn_buf->b_ml.ml_line_count)
3155 {
3156 /* a "\n" at the end of the pattern may take us below the last line */
3157 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003158 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003159 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003160 if (off != 0)
3161 {
3162 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3163 p = base + col;
3164 if (off > 0)
3165 {
3166 while (off-- && *p != NUL)
3167 mb_ptr_adv(p);
3168 }
3169 else if (off < 0)
3170 {
3171 while (off++ && base < p)
3172 mb_ptr_back(base, p);
3173 }
3174 col = (int)(p - base);
3175 }
3176 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177}
3178
3179/*
3180 * Get current line in syntax buffer.
3181 */
3182 static char_u *
3183syn_getcurline()
3184{
3185 return ml_get_buf(syn_buf, current_lnum, FALSE);
3186}
3187
3188/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003189 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003190 * Returns TRUE when there is a match.
3191 */
3192 static int
3193syn_regexec(rmp, lnum, col)
3194 regmmatch_T *rmp;
3195 linenr_T lnum;
3196 colnr_T col;
3197{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003198 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003199 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003200 {
3201 rmp->startpos[0].lnum += lnum;
3202 rmp->endpos[0].lnum += lnum;
3203 return TRUE;
3204 }
3205 return FALSE;
3206}
3207
3208/*
3209 * Check one position in a line for a matching keyword.
3210 * The caller must check if a keyword can start at startcol.
3211 * Return it's ID if found, 0 otherwise.
3212 */
3213 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003214check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215 char_u *line;
3216 int startcol; /* position in line to check for keyword */
3217 int *endcolp; /* return: character after found keyword */
3218 long *flagsp; /* return: flags of matching keyword */
3219 short **next_listp; /* return: next_list of matching keyword */
3220 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003221 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003223 keyentry_T *kp;
3224 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003225 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003226 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003227 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003228 hashtab_T *ht;
3229 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230
3231 /* Find first character after the keyword. First character was already
3232 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003233 kwp = line + startcol;
3234 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235 do
3236 {
3237#ifdef FEAT_MBYTE
3238 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003239 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 else
3241#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003242 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003243 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003244 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245
Bram Moolenaardad6b692005-01-25 22:14:34 +00003246 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 return 0;
3248
3249 /*
3250 * Must make a copy of the keyword, so we can add a NUL and make it
3251 * lowercase.
3252 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003253 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254
3255 /*
3256 * Try twice:
3257 * 1. matching case
3258 * 2. ignoring case
3259 */
3260 for (round = 1; round <= 2; ++round)
3261 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003262 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003263 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003265 if (round == 2) /* ignore case */
3266 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267
3268 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003269 * Find keywords that match. There can be several with different
3270 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 * When current_next_list is non-zero accept only that group, otherwise:
3272 * Accept a not-contained keyword at toplevel.
3273 * Accept a keyword at other levels only if it is in the contains list.
3274 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003275 hi = hash_find(ht, keyword);
3276 if (!HASHITEM_EMPTY(hi))
3277 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003279 if (current_next_list != 0
3280 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3281 : (cur_si == NULL
3282 ? !(kp->flags & HL_CONTAINED)
3283 : in_id_list(cur_si, cur_si->si_cont_list,
3284 &kp->k_syn, kp->flags & HL_CONTAINED)))
3285 {
3286 *endcolp = startcol + kwlen;
3287 *flagsp = kp->flags;
3288 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003289#ifdef FEAT_CONCEAL
3290 *ccharp = kp->k_char;
3291#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003292 return kp->k_syn.id;
3293 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003294 }
3295 }
3296 return 0;
3297}
3298
3299/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003300 * Handle ":syntax conceal" command.
3301 */
3302 static void
3303syn_cmd_conceal(eap, syncing)
3304 exarg_T *eap UNUSED;
3305 int syncing UNUSED;
3306{
3307#ifdef FEAT_CONCEAL
3308 char_u *arg = eap->arg;
3309 char_u *next;
3310
3311 eap->nextcmd = find_nextcmd(arg);
3312 if (eap->skip)
3313 return;
3314
3315 next = skiptowhite(arg);
3316 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3317 curwin->w_s->b_syn_conceal = TRUE;
3318 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3319 curwin->w_s->b_syn_conceal = FALSE;
3320 else
3321 EMSG2(_("E390: Illegal argument: %s"), arg);
3322#endif
3323}
3324
3325/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003326 * Handle ":syntax case" command.
3327 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 static void
3329syn_cmd_case(eap, syncing)
3330 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003331 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003332{
3333 char_u *arg = eap->arg;
3334 char_u *next;
3335
3336 eap->nextcmd = find_nextcmd(arg);
3337 if (eap->skip)
3338 return;
3339
3340 next = skiptowhite(arg);
3341 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003342 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003344 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345 else
3346 EMSG2(_("E390: Illegal argument: %s"), arg);
3347}
3348
3349/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003350 * Handle ":syntax spell" command.
3351 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003352 static void
3353syn_cmd_spell(eap, syncing)
3354 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003355 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003356{
3357 char_u *arg = eap->arg;
3358 char_u *next;
3359
3360 eap->nextcmd = find_nextcmd(arg);
3361 if (eap->skip)
3362 return;
3363
3364 next = skiptowhite(arg);
3365 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003366 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003367 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003368 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003369 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003370 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003371 else
3372 EMSG2(_("E390: Illegal argument: %s"), arg);
3373}
3374
3375/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376 * Clear all syntax info for one buffer.
3377 */
3378 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003379syntax_clear(block)
3380 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381{
3382 int i;
3383
Bram Moolenaar860cae12010-06-05 23:22:07 +02003384 block->b_syn_error = FALSE; /* clear previous error */
3385 block->b_syn_ic = FALSE; /* Use case, by default */
3386 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3387 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388
3389 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003390 clear_keywtab(&block->b_keywtab);
3391 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003392
3393 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003394 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3395 syn_clear_pattern(block, i);
3396 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397
3398 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003399 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3400 syn_clear_cluster(block, i);
3401 ga_clear(&block->b_syn_clusters);
3402 block->b_spell_cluster_id = 0;
3403 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003404
Bram Moolenaar860cae12010-06-05 23:22:07 +02003405 block->b_syn_sync_flags = 0;
3406 block->b_syn_sync_minlines = 0;
3407 block->b_syn_sync_maxlines = 0;
3408 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409
Bram Moolenaar860cae12010-06-05 23:22:07 +02003410 vim_free(block->b_syn_linecont_prog);
3411 block->b_syn_linecont_prog = NULL;
3412 vim_free(block->b_syn_linecont_pat);
3413 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003415 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416#endif
3417
3418 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003419 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420 invalidate_current_state();
3421}
3422
3423/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003424 * Get rid of ownsyntax for window "wp".
3425 */
3426 void
3427reset_synblock(wp)
3428 win_T *wp;
3429{
3430 if (wp->w_s != &wp->w_buffer->b_s)
3431 {
3432 syntax_clear(wp->w_s);
3433 vim_free(wp->w_s);
3434 wp->w_s = &wp->w_buffer->b_s;
3435 }
3436}
3437
3438/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003439 * Clear syncing info for one buffer.
3440 */
3441 static void
3442syntax_sync_clear()
3443{
3444 int i;
3445
3446 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003447 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3448 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3449 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003450
Bram Moolenaar860cae12010-06-05 23:22:07 +02003451 curwin->w_s->b_syn_sync_flags = 0;
3452 curwin->w_s->b_syn_sync_minlines = 0;
3453 curwin->w_s->b_syn_sync_maxlines = 0;
3454 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455
Bram Moolenaar860cae12010-06-05 23:22:07 +02003456 vim_free(curwin->w_s->b_syn_linecont_prog);
3457 curwin->w_s->b_syn_linecont_prog = NULL;
3458 vim_free(curwin->w_s->b_syn_linecont_pat);
3459 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460
Bram Moolenaar860cae12010-06-05 23:22:07 +02003461 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003462}
3463
3464/*
3465 * Remove one pattern from the buffer's pattern list.
3466 */
3467 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468syn_remove_pattern(block, idx)
3469 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003470 int idx;
3471{
3472 synpat_T *spp;
3473
Bram Moolenaar860cae12010-06-05 23:22:07 +02003474 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003475#ifdef FEAT_FOLDING
3476 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003477 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003478#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003481 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3482 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483}
3484
3485/*
3486 * Clear and free one syntax pattern. When clearing all, must be called from
3487 * last to first!
3488 */
3489 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003490syn_clear_pattern(block, i)
3491 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492 int i;
3493{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003494 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3495 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003497 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003499 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3500 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3501 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502 }
3503}
3504
3505/*
3506 * Clear and free one syntax cluster.
3507 */
3508 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509syn_clear_cluster(block, i)
3510 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511 int i;
3512{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003513 vim_free(SYN_CLSTR(block)[i].scl_name);
3514 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3515 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516}
3517
3518/*
3519 * Handle ":syntax clear" command.
3520 */
3521 static void
3522syn_cmd_clear(eap, syncing)
3523 exarg_T *eap;
3524 int syncing;
3525{
3526 char_u *arg = eap->arg;
3527 char_u *arg_end;
3528 int id;
3529
3530 eap->nextcmd = find_nextcmd(arg);
3531 if (eap->skip)
3532 return;
3533
3534 /*
3535 * We have to disable this within ":syn include @group filename",
3536 * because otherwise @group would get deleted.
3537 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3538 * clear".
3539 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003540 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003541 return;
3542
3543 if (ends_excmd(*arg))
3544 {
3545 /*
3546 * No argument: Clear all syntax items.
3547 */
3548 if (syncing)
3549 syntax_sync_clear();
3550 else
3551 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003552 syntax_clear(curwin->w_s);
3553 if (curwin->w_s == &curwin->w_buffer->b_s)
3554 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003555 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003556 }
3557 }
3558 else
3559 {
3560 /*
3561 * Clear the group IDs that are in the argument.
3562 */
3563 while (!ends_excmd(*arg))
3564 {
3565 arg_end = skiptowhite(arg);
3566 if (*arg == '@')
3567 {
3568 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3569 if (id == 0)
3570 {
3571 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3572 break;
3573 }
3574 else
3575 {
3576 /*
3577 * We can't physically delete a cluster without changing
3578 * the IDs of other clusters, so we do the next best thing
3579 * and make it empty.
3580 */
3581 short scl_id = id - SYNID_CLUSTER;
3582
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3584 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003585 }
3586 }
3587 else
3588 {
3589 id = syn_namen2id(arg, (int)(arg_end - arg));
3590 if (id == 0)
3591 {
3592 EMSG2(_(e_nogroup), arg);
3593 break;
3594 }
3595 else
3596 syn_clear_one(id, syncing);
3597 }
3598 arg = skipwhite(arg_end);
3599 }
3600 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003601 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003602 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603}
3604
3605/*
3606 * Clear one syntax group for the current buffer.
3607 */
3608 static void
3609syn_clear_one(id, syncing)
3610 int id;
3611 int syncing;
3612{
3613 synpat_T *spp;
3614 int idx;
3615
3616 /* Clear keywords only when not ":syn sync clear group-name" */
3617 if (!syncing)
3618 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003619 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3620 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621 }
3622
3623 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003624 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003626 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3628 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003629 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003630 }
3631}
3632
3633/*
3634 * Handle ":syntax on" command.
3635 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003636 static void
3637syn_cmd_on(eap, syncing)
3638 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003639 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003640{
3641 syn_cmd_onoff(eap, "syntax");
3642}
3643
3644/*
3645 * Handle ":syntax enable" command.
3646 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647 static void
3648syn_cmd_enable(eap, syncing)
3649 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003650 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003651{
3652 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3653 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003654 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003655}
3656
3657/*
3658 * Handle ":syntax reset" command.
3659 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660 static void
3661syn_cmd_reset(eap, syncing)
3662 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003663 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664{
3665 eap->nextcmd = check_nextcmd(eap->arg);
3666 if (!eap->skip)
3667 {
3668 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3669 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003670 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671 }
3672}
3673
3674/*
3675 * Handle ":syntax manual" command.
3676 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677 static void
3678syn_cmd_manual(eap, syncing)
3679 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003680 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003681{
3682 syn_cmd_onoff(eap, "manual");
3683}
3684
3685/*
3686 * Handle ":syntax off" command.
3687 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688 static void
3689syn_cmd_off(eap, syncing)
3690 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003691 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003692{
3693 syn_cmd_onoff(eap, "nosyntax");
3694}
3695
3696 static void
3697syn_cmd_onoff(eap, name)
3698 exarg_T *eap;
3699 char *name;
3700{
3701 char_u buf[100];
3702
3703 eap->nextcmd = check_nextcmd(eap->arg);
3704 if (!eap->skip)
3705 {
3706 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003707 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003708 do_cmdline_cmd(buf);
3709 }
3710}
3711
3712/*
3713 * Handle ":syntax [list]" command: list current syntax words.
3714 */
3715 static void
3716syn_cmd_list(eap, syncing)
3717 exarg_T *eap;
3718 int syncing; /* when TRUE: list syncing items */
3719{
3720 char_u *arg = eap->arg;
3721 int id;
3722 char_u *arg_end;
3723
3724 eap->nextcmd = find_nextcmd(arg);
3725 if (eap->skip)
3726 return;
3727
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003729 {
3730 MSG(_("No Syntax items defined for this buffer"));
3731 return;
3732 }
3733
3734 if (syncing)
3735 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003736 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737 {
3738 MSG_PUTS(_("syncing on C-style comments"));
3739 syn_lines_msg();
3740 syn_match_msg();
3741 return;
3742 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003743 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003745 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746 MSG_PUTS(_("no syncing"));
3747 else
3748 {
3749 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003750 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 MSG_PUTS(_(" lines before top line"));
3752 syn_match_msg();
3753 }
3754 return;
3755 }
3756 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003757 if (curwin->w_s->b_syn_sync_minlines > 0
3758 || curwin->w_s->b_syn_sync_maxlines > 0
3759 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760 {
3761 MSG_PUTS(_("\nsyncing on items"));
3762 syn_lines_msg();
3763 syn_match_msg();
3764 }
3765 }
3766 else
3767 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3768 if (ends_excmd(*arg))
3769 {
3770 /*
3771 * No argument: List all group IDs and all syntax clusters.
3772 */
3773 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3774 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003775 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776 syn_list_cluster(id);
3777 }
3778 else
3779 {
3780 /*
3781 * List the group IDs and syntax clusters that are in the argument.
3782 */
3783 while (!ends_excmd(*arg) && !got_int)
3784 {
3785 arg_end = skiptowhite(arg);
3786 if (*arg == '@')
3787 {
3788 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3789 if (id == 0)
3790 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3791 else
3792 syn_list_cluster(id - SYNID_CLUSTER);
3793 }
3794 else
3795 {
3796 id = syn_namen2id(arg, (int)(arg_end - arg));
3797 if (id == 0)
3798 EMSG2(_(e_nogroup), arg);
3799 else
3800 syn_list_one(id, syncing, TRUE);
3801 }
3802 arg = skipwhite(arg_end);
3803 }
3804 }
3805 eap->nextcmd = check_nextcmd(arg);
3806}
3807
3808 static void
3809syn_lines_msg()
3810{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003811 if (curwin->w_s->b_syn_sync_maxlines > 0
3812 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 {
3814 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003815 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816 {
3817 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003818 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3819 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 MSG_PUTS(", ");
3821 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003822 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003823 {
3824 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003825 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003826 }
3827 MSG_PUTS(_(" lines before top line"));
3828 }
3829}
3830
3831 static void
3832syn_match_msg()
3833{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003834 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835 {
3836 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003837 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 MSG_PUTS(_(" line breaks"));
3839 }
3840}
3841
3842static int last_matchgroup;
3843
3844struct name_list
3845{
3846 int flag;
3847 char *name;
3848};
3849
3850static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3851
3852/*
3853 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3854 */
3855 static void
3856syn_list_one(id, syncing, link_only)
3857 int id;
3858 int syncing; /* when TRUE: list syncing items */
3859 int link_only; /* when TRUE; list link-only too */
3860{
3861 int attr;
3862 int idx;
3863 int did_header = FALSE;
3864 synpat_T *spp;
3865 static struct name_list namelist1[] =
3866 {
3867 {HL_DISPLAY, "display"},
3868 {HL_CONTAINED, "contained"},
3869 {HL_ONELINE, "oneline"},
3870 {HL_KEEPEND, "keepend"},
3871 {HL_EXTEND, "extend"},
3872 {HL_EXCLUDENL, "excludenl"},
3873 {HL_TRANSP, "transparent"},
3874 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003875#ifdef FEAT_CONCEAL
3876 {HL_CONCEAL, "conceal"},
3877 {HL_CONCEALENDS, "concealends"},
3878#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003879 {0, NULL}
3880 };
3881 static struct name_list namelist2[] =
3882 {
3883 {HL_SKIPWHITE, "skipwhite"},
3884 {HL_SKIPNL, "skipnl"},
3885 {HL_SKIPEMPTY, "skipempty"},
3886 {0, NULL}
3887 };
3888
3889 attr = hl_attr(HLF_D); /* highlight like directories */
3890
3891 /* list the keywords for "id" */
3892 if (!syncing)
3893 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003894 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3895 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 did_header, attr);
3897 }
3898
3899 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003900 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003901 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003902 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3904 continue;
3905
3906 (void)syn_list_header(did_header, 999, id);
3907 did_header = TRUE;
3908 last_matchgroup = 0;
3909 if (spp->sp_type == SPTYPE_MATCH)
3910 {
3911 put_pattern("match", ' ', spp, attr);
3912 msg_putchar(' ');
3913 }
3914 else if (spp->sp_type == SPTYPE_START)
3915 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003916 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3917 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3918 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3919 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3920 while (idx < curwin->w_s->b_syn_patterns.ga_len
3921 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3922 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003923 --idx;
3924 msg_putchar(' ');
3925 }
3926 syn_list_flags(namelist1, spp->sp_flags, attr);
3927
3928 if (spp->sp_cont_list != NULL)
3929 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3930
3931 if (spp->sp_syn.cont_in_list != NULL)
3932 put_id_list((char_u *)"containedin",
3933 spp->sp_syn.cont_in_list, attr);
3934
3935 if (spp->sp_next_list != NULL)
3936 {
3937 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3938 syn_list_flags(namelist2, spp->sp_flags, attr);
3939 }
3940 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3941 {
3942 if (spp->sp_flags & HL_SYNC_HERE)
3943 msg_puts_attr((char_u *)"grouphere", attr);
3944 else
3945 msg_puts_attr((char_u *)"groupthere", attr);
3946 msg_putchar(' ');
3947 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003948 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3950 else
3951 MSG_PUTS("NONE");
3952 msg_putchar(' ');
3953 }
3954 }
3955
3956 /* list the link, if there is one */
3957 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3958 {
3959 (void)syn_list_header(did_header, 999, id);
3960 msg_puts_attr((char_u *)"links to", attr);
3961 msg_putchar(' ');
3962 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3963 }
3964}
3965
3966 static void
3967syn_list_flags(nl, flags, attr)
3968 struct name_list *nl;
3969 int flags;
3970 int attr;
3971{
3972 int i;
3973
3974 for (i = 0; nl[i].flag != 0; ++i)
3975 if (flags & nl[i].flag)
3976 {
3977 msg_puts_attr((char_u *)nl[i].name, attr);
3978 msg_putchar(' ');
3979 }
3980}
3981
3982/*
3983 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3984 */
3985 static void
3986syn_list_cluster(id)
3987 int id;
3988{
3989 int endcol = 15;
3990
3991 /* slight hack: roughly duplicate the guts of syn_list_header() */
3992 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02003993 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994
3995 if (msg_col >= endcol) /* output at least one space */
3996 endcol = msg_col + 1;
3997 if (Columns <= endcol) /* avoid hang for tiny window */
3998 endcol = Columns - 1;
3999
4000 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004001 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004003 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004004 hl_attr(HLF_D));
4005 }
4006 else
4007 {
4008 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4009 msg_puts((char_u *)"=NONE");
4010 }
4011}
4012
4013 static void
4014put_id_list(name, list, attr)
4015 char_u *name;
4016 short *list;
4017 int attr;
4018{
4019 short *p;
4020
4021 msg_puts_attr(name, attr);
4022 msg_putchar('=');
4023 for (p = list; *p; ++p)
4024 {
4025 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4026 {
4027 if (p[1])
4028 MSG_PUTS("ALLBUT");
4029 else
4030 MSG_PUTS("ALL");
4031 }
4032 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4033 {
4034 MSG_PUTS("TOP");
4035 }
4036 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4037 {
4038 MSG_PUTS("CONTAINED");
4039 }
4040 else if (*p >= SYNID_CLUSTER)
4041 {
4042 short scl_id = *p - SYNID_CLUSTER;
4043
4044 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004045 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 }
4047 else
4048 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4049 if (p[1])
4050 msg_putchar(',');
4051 }
4052 msg_putchar(' ');
4053}
4054
4055 static void
4056put_pattern(s, c, spp, attr)
4057 char *s;
4058 int c;
4059 synpat_T *spp;
4060 int attr;
4061{
4062 long n;
4063 int mask;
4064 int first;
4065 static char *sepchars = "/+=-#@\"|'^&";
4066 int i;
4067
4068 /* May have to write "matchgroup=group" */
4069 if (last_matchgroup != spp->sp_syn_match_id)
4070 {
4071 last_matchgroup = spp->sp_syn_match_id;
4072 msg_puts_attr((char_u *)"matchgroup", attr);
4073 msg_putchar('=');
4074 if (last_matchgroup == 0)
4075 msg_outtrans((char_u *)"NONE");
4076 else
4077 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4078 msg_putchar(' ');
4079 }
4080
4081 /* output the name of the pattern and an '=' or ' ' */
4082 msg_puts_attr((char_u *)s, attr);
4083 msg_putchar(c);
4084
4085 /* output the pattern, in between a char that is not in the pattern */
4086 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4087 if (sepchars[++i] == NUL)
4088 {
4089 i = 0; /* no good char found, just use the first one */
4090 break;
4091 }
4092 msg_putchar(sepchars[i]);
4093 msg_outtrans(spp->sp_pattern);
4094 msg_putchar(sepchars[i]);
4095
4096 /* output any pattern options */
4097 first = TRUE;
4098 for (i = 0; i < SPO_COUNT; ++i)
4099 {
4100 mask = (1 << i);
4101 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4102 {
4103 if (!first)
4104 msg_putchar(','); /* separate with commas */
4105 msg_puts((char_u *)spo_name_tab[i]);
4106 n = spp->sp_offsets[i];
4107 if (i != SPO_LC_OFF)
4108 {
4109 if (spp->sp_off_flags & mask)
4110 msg_putchar('s');
4111 else
4112 msg_putchar('e');
4113 if (n > 0)
4114 msg_putchar('+');
4115 }
4116 if (n || i == SPO_LC_OFF)
4117 msg_outnum(n);
4118 first = FALSE;
4119 }
4120 }
4121 msg_putchar(' ');
4122}
4123
4124/*
4125 * List or clear the keywords for one syntax group.
4126 * Return TRUE if the header has been printed.
4127 */
4128 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004129syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004131 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 int did_header; /* header has already been printed */
4133 int attr;
4134{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004135 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004136 hashitem_T *hi;
4137 keyentry_T *kp;
4138 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 int prev_contained = 0;
4140 short *prev_next_list = NULL;
4141 short *prev_cont_in_list = NULL;
4142 int prev_skipnl = 0;
4143 int prev_skipwhite = 0;
4144 int prev_skipempty = 0;
4145
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 /*
4147 * Unfortunately, this list of keywords is not sorted on alphabet but on
4148 * hash value...
4149 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004150 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004151 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004153 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004155 --todo;
4156 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004158 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004160 if (prev_contained != (kp->flags & HL_CONTAINED)
4161 || prev_skipnl != (kp->flags & HL_SKIPNL)
4162 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4163 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4164 || prev_cont_in_list != kp->k_syn.cont_in_list
4165 || prev_next_list != kp->next_list)
4166 outlen = 9999;
4167 else
4168 outlen = (int)STRLEN(kp->keyword);
4169 /* output "contained" and "nextgroup" on each line */
4170 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004172 prev_contained = 0;
4173 prev_next_list = NULL;
4174 prev_cont_in_list = NULL;
4175 prev_skipnl = 0;
4176 prev_skipwhite = 0;
4177 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004179 did_header = TRUE;
4180 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004182 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004184 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004185 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004186 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004188 put_id_list((char_u *)"containedin",
4189 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004191 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004193 if (kp->next_list != prev_next_list)
4194 {
4195 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4196 msg_putchar(' ');
4197 prev_next_list = kp->next_list;
4198 if (kp->flags & HL_SKIPNL)
4199 {
4200 msg_puts_attr((char_u *)"skipnl", attr);
4201 msg_putchar(' ');
4202 prev_skipnl = (kp->flags & HL_SKIPNL);
4203 }
4204 if (kp->flags & HL_SKIPWHITE)
4205 {
4206 msg_puts_attr((char_u *)"skipwhite", attr);
4207 msg_putchar(' ');
4208 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4209 }
4210 if (kp->flags & HL_SKIPEMPTY)
4211 {
4212 msg_puts_attr((char_u *)"skipempty", attr);
4213 msg_putchar(' ');
4214 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4215 }
4216 }
4217 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219 }
4220 }
4221 }
4222
4223 return did_header;
4224}
4225
4226 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004227syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004229 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004231 hashitem_T *hi;
4232 keyentry_T *kp;
4233 keyentry_T *kp_prev;
4234 keyentry_T *kp_next;
4235 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004236
Bram Moolenaardad6b692005-01-25 22:14:34 +00004237 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004238 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004239 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004240 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004241 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004243 --todo;
4244 kp_prev = NULL;
4245 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004246 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004247 if (kp->k_syn.id == id)
4248 {
4249 kp_next = kp->ke_next;
4250 if (kp_prev == NULL)
4251 {
4252 if (kp_next == NULL)
4253 hash_remove(ht, hi);
4254 else
4255 hi->hi_key = KE2HIKEY(kp_next);
4256 }
4257 else
4258 kp_prev->ke_next = kp_next;
4259 vim_free(kp->next_list);
4260 vim_free(kp->k_syn.cont_in_list);
4261 vim_free(kp);
4262 kp = kp_next;
4263 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004265 {
4266 kp_prev = kp;
4267 kp = kp->ke_next;
4268 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 }
4270 }
4271 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004272 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004273}
4274
4275/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004276 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277 */
4278 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279clear_keywtab(ht)
4280 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282 hashitem_T *hi;
4283 int todo;
4284 keyentry_T *kp;
4285 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004286
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004287 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004288 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004290 if (!HASHITEM_EMPTY(hi))
4291 {
4292 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004293 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004294 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004295 kp_next = kp->ke_next;
4296 vim_free(kp->next_list);
4297 vim_free(kp->k_syn.cont_in_list);
4298 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004302 hash_clear(ht);
4303 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304}
4305
4306/*
4307 * Add a keyword to the list of keywords.
4308 */
4309 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004310add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311 char_u *name; /* name of keyword */
4312 int id; /* group ID for this keyword */
4313 int flags; /* flags for this keyword */
4314 short *cont_in_list; /* containedin for this keyword */
4315 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004316 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004318 keyentry_T *kp;
4319 hashtab_T *ht;
4320 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004321 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004322 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004323 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004324
Bram Moolenaar860cae12010-06-05 23:22:07 +02004325 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004326 name_ic = str_foldcase(name, (int)STRLEN(name),
4327 name_folded, MAXKEYWLEN + 1);
4328 else
4329 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4331 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004333 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 kp->k_syn.id = id;
4335 kp->k_syn.inc_tag = current_syn_inc_tag;
4336 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004337 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004340 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004341 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342
Bram Moolenaar860cae12010-06-05 23:22:07 +02004343 if (curwin->w_s->b_syn_ic)
4344 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004346 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347
Bram Moolenaardad6b692005-01-25 22:14:34 +00004348 hash = hash_hash(kp->keyword);
4349 hi = hash_lookup(ht, kp->keyword, hash);
4350 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 /* new keyword, add to hashtable */
4353 kp->ke_next = NULL;
4354 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004357 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 /* keyword already exists, prepend to list */
4359 kp->ke_next = HI2KE(hi);
4360 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362}
4363
4364/*
4365 * Get the start and end of the group name argument.
4366 * Return a pointer to the first argument.
4367 * Return NULL if the end of the command was found instead of further args.
4368 */
4369 static char_u *
4370get_group_name(arg, name_end)
4371 char_u *arg; /* start of the argument */
4372 char_u **name_end; /* pointer to end of the name */
4373{
4374 char_u *rest;
4375
4376 *name_end = skiptowhite(arg);
4377 rest = skipwhite(*name_end);
4378
4379 /*
4380 * Check if there are enough arguments. The first argument may be a
4381 * pattern, where '|' is allowed, so only check for NUL.
4382 */
4383 if (ends_excmd(*arg) || *rest == NUL)
4384 return NULL;
4385 return rest;
4386}
4387
4388/*
4389 * Check for syntax command option arguments.
4390 * This can be called at any place in the list of arguments, and just picks
4391 * out the arguments that are known. Can be called several times in a row to
4392 * collect all options in between other arguments.
4393 * Return a pointer to the next argument (which isn't an option).
4394 * Return NULL for any error;
4395 */
4396 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004397get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004398 char_u *arg; /* next argument to be checked */
4399 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004400 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004402 char_u *gname_start, *gname;
4403 int syn_id;
4404 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004405 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004406 int i;
4407 int fidx;
4408 static struct flag
4409 {
4410 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004411 int argtype;
4412 int flags;
4413 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4414 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4415 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4416 {"eExXtTeEnNdD", 0, HL_EXTEND},
4417 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4418 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4419 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4420 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4421 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4422 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4423 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4424 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4425 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004426 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4427 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4428 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004429 {"cCoOnNtTaAiInNsS", 1, 0},
4430 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4431 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004433 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434
4435 if (arg == NULL) /* already detected error */
4436 return NULL;
4437
Bram Moolenaar860cae12010-06-05 23:22:07 +02004438#ifdef FEAT_CONCEAL
4439 if (curwin->w_s->b_syn_conceal)
4440 opt->flags |= HL_CONCEAL;
4441#endif
4442
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443 for (;;)
4444 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004445 /*
4446 * This is used very often when a large number of keywords is defined.
4447 * Need to skip quickly when no option name is found.
4448 * Also avoid tolower(), it's slow.
4449 */
4450 if (strchr(first_letters, *arg) == NULL)
4451 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452
4453 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4454 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004455 p = flagtab[fidx].name;
4456 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4457 if (arg[len] != p[i] && arg[len] != p[i + 1])
4458 break;
4459 if (p[i] == NUL && (vim_iswhite(arg[len])
4460 || (flagtab[fidx].argtype > 0
4461 ? arg[len] == '='
4462 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004464 if (opt->keyword
4465 && (flagtab[fidx].flags == HL_DISPLAY
4466 || flagtab[fidx].flags == HL_FOLD
4467 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 /* treat "display", "fold" and "extend" as a keyword */
4469 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470 break;
4471 }
4472 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004473 if (fidx < 0) /* no match found */
4474 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004476 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004478 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 {
4480 EMSG(_("E395: contains argument not accepted here"));
4481 return NULL;
4482 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004483 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 return NULL;
4485 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004486 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004488#if 0 /* cannot happen */
4489 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 {
4491 EMSG(_("E396: containedin argument not accepted here"));
4492 return NULL;
4493 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004494#endif
4495 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004496 return NULL;
4497 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004498 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004500 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 return NULL;
4502 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004503 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4504 {
4505#ifdef FEAT_MBYTE
4506 /* cchar=? */
4507 if (has_mbyte)
4508 {
4509# ifdef FEAT_CONCEAL
4510 *conceal_char = mb_ptr2char(arg + 6);
4511# endif
4512 arg += mb_ptr2len(arg + 6) - 1;
4513 }
4514 else
4515#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004516 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004517#ifdef FEAT_CONCEAL
4518 *conceal_char = arg[6];
4519#else
4520 ;
4521#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004522 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004523 arg = skipwhite(arg + 7);
4524 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004526 {
4527 opt->flags |= flagtab[fidx].flags;
4528 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004530 if (flagtab[fidx].flags == HL_SYNC_HERE
4531 || flagtab[fidx].flags == HL_SYNC_THERE)
4532 {
4533 if (opt->sync_idx == NULL)
4534 {
4535 EMSG(_("E393: group[t]here not accepted here"));
4536 return NULL;
4537 }
4538 gname_start = arg;
4539 arg = skiptowhite(arg);
4540 if (gname_start == arg)
4541 return NULL;
4542 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4543 if (gname == NULL)
4544 return NULL;
4545 if (STRCMP(gname, "NONE") == 0)
4546 *opt->sync_idx = NONE_IDX;
4547 else
4548 {
4549 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004550 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4551 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4552 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004553 {
4554 *opt->sync_idx = i;
4555 break;
4556 }
4557 if (i < 0)
4558 {
4559 EMSG2(_("E394: Didn't find region item for %s"), gname);
4560 vim_free(gname);
4561 return NULL;
4562 }
4563 }
4564
4565 vim_free(gname);
4566 arg = skipwhite(arg);
4567 }
4568#ifdef FEAT_FOLDING
4569 else if (flagtab[fidx].flags == HL_FOLD
4570 && foldmethodIsSyntax(curwin))
4571 /* Need to update folds later. */
4572 foldUpdateAll(curwin);
4573#endif
4574 }
4575 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576
4577 return arg;
4578}
4579
4580/*
4581 * Adjustments to syntax item when declared in a ":syn include"'d file.
4582 * Set the contained flag, and if the item is not already contained, add it
4583 * to the specified top-level group, if any.
4584 */
4585 static void
4586syn_incl_toplevel(id, flagsp)
4587 int id;
4588 int *flagsp;
4589{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004590 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004591 return;
4592 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004593 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004594 {
4595 /* We have to alloc this, because syn_combine_list() will free it. */
4596 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004597 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598
4599 if (grp_list != NULL)
4600 {
4601 grp_list[0] = id;
4602 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004603 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004604 CLUSTER_ADD);
4605 }
4606 }
4607}
4608
4609/*
4610 * Handle ":syntax include [@{group-name}] filename" command.
4611 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 static void
4613syn_cmd_include(eap, syncing)
4614 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004615 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004616{
4617 char_u *arg = eap->arg;
4618 int sgl_id = 1;
4619 char_u *group_name_end;
4620 char_u *rest;
4621 char_u *errormsg = NULL;
4622 int prev_toplvl_grp;
4623 int prev_syn_inc_tag;
4624 int source = FALSE;
4625
4626 eap->nextcmd = find_nextcmd(arg);
4627 if (eap->skip)
4628 return;
4629
4630 if (arg[0] == '@')
4631 {
4632 ++arg;
4633 rest = get_group_name(arg, &group_name_end);
4634 if (rest == NULL)
4635 {
4636 EMSG((char_u *)_("E397: Filename required"));
4637 return;
4638 }
4639 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4640 /* separate_nextcmd() and expand_filename() depend on this */
4641 eap->arg = rest;
4642 }
4643
4644 /*
4645 * Everything that's left, up to the next command, should be the
4646 * filename to include.
4647 */
4648 eap->argt |= (XFILE | NOSPC);
4649 separate_nextcmd(eap);
4650 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4651 {
4652 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4653 * file. Need to expand the file name first. In other cases
4654 * ":runtime!" is used. */
4655 source = TRUE;
4656 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4657 {
4658 if (errormsg != NULL)
4659 EMSG(errormsg);
4660 return;
4661 }
4662 }
4663
4664 /*
4665 * Save and restore the existing top-level grouplist id and ":syn
4666 * include" tag around the actual inclusion.
4667 */
4668 prev_syn_inc_tag = current_syn_inc_tag;
4669 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004670 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4671 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004672 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4673 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004675 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004676 current_syn_inc_tag = prev_syn_inc_tag;
4677}
4678
4679/*
4680 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4681 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682 static void
4683syn_cmd_keyword(eap, syncing)
4684 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004685 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686{
4687 char_u *arg = eap->arg;
4688 char_u *group_name_end;
4689 int syn_id;
4690 char_u *rest;
4691 char_u *keyword_copy;
4692 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004693 char_u *kw;
4694 syn_opt_arg_T syn_opt_arg;
4695 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004696 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004697
4698 rest = get_group_name(arg, &group_name_end);
4699
4700 if (rest != NULL)
4701 {
4702 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4703
4704 /* allocate a buffer, for removing the backslashes in the keyword */
4705 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4706 if (keyword_copy != NULL)
4707 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004708 syn_opt_arg.flags = 0;
4709 syn_opt_arg.keyword = TRUE;
4710 syn_opt_arg.sync_idx = NULL;
4711 syn_opt_arg.has_cont_list = FALSE;
4712 syn_opt_arg.cont_in_list = NULL;
4713 syn_opt_arg.next_list = NULL;
4714
Bram Moolenaar071d4272004-06-13 20:20:40 +00004715 /*
4716 * The options given apply to ALL keywords, so all options must be
4717 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004718 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004719 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004720 cnt = 0;
4721 p = keyword_copy;
4722 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004724 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004725 if (rest == NULL || ends_excmd(*rest))
4726 break;
4727 /* Copy the keyword, removing backslashes, and add a NUL. */
4728 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004730 if (*rest == '\\' && rest[1] != NUL)
4731 ++rest;
4732 *p++ = *rest++;
4733 }
4734 *p++ = NUL;
4735 ++cnt;
4736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004737
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004738 if (!eap->skip)
4739 {
4740 /* Adjust flags for use of ":syn include". */
4741 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4742
4743 /*
4744 * 2: Add an entry for each keyword.
4745 */
4746 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4747 {
4748 for (p = vim_strchr(kw, '['); ; )
4749 {
4750 if (p != NULL)
4751 *p = NUL;
4752 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004753 syn_opt_arg.cont_in_list,
4754 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004755 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004756 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004757 if (p[1] == NUL)
4758 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004759 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004760 kw = p + 2; /* skip over the NUL */
4761 break;
4762 }
4763 if (p[1] == ']')
4764 {
4765 kw = p + 1; /* skip over the "]" */
4766 break;
4767 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004768#ifdef FEAT_MBYTE
4769 if (has_mbyte)
4770 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004771 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004772
4773 mch_memmove(p, p + 1, l);
4774 p += l;
4775 }
4776 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004777#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004778 {
4779 p[0] = p[1];
4780 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004781 }
4782 }
4783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004785
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004787 vim_free(syn_opt_arg.cont_in_list);
4788 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004789 }
4790 }
4791
4792 if (rest != NULL)
4793 eap->nextcmd = check_nextcmd(rest);
4794 else
4795 EMSG2(_(e_invarg2), arg);
4796
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004797 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004798 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799}
4800
4801/*
4802 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4803 *
4804 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4805 */
4806 static void
4807syn_cmd_match(eap, syncing)
4808 exarg_T *eap;
4809 int syncing; /* TRUE for ":syntax sync match .. " */
4810{
4811 char_u *arg = eap->arg;
4812 char_u *group_name_end;
4813 char_u *rest;
4814 synpat_T item; /* the item found in the line */
4815 int syn_id;
4816 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004817 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004819 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820
4821 /* Isolate the group name, check for validity */
4822 rest = get_group_name(arg, &group_name_end);
4823
4824 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004825 syn_opt_arg.flags = 0;
4826 syn_opt_arg.keyword = FALSE;
4827 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4828 syn_opt_arg.has_cont_list = TRUE;
4829 syn_opt_arg.cont_list = NULL;
4830 syn_opt_arg.cont_in_list = NULL;
4831 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004832 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833
4834 /* get the pattern. */
4835 init_syn_patterns();
4836 vim_memset(&item, 0, sizeof(item));
4837 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004838 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4839 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004840
4841 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004842 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004843
4844 if (rest != NULL) /* all arguments are valid */
4845 {
4846 /*
4847 * Check for trailing command and illegal trailing arguments.
4848 */
4849 eap->nextcmd = check_nextcmd(rest);
4850 if (!ends_excmd(*rest) || eap->skip)
4851 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004852 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853 && (syn_id = syn_check_group(arg,
4854 (int)(group_name_end - arg))) != 0)
4855 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004856 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857 /*
4858 * Store the pattern in the syn_items list
4859 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004860 idx = curwin->w_s->b_syn_patterns.ga_len;
4861 SYN_ITEMS(curwin->w_s)[idx] = item;
4862 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4863 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4864 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4865 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4866 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4867 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4868 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4869 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004870 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004871#ifdef FEAT_CONCEAL
4872 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4873#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004874 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004875 curwin->w_s->b_syn_containedin = TRUE;
4876 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4877 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878
4879 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004880 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004881 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004883 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004884 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885#endif
4886
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004887 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004888 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889 return; /* don't free the progs and patterns now */
4890 }
4891 }
4892
4893 /*
4894 * Something failed, free the allocated memory.
4895 */
4896 vim_free(item.sp_prog);
4897 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004898 vim_free(syn_opt_arg.cont_list);
4899 vim_free(syn_opt_arg.cont_in_list);
4900 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901
4902 if (rest == NULL)
4903 EMSG2(_(e_invarg2), arg);
4904}
4905
4906/*
4907 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4908 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4909 */
4910 static void
4911syn_cmd_region(eap, syncing)
4912 exarg_T *eap;
4913 int syncing; /* TRUE for ":syntax sync region .." */
4914{
4915 char_u *arg = eap->arg;
4916 char_u *group_name_end;
4917 char_u *rest; /* next arg, NULL on error */
4918 char_u *key_end;
4919 char_u *key = NULL;
4920 char_u *p;
4921 int item;
4922#define ITEM_START 0
4923#define ITEM_SKIP 1
4924#define ITEM_END 2
4925#define ITEM_MATCHGROUP 3
4926 struct pat_ptr
4927 {
4928 synpat_T *pp_synp; /* pointer to syn_pattern */
4929 int pp_matchgroup_id; /* matchgroup ID */
4930 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4931 } *(pat_ptrs[3]);
4932 /* patterns found in the line */
4933 struct pat_ptr *ppp;
4934 struct pat_ptr *ppp_next;
4935 int pat_count = 0; /* nr of syn_patterns found */
4936 int syn_id;
4937 int matchgroup_id = 0;
4938 int not_enough = FALSE; /* not enough arguments */
4939 int illegal = FALSE; /* illegal arguments */
4940 int success = FALSE;
4941 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004942 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004943 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004944
4945 /* Isolate the group name, check for validity */
4946 rest = get_group_name(arg, &group_name_end);
4947
4948 pat_ptrs[0] = NULL;
4949 pat_ptrs[1] = NULL;
4950 pat_ptrs[2] = NULL;
4951
4952 init_syn_patterns();
4953
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004954 syn_opt_arg.flags = 0;
4955 syn_opt_arg.keyword = FALSE;
4956 syn_opt_arg.sync_idx = NULL;
4957 syn_opt_arg.has_cont_list = TRUE;
4958 syn_opt_arg.cont_list = NULL;
4959 syn_opt_arg.cont_in_list = NULL;
4960 syn_opt_arg.next_list = NULL;
4961
Bram Moolenaar071d4272004-06-13 20:20:40 +00004962 /*
4963 * get the options, patterns and matchgroup.
4964 */
4965 while (rest != NULL && !ends_excmd(*rest))
4966 {
4967 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004968 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969 if (rest == NULL || ends_excmd(*rest))
4970 break;
4971
4972 /* must be a pattern or matchgroup then */
4973 key_end = rest;
4974 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4975 ++key_end;
4976 vim_free(key);
4977 key = vim_strnsave_up(rest, (int)(key_end - rest));
4978 if (key == NULL) /* out of memory */
4979 {
4980 rest = NULL;
4981 break;
4982 }
4983 if (STRCMP(key, "MATCHGROUP") == 0)
4984 item = ITEM_MATCHGROUP;
4985 else if (STRCMP(key, "START") == 0)
4986 item = ITEM_START;
4987 else if (STRCMP(key, "END") == 0)
4988 item = ITEM_END;
4989 else if (STRCMP(key, "SKIP") == 0)
4990 {
4991 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4992 {
4993 illegal = TRUE;
4994 break;
4995 }
4996 item = ITEM_SKIP;
4997 }
4998 else
4999 break;
5000 rest = skipwhite(key_end);
5001 if (*rest != '=')
5002 {
5003 rest = NULL;
5004 EMSG2(_("E398: Missing '=': %s"), arg);
5005 break;
5006 }
5007 rest = skipwhite(rest + 1);
5008 if (*rest == NUL)
5009 {
5010 not_enough = TRUE;
5011 break;
5012 }
5013
5014 if (item == ITEM_MATCHGROUP)
5015 {
5016 p = skiptowhite(rest);
5017 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5018 matchgroup_id = 0;
5019 else
5020 {
5021 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5022 if (matchgroup_id == 0)
5023 {
5024 illegal = TRUE;
5025 break;
5026 }
5027 }
5028 rest = skipwhite(p);
5029 }
5030 else
5031 {
5032 /*
5033 * Allocate room for a syn_pattern, and link it in the list of
5034 * syn_patterns for this item, at the start (because the list is
5035 * used from end to start).
5036 */
5037 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5038 if (ppp == NULL)
5039 {
5040 rest = NULL;
5041 break;
5042 }
5043 ppp->pp_next = pat_ptrs[item];
5044 pat_ptrs[item] = ppp;
5045 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5046 if (ppp->pp_synp == NULL)
5047 {
5048 rest = NULL;
5049 break;
5050 }
5051
5052 /*
5053 * Get the syntax pattern and the following offset(s).
5054 */
5055 /* Enable the appropriate \z specials. */
5056 if (item == ITEM_START)
5057 reg_do_extmatch = REX_SET;
5058 else if (item == ITEM_SKIP || item == ITEM_END)
5059 reg_do_extmatch = REX_USE;
5060 rest = get_syn_pattern(rest, ppp->pp_synp);
5061 reg_do_extmatch = 0;
5062 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005063 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005064 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5065 ppp->pp_matchgroup_id = matchgroup_id;
5066 ++pat_count;
5067 }
5068 }
5069 vim_free(key);
5070 if (illegal || not_enough)
5071 rest = NULL;
5072
5073 /*
5074 * Must have a "start" and "end" pattern.
5075 */
5076 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5077 pat_ptrs[ITEM_END] == NULL))
5078 {
5079 not_enough = TRUE;
5080 rest = NULL;
5081 }
5082
5083 if (rest != NULL)
5084 {
5085 /*
5086 * Check for trailing garbage or command.
5087 * If OK, add the item.
5088 */
5089 eap->nextcmd = check_nextcmd(rest);
5090 if (!ends_excmd(*rest) || eap->skip)
5091 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005092 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005093 && (syn_id = syn_check_group(arg,
5094 (int)(group_name_end - arg))) != 0)
5095 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005096 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005097 /*
5098 * Store the start/skip/end in the syn_items list
5099 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005100 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005101 for (item = ITEM_START; item <= ITEM_END; ++item)
5102 {
5103 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5104 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005105 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5106 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5107 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108 (item == ITEM_START) ? SPTYPE_START :
5109 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005110 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5111 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5112 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5113 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005114 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005115#ifdef FEAT_CONCEAL
5116 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5117#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005118 if (item == ITEM_START)
5119 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005120 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005121 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005122 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005123 syn_opt_arg.cont_in_list;
5124 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005125 curwin->w_s->b_syn_containedin = TRUE;
5126 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005127 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005128 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005129 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130 ++idx;
5131#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005132 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005133 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005134#endif
5135 }
5136 }
5137
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005138 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005139 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 success = TRUE; /* don't free the progs and patterns now */
5141 }
5142 }
5143
5144 /*
5145 * Free the allocated memory.
5146 */
5147 for (item = ITEM_START; item <= ITEM_END; ++item)
5148 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5149 {
5150 if (!success)
5151 {
5152 vim_free(ppp->pp_synp->sp_prog);
5153 vim_free(ppp->pp_synp->sp_pattern);
5154 }
5155 vim_free(ppp->pp_synp);
5156 ppp_next = ppp->pp_next;
5157 vim_free(ppp);
5158 }
5159
5160 if (!success)
5161 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005162 vim_free(syn_opt_arg.cont_list);
5163 vim_free(syn_opt_arg.cont_in_list);
5164 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005165 if (not_enough)
5166 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5167 else if (illegal || rest == NULL)
5168 EMSG2(_(e_invarg2), arg);
5169 }
5170}
5171
5172/*
5173 * A simple syntax group ID comparison function suitable for use in qsort()
5174 */
5175 static int
5176#ifdef __BORLANDC__
5177_RTLENTRYF
5178#endif
5179syn_compare_stub(v1, v2)
5180 const void *v1;
5181 const void *v2;
5182{
5183 const short *s1 = v1;
5184 const short *s2 = v2;
5185
5186 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5187}
5188
5189/*
5190 * Combines lists of syntax clusters.
5191 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5192 */
5193 static void
5194syn_combine_list(clstr1, clstr2, list_op)
5195 short **clstr1;
5196 short **clstr2;
5197 int list_op;
5198{
5199 int count1 = 0;
5200 int count2 = 0;
5201 short *g1;
5202 short *g2;
5203 short *clstr = NULL;
5204 int count;
5205 int round;
5206
5207 /*
5208 * Handle degenerate cases.
5209 */
5210 if (*clstr2 == NULL)
5211 return;
5212 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5213 {
5214 if (list_op == CLUSTER_REPLACE)
5215 vim_free(*clstr1);
5216 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5217 *clstr1 = *clstr2;
5218 else
5219 vim_free(*clstr2);
5220 return;
5221 }
5222
5223 for (g1 = *clstr1; *g1; g1++)
5224 ++count1;
5225 for (g2 = *clstr2; *g2; g2++)
5226 ++count2;
5227
5228 /*
5229 * For speed purposes, sort both lists.
5230 */
5231 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5232 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5233
5234 /*
5235 * We proceed in two passes; in round 1, we count the elements to place
5236 * in the new list, and in round 2, we allocate and populate the new
5237 * list. For speed, we use a mergesort-like method, adding the smaller
5238 * of the current elements in each list to the new list.
5239 */
5240 for (round = 1; round <= 2; round++)
5241 {
5242 g1 = *clstr1;
5243 g2 = *clstr2;
5244 count = 0;
5245
5246 /*
5247 * First, loop through the lists until one of them is empty.
5248 */
5249 while (*g1 && *g2)
5250 {
5251 /*
5252 * We always want to add from the first list.
5253 */
5254 if (*g1 < *g2)
5255 {
5256 if (round == 2)
5257 clstr[count] = *g1;
5258 count++;
5259 g1++;
5260 continue;
5261 }
5262 /*
5263 * We only want to add from the second list if we're adding the
5264 * lists.
5265 */
5266 if (list_op == CLUSTER_ADD)
5267 {
5268 if (round == 2)
5269 clstr[count] = *g2;
5270 count++;
5271 }
5272 if (*g1 == *g2)
5273 g1++;
5274 g2++;
5275 }
5276
5277 /*
5278 * Now add the leftovers from whichever list didn't get finished
5279 * first. As before, we only want to add from the second list if
5280 * we're adding the lists.
5281 */
5282 for (; *g1; g1++, count++)
5283 if (round == 2)
5284 clstr[count] = *g1;
5285 if (list_op == CLUSTER_ADD)
5286 for (; *g2; g2++, count++)
5287 if (round == 2)
5288 clstr[count] = *g2;
5289
5290 if (round == 1)
5291 {
5292 /*
5293 * If the group ended up empty, we don't need to allocate any
5294 * space for it.
5295 */
5296 if (count == 0)
5297 {
5298 clstr = NULL;
5299 break;
5300 }
5301 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5302 if (clstr == NULL)
5303 break;
5304 clstr[count] = 0;
5305 }
5306 }
5307
5308 /*
5309 * Finally, put the new list in place.
5310 */
5311 vim_free(*clstr1);
5312 vim_free(*clstr2);
5313 *clstr1 = clstr;
5314}
5315
5316/*
5317 * Lookup a syntax cluster name and return it's ID.
5318 * If it is not found, 0 is returned.
5319 */
5320 static int
5321syn_scl_name2id(name)
5322 char_u *name;
5323{
5324 int i;
5325 char_u *name_u;
5326
5327 /* Avoid using stricmp() too much, it's slow on some systems */
5328 name_u = vim_strsave_up(name);
5329 if (name_u == NULL)
5330 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005331 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5332 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5333 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005334 break;
5335 vim_free(name_u);
5336 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5337}
5338
5339/*
5340 * Like syn_scl_name2id(), but take a pointer + length argument.
5341 */
5342 static int
5343syn_scl_namen2id(linep, len)
5344 char_u *linep;
5345 int len;
5346{
5347 char_u *name;
5348 int id = 0;
5349
5350 name = vim_strnsave(linep, len);
5351 if (name != NULL)
5352 {
5353 id = syn_scl_name2id(name);
5354 vim_free(name);
5355 }
5356 return id;
5357}
5358
5359/*
5360 * Find syntax cluster name in the table and return it's ID.
5361 * The argument is a pointer to the name and the length of the name.
5362 * If it doesn't exist yet, a new entry is created.
5363 * Return 0 for failure.
5364 */
5365 static int
5366syn_check_cluster(pp, len)
5367 char_u *pp;
5368 int len;
5369{
5370 int id;
5371 char_u *name;
5372
5373 name = vim_strnsave(pp, len);
5374 if (name == NULL)
5375 return 0;
5376
5377 id = syn_scl_name2id(name);
5378 if (id == 0) /* doesn't exist yet */
5379 id = syn_add_cluster(name);
5380 else
5381 vim_free(name);
5382 return id;
5383}
5384
5385/*
5386 * Add new syntax cluster and return it's ID.
5387 * "name" must be an allocated string, it will be consumed.
5388 * Return 0 for failure.
5389 */
5390 static int
5391syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005392 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005393{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005394 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395
5396 /*
5397 * First call for this growarray: init growing array.
5398 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005399 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005400 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005401 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5402 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403 }
5404
5405 /*
5406 * Make room for at least one other cluster entry.
5407 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005408 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005409 {
5410 vim_free(name);
5411 return 0;
5412 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005413 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005414
Bram Moolenaar860cae12010-06-05 23:22:07 +02005415 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5416 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5417 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5418 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5419 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005420
Bram Moolenaar217ad922005-03-20 22:37:15 +00005421 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005422 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005423 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005424 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005425
Bram Moolenaar071d4272004-06-13 20:20:40 +00005426 return len + SYNID_CLUSTER;
5427}
5428
5429/*
5430 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5431 * [add={groupname},..] [remove={groupname},..]".
5432 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 static void
5434syn_cmd_cluster(eap, syncing)
5435 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005436 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005437{
5438 char_u *arg = eap->arg;
5439 char_u *group_name_end;
5440 char_u *rest;
5441 int scl_id;
5442 short *clstr_list;
5443 int got_clstr = FALSE;
5444 int opt_len;
5445 int list_op;
5446
5447 eap->nextcmd = find_nextcmd(arg);
5448 if (eap->skip)
5449 return;
5450
5451 rest = get_group_name(arg, &group_name_end);
5452
5453 if (rest != NULL)
5454 {
5455 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005456 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457
5458 for (;;)
5459 {
5460 if (STRNICMP(rest, "add", 3) == 0
5461 && (vim_iswhite(rest[3]) || rest[3] == '='))
5462 {
5463 opt_len = 3;
5464 list_op = CLUSTER_ADD;
5465 }
5466 else if (STRNICMP(rest, "remove", 6) == 0
5467 && (vim_iswhite(rest[6]) || rest[6] == '='))
5468 {
5469 opt_len = 6;
5470 list_op = CLUSTER_SUBTRACT;
5471 }
5472 else if (STRNICMP(rest, "contains", 8) == 0
5473 && (vim_iswhite(rest[8]) || rest[8] == '='))
5474 {
5475 opt_len = 8;
5476 list_op = CLUSTER_REPLACE;
5477 }
5478 else
5479 break;
5480
5481 clstr_list = NULL;
5482 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5483 {
5484 EMSG2(_(e_invarg2), rest);
5485 break;
5486 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005487 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005488 &clstr_list, list_op);
5489 got_clstr = TRUE;
5490 }
5491
5492 if (got_clstr)
5493 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005494 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005495 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005496 }
5497 }
5498
5499 if (!got_clstr)
5500 EMSG(_("E400: No cluster specified"));
5501 if (rest == NULL || !ends_excmd(*rest))
5502 EMSG2(_(e_invarg2), arg);
5503}
5504
5505/*
5506 * On first call for current buffer: Init growing array.
5507 */
5508 static void
5509init_syn_patterns()
5510{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005511 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5512 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005513}
5514
5515/*
5516 * Get one pattern for a ":syntax match" or ":syntax region" command.
5517 * Stores the pattern and program in a synpat_T.
5518 * Returns a pointer to the next argument, or NULL in case of an error.
5519 */
5520 static char_u *
5521get_syn_pattern(arg, ci)
5522 char_u *arg;
5523 synpat_T *ci;
5524{
5525 char_u *end;
5526 int *p;
5527 int idx;
5528 char_u *cpo_save;
5529
5530 /* need at least three chars */
5531 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5532 return NULL;
5533
5534 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5535 if (*end != *arg) /* end delimiter not found */
5536 {
5537 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5538 return NULL;
5539 }
5540 /* store the pattern and compiled regexp program */
5541 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5542 return NULL;
5543
5544 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5545 cpo_save = p_cpo;
5546 p_cpo = (char_u *)"";
5547 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5548 p_cpo = cpo_save;
5549
5550 if (ci->sp_prog == NULL)
5551 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005552 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553
5554 /*
5555 * Check for a match, highlight or region offset.
5556 */
5557 ++end;
5558 do
5559 {
5560 for (idx = SPO_COUNT; --idx >= 0; )
5561 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5562 break;
5563 if (idx >= 0)
5564 {
5565 p = &(ci->sp_offsets[idx]);
5566 if (idx != SPO_LC_OFF)
5567 switch (end[3])
5568 {
5569 case 's': break;
5570 case 'b': break;
5571 case 'e': idx += SPO_COUNT; break;
5572 default: idx = -1; break;
5573 }
5574 if (idx >= 0)
5575 {
5576 ci->sp_off_flags |= (1 << idx);
5577 if (idx == SPO_LC_OFF) /* lc=99 */
5578 {
5579 end += 3;
5580 *p = getdigits(&end);
5581
5582 /* "lc=" offset automatically sets "ms=" offset */
5583 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5584 {
5585 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5586 ci->sp_offsets[SPO_MS_OFF] = *p;
5587 }
5588 }
5589 else /* yy=x+99 */
5590 {
5591 end += 4;
5592 if (*end == '+')
5593 {
5594 ++end;
5595 *p = getdigits(&end); /* positive offset */
5596 }
5597 else if (*end == '-')
5598 {
5599 ++end;
5600 *p = -getdigits(&end); /* negative offset */
5601 }
5602 }
5603 if (*end != ',')
5604 break;
5605 ++end;
5606 }
5607 }
5608 } while (idx >= 0);
5609
5610 if (!ends_excmd(*end) && !vim_iswhite(*end))
5611 {
5612 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5613 return NULL;
5614 }
5615 return skipwhite(end);
5616}
5617
5618/*
5619 * Handle ":syntax sync .." command.
5620 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621 static void
5622syn_cmd_sync(eap, syncing)
5623 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005624 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005625{
5626 char_u *arg_start = eap->arg;
5627 char_u *arg_end;
5628 char_u *key = NULL;
5629 char_u *next_arg;
5630 int illegal = FALSE;
5631 int finished = FALSE;
5632 long n;
5633 char_u *cpo_save;
5634
5635 if (ends_excmd(*arg_start))
5636 {
5637 syn_cmd_list(eap, TRUE);
5638 return;
5639 }
5640
5641 while (!ends_excmd(*arg_start))
5642 {
5643 arg_end = skiptowhite(arg_start);
5644 next_arg = skipwhite(arg_end);
5645 vim_free(key);
5646 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5647 if (STRCMP(key, "CCOMMENT") == 0)
5648 {
5649 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005650 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005651 if (!ends_excmd(*next_arg))
5652 {
5653 arg_end = skiptowhite(next_arg);
5654 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005655 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005656 (int)(arg_end - next_arg));
5657 next_arg = skipwhite(arg_end);
5658 }
5659 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005660 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005661 }
5662 else if ( STRNCMP(key, "LINES", 5) == 0
5663 || STRNCMP(key, "MINLINES", 8) == 0
5664 || STRNCMP(key, "MAXLINES", 8) == 0
5665 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5666 {
5667 if (key[4] == 'S')
5668 arg_end = key + 6;
5669 else if (key[0] == 'L')
5670 arg_end = key + 11;
5671 else
5672 arg_end = key + 9;
5673 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5674 {
5675 illegal = TRUE;
5676 break;
5677 }
5678 n = getdigits(&arg_end);
5679 if (!eap->skip)
5680 {
5681 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005682 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005683 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005684 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005686 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687 }
5688 }
5689 else if (STRCMP(key, "FROMSTART") == 0)
5690 {
5691 if (!eap->skip)
5692 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005693 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5694 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005695 }
5696 }
5697 else if (STRCMP(key, "LINECONT") == 0)
5698 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005699 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 {
5701 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5702 finished = TRUE;
5703 break;
5704 }
5705 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5706 if (*arg_end != *next_arg) /* end delimiter not found */
5707 {
5708 illegal = TRUE;
5709 break;
5710 }
5711
5712 if (!eap->skip)
5713 {
5714 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005715 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 (int)(arg_end - next_arg - 1))) == NULL)
5717 {
5718 finished = TRUE;
5719 break;
5720 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005721 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005722
5723 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5724 cpo_save = p_cpo;
5725 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005726 curwin->w_s->b_syn_linecont_prog =
5727 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005728 p_cpo = cpo_save;
5729
Bram Moolenaar860cae12010-06-05 23:22:07 +02005730 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005731 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005732 vim_free(curwin->w_s->b_syn_linecont_pat);
5733 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734 finished = TRUE;
5735 break;
5736 }
5737 }
5738 next_arg = skipwhite(arg_end + 1);
5739 }
5740 else
5741 {
5742 eap->arg = next_arg;
5743 if (STRCMP(key, "MATCH") == 0)
5744 syn_cmd_match(eap, TRUE);
5745 else if (STRCMP(key, "REGION") == 0)
5746 syn_cmd_region(eap, TRUE);
5747 else if (STRCMP(key, "CLEAR") == 0)
5748 syn_cmd_clear(eap, TRUE);
5749 else
5750 illegal = TRUE;
5751 finished = TRUE;
5752 break;
5753 }
5754 arg_start = next_arg;
5755 }
5756 vim_free(key);
5757 if (illegal)
5758 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5759 else if (!finished)
5760 {
5761 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005762 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005763 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005764 }
5765}
5766
5767/*
5768 * Convert a line of highlight group names into a list of group ID numbers.
5769 * "arg" should point to the "contains" or "nextgroup" keyword.
5770 * "arg" is advanced to after the last group name.
5771 * Careful: the argument is modified (NULs added).
5772 * returns FAIL for some error, OK for success.
5773 */
5774 static int
5775get_id_list(arg, keylen, list)
5776 char_u **arg;
5777 int keylen; /* length of keyword */
5778 short **list; /* where to store the resulting list, if not
5779 NULL, the list is silently skipped! */
5780{
5781 char_u *p = NULL;
5782 char_u *end;
5783 int round;
5784 int count;
5785 int total_count = 0;
5786 short *retval = NULL;
5787 char_u *name;
5788 regmatch_T regmatch;
5789 int id;
5790 int i;
5791 int failed = FALSE;
5792
5793 /*
5794 * We parse the list twice:
5795 * round == 1: count the number of items, allocate the array.
5796 * round == 2: fill the array with the items.
5797 * In round 1 new groups may be added, causing the number of items to
5798 * grow when a regexp is used. In that case round 1 is done once again.
5799 */
5800 for (round = 1; round <= 2; ++round)
5801 {
5802 /*
5803 * skip "contains"
5804 */
5805 p = skipwhite(*arg + keylen);
5806 if (*p != '=')
5807 {
5808 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5809 break;
5810 }
5811 p = skipwhite(p + 1);
5812 if (ends_excmd(*p))
5813 {
5814 EMSG2(_("E406: Empty argument: %s"), *arg);
5815 break;
5816 }
5817
5818 /*
5819 * parse the arguments after "contains"
5820 */
5821 count = 0;
5822 while (!ends_excmd(*p))
5823 {
5824 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5825 ;
5826 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5827 if (name == NULL)
5828 {
5829 failed = TRUE;
5830 break;
5831 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005832 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833 if ( STRCMP(name + 1, "ALLBUT") == 0
5834 || STRCMP(name + 1, "ALL") == 0
5835 || STRCMP(name + 1, "TOP") == 0
5836 || STRCMP(name + 1, "CONTAINED") == 0)
5837 {
5838 if (TOUPPER_ASC(**arg) != 'C')
5839 {
5840 EMSG2(_("E407: %s not allowed here"), name + 1);
5841 failed = TRUE;
5842 vim_free(name);
5843 break;
5844 }
5845 if (count != 0)
5846 {
5847 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5848 failed = TRUE;
5849 vim_free(name);
5850 break;
5851 }
5852 if (name[1] == 'A')
5853 id = SYNID_ALLBUT;
5854 else if (name[1] == 'T')
5855 id = SYNID_TOP;
5856 else
5857 id = SYNID_CONTAINED;
5858 id += current_syn_inc_tag;
5859 }
5860 else if (name[1] == '@')
5861 {
5862 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5863 }
5864 else
5865 {
5866 /*
5867 * Handle full group name.
5868 */
5869 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5870 id = syn_check_group(name + 1, (int)(end - p));
5871 else
5872 {
5873 /*
5874 * Handle match of regexp with group names.
5875 */
5876 *name = '^';
5877 STRCAT(name, "$");
5878 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5879 if (regmatch.regprog == NULL)
5880 {
5881 failed = TRUE;
5882 vim_free(name);
5883 break;
5884 }
5885
5886 regmatch.rm_ic = TRUE;
5887 id = 0;
5888 for (i = highlight_ga.ga_len; --i >= 0; )
5889 {
5890 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5891 (colnr_T)0))
5892 {
5893 if (round == 2)
5894 {
5895 /* Got more items than expected; can happen
5896 * when adding items that match:
5897 * "contains=a.*b,axb".
5898 * Go back to first round */
5899 if (count >= total_count)
5900 {
5901 vim_free(retval);
5902 round = 1;
5903 }
5904 else
5905 retval[count] = i + 1;
5906 }
5907 ++count;
5908 id = -1; /* remember that we found one */
5909 }
5910 }
5911 vim_free(regmatch.regprog);
5912 }
5913 }
5914 vim_free(name);
5915 if (id == 0)
5916 {
5917 EMSG2(_("E409: Unknown group name: %s"), p);
5918 failed = TRUE;
5919 break;
5920 }
5921 if (id > 0)
5922 {
5923 if (round == 2)
5924 {
5925 /* Got more items than expected, go back to first round */
5926 if (count >= total_count)
5927 {
5928 vim_free(retval);
5929 round = 1;
5930 }
5931 else
5932 retval[count] = id;
5933 }
5934 ++count;
5935 }
5936 p = skipwhite(end);
5937 if (*p != ',')
5938 break;
5939 p = skipwhite(p + 1); /* skip comma in between arguments */
5940 }
5941 if (failed)
5942 break;
5943 if (round == 1)
5944 {
5945 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5946 if (retval == NULL)
5947 break;
5948 retval[count] = 0; /* zero means end of the list */
5949 total_count = count;
5950 }
5951 }
5952
5953 *arg = p;
5954 if (failed || retval == NULL)
5955 {
5956 vim_free(retval);
5957 return FAIL;
5958 }
5959
5960 if (*list == NULL)
5961 *list = retval;
5962 else
5963 vim_free(retval); /* list already found, don't overwrite it */
5964
5965 return OK;
5966}
5967
5968/*
5969 * Make a copy of an ID list.
5970 */
5971 static short *
5972copy_id_list(list)
5973 short *list;
5974{
5975 int len;
5976 int count;
5977 short *retval;
5978
5979 if (list == NULL)
5980 return NULL;
5981
5982 for (count = 0; list[count]; ++count)
5983 ;
5984 len = (count + 1) * sizeof(short);
5985 retval = (short *)alloc((unsigned)len);
5986 if (retval != NULL)
5987 mch_memmove(retval, list, (size_t)len);
5988
5989 return retval;
5990}
5991
5992/*
5993 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5994 * "cur_si" can be NULL if not checking the "containedin" list.
5995 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5996 * the current item.
5997 * This function is called very often, keep it fast!!
5998 */
5999 static int
6000in_id_list(cur_si, list, ssp, contained)
6001 stateitem_T *cur_si; /* current item or NULL */
6002 short *list; /* id list */
6003 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6004 int contained; /* group id is contained */
6005{
6006 int retval;
6007 short *scl_list;
6008 short item;
6009 short id = ssp->id;
6010 static int depth = 0;
6011 int r;
6012
6013 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006014 if (cur_si != NULL && ssp->cont_in_list != NULL
6015 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006016 {
6017 /* Ignore transparent items without a contains argument. Double check
6018 * that we don't go back past the first one. */
6019 while ((cur_si->si_flags & HL_TRANS_CONT)
6020 && cur_si > (stateitem_T *)(current_state.ga_data))
6021 --cur_si;
6022 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6023 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006024 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6025 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006026 return TRUE;
6027 }
6028
6029 if (list == NULL)
6030 return FALSE;
6031
6032 /*
6033 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6034 * inside anything. Only allow not-contained groups.
6035 */
6036 if (list == ID_LIST_ALL)
6037 return !contained;
6038
6039 /*
6040 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6041 * contains list. We also require that "id" is at the same ":syn include"
6042 * level as the list.
6043 */
6044 item = *list;
6045 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6046 {
6047 if (item < SYNID_TOP)
6048 {
6049 /* ALL or ALLBUT: accept all groups in the same file */
6050 if (item - SYNID_ALLBUT != ssp->inc_tag)
6051 return FALSE;
6052 }
6053 else if (item < SYNID_CONTAINED)
6054 {
6055 /* TOP: accept all not-contained groups in the same file */
6056 if (item - SYNID_TOP != ssp->inc_tag || contained)
6057 return FALSE;
6058 }
6059 else
6060 {
6061 /* CONTAINED: accept all contained groups in the same file */
6062 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6063 return FALSE;
6064 }
6065 item = *++list;
6066 retval = FALSE;
6067 }
6068 else
6069 retval = TRUE;
6070
6071 /*
6072 * Return "retval" if id is in the contains list.
6073 */
6074 while (item != 0)
6075 {
6076 if (item == id)
6077 return retval;
6078 if (item >= SYNID_CLUSTER)
6079 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006080 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006081 /* restrict recursiveness to 30 to avoid an endless loop for a
6082 * cluster that includes itself (indirectly) */
6083 if (scl_list != NULL && depth < 30)
6084 {
6085 ++depth;
6086 r = in_id_list(NULL, scl_list, ssp, contained);
6087 --depth;
6088 if (r)
6089 return retval;
6090 }
6091 }
6092 item = *++list;
6093 }
6094 return !retval;
6095}
6096
6097struct subcommand
6098{
6099 char *name; /* subcommand name */
6100 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6101};
6102
6103static struct subcommand subcommands[] =
6104{
6105 {"case", syn_cmd_case},
6106 {"clear", syn_cmd_clear},
6107 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006108 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006109 {"enable", syn_cmd_enable},
6110 {"include", syn_cmd_include},
6111 {"keyword", syn_cmd_keyword},
6112 {"list", syn_cmd_list},
6113 {"manual", syn_cmd_manual},
6114 {"match", syn_cmd_match},
6115 {"on", syn_cmd_on},
6116 {"off", syn_cmd_off},
6117 {"region", syn_cmd_region},
6118 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006119 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006120 {"sync", syn_cmd_sync},
6121 {"", syn_cmd_list},
6122 {NULL, NULL}
6123};
6124
6125/*
6126 * ":syntax".
6127 * This searches the subcommands[] table for the subcommand name, and calls a
6128 * syntax_subcommand() function to do the rest.
6129 */
6130 void
6131ex_syntax(eap)
6132 exarg_T *eap;
6133{
6134 char_u *arg = eap->arg;
6135 char_u *subcmd_end;
6136 char_u *subcmd_name;
6137 int i;
6138
6139 syn_cmdlinep = eap->cmdlinep;
6140
6141 /* isolate subcommand name */
6142 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6143 ;
6144 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6145 if (subcmd_name != NULL)
6146 {
6147 if (eap->skip) /* skip error messages for all subcommands */
6148 ++emsg_skip;
6149 for (i = 0; ; ++i)
6150 {
6151 if (subcommands[i].name == NULL)
6152 {
6153 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6154 break;
6155 }
6156 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6157 {
6158 eap->arg = skipwhite(subcmd_end);
6159 (subcommands[i].func)(eap, FALSE);
6160 break;
6161 }
6162 }
6163 vim_free(subcmd_name);
6164 if (eap->skip)
6165 --emsg_skip;
6166 }
6167}
6168
Bram Moolenaar860cae12010-06-05 23:22:07 +02006169 void
6170ex_ownsyntax(eap)
6171 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006172{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006173 char_u *old_value;
6174 char_u *new_value;
6175
Bram Moolenaar860cae12010-06-05 23:22:07 +02006176 if (curwin->w_s == &curwin->w_buffer->b_s)
6177 {
6178 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6179 memset(curwin->w_s, 0, sizeof(synblock_T));
6180#ifdef FEAT_SPELL
6181 curwin->w_p_spell = FALSE; /* No spell checking */
6182 clear_string_option(&curwin->w_s->b_p_spc);
6183 clear_string_option(&curwin->w_s->b_p_spf);
6184 vim_free(curwin->w_s->b_cap_prog);
6185 curwin->w_s->b_cap_prog = NULL;
6186 clear_string_option(&curwin->w_s->b_p_spl);
6187#endif
6188 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006189
6190 /* save value of b:current_syntax */
6191 old_value = get_var_value((char_u *)"b:current_syntax");
6192 if (old_value != NULL)
6193 old_value = vim_strsave(old_value);
6194
6195 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6196 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006197 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006198
6199 /* move value of b:current_syntax to w:current_syntax */
6200 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006201 if (new_value != NULL)
6202 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006203
6204 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006205 if (old_value == NULL)
6206 do_unlet((char_u *)"b:current_syntax", TRUE);
6207 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006208 {
6209 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6210 vim_free(old_value);
6211 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006212}
6213
6214 int
6215syntax_present(win)
6216 win_T *win;
6217{
6218 return (win->w_s->b_syn_patterns.ga_len != 0
6219 || win->w_s->b_syn_clusters.ga_len != 0
6220 || win->w_s->b_keywtab.ht_used > 0
6221 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006222}
6223
6224#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6225
6226static enum
6227{
6228 EXP_SUBCMD, /* expand ":syn" sub-commands */
6229 EXP_CASE /* expand ":syn case" arguments */
6230} expand_what;
6231
Bram Moolenaar4f688582007-07-24 12:34:30 +00006232/*
6233 * Reset include_link, include_default, include_none to 0.
6234 * Called when we are done expanding.
6235 */
6236 void
6237reset_expand_highlight()
6238{
6239 include_link = include_default = include_none = 0;
6240}
6241
6242/*
6243 * Handle command line completion for :match and :echohl command: Add "None"
6244 * as highlight group.
6245 */
6246 void
6247set_context_in_echohl_cmd(xp, arg)
6248 expand_T *xp;
6249 char_u *arg;
6250{
6251 xp->xp_context = EXPAND_HIGHLIGHT;
6252 xp->xp_pattern = arg;
6253 include_none = 1;
6254}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006255
6256/*
6257 * Handle command line completion for :syntax command.
6258 */
6259 void
6260set_context_in_syntax_cmd(xp, arg)
6261 expand_T *xp;
6262 char_u *arg;
6263{
6264 char_u *p;
6265
6266 /* Default: expand subcommands */
6267 xp->xp_context = EXPAND_SYNTAX;
6268 expand_what = EXP_SUBCMD;
6269 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006270 include_link = 0;
6271 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006272
6273 /* (part of) subcommand already typed */
6274 if (*arg != NUL)
6275 {
6276 p = skiptowhite(arg);
6277 if (*p != NUL) /* past first word */
6278 {
6279 xp->xp_pattern = skipwhite(p);
6280 if (*skiptowhite(xp->xp_pattern) != NUL)
6281 xp->xp_context = EXPAND_NOTHING;
6282 else if (STRNICMP(arg, "case", p - arg) == 0)
6283 expand_what = EXP_CASE;
6284 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6285 || STRNICMP(arg, "region", p - arg) == 0
6286 || STRNICMP(arg, "match", p - arg) == 0
6287 || STRNICMP(arg, "list", p - arg) == 0)
6288 xp->xp_context = EXPAND_HIGHLIGHT;
6289 else
6290 xp->xp_context = EXPAND_NOTHING;
6291 }
6292 }
6293}
6294
6295static char *(case_args[]) = {"match", "ignore", NULL};
6296
6297/*
6298 * Function given to ExpandGeneric() to obtain the list syntax names for
6299 * expansion.
6300 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006301 char_u *
6302get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006303 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006304 int idx;
6305{
6306 if (expand_what == EXP_SUBCMD)
6307 return (char_u *)subcommands[idx].name;
6308 return (char_u *)case_args[idx];
6309}
6310
6311#endif /* FEAT_CMDL_COMPL */
6312
Bram Moolenaar071d4272004-06-13 20:20:40 +00006313/*
6314 * Function called for expression evaluation: get syntax ID at file position.
6315 */
6316 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006317syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006318 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006319 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006320 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006321 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006322 int *spellp; /* return: can do spell checking */
6323 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006324{
6325 /* When the position is not after the current position and in the same
6326 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006327 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006328 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006329 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006330 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332 (void)get_syntax_attr(col, NULL, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006333
6334 return (trans ? current_trans_id : current_id);
6335}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336
Bram Moolenaar860cae12010-06-05 23:22:07 +02006337#if defined(FEAT_CONCEAL) || defined(PROTO)
6338/*
6339 * Return conceal substitution character
6340 */
6341 int
6342syn_get_sub_char()
6343{
6344 return current_sub_char;
6345}
6346#endif
6347
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006348#if defined(FEAT_EVAL) || defined(PROTO)
6349/*
6350 * Return the syntax ID at position "i" in the current stack.
6351 * The caller must have called syn_get_id() before to fill the stack.
6352 * Returns -1 when "i" is out of range.
6353 */
6354 int
6355syn_get_stack_item(i)
6356 int i;
6357{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006358 if (i >= current_state.ga_len)
6359 {
6360 /* Need to invalidate the state, because we didn't properly finish it
6361 * for the last character, "keep_state" was TRUE. */
6362 invalidate_current_state();
6363 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006364 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006365 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006366 return CUR_STATE(i).si_id;
6367}
6368#endif
6369
Bram Moolenaar071d4272004-06-13 20:20:40 +00006370#if defined(FEAT_FOLDING) || defined(PROTO)
6371/*
6372 * Function called to get folding level for line "lnum" in window "wp".
6373 */
6374 int
6375syn_get_foldlevel(wp, lnum)
6376 win_T *wp;
6377 long lnum;
6378{
6379 int level = 0;
6380 int i;
6381
6382 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006383 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384 {
6385 syntax_start(wp, lnum);
6386
6387 for (i = 0; i < current_state.ga_len; ++i)
6388 if (CUR_STATE(i).si_flags & HL_FOLD)
6389 ++level;
6390 }
6391 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006392 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006393 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006394 if (level < 0)
6395 level = 0;
6396 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006397 return level;
6398}
6399#endif
6400
6401#endif /* FEAT_SYN_HL */
6402
6403
6404/**************************************
6405 * Highlighting stuff *
6406 **************************************/
6407
6408/*
6409 * The default highlight groups. These are compiled-in for fast startup and
6410 * they still work when the runtime files can't be found.
6411 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006412 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6413 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006414 */
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006415#ifdef FEAT_GUI
6416# define CENT(a, b) b
6417#else
6418# define CENT(a, b) a
6419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006420static char *(highlight_init_both[]) =
6421 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006422 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6423 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6424 CENT("IncSearch term=reverse cterm=reverse",
6425 "IncSearch term=reverse cterm=reverse gui=reverse"),
6426 CENT("ModeMsg term=bold cterm=bold",
6427 "ModeMsg term=bold cterm=bold gui=bold"),
6428 CENT("NonText term=bold ctermfg=Blue",
6429 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6430 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6431 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6432 CENT("StatusLineNC term=reverse cterm=reverse",
6433 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006434#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006435 CENT("VertSplit term=reverse cterm=reverse",
6436 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006437#endif
6438#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006439 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6440 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006441#endif
6442#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006443 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6444 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006445#endif
6446#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006447 CENT("PmenuThumb cterm=reverse",
6448 "PmenuThumb cterm=reverse gui=reverse"),
6449 CENT("PmenuSbar ctermbg=Grey",
6450 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006451#endif
6452#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006453 CENT("TabLineSel term=bold cterm=bold",
6454 "TabLineSel term=bold cterm=bold gui=bold"),
6455 CENT("TabLineFill term=reverse cterm=reverse",
6456 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006457#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006458#ifdef FEAT_GUI
6459 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006460 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006461#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462 NULL
6463 };
6464
6465static char *(highlight_init_light[]) =
6466 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006467 CENT("Directory term=bold ctermfg=DarkBlue",
6468 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6469 CENT("LineNr term=underline ctermfg=Brown",
6470 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6471 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6472 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6473 CENT("Question term=standout ctermfg=DarkGreen",
6474 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6475 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6476 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006477#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006478 CENT("SpellBad term=reverse ctermbg=LightRed",
6479 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6480 CENT("SpellCap term=reverse ctermbg=LightBlue",
6481 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6482 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6483 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6484 CENT("SpellLocal term=underline ctermbg=Cyan",
6485 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006486#endif
6487#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006488 CENT("Pmenu ctermbg=LightMagenta",
6489 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6490 CENT("PmenuSel ctermbg=LightGrey",
6491 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006492#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006493 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6494 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6495 CENT("Title term=bold ctermfg=DarkMagenta",
6496 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6497 CENT("WarningMsg term=standout ctermfg=DarkRed",
6498 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006499#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006500 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6501 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006502#endif
6503#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006504 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6505 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6506 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6507 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006508#endif
6509#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006510 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6511 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006512#endif
6513#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006514 CENT("Visual term=reverse",
6515 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006516#endif
6517#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006518 CENT("DiffAdd term=bold ctermbg=LightBlue",
6519 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6520 CENT("DiffChange term=bold ctermbg=LightMagenta",
6521 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6522 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6523 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006524#endif
6525#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006526 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6527 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006528#endif
6529#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006530 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006531 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006532 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006533 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006534 CENT("ColorColumn term=reverse ctermbg=LightRed",
6535 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006536#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006537#ifdef FEAT_CONCEAL
6538 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6539 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6540#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006541#ifdef FEAT_AUTOCMD
6542 CENT("MatchParen term=reverse ctermbg=Cyan",
6543 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6544#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006545#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006546 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006547#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006548 NULL
6549 };
6550
6551static char *(highlight_init_dark[]) =
6552 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006553 CENT("Directory term=bold ctermfg=LightCyan",
6554 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6555 CENT("LineNr term=underline ctermfg=Yellow",
6556 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6557 CENT("MoreMsg term=bold ctermfg=LightGreen",
6558 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6559 CENT("Question term=standout ctermfg=LightGreen",
6560 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6561 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6562 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6563 CENT("SpecialKey term=bold ctermfg=LightBlue",
6564 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006565#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006566 CENT("SpellBad term=reverse ctermbg=Red",
6567 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6568 CENT("SpellCap term=reverse ctermbg=Blue",
6569 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6570 CENT("SpellRare term=reverse ctermbg=Magenta",
6571 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6572 CENT("SpellLocal term=underline ctermbg=Cyan",
6573 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006574#endif
6575#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006576 CENT("Pmenu ctermbg=Magenta",
6577 "Pmenu ctermbg=Magenta guibg=Magenta"),
6578 CENT("PmenuSel ctermbg=DarkGrey",
6579 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006580#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006581 CENT("Title term=bold ctermfg=LightMagenta",
6582 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6583 CENT("WarningMsg term=standout ctermfg=LightRed",
6584 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006585#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006586 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6587 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006588#endif
6589#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006590 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6591 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6592 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6593 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006594#endif
6595#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006596 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6597 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006598#endif
6599#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006600 CENT("Visual term=reverse",
6601 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006602#endif
6603#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006604 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6605 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6606 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6607 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6608 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6609 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006610#endif
6611#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006612 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6613 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006614#endif
6615#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006616 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006617 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006618 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006619 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006620 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6621 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006622#endif
6623#ifdef FEAT_AUTOCMD
6624 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6625 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006626#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006627#ifdef FEAT_CONCEAL
6628 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6629 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6630#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006631#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006632 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006633#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006634 NULL
6635 };
6636
6637 void
6638init_highlight(both, reset)
6639 int both; /* include groups where 'bg' doesn't matter */
6640 int reset; /* clear group first */
6641{
6642 int i;
6643 char **pp;
6644 static int had_both = FALSE;
6645#ifdef FEAT_EVAL
6646 char_u *p;
6647
6648 /*
6649 * Try finding the color scheme file. Used when a color file was loaded
6650 * and 'background' or 't_Co' is changed.
6651 */
6652 p = get_var_value((char_u *)"g:colors_name");
6653 if (p != NULL && load_colors(p) == OK)
6654 return;
6655#endif
6656
6657 /*
6658 * Didn't use a color file, use the compiled-in colors.
6659 */
6660 if (both)
6661 {
6662 had_both = TRUE;
6663 pp = highlight_init_both;
6664 for (i = 0; pp[i] != NULL; ++i)
6665 do_highlight((char_u *)pp[i], reset, TRUE);
6666 }
6667 else if (!had_both)
6668 /* Don't do anything before the call with both == TRUE from main().
6669 * Not everything has been setup then, and that call will overrule
6670 * everything anyway. */
6671 return;
6672
6673 if (*p_bg == 'l')
6674 pp = highlight_init_light;
6675 else
6676 pp = highlight_init_dark;
6677 for (i = 0; pp[i] != NULL; ++i)
6678 do_highlight((char_u *)pp[i], reset, TRUE);
6679
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006680 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006681 * depend on the number of colors available.
6682 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006683 * to avoid Statement highlighted text disappears.
6684 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006685 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006686 do_highlight((char_u *)(*p_bg == 'l'
6687 ? "Visual cterm=NONE ctermbg=LightGrey"
6688 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006689 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006690 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006691 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6692 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006693 if (*p_bg == 'l')
6694 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6695 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006696
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697#ifdef FEAT_SYN_HL
6698 /*
6699 * If syntax highlighting is enabled load the highlighting for it.
6700 */
6701 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006702 {
6703 static int recursive = 0;
6704
6705 if (recursive >= 5)
6706 EMSG(_("E679: recursive loop loading syncolor.vim"));
6707 else
6708 {
6709 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006710 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006711 --recursive;
6712 }
6713 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006714#endif
6715}
6716
6717/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006718 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006719 * Return OK for success, FAIL for failure.
6720 */
6721 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006722load_colors(name)
6723 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006724{
6725 char_u *buf;
6726 int retval = FAIL;
6727 static int recursive = FALSE;
6728
6729 /* When being called recursively, this is probably because setting
6730 * 'background' caused the highlighting to be reloaded. This means it is
6731 * working, thus we should return OK. */
6732 if (recursive)
6733 return OK;
6734
6735 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006736 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006737 if (buf != NULL)
6738 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006739 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006740 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006741 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006742#ifdef FEAT_AUTOCMD
6743 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6744#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745 }
6746 recursive = FALSE;
6747
6748 return retval;
6749}
6750
6751/*
6752 * Handle the ":highlight .." command.
6753 * When using ":hi clear" this is called recursively for each group with
6754 * "forceit" and "init" both TRUE.
6755 */
6756 void
6757do_highlight(line, forceit, init)
6758 char_u *line;
6759 int forceit;
6760 int init; /* TRUE when called for initializing */
6761{
6762 char_u *name_end;
6763 char_u *p;
6764 char_u *linep;
6765 char_u *key_start;
6766 char_u *arg_start;
6767 char_u *key = NULL, *arg = NULL;
6768 long i;
6769 int off;
6770 int len;
6771 int attr;
6772 int id;
6773 int idx;
6774 int dodefault = FALSE;
6775 int doclear = FALSE;
6776 int dolink = FALSE;
6777 int error = FALSE;
6778 int color;
6779 int is_normal_group = FALSE; /* "Normal" group */
6780#ifdef FEAT_GUI_X11
6781 int is_menu_group = FALSE; /* "Menu" group */
6782 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6783 int is_tooltip_group = FALSE; /* "Tooltip" group */
6784 int do_colors = FALSE; /* need to update colors? */
6785#else
6786# define is_menu_group 0
6787# define is_tooltip_group 0
6788#endif
6789
6790 /*
6791 * If no argument, list current highlighting.
6792 */
6793 if (ends_excmd(*line))
6794 {
6795 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6796 /* TODO: only call when the group has attributes set */
6797 highlight_list_one((int)i);
6798 return;
6799 }
6800
6801 /*
6802 * Isolate the name.
6803 */
6804 name_end = skiptowhite(line);
6805 linep = skipwhite(name_end);
6806
6807 /*
6808 * Check for "default" argument.
6809 */
6810 if (STRNCMP(line, "default", name_end - line) == 0)
6811 {
6812 dodefault = TRUE;
6813 line = linep;
6814 name_end = skiptowhite(line);
6815 linep = skipwhite(name_end);
6816 }
6817
6818 /*
6819 * Check for "clear" or "link" argument.
6820 */
6821 if (STRNCMP(line, "clear", name_end - line) == 0)
6822 doclear = TRUE;
6823 if (STRNCMP(line, "link", name_end - line) == 0)
6824 dolink = TRUE;
6825
6826 /*
6827 * ":highlight {group-name}": list highlighting for one group.
6828 */
6829 if (!doclear && !dolink && ends_excmd(*linep))
6830 {
6831 id = syn_namen2id(line, (int)(name_end - line));
6832 if (id == 0)
6833 EMSG2(_("E411: highlight group not found: %s"), line);
6834 else
6835 highlight_list_one(id);
6836 return;
6837 }
6838
6839 /*
6840 * Handle ":highlight link {from} {to}" command.
6841 */
6842 if (dolink)
6843 {
6844 char_u *from_start = linep;
6845 char_u *from_end;
6846 char_u *to_start;
6847 char_u *to_end;
6848 int from_id;
6849 int to_id;
6850
6851 from_end = skiptowhite(from_start);
6852 to_start = skipwhite(from_end);
6853 to_end = skiptowhite(to_start);
6854
6855 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6856 {
6857 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6858 from_start);
6859 return;
6860 }
6861
6862 if (!ends_excmd(*skipwhite(to_end)))
6863 {
6864 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6865 return;
6866 }
6867
6868 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6869 if (STRNCMP(to_start, "NONE", 4) == 0)
6870 to_id = 0;
6871 else
6872 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6873
6874 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6875 {
6876 /*
6877 * Don't allow a link when there already is some highlighting
6878 * for the group, unless '!' is used
6879 */
6880 if (to_id > 0 && !forceit && !init
6881 && hl_has_settings(from_id - 1, dodefault))
6882 {
6883 if (sourcing_name == NULL && !dodefault)
6884 EMSG(_("E414: group has settings, highlight link ignored"));
6885 }
6886 else
6887 {
6888 if (!init)
6889 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6890 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006891#ifdef FEAT_EVAL
6892 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6893#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006894 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006895 }
6896 }
6897
6898 /* Only call highlight_changed() once, after sourcing a syntax file */
6899 need_highlight_changed = TRUE;
6900
6901 return;
6902 }
6903
6904 if (doclear)
6905 {
6906 /*
6907 * ":highlight clear [group]" command.
6908 */
6909 line = linep;
6910 if (ends_excmd(*line))
6911 {
6912#ifdef FEAT_GUI
6913 /* First, we do not destroy the old values, but allocate the new
6914 * ones and update the display. THEN we destroy the old values.
6915 * If we destroy the old values first, then the old values
6916 * (such as GuiFont's or GuiFontset's) will still be displayed but
6917 * invalid because they were free'd.
6918 */
6919 if (gui.in_use)
6920 {
6921# ifdef FEAT_BEVAL_TIP
6922 gui_init_tooltip_font();
6923# endif
6924# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6925 gui_init_menu_font();
6926# endif
6927 }
6928# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6929 gui_mch_def_colors();
6930# endif
6931# ifdef FEAT_GUI_X11
6932# ifdef FEAT_MENU
6933
6934 /* This only needs to be done when there is no Menu highlight
6935 * group defined by default, which IS currently the case.
6936 */
6937 gui_mch_new_menu_colors();
6938# endif
6939 if (gui.in_use)
6940 {
6941 gui_new_scrollbar_colors();
6942# ifdef FEAT_BEVAL
6943 gui_mch_new_tooltip_colors();
6944# endif
6945# ifdef FEAT_MENU
6946 gui_mch_new_menu_font();
6947# endif
6948 }
6949# endif
6950
6951 /* Ok, we're done allocating the new default graphics items.
6952 * The screen should already be refreshed at this point.
6953 * It is now Ok to clear out the old data.
6954 */
6955#endif
6956#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006957 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006958#endif
6959 restore_cterm_colors();
6960
6961 /*
6962 * Clear all default highlight groups and load the defaults.
6963 */
6964 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6965 highlight_clear(idx);
6966 init_highlight(TRUE, TRUE);
6967#ifdef FEAT_GUI
6968 if (gui.in_use)
6969 highlight_gui_started();
6970#endif
6971 highlight_changed();
6972 redraw_later_clear();
6973 return;
6974 }
6975 name_end = skiptowhite(line);
6976 linep = skipwhite(name_end);
6977 }
6978
6979 /*
6980 * Find the group name in the table. If it does not exist yet, add it.
6981 */
6982 id = syn_check_group(line, (int)(name_end - line));
6983 if (id == 0) /* failed (out of memory) */
6984 return;
6985 idx = id - 1; /* index is ID minus one */
6986
6987 /* Return if "default" was used and the group already has settings. */
6988 if (dodefault && hl_has_settings(idx, TRUE))
6989 return;
6990
6991 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6992 is_normal_group = TRUE;
6993#ifdef FEAT_GUI_X11
6994 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6995 is_menu_group = TRUE;
6996 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6997 is_scrollbar_group = TRUE;
6998 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6999 is_tooltip_group = TRUE;
7000#endif
7001
7002 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7003 if (doclear || (forceit && init))
7004 {
7005 highlight_clear(idx);
7006 if (!doclear)
7007 HL_TABLE()[idx].sg_set = 0;
7008 }
7009
7010 if (!doclear)
7011 while (!ends_excmd(*linep))
7012 {
7013 key_start = linep;
7014 if (*linep == '=')
7015 {
7016 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7017 error = TRUE;
7018 break;
7019 }
7020
7021 /*
7022 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7023 * "guibg").
7024 */
7025 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7026 ++linep;
7027 vim_free(key);
7028 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7029 if (key == NULL)
7030 {
7031 error = TRUE;
7032 break;
7033 }
7034 linep = skipwhite(linep);
7035
7036 if (STRCMP(key, "NONE") == 0)
7037 {
7038 if (!init || HL_TABLE()[idx].sg_set == 0)
7039 {
7040 if (!init)
7041 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7042 highlight_clear(idx);
7043 }
7044 continue;
7045 }
7046
7047 /*
7048 * Check for the equal sign.
7049 */
7050 if (*linep != '=')
7051 {
7052 EMSG2(_("E416: missing equal sign: %s"), key_start);
7053 error = TRUE;
7054 break;
7055 }
7056 ++linep;
7057
7058 /*
7059 * Isolate the argument.
7060 */
7061 linep = skipwhite(linep);
7062 if (*linep == '\'') /* guifg='color name' */
7063 {
7064 arg_start = ++linep;
7065 linep = vim_strchr(linep, '\'');
7066 if (linep == NULL)
7067 {
7068 EMSG2(_(e_invarg2), key_start);
7069 error = TRUE;
7070 break;
7071 }
7072 }
7073 else
7074 {
7075 arg_start = linep;
7076 linep = skiptowhite(linep);
7077 }
7078 if (linep == arg_start)
7079 {
7080 EMSG2(_("E417: missing argument: %s"), key_start);
7081 error = TRUE;
7082 break;
7083 }
7084 vim_free(arg);
7085 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7086 if (arg == NULL)
7087 {
7088 error = TRUE;
7089 break;
7090 }
7091 if (*linep == '\'')
7092 ++linep;
7093
7094 /*
7095 * Store the argument.
7096 */
7097 if ( STRCMP(key, "TERM") == 0
7098 || STRCMP(key, "CTERM") == 0
7099 || STRCMP(key, "GUI") == 0)
7100 {
7101 attr = 0;
7102 off = 0;
7103 while (arg[off] != NUL)
7104 {
7105 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7106 {
7107 len = (int)STRLEN(hl_name_table[i]);
7108 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7109 {
7110 attr |= hl_attr_table[i];
7111 off += len;
7112 break;
7113 }
7114 }
7115 if (i < 0)
7116 {
7117 EMSG2(_("E418: Illegal value: %s"), arg);
7118 error = TRUE;
7119 break;
7120 }
7121 if (arg[off] == ',') /* another one follows */
7122 ++off;
7123 }
7124 if (error)
7125 break;
7126 if (*key == 'T')
7127 {
7128 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7129 {
7130 if (!init)
7131 HL_TABLE()[idx].sg_set |= SG_TERM;
7132 HL_TABLE()[idx].sg_term = attr;
7133 }
7134 }
7135 else if (*key == 'C')
7136 {
7137 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7138 {
7139 if (!init)
7140 HL_TABLE()[idx].sg_set |= SG_CTERM;
7141 HL_TABLE()[idx].sg_cterm = attr;
7142 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7143 }
7144 }
7145#ifdef FEAT_GUI
7146 else
7147 {
7148 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7149 {
7150 if (!init)
7151 HL_TABLE()[idx].sg_set |= SG_GUI;
7152 HL_TABLE()[idx].sg_gui = attr;
7153 }
7154 }
7155#endif
7156 }
7157 else if (STRCMP(key, "FONT") == 0)
7158 {
7159 /* in non-GUI fonts are simply ignored */
7160#ifdef FEAT_GUI
7161 if (!gui.shell_created)
7162 {
7163 /* GUI not started yet, always accept the name. */
7164 vim_free(HL_TABLE()[idx].sg_font_name);
7165 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7166 }
7167 else
7168 {
7169 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7170# ifdef FEAT_XFONTSET
7171 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7172# endif
7173 /* First, save the current font/fontset.
7174 * Then try to allocate the font/fontset.
7175 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7176 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7177 */
7178
7179 HL_TABLE()[idx].sg_font = NOFONT;
7180# ifdef FEAT_XFONTSET
7181 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7182# endif
7183 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7184 is_tooltip_group);
7185
7186# ifdef FEAT_XFONTSET
7187 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7188 {
7189 /* New fontset was accepted. Free the old one, if there was
7190 * one.
7191 */
7192 gui_mch_free_fontset(temp_sg_fontset);
7193 vim_free(HL_TABLE()[idx].sg_font_name);
7194 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7195 }
7196 else
7197 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7198# endif
7199 if (HL_TABLE()[idx].sg_font != NOFONT)
7200 {
7201 /* New font was accepted. Free the old one, if there was
7202 * one.
7203 */
7204 gui_mch_free_font(temp_sg_font);
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_font = temp_sg_font;
7210 }
7211#endif
7212 }
7213 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7214 {
7215 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7216 {
7217 if (!init)
7218 HL_TABLE()[idx].sg_set |= SG_CTERM;
7219
7220 /* When setting the foreground color, and previously the "bold"
7221 * flag was set for a light color, reset it now */
7222 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7223 {
7224 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7225 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7226 }
7227
7228 if (VIM_ISDIGIT(*arg))
7229 color = atoi((char *)arg);
7230 else if (STRICMP(arg, "fg") == 0)
7231 {
7232 if (cterm_normal_fg_color)
7233 color = cterm_normal_fg_color - 1;
7234 else
7235 {
7236 EMSG(_("E419: FG color unknown"));
7237 error = TRUE;
7238 break;
7239 }
7240 }
7241 else if (STRICMP(arg, "bg") == 0)
7242 {
7243 if (cterm_normal_bg_color > 0)
7244 color = cterm_normal_bg_color - 1;
7245 else
7246 {
7247 EMSG(_("E420: BG color unknown"));
7248 error = TRUE;
7249 break;
7250 }
7251 }
7252 else
7253 {
7254 static char *(color_names[28]) = {
7255 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7256 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7257 "Gray", "Grey",
7258 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7259 "Blue", "LightBlue", "Green", "LightGreen",
7260 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7261 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7262 static int color_numbers_16[28] = {0, 1, 2, 3,
7263 4, 5, 6, 6,
7264 7, 7,
7265 7, 7, 8, 8,
7266 9, 9, 10, 10,
7267 11, 11, 12, 12, 13,
7268 13, 14, 14, 15, -1};
7269 /* for xterm with 88 colors... */
7270 static int color_numbers_88[28] = {0, 4, 2, 6,
7271 1, 5, 32, 72,
7272 84, 84,
7273 7, 7, 82, 82,
7274 12, 43, 10, 61,
7275 14, 63, 9, 74, 13,
7276 75, 11, 78, 15, -1};
7277 /* for xterm with 256 colors... */
7278 static int color_numbers_256[28] = {0, 4, 2, 6,
7279 1, 5, 130, 130,
7280 248, 248,
7281 7, 7, 242, 242,
7282 12, 81, 10, 121,
7283 14, 159, 9, 224, 13,
7284 225, 11, 229, 15, -1};
7285 /* for terminals with less than 16 colors... */
7286 static int color_numbers_8[28] = {0, 4, 2, 6,
7287 1, 5, 3, 3,
7288 7, 7,
7289 7, 7, 0+8, 0+8,
7290 4+8, 4+8, 2+8, 2+8,
7291 6+8, 6+8, 1+8, 1+8, 5+8,
7292 5+8, 3+8, 3+8, 7+8, -1};
7293#if defined(__QNXNTO__)
7294 static int *color_numbers_8_qansi = color_numbers_8;
7295 /* On qnx, the 8 & 16 color arrays are the same */
7296 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7297 color_numbers_8_qansi = color_numbers_16;
7298#endif
7299
7300 /* reduce calls to STRICMP a bit, it can be slow */
7301 off = TOUPPER_ASC(*arg);
7302 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7303 if (off == color_names[i][0]
7304 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7305 break;
7306 if (i < 0)
7307 {
7308 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7309 error = TRUE;
7310 break;
7311 }
7312
7313 /* Use the _16 table to check if its a valid color name. */
7314 color = color_numbers_16[i];
7315 if (color >= 0)
7316 {
7317 if (t_colors == 8)
7318 {
7319 /* t_Co is 8: use the 8 colors table */
7320#if defined(__QNXNTO__)
7321 color = color_numbers_8_qansi[i];
7322#else
7323 color = color_numbers_8[i];
7324#endif
7325 if (key[5] == 'F')
7326 {
7327 /* set/reset bold attribute to get light foreground
7328 * colors (on some terminals, e.g. "linux") */
7329 if (color & 8)
7330 {
7331 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7332 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7333 }
7334 else
7335 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7336 }
7337 color &= 7; /* truncate to 8 colors */
7338 }
7339 else if (t_colors == 16 || t_colors == 88
7340 || t_colors == 256)
7341 {
7342 /*
7343 * Guess: if the termcap entry ends in 'm', it is
7344 * probably an xterm-like terminal. Use the changed
7345 * order for colors.
7346 */
7347 if (*T_CAF != NUL)
7348 p = T_CAF;
7349 else
7350 p = T_CSF;
7351 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7352 switch (t_colors)
7353 {
7354 case 16:
7355 color = color_numbers_8[i];
7356 break;
7357 case 88:
7358 color = color_numbers_88[i];
7359 break;
7360 case 256:
7361 color = color_numbers_256[i];
7362 break;
7363 }
7364 }
7365 }
7366 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007367 /* Add one to the argument, to avoid zero. Zero is used for
7368 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007369 if (key[5] == 'F')
7370 {
7371 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7372 if (is_normal_group)
7373 {
7374 cterm_normal_fg_color = color + 1;
7375 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7376#ifdef FEAT_GUI
7377 /* Don't do this if the GUI is used. */
7378 if (!gui.in_use && !gui.starting)
7379#endif
7380 {
7381 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007382 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007383 term_fg_color(color);
7384 }
7385 }
7386 }
7387 else
7388 {
7389 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7390 if (is_normal_group)
7391 {
7392 cterm_normal_bg_color = color + 1;
7393#ifdef FEAT_GUI
7394 /* Don't mess with 'background' if the GUI is used. */
7395 if (!gui.in_use && !gui.starting)
7396#endif
7397 {
7398 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007399 if (color >= 0)
7400 {
7401 if (termcap_active)
7402 term_bg_color(color);
7403 if (t_colors < 16)
7404 i = (color == 0 || color == 4);
7405 else
7406 i = (color < 7 || color == 8);
7407 /* Set the 'background' option if the value is
7408 * wrong. */
7409 if (i != (*p_bg == 'd'))
7410 set_option_value((char_u *)"bg", 0L,
7411 i ? (char_u *)"dark"
7412 : (char_u *)"light", 0);
7413 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007414 }
7415 }
7416 }
7417 }
7418 }
7419 else if (STRCMP(key, "GUIFG") == 0)
7420 {
7421#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007422 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007423 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007424 if (!init)
7425 HL_TABLE()[idx].sg_set |= SG_GUI;
7426
7427 i = color_name2handle(arg);
7428 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7429 {
7430 HL_TABLE()[idx].sg_gui_fg = i;
7431 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7432 if (STRCMP(arg, "NONE"))
7433 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7434 else
7435 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007436# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007437 if (is_menu_group)
7438 gui.menu_fg_pixel = i;
7439 if (is_scrollbar_group)
7440 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007441# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007442 if (is_tooltip_group)
7443 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007444# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007445 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007446# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007447 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007448 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007449#endif
7450 }
7451 else if (STRCMP(key, "GUIBG") == 0)
7452 {
7453#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007454 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007455 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007456 if (!init)
7457 HL_TABLE()[idx].sg_set |= SG_GUI;
7458
7459 i = color_name2handle(arg);
7460 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7461 {
7462 HL_TABLE()[idx].sg_gui_bg = i;
7463 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7464 if (STRCMP(arg, "NONE") != 0)
7465 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7466 else
7467 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007468# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007469 if (is_menu_group)
7470 gui.menu_bg_pixel = i;
7471 if (is_scrollbar_group)
7472 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007473# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007474 if (is_tooltip_group)
7475 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007476# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007477 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007478# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007479 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007480 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007481#endif
7482 }
7483 else if (STRCMP(key, "GUISP") == 0)
7484 {
7485#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
7486 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7487 {
7488 if (!init)
7489 HL_TABLE()[idx].sg_set |= SG_GUI;
7490
7491 i = color_name2handle(arg);
7492 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7493 {
7494 HL_TABLE()[idx].sg_gui_sp = i;
7495 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7496 if (STRCMP(arg, "NONE") != 0)
7497 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7498 else
7499 HL_TABLE()[idx].sg_gui_sp_name = NULL;
7500 }
7501 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007502#endif
7503 }
7504 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7505 {
7506 char_u buf[100];
7507 char_u *tname;
7508
7509 if (!init)
7510 HL_TABLE()[idx].sg_set |= SG_TERM;
7511
7512 /*
7513 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007514 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007515 */
7516 if (STRNCMP(arg, "t_", 2) == 0)
7517 {
7518 off = 0;
7519 buf[0] = 0;
7520 while (arg[off] != NUL)
7521 {
7522 /* Isolate one termcap name */
7523 for (len = 0; arg[off + len] &&
7524 arg[off + len] != ','; ++len)
7525 ;
7526 tname = vim_strnsave(arg + off, len);
7527 if (tname == NULL) /* out of memory */
7528 {
7529 error = TRUE;
7530 break;
7531 }
7532 /* lookup the escape sequence for the item */
7533 p = get_term_code(tname);
7534 vim_free(tname);
7535 if (p == NULL) /* ignore non-existing things */
7536 p = (char_u *)"";
7537
7538 /* Append it to the already found stuff */
7539 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7540 {
7541 EMSG2(_("E422: terminal code too long: %s"), arg);
7542 error = TRUE;
7543 break;
7544 }
7545 STRCAT(buf, p);
7546
7547 /* Advance to the next item */
7548 off += len;
7549 if (arg[off] == ',') /* another one follows */
7550 ++off;
7551 }
7552 }
7553 else
7554 {
7555 /*
7556 * Copy characters from arg[] to buf[], translating <> codes.
7557 */
7558 for (p = arg, off = 0; off < 100 && *p; )
7559 {
7560 len = trans_special(&p, buf + off, FALSE);
7561 if (len) /* recognized special char */
7562 off += len;
7563 else /* copy as normal char */
7564 buf[off++] = *p++;
7565 }
7566 buf[off] = NUL;
7567 }
7568 if (error)
7569 break;
7570
7571 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7572 p = NULL;
7573 else
7574 p = vim_strsave(buf);
7575 if (key[2] == 'A')
7576 {
7577 vim_free(HL_TABLE()[idx].sg_start);
7578 HL_TABLE()[idx].sg_start = p;
7579 }
7580 else
7581 {
7582 vim_free(HL_TABLE()[idx].sg_stop);
7583 HL_TABLE()[idx].sg_stop = p;
7584 }
7585 }
7586 else
7587 {
7588 EMSG2(_("E423: Illegal argument: %s"), key_start);
7589 error = TRUE;
7590 break;
7591 }
7592
7593 /*
7594 * When highlighting has been given for a group, don't link it.
7595 */
7596 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7597 HL_TABLE()[idx].sg_link = 0;
7598
7599 /*
7600 * Continue with next argument.
7601 */
7602 linep = skipwhite(linep);
7603 }
7604
7605 /*
7606 * If there is an error, and it's a new entry, remove it from the table.
7607 */
7608 if (error && idx == highlight_ga.ga_len)
7609 syn_unadd_group();
7610 else
7611 {
7612 if (is_normal_group)
7613 {
7614 HL_TABLE()[idx].sg_term_attr = 0;
7615 HL_TABLE()[idx].sg_cterm_attr = 0;
7616#ifdef FEAT_GUI
7617 HL_TABLE()[idx].sg_gui_attr = 0;
7618 /*
7619 * Need to update all groups, because they might be using "bg"
7620 * and/or "fg", which have been changed now.
7621 */
7622 if (gui.in_use)
7623 highlight_gui_started();
7624#endif
7625 }
7626#ifdef FEAT_GUI_X11
7627# ifdef FEAT_MENU
7628 else if (is_menu_group)
7629 {
7630 if (gui.in_use && do_colors)
7631 gui_mch_new_menu_colors();
7632 }
7633# endif
7634 else if (is_scrollbar_group)
7635 {
7636 if (gui.in_use && do_colors)
7637 gui_new_scrollbar_colors();
7638 }
7639# ifdef FEAT_BEVAL
7640 else if (is_tooltip_group)
7641 {
7642 if (gui.in_use && do_colors)
7643 gui_mch_new_tooltip_colors();
7644 }
7645# endif
7646#endif
7647 else
7648 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007649#ifdef FEAT_EVAL
7650 HL_TABLE()[idx].sg_scriptID = current_SID;
7651#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007652 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007653 }
7654 vim_free(key);
7655 vim_free(arg);
7656
7657 /* Only call highlight_changed() once, after sourcing a syntax file */
7658 need_highlight_changed = TRUE;
7659}
7660
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007661#if defined(EXITFREE) || defined(PROTO)
7662 void
7663free_highlight()
7664{
7665 int i;
7666
7667 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007668 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007669 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007670 vim_free(HL_TABLE()[i].sg_name);
7671 vim_free(HL_TABLE()[i].sg_name_u);
7672 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007673 ga_clear(&highlight_ga);
7674}
7675#endif
7676
Bram Moolenaar071d4272004-06-13 20:20:40 +00007677/*
7678 * Reset the cterm colors to what they were before Vim was started, if
7679 * possible. Otherwise reset them to zero.
7680 */
7681 void
7682restore_cterm_colors()
7683{
7684#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7685 /* Since t_me has been set, this probably means that the user
7686 * wants to use this as default colors. Need to reset default
7687 * background/foreground colors. */
7688 mch_set_normal_colors();
7689#else
7690 cterm_normal_fg_color = 0;
7691 cterm_normal_fg_bold = 0;
7692 cterm_normal_bg_color = 0;
7693#endif
7694}
7695
7696/*
7697 * Return TRUE if highlight group "idx" has any settings.
7698 * When "check_link" is TRUE also check for an existing link.
7699 */
7700 static int
7701hl_has_settings(idx, check_link)
7702 int idx;
7703 int check_link;
7704{
7705 return ( HL_TABLE()[idx].sg_term_attr != 0
7706 || HL_TABLE()[idx].sg_cterm_attr != 0
7707#ifdef FEAT_GUI
7708 || HL_TABLE()[idx].sg_gui_attr != 0
7709#endif
7710 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7711}
7712
7713/*
7714 * Clear highlighting for one group.
7715 */
7716 static void
7717highlight_clear(idx)
7718 int idx;
7719{
7720 HL_TABLE()[idx].sg_term = 0;
7721 vim_free(HL_TABLE()[idx].sg_start);
7722 HL_TABLE()[idx].sg_start = NULL;
7723 vim_free(HL_TABLE()[idx].sg_stop);
7724 HL_TABLE()[idx].sg_stop = NULL;
7725 HL_TABLE()[idx].sg_term_attr = 0;
7726 HL_TABLE()[idx].sg_cterm = 0;
7727 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7728 HL_TABLE()[idx].sg_cterm_fg = 0;
7729 HL_TABLE()[idx].sg_cterm_bg = 0;
7730 HL_TABLE()[idx].sg_cterm_attr = 0;
7731#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7732 HL_TABLE()[idx].sg_gui = 0;
7733 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7734 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7735 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7736 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7737 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7738 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007739 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7740 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7741 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007742 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7743 HL_TABLE()[idx].sg_font = NOFONT;
7744# ifdef FEAT_XFONTSET
7745 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7746 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7747# endif
7748 vim_free(HL_TABLE()[idx].sg_font_name);
7749 HL_TABLE()[idx].sg_font_name = NULL;
7750 HL_TABLE()[idx].sg_gui_attr = 0;
7751#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007752#ifdef FEAT_EVAL
7753 /* Clear the script ID only when there is no link, since that is not
7754 * cleared. */
7755 if (HL_TABLE()[idx].sg_link == 0)
7756 HL_TABLE()[idx].sg_scriptID = 0;
7757#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007758}
7759
7760#if defined(FEAT_GUI) || defined(PROTO)
7761/*
7762 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007763 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007764 * "Tooltip" colors.
7765 */
7766 void
7767set_normal_colors()
7768{
7769 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007770 &gui.norm_pixel, &gui.back_pixel,
7771 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007772 {
7773 gui_mch_new_colors();
7774 must_redraw = CLEAR;
7775 }
7776#ifdef FEAT_GUI_X11
7777 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007778 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7779 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780 {
7781# ifdef FEAT_MENU
7782 gui_mch_new_menu_colors();
7783# endif
7784 must_redraw = CLEAR;
7785 }
7786# ifdef FEAT_BEVAL
7787 if (set_group_colors((char_u *)"Tooltip",
7788 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7789 FALSE, FALSE, TRUE))
7790 {
7791# ifdef FEAT_TOOLBAR
7792 gui_mch_new_tooltip_colors();
7793# endif
7794 must_redraw = CLEAR;
7795 }
7796#endif
7797 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007798 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7799 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800 {
7801 gui_new_scrollbar_colors();
7802 must_redraw = CLEAR;
7803 }
7804#endif
7805}
7806
7807/*
7808 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7809 */
7810 static int
7811set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7812 char_u *name;
7813 guicolor_T *fgp;
7814 guicolor_T *bgp;
7815 int do_menu;
7816 int use_norm;
7817 int do_tooltip;
7818{
7819 int idx;
7820
7821 idx = syn_name2id(name) - 1;
7822 if (idx >= 0)
7823 {
7824 gui_do_one_color(idx, do_menu, do_tooltip);
7825
7826 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7827 *fgp = HL_TABLE()[idx].sg_gui_fg;
7828 else if (use_norm)
7829 *fgp = gui.def_norm_pixel;
7830 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7831 *bgp = HL_TABLE()[idx].sg_gui_bg;
7832 else if (use_norm)
7833 *bgp = gui.def_back_pixel;
7834 return TRUE;
7835 }
7836 return FALSE;
7837}
7838
7839/*
7840 * Get the font of the "Normal" group.
7841 * Returns "" when it's not found or not set.
7842 */
7843 char_u *
7844hl_get_font_name()
7845{
7846 int id;
7847 char_u *s;
7848
7849 id = syn_name2id((char_u *)"Normal");
7850 if (id > 0)
7851 {
7852 s = HL_TABLE()[id - 1].sg_font_name;
7853 if (s != NULL)
7854 return s;
7855 }
7856 return (char_u *)"";
7857}
7858
7859/*
7860 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7861 * actually chosen to be used.
7862 */
7863 void
7864hl_set_font_name(font_name)
7865 char_u *font_name;
7866{
7867 int id;
7868
7869 id = syn_name2id((char_u *)"Normal");
7870 if (id > 0)
7871 {
7872 vim_free(HL_TABLE()[id - 1].sg_font_name);
7873 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7874 }
7875}
7876
7877/*
7878 * Set background color for "Normal" group. Called by gui_set_bg_color()
7879 * when the color is known.
7880 */
7881 void
7882hl_set_bg_color_name(name)
7883 char_u *name; /* must have been allocated */
7884{
7885 int id;
7886
7887 if (name != NULL)
7888 {
7889 id = syn_name2id((char_u *)"Normal");
7890 if (id > 0)
7891 {
7892 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7893 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7894 }
7895 }
7896}
7897
7898/*
7899 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7900 * when the color is known.
7901 */
7902 void
7903hl_set_fg_color_name(name)
7904 char_u *name; /* must have been allocated */
7905{
7906 int id;
7907
7908 if (name != NULL)
7909 {
7910 id = syn_name2id((char_u *)"Normal");
7911 if (id > 0)
7912 {
7913 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7914 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7915 }
7916 }
7917}
7918
7919/*
7920 * Return the handle for a color name.
7921 * Returns INVALCOLOR when failed.
7922 */
7923 static guicolor_T
7924color_name2handle(name)
7925 char_u *name;
7926{
7927 if (STRCMP(name, "NONE") == 0)
7928 return INVALCOLOR;
7929
7930 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7931 return gui.norm_pixel;
7932 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7933 return gui.back_pixel;
7934
7935 return gui_get_color(name);
7936}
7937
7938/*
7939 * Return the handle for a font name.
7940 * Returns NOFONT when failed.
7941 */
7942 static GuiFont
7943font_name2handle(name)
7944 char_u *name;
7945{
7946 if (STRCMP(name, "NONE") == 0)
7947 return NOFONT;
7948
7949 return gui_mch_get_font(name, TRUE);
7950}
7951
7952# ifdef FEAT_XFONTSET
7953/*
7954 * Return the handle for a fontset name.
7955 * Returns NOFONTSET when failed.
7956 */
7957 static GuiFontset
7958fontset_name2handle(name, fixed_width)
7959 char_u *name;
7960 int fixed_width;
7961{
7962 if (STRCMP(name, "NONE") == 0)
7963 return NOFONTSET;
7964
7965 return gui_mch_get_fontset(name, TRUE, fixed_width);
7966}
7967# endif
7968
7969/*
7970 * Get the font or fontset for one highlight group.
7971 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007972 static void
7973hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7974 int idx;
7975 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00007976 int do_normal; /* set normal font */
7977 int do_menu UNUSED; /* set menu font */
7978 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007979{
7980# ifdef FEAT_XFONTSET
7981 /* If 'guifontset' is not empty, first try using the name as a
7982 * fontset. If that doesn't work, use it as a font name. */
7983 if (*p_guifontset != NUL
7984# ifdef FONTSET_ALWAYS
7985 || do_menu
7986# endif
7987# ifdef FEAT_BEVAL_TIP
7988 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7989 || do_tooltip
7990# endif
7991 )
7992 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7993# ifdef FONTSET_ALWAYS
7994 || do_menu
7995# endif
7996# ifdef FEAT_BEVAL_TIP
7997 || do_tooltip
7998# endif
7999 );
8000 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8001 {
8002 /* If it worked and it's the Normal group, use it as the
8003 * normal fontset. Same for the Menu group. */
8004 if (do_normal)
8005 gui_init_font(arg, TRUE);
8006# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8007 if (do_menu)
8008 {
8009# ifdef FONTSET_ALWAYS
8010 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8011# else
8012 /* YIKES! This is a bug waiting to crash the program */
8013 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8014# endif
8015 gui_mch_new_menu_font();
8016 }
8017# ifdef FEAT_BEVAL
8018 if (do_tooltip)
8019 {
8020 /* The Athena widget set cannot currently handle switching between
8021 * displaying a single font and a fontset.
8022 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008023 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008024 * XFontStruct is used.
8025 */
8026 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8027 gui_mch_new_tooltip_font();
8028 }
8029# endif
8030# endif
8031 }
8032 else
8033# endif
8034 {
8035 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8036 /* If it worked and it's the Normal group, use it as the
8037 * normal font. Same for the Menu group. */
8038 if (HL_TABLE()[idx].sg_font != NOFONT)
8039 {
8040 if (do_normal)
8041 gui_init_font(arg, FALSE);
8042#ifndef FONTSET_ALWAYS
8043# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8044 if (do_menu)
8045 {
8046 gui.menu_font = HL_TABLE()[idx].sg_font;
8047 gui_mch_new_menu_font();
8048 }
8049# endif
8050#endif
8051 }
8052 }
8053}
8054
8055#endif /* FEAT_GUI */
8056
8057/*
8058 * Table with the specifications for an attribute number.
8059 * Note that this table is used by ALL buffers. This is required because the
8060 * GUI can redraw at any time for any buffer.
8061 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008062static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008063
8064#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8065
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008066static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008067
8068#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8069
8070#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008071static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008072
8073#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8074#endif
8075
8076/*
8077 * Return the attr number for a set of colors and font.
8078 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8079 * if the combination is new.
8080 * Return 0 for error (no more room).
8081 */
8082 static int
8083get_attr_entry(table, aep)
8084 garray_T *table;
8085 attrentry_T *aep;
8086{
8087 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008088 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008089 static int recursive = FALSE;
8090
8091 /*
8092 * Init the table, in case it wasn't done yet.
8093 */
8094 table->ga_itemsize = sizeof(attrentry_T);
8095 table->ga_growsize = 7;
8096
8097 /*
8098 * Try to find an entry with the same specifications.
8099 */
8100 for (i = 0; i < table->ga_len; ++i)
8101 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008102 taep = &(((attrentry_T *)table->ga_data)[i]);
8103 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008104 && (
8105#ifdef FEAT_GUI
8106 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008107 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8108 && aep->ae_u.gui.bg_color
8109 == taep->ae_u.gui.bg_color
8110 && aep->ae_u.gui.sp_color
8111 == taep->ae_u.gui.sp_color
8112 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008114 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008115# endif
8116 ))
8117 ||
8118#endif
8119 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008120 && (aep->ae_u.term.start == NULL)
8121 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008122 && (aep->ae_u.term.start == NULL
8123 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008124 taep->ae_u.term.start) == 0)
8125 && (aep->ae_u.term.stop == NULL)
8126 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008127 && (aep->ae_u.term.stop == NULL
8128 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008129 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008130 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008131 && aep->ae_u.cterm.fg_color
8132 == taep->ae_u.cterm.fg_color
8133 && aep->ae_u.cterm.bg_color
8134 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008135 ))
8136
8137 return i + ATTR_OFF;
8138 }
8139
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008140 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 {
8142 /*
8143 * Running out of attribute entries! remove all attributes, and
8144 * compute new ones for all groups.
8145 * When called recursively, we are really out of numbers.
8146 */
8147 if (recursive)
8148 {
8149 EMSG(_("E424: Too many different highlighting attributes in use"));
8150 return 0;
8151 }
8152 recursive = TRUE;
8153
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008154 clear_hl_tables();
8155
Bram Moolenaar071d4272004-06-13 20:20:40 +00008156 must_redraw = CLEAR;
8157
8158 for (i = 0; i < highlight_ga.ga_len; ++i)
8159 set_hl_attr(i);
8160
8161 recursive = FALSE;
8162 }
8163
8164 /*
8165 * This is a new combination of colors and font, add an entry.
8166 */
8167 if (ga_grow(table, 1) == FAIL)
8168 return 0;
8169
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008170 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8171 vim_memset(taep, 0, sizeof(attrentry_T));
8172 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008173#ifdef FEAT_GUI
8174 if (table == &gui_attr_table)
8175 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008176 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8177 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8178 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8179 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008180# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008181 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182# endif
8183 }
8184#endif
8185 if (table == &term_attr_table)
8186 {
8187 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008188 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008189 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008190 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008191 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008192 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008194 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008195 }
8196 else if (table == &cterm_attr_table)
8197 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008198 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8199 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200 }
8201 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008202 return (table->ga_len - 1 + ATTR_OFF);
8203}
8204
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008205/*
8206 * Clear all highlight tables.
8207 */
8208 void
8209clear_hl_tables()
8210{
8211 int i;
8212 attrentry_T *taep;
8213
8214#ifdef FEAT_GUI
8215 ga_clear(&gui_attr_table);
8216#endif
8217 for (i = 0; i < term_attr_table.ga_len; ++i)
8218 {
8219 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8220 vim_free(taep->ae_u.term.start);
8221 vim_free(taep->ae_u.term.stop);
8222 }
8223 ga_clear(&term_attr_table);
8224 ga_clear(&cterm_attr_table);
8225}
8226
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008227#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008228/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008229 * Combine special attributes (e.g., for spelling) with other attributes
8230 * (e.g., for syntax highlighting).
8231 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008232 * This creates a new group when required.
8233 * Since we expect there to be few spelling mistakes we don't cache the
8234 * result.
8235 * Return the resulting attributes.
8236 */
8237 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008238hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008239 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008240 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008241{
8242 attrentry_T *char_aep = NULL;
8243 attrentry_T *spell_aep;
8244 attrentry_T new_en;
8245
8246 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008247 return prim_attr;
8248 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8249 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008250#ifdef FEAT_GUI
8251 if (gui.in_use)
8252 {
8253 if (char_attr > HL_ALL)
8254 char_aep = syn_gui_attr2entry(char_attr);
8255 if (char_aep != NULL)
8256 new_en = *char_aep;
8257 else
8258 {
8259 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008260 new_en.ae_u.gui.fg_color = INVALCOLOR;
8261 new_en.ae_u.gui.bg_color = INVALCOLOR;
8262 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008263 if (char_attr <= HL_ALL)
8264 new_en.ae_attr = char_attr;
8265 }
8266
Bram Moolenaar30abd282005-06-22 22:35:10 +00008267 if (prim_attr <= HL_ALL)
8268 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008269 else
8270 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008271 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008272 if (spell_aep != NULL)
8273 {
8274 new_en.ae_attr |= spell_aep->ae_attr;
8275 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8276 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8277 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8278 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8279 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8280 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8281 if (spell_aep->ae_u.gui.font != NOFONT)
8282 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8283# ifdef FEAT_XFONTSET
8284 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8285 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8286# endif
8287 }
8288 }
8289 return get_attr_entry(&gui_attr_table, &new_en);
8290 }
8291#endif
8292
8293 if (t_colors > 1)
8294 {
8295 if (char_attr > HL_ALL)
8296 char_aep = syn_cterm_attr2entry(char_attr);
8297 if (char_aep != NULL)
8298 new_en = *char_aep;
8299 else
8300 {
8301 vim_memset(&new_en, 0, sizeof(new_en));
8302 if (char_attr <= HL_ALL)
8303 new_en.ae_attr = char_attr;
8304 }
8305
Bram Moolenaar30abd282005-06-22 22:35:10 +00008306 if (prim_attr <= HL_ALL)
8307 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008308 else
8309 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008310 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008311 if (spell_aep != NULL)
8312 {
8313 new_en.ae_attr |= spell_aep->ae_attr;
8314 if (spell_aep->ae_u.cterm.fg_color > 0)
8315 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8316 if (spell_aep->ae_u.cterm.bg_color > 0)
8317 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8318 }
8319 }
8320 return get_attr_entry(&cterm_attr_table, &new_en);
8321 }
8322
8323 if (char_attr > HL_ALL)
8324 char_aep = syn_term_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 Moolenaar3b181812005-12-17 22:10:02 +00008338 spell_aep = syn_term_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.term.start != NULL)
8343 {
8344 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8345 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8346 }
8347 }
8348 }
8349 return get_attr_entry(&term_attr_table, &new_en);
8350}
8351#endif
8352
Bram Moolenaar071d4272004-06-13 20:20:40 +00008353#ifdef FEAT_GUI
8354
8355 attrentry_T *
8356syn_gui_attr2entry(attr)
8357 int attr;
8358{
8359 attr -= ATTR_OFF;
8360 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8361 return NULL;
8362 return &(GUI_ATTR_ENTRY(attr));
8363}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364#endif /* FEAT_GUI */
8365
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008366/*
8367 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8368 * Only to be used when "attr" > HL_ALL.
8369 */
8370 int
8371syn_attr2attr(attr)
8372 int attr;
8373{
8374 attrentry_T *aep;
8375
8376#ifdef FEAT_GUI
8377 if (gui.in_use)
8378 aep = syn_gui_attr2entry(attr);
8379 else
8380#endif
8381 if (t_colors > 1)
8382 aep = syn_cterm_attr2entry(attr);
8383 else
8384 aep = syn_term_attr2entry(attr);
8385
8386 if (aep == NULL) /* highlighting not set */
8387 return 0;
8388 return aep->ae_attr;
8389}
8390
8391
Bram Moolenaar071d4272004-06-13 20:20:40 +00008392 attrentry_T *
8393syn_term_attr2entry(attr)
8394 int attr;
8395{
8396 attr -= ATTR_OFF;
8397 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8398 return NULL;
8399 return &(TERM_ATTR_ENTRY(attr));
8400}
8401
8402 attrentry_T *
8403syn_cterm_attr2entry(attr)
8404 int attr;
8405{
8406 attr -= ATTR_OFF;
8407 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8408 return NULL;
8409 return &(CTERM_ATTR_ENTRY(attr));
8410}
8411
8412#define LIST_ATTR 1
8413#define LIST_STRING 2
8414#define LIST_INT 3
8415
8416 static void
8417highlight_list_one(id)
8418 int id;
8419{
8420 struct hl_group *sgp;
8421 int didh = FALSE;
8422
8423 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8424
8425 didh = highlight_list_arg(id, didh, LIST_ATTR,
8426 sgp->sg_term, NULL, "term");
8427 didh = highlight_list_arg(id, didh, LIST_STRING,
8428 0, sgp->sg_start, "start");
8429 didh = highlight_list_arg(id, didh, LIST_STRING,
8430 0, sgp->sg_stop, "stop");
8431
8432 didh = highlight_list_arg(id, didh, LIST_ATTR,
8433 sgp->sg_cterm, NULL, "cterm");
8434 didh = highlight_list_arg(id, didh, LIST_INT,
8435 sgp->sg_cterm_fg, NULL, "ctermfg");
8436 didh = highlight_list_arg(id, didh, LIST_INT,
8437 sgp->sg_cterm_bg, NULL, "ctermbg");
8438
8439#ifdef FEAT_GUI
8440 didh = highlight_list_arg(id, didh, LIST_ATTR,
8441 sgp->sg_gui, NULL, "gui");
8442 didh = highlight_list_arg(id, didh, LIST_STRING,
8443 0, sgp->sg_gui_fg_name, "guifg");
8444 didh = highlight_list_arg(id, didh, LIST_STRING,
8445 0, sgp->sg_gui_bg_name, "guibg");
8446 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008447 0, sgp->sg_gui_sp_name, "guisp");
8448 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008449 0, sgp->sg_font_name, "font");
8450#endif
8451
Bram Moolenaar661b1822005-07-28 22:36:45 +00008452 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008453 {
8454 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008455 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008456 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8457 msg_putchar(' ');
8458 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8459 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008460
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008461 if (!didh)
8462 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008463#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008464 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008465 last_set_msg(sgp->sg_scriptID);
8466#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008467}
8468
8469 static int
8470highlight_list_arg(id, didh, type, iarg, sarg, name)
8471 int id;
8472 int didh;
8473 int type;
8474 int iarg;
8475 char_u *sarg;
8476 char *name;
8477{
8478 char_u buf[100];
8479 char_u *ts;
8480 int i;
8481
Bram Moolenaar661b1822005-07-28 22:36:45 +00008482 if (got_int)
8483 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008484 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8485 {
8486 ts = buf;
8487 if (type == LIST_INT)
8488 sprintf((char *)buf, "%d", iarg - 1);
8489 else if (type == LIST_STRING)
8490 ts = sarg;
8491 else /* type == LIST_ATTR */
8492 {
8493 buf[0] = NUL;
8494 for (i = 0; hl_attr_table[i] != 0; ++i)
8495 {
8496 if (iarg & hl_attr_table[i])
8497 {
8498 if (buf[0] != NUL)
8499 STRCAT(buf, ",");
8500 STRCAT(buf, hl_name_table[i]);
8501 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8502 }
8503 }
8504 }
8505
8506 (void)syn_list_header(didh,
8507 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8508 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008509 if (!got_int)
8510 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008511 if (*name != NUL)
8512 {
8513 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8514 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8515 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008516 msg_outtrans(ts);
8517 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 }
8519 return didh;
8520}
8521
8522#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8523/*
8524 * Return "1" if highlight group "id" has attribute "flag".
8525 * Return NULL otherwise.
8526 */
8527 char_u *
8528highlight_has_attr(id, flag, modec)
8529 int id;
8530 int flag;
8531 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8532{
8533 int attr;
8534
8535 if (id <= 0 || id > highlight_ga.ga_len)
8536 return NULL;
8537
8538#ifdef FEAT_GUI
8539 if (modec == 'g')
8540 attr = HL_TABLE()[id - 1].sg_gui;
8541 else
8542#endif
8543 if (modec == 'c')
8544 attr = HL_TABLE()[id - 1].sg_cterm;
8545 else
8546 attr = HL_TABLE()[id - 1].sg_term;
8547
8548 if (attr & flag)
8549 return (char_u *)"1";
8550 return NULL;
8551}
8552#endif
8553
8554#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8555/*
8556 * Return color name of highlight group "id".
8557 */
8558 char_u *
8559highlight_color(id, what, modec)
8560 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008561 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008562 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8563{
8564 static char_u name[20];
8565 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008566 int fg = FALSE;
8567# ifdef FEAT_GUI
8568 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008569 int font = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008570# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008571
8572 if (id <= 0 || id > highlight_ga.ga_len)
8573 return NULL;
8574
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008575 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008576 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008577# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008578 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008579 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008580 font = TRUE;
8581 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008582 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008583 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8584 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585 if (modec == 'g')
8586 {
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008587 /* return font name */
8588 if (font)
8589 return HL_TABLE()[id - 1].sg_font_name;
8590
Bram Moolenaar071d4272004-06-13 20:20:40 +00008591 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008592 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008593 {
8594 guicolor_T color;
8595 long_u rgb;
8596 static char_u buf[10];
8597
8598 if (fg)
8599 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008600 else if (sp)
8601 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008602 else
8603 color = HL_TABLE()[id - 1].sg_gui_bg;
8604 if (color == INVALCOLOR)
8605 return NULL;
8606 rgb = gui_mch_get_rgb(color);
8607 sprintf((char *)buf, "#%02x%02x%02x",
8608 (unsigned)(rgb >> 16),
8609 (unsigned)(rgb >> 8) & 255,
8610 (unsigned)rgb & 255);
8611 return buf;
8612 }
8613 if (fg)
8614 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008615 if (sp)
8616 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008617 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8618 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008619 if (font || sp)
8620 return NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008621# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008622 if (modec == 'c')
8623 {
8624 if (fg)
8625 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8626 else
8627 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8628 sprintf((char *)name, "%d", n);
8629 return name;
8630 }
8631 /* term doesn't have color */
8632 return NULL;
8633}
8634#endif
8635
8636#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8637 || defined(PROTO)
8638/*
8639 * Return color name of highlight group "id" as RGB value.
8640 */
8641 long_u
8642highlight_gui_color_rgb(id, fg)
8643 int id;
8644 int fg; /* TRUE = fg, FALSE = bg */
8645{
8646 guicolor_T color;
8647
8648 if (id <= 0 || id > highlight_ga.ga_len)
8649 return 0L;
8650
8651 if (fg)
8652 color = HL_TABLE()[id - 1].sg_gui_fg;
8653 else
8654 color = HL_TABLE()[id - 1].sg_gui_bg;
8655
8656 if (color == INVALCOLOR)
8657 return 0L;
8658
8659 return gui_mch_get_rgb(color);
8660}
8661#endif
8662
8663/*
8664 * Output the syntax list header.
8665 * Return TRUE when started a new line.
8666 */
8667 static int
8668syn_list_header(did_header, outlen, id)
8669 int did_header; /* did header already */
8670 int outlen; /* length of string that comes */
8671 int id; /* highlight group id */
8672{
8673 int endcol = 19;
8674 int newline = TRUE;
8675
8676 if (!did_header)
8677 {
8678 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008679 if (got_int)
8680 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8682 endcol = 15;
8683 }
8684 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008685 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008686 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008687 if (got_int)
8688 return TRUE;
8689 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008690 else
8691 {
8692 if (msg_col >= endcol) /* wrap around is like starting a new line */
8693 newline = FALSE;
8694 }
8695
8696 if (msg_col >= endcol) /* output at least one space */
8697 endcol = msg_col + 1;
8698 if (Columns <= endcol) /* avoid hang for tiny window */
8699 endcol = Columns - 1;
8700
8701 msg_advance(endcol);
8702
8703 /* Show "xxx" with the attributes. */
8704 if (!did_header)
8705 {
8706 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8707 msg_putchar(' ');
8708 }
8709
8710 return newline;
8711}
8712
8713/*
8714 * Set the attribute numbers for a highlight group.
8715 * Called after one of the attributes has changed.
8716 */
8717 static void
8718set_hl_attr(idx)
8719 int idx; /* index in array */
8720{
8721 attrentry_T at_en;
8722 struct hl_group *sgp = HL_TABLE() + idx;
8723
8724 /* The "Normal" group doesn't need an attribute number */
8725 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8726 return;
8727
8728#ifdef FEAT_GUI
8729 /*
8730 * For the GUI mode: If there are other than "normal" highlighting
8731 * attributes, need to allocate an attr number.
8732 */
8733 if (sgp->sg_gui_fg == INVALCOLOR
8734 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008735 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008736 && sgp->sg_font == NOFONT
8737# ifdef FEAT_XFONTSET
8738 && sgp->sg_fontset == NOFONTSET
8739# endif
8740 )
8741 {
8742 sgp->sg_gui_attr = sgp->sg_gui;
8743 }
8744 else
8745 {
8746 at_en.ae_attr = sgp->sg_gui;
8747 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8748 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008749 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008750 at_en.ae_u.gui.font = sgp->sg_font;
8751# ifdef FEAT_XFONTSET
8752 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8753# endif
8754 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8755 }
8756#endif
8757 /*
8758 * For the term mode: If there are other than "normal" highlighting
8759 * attributes, need to allocate an attr number.
8760 */
8761 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8762 sgp->sg_term_attr = sgp->sg_term;
8763 else
8764 {
8765 at_en.ae_attr = sgp->sg_term;
8766 at_en.ae_u.term.start = sgp->sg_start;
8767 at_en.ae_u.term.stop = sgp->sg_stop;
8768 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8769 }
8770
8771 /*
8772 * For the color term mode: If there are other than "normal"
8773 * highlighting attributes, need to allocate an attr number.
8774 */
8775 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8776 sgp->sg_cterm_attr = sgp->sg_cterm;
8777 else
8778 {
8779 at_en.ae_attr = sgp->sg_cterm;
8780 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8781 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8782 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8783 }
8784}
8785
8786/*
8787 * Lookup a highlight group name and return it's ID.
8788 * If it is not found, 0 is returned.
8789 */
8790 int
8791syn_name2id(name)
8792 char_u *name;
8793{
8794 int i;
8795 char_u name_u[200];
8796
8797 /* Avoid using stricmp() too much, it's slow on some systems */
8798 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8799 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008800 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008801 vim_strup(name_u);
8802 for (i = highlight_ga.ga_len; --i >= 0; )
8803 if (HL_TABLE()[i].sg_name_u != NULL
8804 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8805 break;
8806 return i + 1;
8807}
8808
8809#if defined(FEAT_EVAL) || defined(PROTO)
8810/*
8811 * Return TRUE if highlight group "name" exists.
8812 */
8813 int
8814highlight_exists(name)
8815 char_u *name;
8816{
8817 return (syn_name2id(name) > 0);
8818}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008819
8820# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8821/*
8822 * Return the name of highlight group "id".
8823 * When not a valid ID return an empty string.
8824 */
8825 char_u *
8826syn_id2name(id)
8827 int id;
8828{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008829 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008830 return (char_u *)"";
8831 return HL_TABLE()[id - 1].sg_name;
8832}
8833# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008834#endif
8835
8836/*
8837 * Like syn_name2id(), but take a pointer + length argument.
8838 */
8839 int
8840syn_namen2id(linep, len)
8841 char_u *linep;
8842 int len;
8843{
8844 char_u *name;
8845 int id = 0;
8846
8847 name = vim_strnsave(linep, len);
8848 if (name != NULL)
8849 {
8850 id = syn_name2id(name);
8851 vim_free(name);
8852 }
8853 return id;
8854}
8855
8856/*
8857 * Find highlight group name in the table and return it's ID.
8858 * The argument is a pointer to the name and the length of the name.
8859 * If it doesn't exist yet, a new entry is created.
8860 * Return 0 for failure.
8861 */
8862 int
8863syn_check_group(pp, len)
8864 char_u *pp;
8865 int len;
8866{
8867 int id;
8868 char_u *name;
8869
8870 name = vim_strnsave(pp, len);
8871 if (name == NULL)
8872 return 0;
8873
8874 id = syn_name2id(name);
8875 if (id == 0) /* doesn't exist yet */
8876 id = syn_add_group(name);
8877 else
8878 vim_free(name);
8879 return id;
8880}
8881
8882/*
8883 * Add new highlight group and return it's ID.
8884 * "name" must be an allocated string, it will be consumed.
8885 * Return 0 for failure.
8886 */
8887 static int
8888syn_add_group(name)
8889 char_u *name;
8890{
8891 char_u *p;
8892
8893 /* Check that the name is ASCII letters, digits and underscore. */
8894 for (p = name; *p != NUL; ++p)
8895 {
8896 if (!vim_isprintc(*p))
8897 {
8898 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008899 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008900 return 0;
8901 }
8902 else if (!ASCII_ISALNUM(*p) && *p != '_')
8903 {
8904 /* This is an error, but since there previously was no check only
8905 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008906 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008907 MSG(_("W18: Invalid character in group name"));
8908 break;
8909 }
8910 }
8911
8912 /*
8913 * First call for this growarray: init growing array.
8914 */
8915 if (highlight_ga.ga_data == NULL)
8916 {
8917 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8918 highlight_ga.ga_growsize = 10;
8919 }
8920
8921 /*
8922 * Make room for at least one other syntax_highlight entry.
8923 */
8924 if (ga_grow(&highlight_ga, 1) == FAIL)
8925 {
8926 vim_free(name);
8927 return 0;
8928 }
8929
8930 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8931 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8932 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8933#ifdef FEAT_GUI
8934 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8935 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008936 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008937#endif
8938 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008939
8940 return highlight_ga.ga_len; /* ID is index plus one */
8941}
8942
8943/*
8944 * When, just after calling syn_add_group(), an error is discovered, this
8945 * function deletes the new name.
8946 */
8947 static void
8948syn_unadd_group()
8949{
8950 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008951 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8952 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8953}
8954
8955/*
8956 * Translate a group ID to highlight attributes.
8957 */
8958 int
8959syn_id2attr(hl_id)
8960 int hl_id;
8961{
8962 int attr;
8963 struct hl_group *sgp;
8964
8965 hl_id = syn_get_final_id(hl_id);
8966 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8967
8968#ifdef FEAT_GUI
8969 /*
8970 * Only use GUI attr when the GUI is being used.
8971 */
8972 if (gui.in_use)
8973 attr = sgp->sg_gui_attr;
8974 else
8975#endif
8976 if (t_colors > 1)
8977 attr = sgp->sg_cterm_attr;
8978 else
8979 attr = sgp->sg_term_attr;
8980
8981 return attr;
8982}
8983
8984#ifdef FEAT_GUI
8985/*
8986 * Get the GUI colors and attributes for a group ID.
8987 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8988 */
8989 int
8990syn_id2colors(hl_id, fgp, bgp)
8991 int hl_id;
8992 guicolor_T *fgp;
8993 guicolor_T *bgp;
8994{
8995 struct hl_group *sgp;
8996
8997 hl_id = syn_get_final_id(hl_id);
8998 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8999
9000 *fgp = sgp->sg_gui_fg;
9001 *bgp = sgp->sg_gui_bg;
9002 return sgp->sg_gui;
9003}
9004#endif
9005
9006/*
9007 * Translate a group ID to the final group ID (following links).
9008 */
9009 int
9010syn_get_final_id(hl_id)
9011 int hl_id;
9012{
9013 int count;
9014 struct hl_group *sgp;
9015
9016 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9017 return 0; /* Can be called from eval!! */
9018
9019 /*
9020 * Follow links until there is no more.
9021 * Look out for loops! Break after 100 links.
9022 */
9023 for (count = 100; --count >= 0; )
9024 {
9025 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9026 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9027 break;
9028 hl_id = sgp->sg_link;
9029 }
9030
9031 return hl_id;
9032}
9033
9034#ifdef FEAT_GUI
9035/*
9036 * Call this function just after the GUI has started.
9037 * It finds the font and color handles for the highlighting groups.
9038 */
9039 void
9040highlight_gui_started()
9041{
9042 int idx;
9043
9044 /* First get the colors from the "Normal" and "Menu" group, if set */
9045 set_normal_colors();
9046
9047 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9048 gui_do_one_color(idx, FALSE, FALSE);
9049
9050 highlight_changed();
9051}
9052
9053 static void
9054gui_do_one_color(idx, do_menu, do_tooltip)
9055 int idx;
9056 int do_menu; /* TRUE: might set the menu font */
9057 int do_tooltip; /* TRUE: might set the tooltip font */
9058{
9059 int didit = FALSE;
9060
9061 if (HL_TABLE()[idx].sg_font_name != NULL)
9062 {
9063 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9064 do_tooltip);
9065 didit = TRUE;
9066 }
9067 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9068 {
9069 HL_TABLE()[idx].sg_gui_fg =
9070 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9071 didit = TRUE;
9072 }
9073 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9074 {
9075 HL_TABLE()[idx].sg_gui_bg =
9076 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9077 didit = TRUE;
9078 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009079 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9080 {
9081 HL_TABLE()[idx].sg_gui_sp =
9082 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9083 didit = TRUE;
9084 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009085 if (didit) /* need to get a new attr number */
9086 set_hl_attr(idx);
9087}
9088
9089#endif
9090
9091/*
9092 * Translate the 'highlight' option into attributes in highlight_attr[] and
9093 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9094 * corresponding highlights to use on top of HLF_SNC is computed.
9095 * Called only when the 'highlight' option has been changed and upon first
9096 * screen redraw after any :highlight command.
9097 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9098 */
9099 int
9100highlight_changed()
9101{
9102 int hlf;
9103 int i;
9104 char_u *p;
9105 int attr;
9106 char_u *end;
9107 int id;
9108#ifdef USER_HIGHLIGHT
9109 char_u userhl[10];
9110# ifdef FEAT_STL_OPT
9111 int id_SNC = -1;
9112 int id_S = -1;
9113 int hlcnt;
9114# endif
9115#endif
9116 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9117
9118 need_highlight_changed = FALSE;
9119
9120 /*
9121 * Clear all attributes.
9122 */
9123 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9124 highlight_attr[hlf] = 0;
9125
9126 /*
9127 * First set all attributes to their default value.
9128 * Then use the attributes from the 'highlight' option.
9129 */
9130 for (i = 0; i < 2; ++i)
9131 {
9132 if (i)
9133 p = p_hl;
9134 else
9135 p = get_highlight_default();
9136 if (p == NULL) /* just in case */
9137 continue;
9138
9139 while (*p)
9140 {
9141 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9142 if (hl_flags[hlf] == *p)
9143 break;
9144 ++p;
9145 if (hlf == (int)HLF_COUNT || *p == NUL)
9146 return FAIL;
9147
9148 /*
9149 * Allow several hl_flags to be combined, like "bu" for
9150 * bold-underlined.
9151 */
9152 attr = 0;
9153 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9154 {
9155 if (vim_iswhite(*p)) /* ignore white space */
9156 continue;
9157
9158 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9159 return FAIL;
9160
9161 switch (*p)
9162 {
9163 case 'b': attr |= HL_BOLD;
9164 break;
9165 case 'i': attr |= HL_ITALIC;
9166 break;
9167 case '-':
9168 case 'n': /* no highlighting */
9169 break;
9170 case 'r': attr |= HL_INVERSE;
9171 break;
9172 case 's': attr |= HL_STANDOUT;
9173 break;
9174 case 'u': attr |= HL_UNDERLINE;
9175 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009176 case 'c': attr |= HL_UNDERCURL;
9177 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009178 case ':': ++p; /* highlight group name */
9179 if (attr || *p == NUL) /* no combinations */
9180 return FAIL;
9181 end = vim_strchr(p, ',');
9182 if (end == NULL)
9183 end = p + STRLEN(p);
9184 id = syn_check_group(p, (int)(end - p));
9185 if (id == 0)
9186 return FAIL;
9187 attr = syn_id2attr(id);
9188 p = end - 1;
9189#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9190 if (hlf == (int)HLF_SNC)
9191 id_SNC = syn_get_final_id(id);
9192 else if (hlf == (int)HLF_S)
9193 id_S = syn_get_final_id(id);
9194#endif
9195 break;
9196 default: return FAIL;
9197 }
9198 }
9199 highlight_attr[hlf] = attr;
9200
9201 p = skip_to_option_part(p); /* skip comma and spaces */
9202 }
9203 }
9204
9205#ifdef USER_HIGHLIGHT
9206 /* Setup the user highlights
9207 *
9208 * Temporarily utilize 10 more hl entries. Have to be in there
9209 * simultaneously in case of table overflows in get_attr_entry()
9210 */
9211# ifdef FEAT_STL_OPT
9212 if (ga_grow(&highlight_ga, 10) == FAIL)
9213 return FAIL;
9214 hlcnt = highlight_ga.ga_len;
9215 if (id_S == 0)
9216 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009217 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009218 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9219 id_S = hlcnt + 10;
9220 }
9221# endif
9222 for (i = 0; i < 9; i++)
9223 {
9224 sprintf((char *)userhl, "User%d", i + 1);
9225 id = syn_name2id(userhl);
9226 if (id == 0)
9227 {
9228 highlight_user[i] = 0;
9229# ifdef FEAT_STL_OPT
9230 highlight_stlnc[i] = 0;
9231# endif
9232 }
9233 else
9234 {
9235# ifdef FEAT_STL_OPT
9236 struct hl_group *hlt = HL_TABLE();
9237# endif
9238
9239 highlight_user[i] = syn_id2attr(id);
9240# ifdef FEAT_STL_OPT
9241 if (id_SNC == 0)
9242 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009243 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009244 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9245 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
9246# ifdef FEAT_GUI
9247 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9248# endif
9249 }
9250 else
9251 mch_memmove(&hlt[hlcnt + i],
9252 &hlt[id_SNC - 1],
9253 sizeof(struct hl_group));
9254 hlt[hlcnt + i].sg_link = 0;
9255
9256 /* Apply difference between UserX and HLF_S to HLF_SNC */
9257 hlt[hlcnt + i].sg_term ^=
9258 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9259 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9260 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9261 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9262 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9263 hlt[hlcnt + i].sg_cterm ^=
9264 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9265 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9266 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9267 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9268 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9269# ifdef FEAT_GUI
9270 hlt[hlcnt + i].sg_gui ^=
9271 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9272 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9273 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9274 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9275 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009276 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9277 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009278 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9279 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9280# ifdef FEAT_XFONTSET
9281 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9282 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9283# endif
9284# endif
9285 highlight_ga.ga_len = hlcnt + i + 1;
9286 set_hl_attr(hlcnt + i); /* At long last we can apply */
9287 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9288# endif
9289 }
9290 }
9291# ifdef FEAT_STL_OPT
9292 highlight_ga.ga_len = hlcnt;
9293# endif
9294
9295#endif /* USER_HIGHLIGHT */
9296
9297 return OK;
9298}
9299
Bram Moolenaar4f688582007-07-24 12:34:30 +00009300#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009301
9302static void highlight_list __ARGS((void));
9303static void highlight_list_two __ARGS((int cnt, int attr));
9304
9305/*
9306 * Handle command line completion for :highlight command.
9307 */
9308 void
9309set_context_in_highlight_cmd(xp, arg)
9310 expand_T *xp;
9311 char_u *arg;
9312{
9313 char_u *p;
9314
9315 /* Default: expand group names */
9316 xp->xp_context = EXPAND_HIGHLIGHT;
9317 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009318 include_link = 2;
9319 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009320
9321 /* (part of) subcommand already typed */
9322 if (*arg != NUL)
9323 {
9324 p = skiptowhite(arg);
9325 if (*p != NUL) /* past "default" or group name */
9326 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009327 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009328 if (STRNCMP("default", arg, p - arg) == 0)
9329 {
9330 arg = skipwhite(p);
9331 xp->xp_pattern = arg;
9332 p = skiptowhite(arg);
9333 }
9334 if (*p != NUL) /* past group name */
9335 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009336 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009337 if (arg[1] == 'i' && arg[0] == 'N')
9338 highlight_list();
9339 if (STRNCMP("link", arg, p - arg) == 0
9340 || STRNCMP("clear", arg, p - arg) == 0)
9341 {
9342 xp->xp_pattern = skipwhite(p);
9343 p = skiptowhite(xp->xp_pattern);
9344 if (*p != NUL) /* past first group name */
9345 {
9346 xp->xp_pattern = skipwhite(p);
9347 p = skiptowhite(xp->xp_pattern);
9348 }
9349 }
9350 if (*p != NUL) /* past group name(s) */
9351 xp->xp_context = EXPAND_NOTHING;
9352 }
9353 }
9354 }
9355}
9356
9357/*
9358 * List highlighting matches in a nice way.
9359 */
9360 static void
9361highlight_list()
9362{
9363 int i;
9364
9365 for (i = 10; --i >= 0; )
9366 highlight_list_two(i, hl_attr(HLF_D));
9367 for (i = 40; --i >= 0; )
9368 highlight_list_two(99, 0);
9369}
9370
9371 static void
9372highlight_list_two(cnt, attr)
9373 int cnt;
9374 int attr;
9375{
9376 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9377 msg_clr_eos();
9378 out_flush();
9379 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9380}
9381
9382#endif /* FEAT_CMDL_COMPL */
9383
9384#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9385 || defined(FEAT_SIGNS) || defined(PROTO)
9386/*
9387 * Function given to ExpandGeneric() to obtain the list of group names.
9388 * Also used for synIDattr() function.
9389 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009390 char_u *
9391get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009392 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009393 int idx;
9394{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009395#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009396 if (idx == highlight_ga.ga_len && include_none != 0)
9397 return (char_u *)"none";
9398 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009399 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009400 if (idx == highlight_ga.ga_len + include_none + include_default
9401 && include_link != 0)
9402 return (char_u *)"link";
9403 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9404 && include_link != 0)
9405 return (char_u *)"clear";
9406#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407 if (idx < 0 || idx >= highlight_ga.ga_len)
9408 return NULL;
9409 return HL_TABLE()[idx].sg_name;
9410}
9411#endif
9412
Bram Moolenaar4f688582007-07-24 12:34:30 +00009413#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009414/*
9415 * Free all the highlight group fonts.
9416 * Used when quitting for systems which need it.
9417 */
9418 void
9419free_highlight_fonts()
9420{
9421 int idx;
9422
9423 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9424 {
9425 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9426 HL_TABLE()[idx].sg_font = NOFONT;
9427# ifdef FEAT_XFONTSET
9428 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9429 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9430# endif
9431 }
9432
9433 gui_mch_free_font(gui.norm_font);
9434# ifdef FEAT_XFONTSET
9435 gui_mch_free_fontset(gui.fontset);
9436# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009437# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009438 gui_mch_free_font(gui.bold_font);
9439 gui_mch_free_font(gui.ital_font);
9440 gui_mch_free_font(gui.boldital_font);
9441# endif
9442}
9443#endif
9444
9445/**************************************
9446 * End of Highlighting stuff *
9447 **************************************/