blob: c19c5d964d9e0358aaf47e3e662d07910ce9d0fb [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
238 * files from from leaking into ALLBUT lists, we assign a unique ID to the
239 * 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/*
3424 * Clear syncing info for one buffer.
3425 */
3426 static void
3427syntax_sync_clear()
3428{
3429 int i;
3430
3431 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003432 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3433 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3434 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003435
Bram Moolenaar860cae12010-06-05 23:22:07 +02003436 curwin->w_s->b_syn_sync_flags = 0;
3437 curwin->w_s->b_syn_sync_minlines = 0;
3438 curwin->w_s->b_syn_sync_maxlines = 0;
3439 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003440
Bram Moolenaar860cae12010-06-05 23:22:07 +02003441 vim_free(curwin->w_s->b_syn_linecont_prog);
3442 curwin->w_s->b_syn_linecont_prog = NULL;
3443 vim_free(curwin->w_s->b_syn_linecont_pat);
3444 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445
Bram Moolenaar860cae12010-06-05 23:22:07 +02003446 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447}
3448
3449/*
3450 * Remove one pattern from the buffer's pattern list.
3451 */
3452 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003453syn_remove_pattern(block, idx)
3454 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455 int idx;
3456{
3457 synpat_T *spp;
3458
Bram Moolenaar860cae12010-06-05 23:22:07 +02003459 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460#ifdef FEAT_FOLDING
3461 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003462 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003463#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003464 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003465 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003466 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3467 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468}
3469
3470/*
3471 * Clear and free one syntax pattern. When clearing all, must be called from
3472 * last to first!
3473 */
3474 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003475syn_clear_pattern(block, i)
3476 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 int i;
3478{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3480 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003482 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003484 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3485 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3486 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 }
3488}
3489
3490/*
3491 * Clear and free one syntax cluster.
3492 */
3493 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003494syn_clear_cluster(block, i)
3495 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 int i;
3497{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 vim_free(SYN_CLSTR(block)[i].scl_name);
3499 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3500 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501}
3502
3503/*
3504 * Handle ":syntax clear" command.
3505 */
3506 static void
3507syn_cmd_clear(eap, syncing)
3508 exarg_T *eap;
3509 int syncing;
3510{
3511 char_u *arg = eap->arg;
3512 char_u *arg_end;
3513 int id;
3514
3515 eap->nextcmd = find_nextcmd(arg);
3516 if (eap->skip)
3517 return;
3518
3519 /*
3520 * We have to disable this within ":syn include @group filename",
3521 * because otherwise @group would get deleted.
3522 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3523 * clear".
3524 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526 return;
3527
3528 if (ends_excmd(*arg))
3529 {
3530 /*
3531 * No argument: Clear all syntax items.
3532 */
3533 if (syncing)
3534 syntax_sync_clear();
3535 else
3536 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003537 syntax_clear(curwin->w_s);
3538 if (curwin->w_s == &curwin->w_buffer->b_s)
3539 do_unlet((char_u *)"b:current_syntax", TRUE);
3540 else
3541 do_unlet((char_u *)"w:current_syntax", TRUE);
3542
Bram Moolenaar071d4272004-06-13 20:20:40 +00003543 }
3544 }
3545 else
3546 {
3547 /*
3548 * Clear the group IDs that are in the argument.
3549 */
3550 while (!ends_excmd(*arg))
3551 {
3552 arg_end = skiptowhite(arg);
3553 if (*arg == '@')
3554 {
3555 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3556 if (id == 0)
3557 {
3558 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3559 break;
3560 }
3561 else
3562 {
3563 /*
3564 * We can't physically delete a cluster without changing
3565 * the IDs of other clusters, so we do the next best thing
3566 * and make it empty.
3567 */
3568 short scl_id = id - SYNID_CLUSTER;
3569
Bram Moolenaar860cae12010-06-05 23:22:07 +02003570 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3571 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572 }
3573 }
3574 else
3575 {
3576 id = syn_namen2id(arg, (int)(arg_end - arg));
3577 if (id == 0)
3578 {
3579 EMSG2(_(e_nogroup), arg);
3580 break;
3581 }
3582 else
3583 syn_clear_one(id, syncing);
3584 }
3585 arg = skipwhite(arg_end);
3586 }
3587 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003588 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590}
3591
3592/*
3593 * Clear one syntax group for the current buffer.
3594 */
3595 static void
3596syn_clear_one(id, syncing)
3597 int id;
3598 int syncing;
3599{
3600 synpat_T *spp;
3601 int idx;
3602
3603 /* Clear keywords only when not ":syn sync clear group-name" */
3604 if (!syncing)
3605 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3607 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608 }
3609
3610 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003612 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003613 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3615 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003616 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617 }
3618}
3619
3620/*
3621 * Handle ":syntax on" command.
3622 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623 static void
3624syn_cmd_on(eap, syncing)
3625 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003626 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627{
3628 syn_cmd_onoff(eap, "syntax");
3629}
3630
3631/*
3632 * Handle ":syntax enable" command.
3633 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634 static void
3635syn_cmd_enable(eap, syncing)
3636 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003637 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638{
3639 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3640 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003641 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003642}
3643
3644/*
3645 * Handle ":syntax reset" command.
3646 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647 static void
3648syn_cmd_reset(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 eap->nextcmd = check_nextcmd(eap->arg);
3653 if (!eap->skip)
3654 {
3655 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3656 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003657 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003658 }
3659}
3660
3661/*
3662 * Handle ":syntax manual" command.
3663 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003664 static void
3665syn_cmd_manual(eap, syncing)
3666 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003667 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668{
3669 syn_cmd_onoff(eap, "manual");
3670}
3671
3672/*
3673 * Handle ":syntax off" command.
3674 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 static void
3676syn_cmd_off(eap, syncing)
3677 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003678 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679{
3680 syn_cmd_onoff(eap, "nosyntax");
3681}
3682
3683 static void
3684syn_cmd_onoff(eap, name)
3685 exarg_T *eap;
3686 char *name;
3687{
3688 char_u buf[100];
3689
3690 eap->nextcmd = check_nextcmd(eap->arg);
3691 if (!eap->skip)
3692 {
3693 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003694 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 do_cmdline_cmd(buf);
3696 }
3697}
3698
3699/*
3700 * Handle ":syntax [list]" command: list current syntax words.
3701 */
3702 static void
3703syn_cmd_list(eap, syncing)
3704 exarg_T *eap;
3705 int syncing; /* when TRUE: list syncing items */
3706{
3707 char_u *arg = eap->arg;
3708 int id;
3709 char_u *arg_end;
3710
3711 eap->nextcmd = find_nextcmd(arg);
3712 if (eap->skip)
3713 return;
3714
Bram Moolenaar860cae12010-06-05 23:22:07 +02003715 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 {
3717 MSG(_("No Syntax items defined for this buffer"));
3718 return;
3719 }
3720
3721 if (syncing)
3722 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003723 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 {
3725 MSG_PUTS(_("syncing on C-style comments"));
3726 syn_lines_msg();
3727 syn_match_msg();
3728 return;
3729 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003730 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003732 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 MSG_PUTS(_("no syncing"));
3734 else
3735 {
3736 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003737 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738 MSG_PUTS(_(" lines before top line"));
3739 syn_match_msg();
3740 }
3741 return;
3742 }
3743 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003744 if (curwin->w_s->b_syn_sync_minlines > 0
3745 || curwin->w_s->b_syn_sync_maxlines > 0
3746 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003747 {
3748 MSG_PUTS(_("\nsyncing on items"));
3749 syn_lines_msg();
3750 syn_match_msg();
3751 }
3752 }
3753 else
3754 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3755 if (ends_excmd(*arg))
3756 {
3757 /*
3758 * No argument: List all group IDs and all syntax clusters.
3759 */
3760 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3761 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003762 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 syn_list_cluster(id);
3764 }
3765 else
3766 {
3767 /*
3768 * List the group IDs and syntax clusters that are in the argument.
3769 */
3770 while (!ends_excmd(*arg) && !got_int)
3771 {
3772 arg_end = skiptowhite(arg);
3773 if (*arg == '@')
3774 {
3775 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3776 if (id == 0)
3777 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3778 else
3779 syn_list_cluster(id - SYNID_CLUSTER);
3780 }
3781 else
3782 {
3783 id = syn_namen2id(arg, (int)(arg_end - arg));
3784 if (id == 0)
3785 EMSG2(_(e_nogroup), arg);
3786 else
3787 syn_list_one(id, syncing, TRUE);
3788 }
3789 arg = skipwhite(arg_end);
3790 }
3791 }
3792 eap->nextcmd = check_nextcmd(arg);
3793}
3794
3795 static void
3796syn_lines_msg()
3797{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003798 if (curwin->w_s->b_syn_sync_maxlines > 0
3799 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800 {
3801 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003802 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003803 {
3804 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003805 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3806 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807 MSG_PUTS(", ");
3808 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003809 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003810 {
3811 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003812 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813 }
3814 MSG_PUTS(_(" lines before top line"));
3815 }
3816}
3817
3818 static void
3819syn_match_msg()
3820{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003821 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 {
3823 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003824 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 MSG_PUTS(_(" line breaks"));
3826 }
3827}
3828
3829static int last_matchgroup;
3830
3831struct name_list
3832{
3833 int flag;
3834 char *name;
3835};
3836
3837static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3838
3839/*
3840 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3841 */
3842 static void
3843syn_list_one(id, syncing, link_only)
3844 int id;
3845 int syncing; /* when TRUE: list syncing items */
3846 int link_only; /* when TRUE; list link-only too */
3847{
3848 int attr;
3849 int idx;
3850 int did_header = FALSE;
3851 synpat_T *spp;
3852 static struct name_list namelist1[] =
3853 {
3854 {HL_DISPLAY, "display"},
3855 {HL_CONTAINED, "contained"},
3856 {HL_ONELINE, "oneline"},
3857 {HL_KEEPEND, "keepend"},
3858 {HL_EXTEND, "extend"},
3859 {HL_EXCLUDENL, "excludenl"},
3860 {HL_TRANSP, "transparent"},
3861 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003862#ifdef FEAT_CONCEAL
3863 {HL_CONCEAL, "conceal"},
3864 {HL_CONCEALENDS, "concealends"},
3865#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 {0, NULL}
3867 };
3868 static struct name_list namelist2[] =
3869 {
3870 {HL_SKIPWHITE, "skipwhite"},
3871 {HL_SKIPNL, "skipnl"},
3872 {HL_SKIPEMPTY, "skipempty"},
3873 {0, NULL}
3874 };
3875
3876 attr = hl_attr(HLF_D); /* highlight like directories */
3877
3878 /* list the keywords for "id" */
3879 if (!syncing)
3880 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003881 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3882 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 did_header, attr);
3884 }
3885
3886 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003887 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003889 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003890 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3891 continue;
3892
3893 (void)syn_list_header(did_header, 999, id);
3894 did_header = TRUE;
3895 last_matchgroup = 0;
3896 if (spp->sp_type == SPTYPE_MATCH)
3897 {
3898 put_pattern("match", ' ', spp, attr);
3899 msg_putchar(' ');
3900 }
3901 else if (spp->sp_type == SPTYPE_START)
3902 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003903 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3904 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3905 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3906 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3907 while (idx < curwin->w_s->b_syn_patterns.ga_len
3908 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3909 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003910 --idx;
3911 msg_putchar(' ');
3912 }
3913 syn_list_flags(namelist1, spp->sp_flags, attr);
3914
3915 if (spp->sp_cont_list != NULL)
3916 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3917
3918 if (spp->sp_syn.cont_in_list != NULL)
3919 put_id_list((char_u *)"containedin",
3920 spp->sp_syn.cont_in_list, attr);
3921
3922 if (spp->sp_next_list != NULL)
3923 {
3924 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3925 syn_list_flags(namelist2, spp->sp_flags, attr);
3926 }
3927 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3928 {
3929 if (spp->sp_flags & HL_SYNC_HERE)
3930 msg_puts_attr((char_u *)"grouphere", attr);
3931 else
3932 msg_puts_attr((char_u *)"groupthere", attr);
3933 msg_putchar(' ');
3934 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003935 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3937 else
3938 MSG_PUTS("NONE");
3939 msg_putchar(' ');
3940 }
3941 }
3942
3943 /* list the link, if there is one */
3944 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3945 {
3946 (void)syn_list_header(did_header, 999, id);
3947 msg_puts_attr((char_u *)"links to", attr);
3948 msg_putchar(' ');
3949 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3950 }
3951}
3952
3953 static void
3954syn_list_flags(nl, flags, attr)
3955 struct name_list *nl;
3956 int flags;
3957 int attr;
3958{
3959 int i;
3960
3961 for (i = 0; nl[i].flag != 0; ++i)
3962 if (flags & nl[i].flag)
3963 {
3964 msg_puts_attr((char_u *)nl[i].name, attr);
3965 msg_putchar(' ');
3966 }
3967}
3968
3969/*
3970 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3971 */
3972 static void
3973syn_list_cluster(id)
3974 int id;
3975{
3976 int endcol = 15;
3977
3978 /* slight hack: roughly duplicate the guts of syn_list_header() */
3979 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02003980 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981
3982 if (msg_col >= endcol) /* output at least one space */
3983 endcol = msg_col + 1;
3984 if (Columns <= endcol) /* avoid hang for tiny window */
3985 endcol = Columns - 1;
3986
3987 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003988 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003989 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003990 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 hl_attr(HLF_D));
3992 }
3993 else
3994 {
3995 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3996 msg_puts((char_u *)"=NONE");
3997 }
3998}
3999
4000 static void
4001put_id_list(name, list, attr)
4002 char_u *name;
4003 short *list;
4004 int attr;
4005{
4006 short *p;
4007
4008 msg_puts_attr(name, attr);
4009 msg_putchar('=');
4010 for (p = list; *p; ++p)
4011 {
4012 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4013 {
4014 if (p[1])
4015 MSG_PUTS("ALLBUT");
4016 else
4017 MSG_PUTS("ALL");
4018 }
4019 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4020 {
4021 MSG_PUTS("TOP");
4022 }
4023 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4024 {
4025 MSG_PUTS("CONTAINED");
4026 }
4027 else if (*p >= SYNID_CLUSTER)
4028 {
4029 short scl_id = *p - SYNID_CLUSTER;
4030
4031 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004032 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033 }
4034 else
4035 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4036 if (p[1])
4037 msg_putchar(',');
4038 }
4039 msg_putchar(' ');
4040}
4041
4042 static void
4043put_pattern(s, c, spp, attr)
4044 char *s;
4045 int c;
4046 synpat_T *spp;
4047 int attr;
4048{
4049 long n;
4050 int mask;
4051 int first;
4052 static char *sepchars = "/+=-#@\"|'^&";
4053 int i;
4054
4055 /* May have to write "matchgroup=group" */
4056 if (last_matchgroup != spp->sp_syn_match_id)
4057 {
4058 last_matchgroup = spp->sp_syn_match_id;
4059 msg_puts_attr((char_u *)"matchgroup", attr);
4060 msg_putchar('=');
4061 if (last_matchgroup == 0)
4062 msg_outtrans((char_u *)"NONE");
4063 else
4064 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4065 msg_putchar(' ');
4066 }
4067
4068 /* output the name of the pattern and an '=' or ' ' */
4069 msg_puts_attr((char_u *)s, attr);
4070 msg_putchar(c);
4071
4072 /* output the pattern, in between a char that is not in the pattern */
4073 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4074 if (sepchars[++i] == NUL)
4075 {
4076 i = 0; /* no good char found, just use the first one */
4077 break;
4078 }
4079 msg_putchar(sepchars[i]);
4080 msg_outtrans(spp->sp_pattern);
4081 msg_putchar(sepchars[i]);
4082
4083 /* output any pattern options */
4084 first = TRUE;
4085 for (i = 0; i < SPO_COUNT; ++i)
4086 {
4087 mask = (1 << i);
4088 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4089 {
4090 if (!first)
4091 msg_putchar(','); /* separate with commas */
4092 msg_puts((char_u *)spo_name_tab[i]);
4093 n = spp->sp_offsets[i];
4094 if (i != SPO_LC_OFF)
4095 {
4096 if (spp->sp_off_flags & mask)
4097 msg_putchar('s');
4098 else
4099 msg_putchar('e');
4100 if (n > 0)
4101 msg_putchar('+');
4102 }
4103 if (n || i == SPO_LC_OFF)
4104 msg_outnum(n);
4105 first = FALSE;
4106 }
4107 }
4108 msg_putchar(' ');
4109}
4110
4111/*
4112 * List or clear the keywords for one syntax group.
4113 * Return TRUE if the header has been printed.
4114 */
4115 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004116syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004118 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119 int did_header; /* header has already been printed */
4120 int attr;
4121{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004122 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004123 hashitem_T *hi;
4124 keyentry_T *kp;
4125 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 int prev_contained = 0;
4127 short *prev_next_list = NULL;
4128 short *prev_cont_in_list = NULL;
4129 int prev_skipnl = 0;
4130 int prev_skipwhite = 0;
4131 int prev_skipempty = 0;
4132
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133 /*
4134 * Unfortunately, this list of keywords is not sorted on alphabet but on
4135 * hash value...
4136 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004137 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004138 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004140 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004141 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004142 --todo;
4143 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004144 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004145 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004147 if (prev_contained != (kp->flags & HL_CONTAINED)
4148 || prev_skipnl != (kp->flags & HL_SKIPNL)
4149 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4150 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4151 || prev_cont_in_list != kp->k_syn.cont_in_list
4152 || prev_next_list != kp->next_list)
4153 outlen = 9999;
4154 else
4155 outlen = (int)STRLEN(kp->keyword);
4156 /* output "contained" and "nextgroup" on each line */
4157 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004159 prev_contained = 0;
4160 prev_next_list = NULL;
4161 prev_cont_in_list = NULL;
4162 prev_skipnl = 0;
4163 prev_skipwhite = 0;
4164 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004166 did_header = TRUE;
4167 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004169 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004171 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004173 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004175 put_id_list((char_u *)"containedin",
4176 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004178 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004179 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004180 if (kp->next_list != prev_next_list)
4181 {
4182 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4183 msg_putchar(' ');
4184 prev_next_list = kp->next_list;
4185 if (kp->flags & HL_SKIPNL)
4186 {
4187 msg_puts_attr((char_u *)"skipnl", attr);
4188 msg_putchar(' ');
4189 prev_skipnl = (kp->flags & HL_SKIPNL);
4190 }
4191 if (kp->flags & HL_SKIPWHITE)
4192 {
4193 msg_puts_attr((char_u *)"skipwhite", attr);
4194 msg_putchar(' ');
4195 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4196 }
4197 if (kp->flags & HL_SKIPEMPTY)
4198 {
4199 msg_puts_attr((char_u *)"skipempty", attr);
4200 msg_putchar(' ');
4201 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4202 }
4203 }
4204 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 }
4207 }
4208 }
4209
4210 return did_header;
4211}
4212
4213 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004214syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004216 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004218 hashitem_T *hi;
4219 keyentry_T *kp;
4220 keyentry_T *kp_prev;
4221 keyentry_T *kp_next;
4222 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223
Bram Moolenaardad6b692005-01-25 22:14:34 +00004224 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004225 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004226 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004228 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004229 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004230 --todo;
4231 kp_prev = NULL;
4232 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004233 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004234 if (kp->k_syn.id == id)
4235 {
4236 kp_next = kp->ke_next;
4237 if (kp_prev == NULL)
4238 {
4239 if (kp_next == NULL)
4240 hash_remove(ht, hi);
4241 else
4242 hi->hi_key = KE2HIKEY(kp_next);
4243 }
4244 else
4245 kp_prev->ke_next = kp_next;
4246 vim_free(kp->next_list);
4247 vim_free(kp->k_syn.cont_in_list);
4248 vim_free(kp);
4249 kp = kp_next;
4250 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004252 {
4253 kp_prev = kp;
4254 kp = kp->ke_next;
4255 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 }
4257 }
4258 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004259 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260}
4261
4262/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 */
4265 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004266clear_keywtab(ht)
4267 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004269 hashitem_T *hi;
4270 int todo;
4271 keyentry_T *kp;
4272 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004273
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004274 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277 if (!HASHITEM_EMPTY(hi))
4278 {
4279 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004280 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282 kp_next = kp->ke_next;
4283 vim_free(kp->next_list);
4284 vim_free(kp->k_syn.cont_in_list);
4285 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004286 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004287 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004289 hash_clear(ht);
4290 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291}
4292
4293/*
4294 * Add a keyword to the list of keywords.
4295 */
4296 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004297add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298 char_u *name; /* name of keyword */
4299 int id; /* group ID for this keyword */
4300 int flags; /* flags for this keyword */
4301 short *cont_in_list; /* containedin for this keyword */
4302 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004303 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004305 keyentry_T *kp;
4306 hashtab_T *ht;
4307 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004308 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004309 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004310 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311
Bram Moolenaar860cae12010-06-05 23:22:07 +02004312 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004313 name_ic = str_foldcase(name, (int)STRLEN(name),
4314 name_folded, MAXKEYWLEN + 1);
4315 else
4316 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004317 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4318 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004320 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321 kp->k_syn.id = id;
4322 kp->k_syn.inc_tag = current_syn_inc_tag;
4323 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004324 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004327 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329
Bram Moolenaar860cae12010-06-05 23:22:07 +02004330 if (curwin->w_s->b_syn_ic)
4331 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004333 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 hash = hash_hash(kp->keyword);
4336 hi = hash_lookup(ht, kp->keyword, hash);
4337 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004338 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004339 /* new keyword, add to hashtable */
4340 kp->ke_next = NULL;
4341 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004343 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004344 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004345 /* keyword already exists, prepend to list */
4346 kp->ke_next = HI2KE(hi);
4347 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349}
4350
4351/*
4352 * Get the start and end of the group name argument.
4353 * Return a pointer to the first argument.
4354 * Return NULL if the end of the command was found instead of further args.
4355 */
4356 static char_u *
4357get_group_name(arg, name_end)
4358 char_u *arg; /* start of the argument */
4359 char_u **name_end; /* pointer to end of the name */
4360{
4361 char_u *rest;
4362
4363 *name_end = skiptowhite(arg);
4364 rest = skipwhite(*name_end);
4365
4366 /*
4367 * Check if there are enough arguments. The first argument may be a
4368 * pattern, where '|' is allowed, so only check for NUL.
4369 */
4370 if (ends_excmd(*arg) || *rest == NUL)
4371 return NULL;
4372 return rest;
4373}
4374
4375/*
4376 * Check for syntax command option arguments.
4377 * This can be called at any place in the list of arguments, and just picks
4378 * out the arguments that are known. Can be called several times in a row to
4379 * collect all options in between other arguments.
4380 * Return a pointer to the next argument (which isn't an option).
4381 * Return NULL for any error;
4382 */
4383 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004384get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004385 char_u *arg; /* next argument to be checked */
4386 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004387 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004388{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 char_u *gname_start, *gname;
4390 int syn_id;
4391 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004392 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 int i;
4394 int fidx;
4395 static struct flag
4396 {
4397 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004398 int argtype;
4399 int flags;
4400 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4401 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4402 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4403 {"eExXtTeEnNdD", 0, HL_EXTEND},
4404 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4405 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4406 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4407 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4408 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4409 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4410 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4411 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4412 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004413 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4414 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4415 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004416 {"cCoOnNtTaAiInNsS", 1, 0},
4417 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4418 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004420 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004421
4422 if (arg == NULL) /* already detected error */
4423 return NULL;
4424
Bram Moolenaar860cae12010-06-05 23:22:07 +02004425#ifdef FEAT_CONCEAL
4426 if (curwin->w_s->b_syn_conceal)
4427 opt->flags |= HL_CONCEAL;
4428#endif
4429
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430 for (;;)
4431 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004432 /*
4433 * This is used very often when a large number of keywords is defined.
4434 * Need to skip quickly when no option name is found.
4435 * Also avoid tolower(), it's slow.
4436 */
4437 if (strchr(first_letters, *arg) == NULL)
4438 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004439
4440 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4441 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004442 p = flagtab[fidx].name;
4443 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4444 if (arg[len] != p[i] && arg[len] != p[i + 1])
4445 break;
4446 if (p[i] == NUL && (vim_iswhite(arg[len])
4447 || (flagtab[fidx].argtype > 0
4448 ? arg[len] == '='
4449 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004450 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004451 if (opt->keyword
4452 && (flagtab[fidx].flags == HL_DISPLAY
4453 || flagtab[fidx].flags == HL_FOLD
4454 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004455 /* treat "display", "fold" and "extend" as a keyword */
4456 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004457 break;
4458 }
4459 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004460 if (fidx < 0) /* no match found */
4461 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004462
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004463 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004465 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 {
4467 EMSG(_("E395: contains argument not accepted here"));
4468 return NULL;
4469 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004470 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 return NULL;
4472 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004473 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004475#if 0 /* cannot happen */
4476 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 {
4478 EMSG(_("E396: containedin argument not accepted here"));
4479 return NULL;
4480 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004481#endif
4482 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004483 return NULL;
4484 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004485 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004487 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488 return NULL;
4489 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004490 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4491 {
4492#ifdef FEAT_MBYTE
4493 /* cchar=? */
4494 if (has_mbyte)
4495 {
4496# ifdef FEAT_CONCEAL
4497 *conceal_char = mb_ptr2char(arg + 6);
4498# endif
4499 arg += mb_ptr2len(arg + 6) - 1;
4500 }
4501 else
4502#endif
4503#ifdef FEAT_CONCEAL
4504 *conceal_char = arg[6];
4505#else
4506 ;
4507#endif
4508 arg = skipwhite(arg + 7);
4509 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004511 {
4512 opt->flags |= flagtab[fidx].flags;
4513 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 if (flagtab[fidx].flags == HL_SYNC_HERE
4516 || flagtab[fidx].flags == HL_SYNC_THERE)
4517 {
4518 if (opt->sync_idx == NULL)
4519 {
4520 EMSG(_("E393: group[t]here not accepted here"));
4521 return NULL;
4522 }
4523 gname_start = arg;
4524 arg = skiptowhite(arg);
4525 if (gname_start == arg)
4526 return NULL;
4527 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4528 if (gname == NULL)
4529 return NULL;
4530 if (STRCMP(gname, "NONE") == 0)
4531 *opt->sync_idx = NONE_IDX;
4532 else
4533 {
4534 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004535 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4536 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4537 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004538 {
4539 *opt->sync_idx = i;
4540 break;
4541 }
4542 if (i < 0)
4543 {
4544 EMSG2(_("E394: Didn't find region item for %s"), gname);
4545 vim_free(gname);
4546 return NULL;
4547 }
4548 }
4549
4550 vim_free(gname);
4551 arg = skipwhite(arg);
4552 }
4553#ifdef FEAT_FOLDING
4554 else if (flagtab[fidx].flags == HL_FOLD
4555 && foldmethodIsSyntax(curwin))
4556 /* Need to update folds later. */
4557 foldUpdateAll(curwin);
4558#endif
4559 }
4560 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561
4562 return arg;
4563}
4564
4565/*
4566 * Adjustments to syntax item when declared in a ":syn include"'d file.
4567 * Set the contained flag, and if the item is not already contained, add it
4568 * to the specified top-level group, if any.
4569 */
4570 static void
4571syn_incl_toplevel(id, flagsp)
4572 int id;
4573 int *flagsp;
4574{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004575 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004576 return;
4577 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004578 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 {
4580 /* We have to alloc this, because syn_combine_list() will free it. */
4581 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004582 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583
4584 if (grp_list != NULL)
4585 {
4586 grp_list[0] = id;
4587 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004588 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 CLUSTER_ADD);
4590 }
4591 }
4592}
4593
4594/*
4595 * Handle ":syntax include [@{group-name}] filename" command.
4596 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004597 static void
4598syn_cmd_include(eap, syncing)
4599 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004600 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601{
4602 char_u *arg = eap->arg;
4603 int sgl_id = 1;
4604 char_u *group_name_end;
4605 char_u *rest;
4606 char_u *errormsg = NULL;
4607 int prev_toplvl_grp;
4608 int prev_syn_inc_tag;
4609 int source = FALSE;
4610
4611 eap->nextcmd = find_nextcmd(arg);
4612 if (eap->skip)
4613 return;
4614
4615 if (arg[0] == '@')
4616 {
4617 ++arg;
4618 rest = get_group_name(arg, &group_name_end);
4619 if (rest == NULL)
4620 {
4621 EMSG((char_u *)_("E397: Filename required"));
4622 return;
4623 }
4624 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4625 /* separate_nextcmd() and expand_filename() depend on this */
4626 eap->arg = rest;
4627 }
4628
4629 /*
4630 * Everything that's left, up to the next command, should be the
4631 * filename to include.
4632 */
4633 eap->argt |= (XFILE | NOSPC);
4634 separate_nextcmd(eap);
4635 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4636 {
4637 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4638 * file. Need to expand the file name first. In other cases
4639 * ":runtime!" is used. */
4640 source = TRUE;
4641 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4642 {
4643 if (errormsg != NULL)
4644 EMSG(errormsg);
4645 return;
4646 }
4647 }
4648
4649 /*
4650 * Save and restore the existing top-level grouplist id and ":syn
4651 * include" tag around the actual inclusion.
4652 */
4653 prev_syn_inc_tag = current_syn_inc_tag;
4654 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004655 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4656 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004657 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4658 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004660 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004661 current_syn_inc_tag = prev_syn_inc_tag;
4662}
4663
4664/*
4665 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4666 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667 static void
4668syn_cmd_keyword(eap, syncing)
4669 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004670 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671{
4672 char_u *arg = eap->arg;
4673 char_u *group_name_end;
4674 int syn_id;
4675 char_u *rest;
4676 char_u *keyword_copy;
4677 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004678 char_u *kw;
4679 syn_opt_arg_T syn_opt_arg;
4680 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004681 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682
4683 rest = get_group_name(arg, &group_name_end);
4684
4685 if (rest != NULL)
4686 {
4687 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4688
4689 /* allocate a buffer, for removing the backslashes in the keyword */
4690 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4691 if (keyword_copy != NULL)
4692 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004693 syn_opt_arg.flags = 0;
4694 syn_opt_arg.keyword = TRUE;
4695 syn_opt_arg.sync_idx = NULL;
4696 syn_opt_arg.has_cont_list = FALSE;
4697 syn_opt_arg.cont_in_list = NULL;
4698 syn_opt_arg.next_list = NULL;
4699
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 /*
4701 * The options given apply to ALL keywords, so all options must be
4702 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004703 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004705 cnt = 0;
4706 p = keyword_copy;
4707 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004708 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004709 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004710 if (rest == NULL || ends_excmd(*rest))
4711 break;
4712 /* Copy the keyword, removing backslashes, and add a NUL. */
4713 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004714 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004715 if (*rest == '\\' && rest[1] != NUL)
4716 ++rest;
4717 *p++ = *rest++;
4718 }
4719 *p++ = NUL;
4720 ++cnt;
4721 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004723 if (!eap->skip)
4724 {
4725 /* Adjust flags for use of ":syn include". */
4726 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4727
4728 /*
4729 * 2: Add an entry for each keyword.
4730 */
4731 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4732 {
4733 for (p = vim_strchr(kw, '['); ; )
4734 {
4735 if (p != NULL)
4736 *p = NUL;
4737 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004738 syn_opt_arg.cont_in_list,
4739 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004740 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004741 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004742 if (p[1] == NUL)
4743 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004744 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004745 kw = p + 2; /* skip over the NUL */
4746 break;
4747 }
4748 if (p[1] == ']')
4749 {
4750 kw = p + 1; /* skip over the "]" */
4751 break;
4752 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004753#ifdef FEAT_MBYTE
4754 if (has_mbyte)
4755 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004756 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004757
4758 mch_memmove(p, p + 1, l);
4759 p += l;
4760 }
4761 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004763 {
4764 p[0] = p[1];
4765 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 }
4767 }
4768 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004769 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004770
Bram Moolenaar071d4272004-06-13 20:20:40 +00004771 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004772 vim_free(syn_opt_arg.cont_in_list);
4773 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 }
4775 }
4776
4777 if (rest != NULL)
4778 eap->nextcmd = check_nextcmd(rest);
4779 else
4780 EMSG2(_(e_invarg2), arg);
4781
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004782 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004783 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784}
4785
4786/*
4787 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4788 *
4789 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4790 */
4791 static void
4792syn_cmd_match(eap, syncing)
4793 exarg_T *eap;
4794 int syncing; /* TRUE for ":syntax sync match .. " */
4795{
4796 char_u *arg = eap->arg;
4797 char_u *group_name_end;
4798 char_u *rest;
4799 synpat_T item; /* the item found in the line */
4800 int syn_id;
4801 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004802 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004803 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004804 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805
4806 /* Isolate the group name, check for validity */
4807 rest = get_group_name(arg, &group_name_end);
4808
4809 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004810 syn_opt_arg.flags = 0;
4811 syn_opt_arg.keyword = FALSE;
4812 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4813 syn_opt_arg.has_cont_list = TRUE;
4814 syn_opt_arg.cont_list = NULL;
4815 syn_opt_arg.cont_in_list = NULL;
4816 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004817 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818
4819 /* get the pattern. */
4820 init_syn_patterns();
4821 vim_memset(&item, 0, sizeof(item));
4822 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004823 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4824 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825
4826 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004827 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828
4829 if (rest != NULL) /* all arguments are valid */
4830 {
4831 /*
4832 * Check for trailing command and illegal trailing arguments.
4833 */
4834 eap->nextcmd = check_nextcmd(rest);
4835 if (!ends_excmd(*rest) || eap->skip)
4836 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004837 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838 && (syn_id = syn_check_group(arg,
4839 (int)(group_name_end - arg))) != 0)
4840 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004841 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004842 /*
4843 * Store the pattern in the syn_items list
4844 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004845 idx = curwin->w_s->b_syn_patterns.ga_len;
4846 SYN_ITEMS(curwin->w_s)[idx] = item;
4847 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4848 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4849 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4850 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4851 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4852 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4853 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4854 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004855 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004856#ifdef FEAT_CONCEAL
4857 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
4858#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004859 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004860 curwin->w_s->b_syn_containedin = TRUE;
4861 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4862 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004863
4864 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004865 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004866 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004868 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004869 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004870#endif
4871
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004872 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004873 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004874 return; /* don't free the progs and patterns now */
4875 }
4876 }
4877
4878 /*
4879 * Something failed, free the allocated memory.
4880 */
4881 vim_free(item.sp_prog);
4882 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004883 vim_free(syn_opt_arg.cont_list);
4884 vim_free(syn_opt_arg.cont_in_list);
4885 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886
4887 if (rest == NULL)
4888 EMSG2(_(e_invarg2), arg);
4889}
4890
4891/*
4892 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4893 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4894 */
4895 static void
4896syn_cmd_region(eap, syncing)
4897 exarg_T *eap;
4898 int syncing; /* TRUE for ":syntax sync region .." */
4899{
4900 char_u *arg = eap->arg;
4901 char_u *group_name_end;
4902 char_u *rest; /* next arg, NULL on error */
4903 char_u *key_end;
4904 char_u *key = NULL;
4905 char_u *p;
4906 int item;
4907#define ITEM_START 0
4908#define ITEM_SKIP 1
4909#define ITEM_END 2
4910#define ITEM_MATCHGROUP 3
4911 struct pat_ptr
4912 {
4913 synpat_T *pp_synp; /* pointer to syn_pattern */
4914 int pp_matchgroup_id; /* matchgroup ID */
4915 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4916 } *(pat_ptrs[3]);
4917 /* patterns found in the line */
4918 struct pat_ptr *ppp;
4919 struct pat_ptr *ppp_next;
4920 int pat_count = 0; /* nr of syn_patterns found */
4921 int syn_id;
4922 int matchgroup_id = 0;
4923 int not_enough = FALSE; /* not enough arguments */
4924 int illegal = FALSE; /* illegal arguments */
4925 int success = FALSE;
4926 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004927 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004928 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004929
4930 /* Isolate the group name, check for validity */
4931 rest = get_group_name(arg, &group_name_end);
4932
4933 pat_ptrs[0] = NULL;
4934 pat_ptrs[1] = NULL;
4935 pat_ptrs[2] = NULL;
4936
4937 init_syn_patterns();
4938
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004939 syn_opt_arg.flags = 0;
4940 syn_opt_arg.keyword = FALSE;
4941 syn_opt_arg.sync_idx = NULL;
4942 syn_opt_arg.has_cont_list = TRUE;
4943 syn_opt_arg.cont_list = NULL;
4944 syn_opt_arg.cont_in_list = NULL;
4945 syn_opt_arg.next_list = NULL;
4946
Bram Moolenaar071d4272004-06-13 20:20:40 +00004947 /*
4948 * get the options, patterns and matchgroup.
4949 */
4950 while (rest != NULL && !ends_excmd(*rest))
4951 {
4952 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004953 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004954 if (rest == NULL || ends_excmd(*rest))
4955 break;
4956
4957 /* must be a pattern or matchgroup then */
4958 key_end = rest;
4959 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4960 ++key_end;
4961 vim_free(key);
4962 key = vim_strnsave_up(rest, (int)(key_end - rest));
4963 if (key == NULL) /* out of memory */
4964 {
4965 rest = NULL;
4966 break;
4967 }
4968 if (STRCMP(key, "MATCHGROUP") == 0)
4969 item = ITEM_MATCHGROUP;
4970 else if (STRCMP(key, "START") == 0)
4971 item = ITEM_START;
4972 else if (STRCMP(key, "END") == 0)
4973 item = ITEM_END;
4974 else if (STRCMP(key, "SKIP") == 0)
4975 {
4976 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4977 {
4978 illegal = TRUE;
4979 break;
4980 }
4981 item = ITEM_SKIP;
4982 }
4983 else
4984 break;
4985 rest = skipwhite(key_end);
4986 if (*rest != '=')
4987 {
4988 rest = NULL;
4989 EMSG2(_("E398: Missing '=': %s"), arg);
4990 break;
4991 }
4992 rest = skipwhite(rest + 1);
4993 if (*rest == NUL)
4994 {
4995 not_enough = TRUE;
4996 break;
4997 }
4998
4999 if (item == ITEM_MATCHGROUP)
5000 {
5001 p = skiptowhite(rest);
5002 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5003 matchgroup_id = 0;
5004 else
5005 {
5006 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5007 if (matchgroup_id == 0)
5008 {
5009 illegal = TRUE;
5010 break;
5011 }
5012 }
5013 rest = skipwhite(p);
5014 }
5015 else
5016 {
5017 /*
5018 * Allocate room for a syn_pattern, and link it in the list of
5019 * syn_patterns for this item, at the start (because the list is
5020 * used from end to start).
5021 */
5022 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5023 if (ppp == NULL)
5024 {
5025 rest = NULL;
5026 break;
5027 }
5028 ppp->pp_next = pat_ptrs[item];
5029 pat_ptrs[item] = ppp;
5030 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5031 if (ppp->pp_synp == NULL)
5032 {
5033 rest = NULL;
5034 break;
5035 }
5036
5037 /*
5038 * Get the syntax pattern and the following offset(s).
5039 */
5040 /* Enable the appropriate \z specials. */
5041 if (item == ITEM_START)
5042 reg_do_extmatch = REX_SET;
5043 else if (item == ITEM_SKIP || item == ITEM_END)
5044 reg_do_extmatch = REX_USE;
5045 rest = get_syn_pattern(rest, ppp->pp_synp);
5046 reg_do_extmatch = 0;
5047 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005048 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005049 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5050 ppp->pp_matchgroup_id = matchgroup_id;
5051 ++pat_count;
5052 }
5053 }
5054 vim_free(key);
5055 if (illegal || not_enough)
5056 rest = NULL;
5057
5058 /*
5059 * Must have a "start" and "end" pattern.
5060 */
5061 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5062 pat_ptrs[ITEM_END] == NULL))
5063 {
5064 not_enough = TRUE;
5065 rest = NULL;
5066 }
5067
5068 if (rest != NULL)
5069 {
5070 /*
5071 * Check for trailing garbage or command.
5072 * If OK, add the item.
5073 */
5074 eap->nextcmd = check_nextcmd(rest);
5075 if (!ends_excmd(*rest) || eap->skip)
5076 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005077 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078 && (syn_id = syn_check_group(arg,
5079 (int)(group_name_end - arg))) != 0)
5080 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005081 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005082 /*
5083 * Store the start/skip/end in the syn_items list
5084 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005085 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005086 for (item = ITEM_START; item <= ITEM_END; ++item)
5087 {
5088 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5089 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005090 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5091 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5092 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005093 (item == ITEM_START) ? SPTYPE_START :
5094 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005095 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5096 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
5097 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
5098 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005099 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005100#ifdef FEAT_CONCEAL
5101 SYN_ITEMS(curwin->w_s)[idx].sp_char = conceal_char;
5102#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103 if (item == ITEM_START)
5104 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005105 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005106 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005107 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005108 syn_opt_arg.cont_in_list;
5109 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005110 curwin->w_s->b_syn_containedin = TRUE;
5111 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005112 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005114 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005115 ++idx;
5116#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005117 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005118 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005119#endif
5120 }
5121 }
5122
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005123 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005124 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005125 success = TRUE; /* don't free the progs and patterns now */
5126 }
5127 }
5128
5129 /*
5130 * Free the allocated memory.
5131 */
5132 for (item = ITEM_START; item <= ITEM_END; ++item)
5133 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5134 {
5135 if (!success)
5136 {
5137 vim_free(ppp->pp_synp->sp_prog);
5138 vim_free(ppp->pp_synp->sp_pattern);
5139 }
5140 vim_free(ppp->pp_synp);
5141 ppp_next = ppp->pp_next;
5142 vim_free(ppp);
5143 }
5144
5145 if (!success)
5146 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005147 vim_free(syn_opt_arg.cont_list);
5148 vim_free(syn_opt_arg.cont_in_list);
5149 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150 if (not_enough)
5151 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5152 else if (illegal || rest == NULL)
5153 EMSG2(_(e_invarg2), arg);
5154 }
5155}
5156
5157/*
5158 * A simple syntax group ID comparison function suitable for use in qsort()
5159 */
5160 static int
5161#ifdef __BORLANDC__
5162_RTLENTRYF
5163#endif
5164syn_compare_stub(v1, v2)
5165 const void *v1;
5166 const void *v2;
5167{
5168 const short *s1 = v1;
5169 const short *s2 = v2;
5170
5171 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5172}
5173
5174/*
5175 * Combines lists of syntax clusters.
5176 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5177 */
5178 static void
5179syn_combine_list(clstr1, clstr2, list_op)
5180 short **clstr1;
5181 short **clstr2;
5182 int list_op;
5183{
5184 int count1 = 0;
5185 int count2 = 0;
5186 short *g1;
5187 short *g2;
5188 short *clstr = NULL;
5189 int count;
5190 int round;
5191
5192 /*
5193 * Handle degenerate cases.
5194 */
5195 if (*clstr2 == NULL)
5196 return;
5197 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5198 {
5199 if (list_op == CLUSTER_REPLACE)
5200 vim_free(*clstr1);
5201 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5202 *clstr1 = *clstr2;
5203 else
5204 vim_free(*clstr2);
5205 return;
5206 }
5207
5208 for (g1 = *clstr1; *g1; g1++)
5209 ++count1;
5210 for (g2 = *clstr2; *g2; g2++)
5211 ++count2;
5212
5213 /*
5214 * For speed purposes, sort both lists.
5215 */
5216 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5217 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5218
5219 /*
5220 * We proceed in two passes; in round 1, we count the elements to place
5221 * in the new list, and in round 2, we allocate and populate the new
5222 * list. For speed, we use a mergesort-like method, adding the smaller
5223 * of the current elements in each list to the new list.
5224 */
5225 for (round = 1; round <= 2; round++)
5226 {
5227 g1 = *clstr1;
5228 g2 = *clstr2;
5229 count = 0;
5230
5231 /*
5232 * First, loop through the lists until one of them is empty.
5233 */
5234 while (*g1 && *g2)
5235 {
5236 /*
5237 * We always want to add from the first list.
5238 */
5239 if (*g1 < *g2)
5240 {
5241 if (round == 2)
5242 clstr[count] = *g1;
5243 count++;
5244 g1++;
5245 continue;
5246 }
5247 /*
5248 * We only want to add from the second list if we're adding the
5249 * lists.
5250 */
5251 if (list_op == CLUSTER_ADD)
5252 {
5253 if (round == 2)
5254 clstr[count] = *g2;
5255 count++;
5256 }
5257 if (*g1 == *g2)
5258 g1++;
5259 g2++;
5260 }
5261
5262 /*
5263 * Now add the leftovers from whichever list didn't get finished
5264 * first. As before, we only want to add from the second list if
5265 * we're adding the lists.
5266 */
5267 for (; *g1; g1++, count++)
5268 if (round == 2)
5269 clstr[count] = *g1;
5270 if (list_op == CLUSTER_ADD)
5271 for (; *g2; g2++, count++)
5272 if (round == 2)
5273 clstr[count] = *g2;
5274
5275 if (round == 1)
5276 {
5277 /*
5278 * If the group ended up empty, we don't need to allocate any
5279 * space for it.
5280 */
5281 if (count == 0)
5282 {
5283 clstr = NULL;
5284 break;
5285 }
5286 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5287 if (clstr == NULL)
5288 break;
5289 clstr[count] = 0;
5290 }
5291 }
5292
5293 /*
5294 * Finally, put the new list in place.
5295 */
5296 vim_free(*clstr1);
5297 vim_free(*clstr2);
5298 *clstr1 = clstr;
5299}
5300
5301/*
5302 * Lookup a syntax cluster name and return it's ID.
5303 * If it is not found, 0 is returned.
5304 */
5305 static int
5306syn_scl_name2id(name)
5307 char_u *name;
5308{
5309 int i;
5310 char_u *name_u;
5311
5312 /* Avoid using stricmp() too much, it's slow on some systems */
5313 name_u = vim_strsave_up(name);
5314 if (name_u == NULL)
5315 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005316 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5317 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5318 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005319 break;
5320 vim_free(name_u);
5321 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5322}
5323
5324/*
5325 * Like syn_scl_name2id(), but take a pointer + length argument.
5326 */
5327 static int
5328syn_scl_namen2id(linep, len)
5329 char_u *linep;
5330 int len;
5331{
5332 char_u *name;
5333 int id = 0;
5334
5335 name = vim_strnsave(linep, len);
5336 if (name != NULL)
5337 {
5338 id = syn_scl_name2id(name);
5339 vim_free(name);
5340 }
5341 return id;
5342}
5343
5344/*
5345 * Find syntax cluster name in the table and return it's ID.
5346 * The argument is a pointer to the name and the length of the name.
5347 * If it doesn't exist yet, a new entry is created.
5348 * Return 0 for failure.
5349 */
5350 static int
5351syn_check_cluster(pp, len)
5352 char_u *pp;
5353 int len;
5354{
5355 int id;
5356 char_u *name;
5357
5358 name = vim_strnsave(pp, len);
5359 if (name == NULL)
5360 return 0;
5361
5362 id = syn_scl_name2id(name);
5363 if (id == 0) /* doesn't exist yet */
5364 id = syn_add_cluster(name);
5365 else
5366 vim_free(name);
5367 return id;
5368}
5369
5370/*
5371 * Add new syntax cluster and return it's ID.
5372 * "name" must be an allocated string, it will be consumed.
5373 * Return 0 for failure.
5374 */
5375 static int
5376syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005377 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005378{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005379 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005380
5381 /*
5382 * First call for this growarray: init growing array.
5383 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005384 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005385 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005386 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5387 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005388 }
5389
5390 /*
5391 * Make room for at least one other cluster entry.
5392 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005393 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 {
5395 vim_free(name);
5396 return 0;
5397 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005398 len = curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005399
Bram Moolenaar860cae12010-06-05 23:22:07 +02005400 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5401 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5402 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5403 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5404 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005405
Bram Moolenaar217ad922005-03-20 22:37:15 +00005406 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005407 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005408 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005409 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005410
Bram Moolenaar071d4272004-06-13 20:20:40 +00005411 return len + SYNID_CLUSTER;
5412}
5413
5414/*
5415 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5416 * [add={groupname},..] [remove={groupname},..]".
5417 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005418 static void
5419syn_cmd_cluster(eap, syncing)
5420 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005421 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005422{
5423 char_u *arg = eap->arg;
5424 char_u *group_name_end;
5425 char_u *rest;
5426 int scl_id;
5427 short *clstr_list;
5428 int got_clstr = FALSE;
5429 int opt_len;
5430 int list_op;
5431
5432 eap->nextcmd = find_nextcmd(arg);
5433 if (eap->skip)
5434 return;
5435
5436 rest = get_group_name(arg, &group_name_end);
5437
5438 if (rest != NULL)
5439 {
5440 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005441 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005442
5443 for (;;)
5444 {
5445 if (STRNICMP(rest, "add", 3) == 0
5446 && (vim_iswhite(rest[3]) || rest[3] == '='))
5447 {
5448 opt_len = 3;
5449 list_op = CLUSTER_ADD;
5450 }
5451 else if (STRNICMP(rest, "remove", 6) == 0
5452 && (vim_iswhite(rest[6]) || rest[6] == '='))
5453 {
5454 opt_len = 6;
5455 list_op = CLUSTER_SUBTRACT;
5456 }
5457 else if (STRNICMP(rest, "contains", 8) == 0
5458 && (vim_iswhite(rest[8]) || rest[8] == '='))
5459 {
5460 opt_len = 8;
5461 list_op = CLUSTER_REPLACE;
5462 }
5463 else
5464 break;
5465
5466 clstr_list = NULL;
5467 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5468 {
5469 EMSG2(_(e_invarg2), rest);
5470 break;
5471 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005472 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005473 &clstr_list, list_op);
5474 got_clstr = TRUE;
5475 }
5476
5477 if (got_clstr)
5478 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005479 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005480 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005481 }
5482 }
5483
5484 if (!got_clstr)
5485 EMSG(_("E400: No cluster specified"));
5486 if (rest == NULL || !ends_excmd(*rest))
5487 EMSG2(_(e_invarg2), arg);
5488}
5489
5490/*
5491 * On first call for current buffer: Init growing array.
5492 */
5493 static void
5494init_syn_patterns()
5495{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005496 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5497 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005498}
5499
5500/*
5501 * Get one pattern for a ":syntax match" or ":syntax region" command.
5502 * Stores the pattern and program in a synpat_T.
5503 * Returns a pointer to the next argument, or NULL in case of an error.
5504 */
5505 static char_u *
5506get_syn_pattern(arg, ci)
5507 char_u *arg;
5508 synpat_T *ci;
5509{
5510 char_u *end;
5511 int *p;
5512 int idx;
5513 char_u *cpo_save;
5514
5515 /* need at least three chars */
5516 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5517 return NULL;
5518
5519 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5520 if (*end != *arg) /* end delimiter not found */
5521 {
5522 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5523 return NULL;
5524 }
5525 /* store the pattern and compiled regexp program */
5526 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5527 return NULL;
5528
5529 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5530 cpo_save = p_cpo;
5531 p_cpo = (char_u *)"";
5532 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5533 p_cpo = cpo_save;
5534
5535 if (ci->sp_prog == NULL)
5536 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005537 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538
5539 /*
5540 * Check for a match, highlight or region offset.
5541 */
5542 ++end;
5543 do
5544 {
5545 for (idx = SPO_COUNT; --idx >= 0; )
5546 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5547 break;
5548 if (idx >= 0)
5549 {
5550 p = &(ci->sp_offsets[idx]);
5551 if (idx != SPO_LC_OFF)
5552 switch (end[3])
5553 {
5554 case 's': break;
5555 case 'b': break;
5556 case 'e': idx += SPO_COUNT; break;
5557 default: idx = -1; break;
5558 }
5559 if (idx >= 0)
5560 {
5561 ci->sp_off_flags |= (1 << idx);
5562 if (idx == SPO_LC_OFF) /* lc=99 */
5563 {
5564 end += 3;
5565 *p = getdigits(&end);
5566
5567 /* "lc=" offset automatically sets "ms=" offset */
5568 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5569 {
5570 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5571 ci->sp_offsets[SPO_MS_OFF] = *p;
5572 }
5573 }
5574 else /* yy=x+99 */
5575 {
5576 end += 4;
5577 if (*end == '+')
5578 {
5579 ++end;
5580 *p = getdigits(&end); /* positive offset */
5581 }
5582 else if (*end == '-')
5583 {
5584 ++end;
5585 *p = -getdigits(&end); /* negative offset */
5586 }
5587 }
5588 if (*end != ',')
5589 break;
5590 ++end;
5591 }
5592 }
5593 } while (idx >= 0);
5594
5595 if (!ends_excmd(*end) && !vim_iswhite(*end))
5596 {
5597 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5598 return NULL;
5599 }
5600 return skipwhite(end);
5601}
5602
5603/*
5604 * Handle ":syntax sync .." command.
5605 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606 static void
5607syn_cmd_sync(eap, syncing)
5608 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005609 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005610{
5611 char_u *arg_start = eap->arg;
5612 char_u *arg_end;
5613 char_u *key = NULL;
5614 char_u *next_arg;
5615 int illegal = FALSE;
5616 int finished = FALSE;
5617 long n;
5618 char_u *cpo_save;
5619
5620 if (ends_excmd(*arg_start))
5621 {
5622 syn_cmd_list(eap, TRUE);
5623 return;
5624 }
5625
5626 while (!ends_excmd(*arg_start))
5627 {
5628 arg_end = skiptowhite(arg_start);
5629 next_arg = skipwhite(arg_end);
5630 vim_free(key);
5631 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5632 if (STRCMP(key, "CCOMMENT") == 0)
5633 {
5634 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005635 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005636 if (!ends_excmd(*next_arg))
5637 {
5638 arg_end = skiptowhite(next_arg);
5639 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005640 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005641 (int)(arg_end - next_arg));
5642 next_arg = skipwhite(arg_end);
5643 }
5644 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005645 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005646 }
5647 else if ( STRNCMP(key, "LINES", 5) == 0
5648 || STRNCMP(key, "MINLINES", 8) == 0
5649 || STRNCMP(key, "MAXLINES", 8) == 0
5650 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5651 {
5652 if (key[4] == 'S')
5653 arg_end = key + 6;
5654 else if (key[0] == 'L')
5655 arg_end = key + 11;
5656 else
5657 arg_end = key + 9;
5658 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5659 {
5660 illegal = TRUE;
5661 break;
5662 }
5663 n = getdigits(&arg_end);
5664 if (!eap->skip)
5665 {
5666 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005667 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005668 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005669 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005671 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 }
5673 }
5674 else if (STRCMP(key, "FROMSTART") == 0)
5675 {
5676 if (!eap->skip)
5677 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005678 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5679 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 }
5681 }
5682 else if (STRCMP(key, "LINECONT") == 0)
5683 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005684 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685 {
5686 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5687 finished = TRUE;
5688 break;
5689 }
5690 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5691 if (*arg_end != *next_arg) /* end delimiter not found */
5692 {
5693 illegal = TRUE;
5694 break;
5695 }
5696
5697 if (!eap->skip)
5698 {
5699 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005700 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005701 (int)(arg_end - next_arg - 1))) == NULL)
5702 {
5703 finished = TRUE;
5704 break;
5705 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005706 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005707
5708 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5709 cpo_save = p_cpo;
5710 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005711 curwin->w_s->b_syn_linecont_prog =
5712 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005713 p_cpo = cpo_save;
5714
Bram Moolenaar860cae12010-06-05 23:22:07 +02005715 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005716 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005717 vim_free(curwin->w_s->b_syn_linecont_pat);
5718 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005719 finished = TRUE;
5720 break;
5721 }
5722 }
5723 next_arg = skipwhite(arg_end + 1);
5724 }
5725 else
5726 {
5727 eap->arg = next_arg;
5728 if (STRCMP(key, "MATCH") == 0)
5729 syn_cmd_match(eap, TRUE);
5730 else if (STRCMP(key, "REGION") == 0)
5731 syn_cmd_region(eap, TRUE);
5732 else if (STRCMP(key, "CLEAR") == 0)
5733 syn_cmd_clear(eap, TRUE);
5734 else
5735 illegal = TRUE;
5736 finished = TRUE;
5737 break;
5738 }
5739 arg_start = next_arg;
5740 }
5741 vim_free(key);
5742 if (illegal)
5743 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5744 else if (!finished)
5745 {
5746 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005747 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005748 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749 }
5750}
5751
5752/*
5753 * Convert a line of highlight group names into a list of group ID numbers.
5754 * "arg" should point to the "contains" or "nextgroup" keyword.
5755 * "arg" is advanced to after the last group name.
5756 * Careful: the argument is modified (NULs added).
5757 * returns FAIL for some error, OK for success.
5758 */
5759 static int
5760get_id_list(arg, keylen, list)
5761 char_u **arg;
5762 int keylen; /* length of keyword */
5763 short **list; /* where to store the resulting list, if not
5764 NULL, the list is silently skipped! */
5765{
5766 char_u *p = NULL;
5767 char_u *end;
5768 int round;
5769 int count;
5770 int total_count = 0;
5771 short *retval = NULL;
5772 char_u *name;
5773 regmatch_T regmatch;
5774 int id;
5775 int i;
5776 int failed = FALSE;
5777
5778 /*
5779 * We parse the list twice:
5780 * round == 1: count the number of items, allocate the array.
5781 * round == 2: fill the array with the items.
5782 * In round 1 new groups may be added, causing the number of items to
5783 * grow when a regexp is used. In that case round 1 is done once again.
5784 */
5785 for (round = 1; round <= 2; ++round)
5786 {
5787 /*
5788 * skip "contains"
5789 */
5790 p = skipwhite(*arg + keylen);
5791 if (*p != '=')
5792 {
5793 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5794 break;
5795 }
5796 p = skipwhite(p + 1);
5797 if (ends_excmd(*p))
5798 {
5799 EMSG2(_("E406: Empty argument: %s"), *arg);
5800 break;
5801 }
5802
5803 /*
5804 * parse the arguments after "contains"
5805 */
5806 count = 0;
5807 while (!ends_excmd(*p))
5808 {
5809 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5810 ;
5811 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5812 if (name == NULL)
5813 {
5814 failed = TRUE;
5815 break;
5816 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005817 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005818 if ( STRCMP(name + 1, "ALLBUT") == 0
5819 || STRCMP(name + 1, "ALL") == 0
5820 || STRCMP(name + 1, "TOP") == 0
5821 || STRCMP(name + 1, "CONTAINED") == 0)
5822 {
5823 if (TOUPPER_ASC(**arg) != 'C')
5824 {
5825 EMSG2(_("E407: %s not allowed here"), name + 1);
5826 failed = TRUE;
5827 vim_free(name);
5828 break;
5829 }
5830 if (count != 0)
5831 {
5832 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5833 failed = TRUE;
5834 vim_free(name);
5835 break;
5836 }
5837 if (name[1] == 'A')
5838 id = SYNID_ALLBUT;
5839 else if (name[1] == 'T')
5840 id = SYNID_TOP;
5841 else
5842 id = SYNID_CONTAINED;
5843 id += current_syn_inc_tag;
5844 }
5845 else if (name[1] == '@')
5846 {
5847 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5848 }
5849 else
5850 {
5851 /*
5852 * Handle full group name.
5853 */
5854 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5855 id = syn_check_group(name + 1, (int)(end - p));
5856 else
5857 {
5858 /*
5859 * Handle match of regexp with group names.
5860 */
5861 *name = '^';
5862 STRCAT(name, "$");
5863 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5864 if (regmatch.regprog == NULL)
5865 {
5866 failed = TRUE;
5867 vim_free(name);
5868 break;
5869 }
5870
5871 regmatch.rm_ic = TRUE;
5872 id = 0;
5873 for (i = highlight_ga.ga_len; --i >= 0; )
5874 {
5875 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5876 (colnr_T)0))
5877 {
5878 if (round == 2)
5879 {
5880 /* Got more items than expected; can happen
5881 * when adding items that match:
5882 * "contains=a.*b,axb".
5883 * Go back to first round */
5884 if (count >= total_count)
5885 {
5886 vim_free(retval);
5887 round = 1;
5888 }
5889 else
5890 retval[count] = i + 1;
5891 }
5892 ++count;
5893 id = -1; /* remember that we found one */
5894 }
5895 }
5896 vim_free(regmatch.regprog);
5897 }
5898 }
5899 vim_free(name);
5900 if (id == 0)
5901 {
5902 EMSG2(_("E409: Unknown group name: %s"), p);
5903 failed = TRUE;
5904 break;
5905 }
5906 if (id > 0)
5907 {
5908 if (round == 2)
5909 {
5910 /* Got more items than expected, go back to first round */
5911 if (count >= total_count)
5912 {
5913 vim_free(retval);
5914 round = 1;
5915 }
5916 else
5917 retval[count] = id;
5918 }
5919 ++count;
5920 }
5921 p = skipwhite(end);
5922 if (*p != ',')
5923 break;
5924 p = skipwhite(p + 1); /* skip comma in between arguments */
5925 }
5926 if (failed)
5927 break;
5928 if (round == 1)
5929 {
5930 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5931 if (retval == NULL)
5932 break;
5933 retval[count] = 0; /* zero means end of the list */
5934 total_count = count;
5935 }
5936 }
5937
5938 *arg = p;
5939 if (failed || retval == NULL)
5940 {
5941 vim_free(retval);
5942 return FAIL;
5943 }
5944
5945 if (*list == NULL)
5946 *list = retval;
5947 else
5948 vim_free(retval); /* list already found, don't overwrite it */
5949
5950 return OK;
5951}
5952
5953/*
5954 * Make a copy of an ID list.
5955 */
5956 static short *
5957copy_id_list(list)
5958 short *list;
5959{
5960 int len;
5961 int count;
5962 short *retval;
5963
5964 if (list == NULL)
5965 return NULL;
5966
5967 for (count = 0; list[count]; ++count)
5968 ;
5969 len = (count + 1) * sizeof(short);
5970 retval = (short *)alloc((unsigned)len);
5971 if (retval != NULL)
5972 mch_memmove(retval, list, (size_t)len);
5973
5974 return retval;
5975}
5976
5977/*
5978 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5979 * "cur_si" can be NULL if not checking the "containedin" list.
5980 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5981 * the current item.
5982 * This function is called very often, keep it fast!!
5983 */
5984 static int
5985in_id_list(cur_si, list, ssp, contained)
5986 stateitem_T *cur_si; /* current item or NULL */
5987 short *list; /* id list */
5988 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5989 int contained; /* group id is contained */
5990{
5991 int retval;
5992 short *scl_list;
5993 short item;
5994 short id = ssp->id;
5995 static int depth = 0;
5996 int r;
5997
5998 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005999 if (cur_si != NULL && ssp->cont_in_list != NULL
6000 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006001 {
6002 /* Ignore transparent items without a contains argument. Double check
6003 * that we don't go back past the first one. */
6004 while ((cur_si->si_flags & HL_TRANS_CONT)
6005 && cur_si > (stateitem_T *)(current_state.ga_data))
6006 --cur_si;
6007 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6008 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006009 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6010 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 return TRUE;
6012 }
6013
6014 if (list == NULL)
6015 return FALSE;
6016
6017 /*
6018 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6019 * inside anything. Only allow not-contained groups.
6020 */
6021 if (list == ID_LIST_ALL)
6022 return !contained;
6023
6024 /*
6025 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6026 * contains list. We also require that "id" is at the same ":syn include"
6027 * level as the list.
6028 */
6029 item = *list;
6030 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6031 {
6032 if (item < SYNID_TOP)
6033 {
6034 /* ALL or ALLBUT: accept all groups in the same file */
6035 if (item - SYNID_ALLBUT != ssp->inc_tag)
6036 return FALSE;
6037 }
6038 else if (item < SYNID_CONTAINED)
6039 {
6040 /* TOP: accept all not-contained groups in the same file */
6041 if (item - SYNID_TOP != ssp->inc_tag || contained)
6042 return FALSE;
6043 }
6044 else
6045 {
6046 /* CONTAINED: accept all contained groups in the same file */
6047 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6048 return FALSE;
6049 }
6050 item = *++list;
6051 retval = FALSE;
6052 }
6053 else
6054 retval = TRUE;
6055
6056 /*
6057 * Return "retval" if id is in the contains list.
6058 */
6059 while (item != 0)
6060 {
6061 if (item == id)
6062 return retval;
6063 if (item >= SYNID_CLUSTER)
6064 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006065 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006066 /* restrict recursiveness to 30 to avoid an endless loop for a
6067 * cluster that includes itself (indirectly) */
6068 if (scl_list != NULL && depth < 30)
6069 {
6070 ++depth;
6071 r = in_id_list(NULL, scl_list, ssp, contained);
6072 --depth;
6073 if (r)
6074 return retval;
6075 }
6076 }
6077 item = *++list;
6078 }
6079 return !retval;
6080}
6081
6082struct subcommand
6083{
6084 char *name; /* subcommand name */
6085 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6086};
6087
6088static struct subcommand subcommands[] =
6089{
6090 {"case", syn_cmd_case},
6091 {"clear", syn_cmd_clear},
6092 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006093 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006094 {"enable", syn_cmd_enable},
6095 {"include", syn_cmd_include},
6096 {"keyword", syn_cmd_keyword},
6097 {"list", syn_cmd_list},
6098 {"manual", syn_cmd_manual},
6099 {"match", syn_cmd_match},
6100 {"on", syn_cmd_on},
6101 {"off", syn_cmd_off},
6102 {"region", syn_cmd_region},
6103 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006104 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006105 {"sync", syn_cmd_sync},
6106 {"", syn_cmd_list},
6107 {NULL, NULL}
6108};
6109
6110/*
6111 * ":syntax".
6112 * This searches the subcommands[] table for the subcommand name, and calls a
6113 * syntax_subcommand() function to do the rest.
6114 */
6115 void
6116ex_syntax(eap)
6117 exarg_T *eap;
6118{
6119 char_u *arg = eap->arg;
6120 char_u *subcmd_end;
6121 char_u *subcmd_name;
6122 int i;
6123
6124 syn_cmdlinep = eap->cmdlinep;
6125
6126 /* isolate subcommand name */
6127 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6128 ;
6129 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6130 if (subcmd_name != NULL)
6131 {
6132 if (eap->skip) /* skip error messages for all subcommands */
6133 ++emsg_skip;
6134 for (i = 0; ; ++i)
6135 {
6136 if (subcommands[i].name == NULL)
6137 {
6138 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6139 break;
6140 }
6141 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6142 {
6143 eap->arg = skipwhite(subcmd_end);
6144 (subcommands[i].func)(eap, FALSE);
6145 break;
6146 }
6147 }
6148 vim_free(subcmd_name);
6149 if (eap->skip)
6150 --emsg_skip;
6151 }
6152}
6153
Bram Moolenaar860cae12010-06-05 23:22:07 +02006154 void
6155ex_ownsyntax(eap)
6156 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006157{
Bram Moolenaar860cae12010-06-05 23:22:07 +02006158 if (curwin->w_s == &curwin->w_buffer->b_s)
6159 {
6160 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6161 memset(curwin->w_s, 0, sizeof(synblock_T));
6162#ifdef FEAT_SPELL
6163 curwin->w_p_spell = FALSE; /* No spell checking */
6164 clear_string_option(&curwin->w_s->b_p_spc);
6165 clear_string_option(&curwin->w_s->b_p_spf);
6166 vim_free(curwin->w_s->b_cap_prog);
6167 curwin->w_s->b_cap_prog = NULL;
6168 clear_string_option(&curwin->w_s->b_p_spl);
6169#endif
6170 }
6171 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
6172}
6173
6174 int
6175syntax_present(win)
6176 win_T *win;
6177{
6178 return (win->w_s->b_syn_patterns.ga_len != 0
6179 || win->w_s->b_syn_clusters.ga_len != 0
6180 || win->w_s->b_keywtab.ht_used > 0
6181 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006182}
6183
6184#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6185
6186static enum
6187{
6188 EXP_SUBCMD, /* expand ":syn" sub-commands */
6189 EXP_CASE /* expand ":syn case" arguments */
6190} expand_what;
6191
Bram Moolenaar4f688582007-07-24 12:34:30 +00006192/*
6193 * Reset include_link, include_default, include_none to 0.
6194 * Called when we are done expanding.
6195 */
6196 void
6197reset_expand_highlight()
6198{
6199 include_link = include_default = include_none = 0;
6200}
6201
6202/*
6203 * Handle command line completion for :match and :echohl command: Add "None"
6204 * as highlight group.
6205 */
6206 void
6207set_context_in_echohl_cmd(xp, arg)
6208 expand_T *xp;
6209 char_u *arg;
6210{
6211 xp->xp_context = EXPAND_HIGHLIGHT;
6212 xp->xp_pattern = arg;
6213 include_none = 1;
6214}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006215
6216/*
6217 * Handle command line completion for :syntax command.
6218 */
6219 void
6220set_context_in_syntax_cmd(xp, arg)
6221 expand_T *xp;
6222 char_u *arg;
6223{
6224 char_u *p;
6225
6226 /* Default: expand subcommands */
6227 xp->xp_context = EXPAND_SYNTAX;
6228 expand_what = EXP_SUBCMD;
6229 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006230 include_link = 0;
6231 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006232
6233 /* (part of) subcommand already typed */
6234 if (*arg != NUL)
6235 {
6236 p = skiptowhite(arg);
6237 if (*p != NUL) /* past first word */
6238 {
6239 xp->xp_pattern = skipwhite(p);
6240 if (*skiptowhite(xp->xp_pattern) != NUL)
6241 xp->xp_context = EXPAND_NOTHING;
6242 else if (STRNICMP(arg, "case", p - arg) == 0)
6243 expand_what = EXP_CASE;
6244 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6245 || STRNICMP(arg, "region", p - arg) == 0
6246 || STRNICMP(arg, "match", p - arg) == 0
6247 || STRNICMP(arg, "list", p - arg) == 0)
6248 xp->xp_context = EXPAND_HIGHLIGHT;
6249 else
6250 xp->xp_context = EXPAND_NOTHING;
6251 }
6252 }
6253}
6254
6255static char *(case_args[]) = {"match", "ignore", NULL};
6256
6257/*
6258 * Function given to ExpandGeneric() to obtain the list syntax names for
6259 * expansion.
6260 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006261 char_u *
6262get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006263 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006264 int idx;
6265{
6266 if (expand_what == EXP_SUBCMD)
6267 return (char_u *)subcommands[idx].name;
6268 return (char_u *)case_args[idx];
6269}
6270
6271#endif /* FEAT_CMDL_COMPL */
6272
Bram Moolenaar071d4272004-06-13 20:20:40 +00006273/*
6274 * Function called for expression evaluation: get syntax ID at file position.
6275 */
6276 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006277syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006278 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006279 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006280 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006281 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006282 int *spellp; /* return: can do spell checking */
6283 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006284{
6285 /* When the position is not after the current position and in the same
6286 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006287 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006288 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006289 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006290 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006291
Bram Moolenaar860cae12010-06-05 23:22:07 +02006292 (void)get_syntax_attr(col, NULL, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006293
6294 return (trans ? current_trans_id : current_id);
6295}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006296
Bram Moolenaar860cae12010-06-05 23:22:07 +02006297#if defined(FEAT_CONCEAL) || defined(PROTO)
6298/*
6299 * Return conceal substitution character
6300 */
6301 int
6302syn_get_sub_char()
6303{
6304 return current_sub_char;
6305}
6306#endif
6307
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006308#if defined(FEAT_EVAL) || defined(PROTO)
6309/*
6310 * Return the syntax ID at position "i" in the current stack.
6311 * The caller must have called syn_get_id() before to fill the stack.
6312 * Returns -1 when "i" is out of range.
6313 */
6314 int
6315syn_get_stack_item(i)
6316 int i;
6317{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006318 if (i >= current_state.ga_len)
6319 {
6320 /* Need to invalidate the state, because we didn't properly finish it
6321 * for the last character, "keep_state" was TRUE. */
6322 invalidate_current_state();
6323 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006324 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006325 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006326 return CUR_STATE(i).si_id;
6327}
6328#endif
6329
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330#if defined(FEAT_FOLDING) || defined(PROTO)
6331/*
6332 * Function called to get folding level for line "lnum" in window "wp".
6333 */
6334 int
6335syn_get_foldlevel(wp, lnum)
6336 win_T *wp;
6337 long lnum;
6338{
6339 int level = 0;
6340 int i;
6341
6342 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006343 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006344 {
6345 syntax_start(wp, lnum);
6346
6347 for (i = 0; i < current_state.ga_len; ++i)
6348 if (CUR_STATE(i).si_flags & HL_FOLD)
6349 ++level;
6350 }
6351 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006352 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006354 if (level < 0)
6355 level = 0;
6356 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357 return level;
6358}
6359#endif
6360
6361#endif /* FEAT_SYN_HL */
6362
6363
6364/**************************************
6365 * Highlighting stuff *
6366 **************************************/
6367
6368/*
6369 * The default highlight groups. These are compiled-in for fast startup and
6370 * they still work when the runtime files can't be found.
6371 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006372 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6373 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006374 */
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006375#ifdef FEAT_GUI
6376# define CENT(a, b) b
6377#else
6378# define CENT(a, b) a
6379#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006380static char *(highlight_init_both[]) =
6381 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006382 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6383 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6384 CENT("IncSearch term=reverse cterm=reverse",
6385 "IncSearch term=reverse cterm=reverse gui=reverse"),
6386 CENT("ModeMsg term=bold cterm=bold",
6387 "ModeMsg term=bold cterm=bold gui=bold"),
6388 CENT("NonText term=bold ctermfg=Blue",
6389 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6390 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6391 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6392 CENT("StatusLineNC term=reverse cterm=reverse",
6393 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006394#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006395 CENT("VertSplit term=reverse cterm=reverse",
6396 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006397#endif
6398#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006399 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6400 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006401#endif
6402#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006403 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6404 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006405#endif
6406#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006407 CENT("PmenuThumb cterm=reverse",
6408 "PmenuThumb cterm=reverse gui=reverse"),
6409 CENT("PmenuSbar ctermbg=Grey",
6410 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006411#endif
6412#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006413 CENT("TabLineSel term=bold cterm=bold",
6414 "TabLineSel term=bold cterm=bold gui=bold"),
6415 CENT("TabLineFill term=reverse cterm=reverse",
6416 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006417#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418#ifdef FEAT_GUI
6419 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006420 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006421#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006422 NULL
6423 };
6424
6425static char *(highlight_init_light[]) =
6426 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006427 CENT("Directory term=bold ctermfg=DarkBlue",
6428 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6429 CENT("LineNr term=underline ctermfg=Brown",
6430 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6431 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6432 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6433 CENT("Question term=standout ctermfg=DarkGreen",
6434 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6435 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6436 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006437#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006438 CENT("SpellBad term=reverse ctermbg=LightRed",
6439 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6440 CENT("SpellCap term=reverse ctermbg=LightBlue",
6441 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6442 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6443 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6444 CENT("SpellLocal term=underline ctermbg=Cyan",
6445 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006446#endif
6447#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006448 CENT("Pmenu ctermbg=LightMagenta",
6449 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6450 CENT("PmenuSel ctermbg=LightGrey",
6451 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006452#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006453 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6454 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6455 CENT("Title term=bold ctermfg=DarkMagenta",
6456 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6457 CENT("WarningMsg term=standout ctermfg=DarkRed",
6458 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006459#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006460 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6461 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006462#endif
6463#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006464 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6465 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6466 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6467 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006468#endif
6469#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006470 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6471 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006472#endif
6473#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006474 CENT("Visual term=reverse",
6475 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006476#endif
6477#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006478 CENT("DiffAdd term=bold ctermbg=LightBlue",
6479 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6480 CENT("DiffChange term=bold ctermbg=LightMagenta",
6481 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6482 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6483 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006484#endif
6485#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006486 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6487 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006488#endif
6489#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006490 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006491 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006492 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006493 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006494#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006495#ifdef FEAT_CONCEAL
6496 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6497 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6498#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006499#ifdef FEAT_AUTOCMD
6500 CENT("MatchParen term=reverse ctermbg=Cyan",
6501 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6502#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006503#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006504 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006505#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006506 NULL
6507 };
6508
6509static char *(highlight_init_dark[]) =
6510 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006511 CENT("Directory term=bold ctermfg=LightCyan",
6512 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6513 CENT("LineNr term=underline ctermfg=Yellow",
6514 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6515 CENT("MoreMsg term=bold ctermfg=LightGreen",
6516 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6517 CENT("Question term=standout ctermfg=LightGreen",
6518 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6519 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6520 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6521 CENT("SpecialKey term=bold ctermfg=LightBlue",
6522 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006523#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006524 CENT("SpellBad term=reverse ctermbg=Red",
6525 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6526 CENT("SpellCap term=reverse ctermbg=Blue",
6527 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6528 CENT("SpellRare term=reverse ctermbg=Magenta",
6529 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6530 CENT("SpellLocal term=underline ctermbg=Cyan",
6531 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006532#endif
6533#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006534 CENT("Pmenu ctermbg=Magenta",
6535 "Pmenu ctermbg=Magenta guibg=Magenta"),
6536 CENT("PmenuSel ctermbg=DarkGrey",
6537 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006538#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006539 CENT("Title term=bold ctermfg=LightMagenta",
6540 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6541 CENT("WarningMsg term=standout ctermfg=LightRed",
6542 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006543#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006544 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6545 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006546#endif
6547#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006548 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6549 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6550 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6551 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006552#endif
6553#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006554 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6555 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006556#endif
6557#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006558 CENT("Visual term=reverse",
6559 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006560#endif
6561#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006562 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6563 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6564 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6565 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6566 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6567 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006568#endif
6569#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006570 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6571 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006572#endif
6573#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006574 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006575 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006576 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006577 "CursorLine term=underline cterm=underline guibg=Grey40"),
6578#endif
6579#ifdef FEAT_AUTOCMD
6580 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6581 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006582#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006583#ifdef FEAT_CONCEAL
6584 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6585 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6586#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006587#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006588 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006589#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590 NULL
6591 };
6592
6593 void
6594init_highlight(both, reset)
6595 int both; /* include groups where 'bg' doesn't matter */
6596 int reset; /* clear group first */
6597{
6598 int i;
6599 char **pp;
6600 static int had_both = FALSE;
6601#ifdef FEAT_EVAL
6602 char_u *p;
6603
6604 /*
6605 * Try finding the color scheme file. Used when a color file was loaded
6606 * and 'background' or 't_Co' is changed.
6607 */
6608 p = get_var_value((char_u *)"g:colors_name");
6609 if (p != NULL && load_colors(p) == OK)
6610 return;
6611#endif
6612
6613 /*
6614 * Didn't use a color file, use the compiled-in colors.
6615 */
6616 if (both)
6617 {
6618 had_both = TRUE;
6619 pp = highlight_init_both;
6620 for (i = 0; pp[i] != NULL; ++i)
6621 do_highlight((char_u *)pp[i], reset, TRUE);
6622 }
6623 else if (!had_both)
6624 /* Don't do anything before the call with both == TRUE from main().
6625 * Not everything has been setup then, and that call will overrule
6626 * everything anyway. */
6627 return;
6628
6629 if (*p_bg == 'l')
6630 pp = highlight_init_light;
6631 else
6632 pp = highlight_init_dark;
6633 for (i = 0; pp[i] != NULL; ++i)
6634 do_highlight((char_u *)pp[i], reset, TRUE);
6635
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006636 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006637 * depend on the number of colors available.
6638 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006639 * to avoid Statement highlighted text disappears.
6640 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006641 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006642 do_highlight((char_u *)(*p_bg == 'l'
6643 ? "Visual cterm=NONE ctermbg=LightGrey"
6644 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006645 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006646 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006647 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6648 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006649 if (*p_bg == 'l')
6650 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6651 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006652
Bram Moolenaar071d4272004-06-13 20:20:40 +00006653#ifdef FEAT_SYN_HL
6654 /*
6655 * If syntax highlighting is enabled load the highlighting for it.
6656 */
6657 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006658 {
6659 static int recursive = 0;
6660
6661 if (recursive >= 5)
6662 EMSG(_("E679: recursive loop loading syncolor.vim"));
6663 else
6664 {
6665 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006666 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006667 --recursive;
6668 }
6669 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006670#endif
6671}
6672
6673/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006674 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006675 * Return OK for success, FAIL for failure.
6676 */
6677 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006678load_colors(name)
6679 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006680{
6681 char_u *buf;
6682 int retval = FAIL;
6683 static int recursive = FALSE;
6684
6685 /* When being called recursively, this is probably because setting
6686 * 'background' caused the highlighting to be reloaded. This means it is
6687 * working, thus we should return OK. */
6688 if (recursive)
6689 return OK;
6690
6691 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006692 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006693 if (buf != NULL)
6694 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006695 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006696 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006698#ifdef FEAT_AUTOCMD
6699 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6700#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006701 }
6702 recursive = FALSE;
6703
6704 return retval;
6705}
6706
6707/*
6708 * Handle the ":highlight .." command.
6709 * When using ":hi clear" this is called recursively for each group with
6710 * "forceit" and "init" both TRUE.
6711 */
6712 void
6713do_highlight(line, forceit, init)
6714 char_u *line;
6715 int forceit;
6716 int init; /* TRUE when called for initializing */
6717{
6718 char_u *name_end;
6719 char_u *p;
6720 char_u *linep;
6721 char_u *key_start;
6722 char_u *arg_start;
6723 char_u *key = NULL, *arg = NULL;
6724 long i;
6725 int off;
6726 int len;
6727 int attr;
6728 int id;
6729 int idx;
6730 int dodefault = FALSE;
6731 int doclear = FALSE;
6732 int dolink = FALSE;
6733 int error = FALSE;
6734 int color;
6735 int is_normal_group = FALSE; /* "Normal" group */
6736#ifdef FEAT_GUI_X11
6737 int is_menu_group = FALSE; /* "Menu" group */
6738 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6739 int is_tooltip_group = FALSE; /* "Tooltip" group */
6740 int do_colors = FALSE; /* need to update colors? */
6741#else
6742# define is_menu_group 0
6743# define is_tooltip_group 0
6744#endif
6745
6746 /*
6747 * If no argument, list current highlighting.
6748 */
6749 if (ends_excmd(*line))
6750 {
6751 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6752 /* TODO: only call when the group has attributes set */
6753 highlight_list_one((int)i);
6754 return;
6755 }
6756
6757 /*
6758 * Isolate the name.
6759 */
6760 name_end = skiptowhite(line);
6761 linep = skipwhite(name_end);
6762
6763 /*
6764 * Check for "default" argument.
6765 */
6766 if (STRNCMP(line, "default", name_end - line) == 0)
6767 {
6768 dodefault = TRUE;
6769 line = linep;
6770 name_end = skiptowhite(line);
6771 linep = skipwhite(name_end);
6772 }
6773
6774 /*
6775 * Check for "clear" or "link" argument.
6776 */
6777 if (STRNCMP(line, "clear", name_end - line) == 0)
6778 doclear = TRUE;
6779 if (STRNCMP(line, "link", name_end - line) == 0)
6780 dolink = TRUE;
6781
6782 /*
6783 * ":highlight {group-name}": list highlighting for one group.
6784 */
6785 if (!doclear && !dolink && ends_excmd(*linep))
6786 {
6787 id = syn_namen2id(line, (int)(name_end - line));
6788 if (id == 0)
6789 EMSG2(_("E411: highlight group not found: %s"), line);
6790 else
6791 highlight_list_one(id);
6792 return;
6793 }
6794
6795 /*
6796 * Handle ":highlight link {from} {to}" command.
6797 */
6798 if (dolink)
6799 {
6800 char_u *from_start = linep;
6801 char_u *from_end;
6802 char_u *to_start;
6803 char_u *to_end;
6804 int from_id;
6805 int to_id;
6806
6807 from_end = skiptowhite(from_start);
6808 to_start = skipwhite(from_end);
6809 to_end = skiptowhite(to_start);
6810
6811 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6812 {
6813 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6814 from_start);
6815 return;
6816 }
6817
6818 if (!ends_excmd(*skipwhite(to_end)))
6819 {
6820 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6821 return;
6822 }
6823
6824 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6825 if (STRNCMP(to_start, "NONE", 4) == 0)
6826 to_id = 0;
6827 else
6828 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6829
6830 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6831 {
6832 /*
6833 * Don't allow a link when there already is some highlighting
6834 * for the group, unless '!' is used
6835 */
6836 if (to_id > 0 && !forceit && !init
6837 && hl_has_settings(from_id - 1, dodefault))
6838 {
6839 if (sourcing_name == NULL && !dodefault)
6840 EMSG(_("E414: group has settings, highlight link ignored"));
6841 }
6842 else
6843 {
6844 if (!init)
6845 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6846 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006847#ifdef FEAT_EVAL
6848 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6849#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006850 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006851 }
6852 }
6853
6854 /* Only call highlight_changed() once, after sourcing a syntax file */
6855 need_highlight_changed = TRUE;
6856
6857 return;
6858 }
6859
6860 if (doclear)
6861 {
6862 /*
6863 * ":highlight clear [group]" command.
6864 */
6865 line = linep;
6866 if (ends_excmd(*line))
6867 {
6868#ifdef FEAT_GUI
6869 /* First, we do not destroy the old values, but allocate the new
6870 * ones and update the display. THEN we destroy the old values.
6871 * If we destroy the old values first, then the old values
6872 * (such as GuiFont's or GuiFontset's) will still be displayed but
6873 * invalid because they were free'd.
6874 */
6875 if (gui.in_use)
6876 {
6877# ifdef FEAT_BEVAL_TIP
6878 gui_init_tooltip_font();
6879# endif
6880# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6881 gui_init_menu_font();
6882# endif
6883 }
6884# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6885 gui_mch_def_colors();
6886# endif
6887# ifdef FEAT_GUI_X11
6888# ifdef FEAT_MENU
6889
6890 /* This only needs to be done when there is no Menu highlight
6891 * group defined by default, which IS currently the case.
6892 */
6893 gui_mch_new_menu_colors();
6894# endif
6895 if (gui.in_use)
6896 {
6897 gui_new_scrollbar_colors();
6898# ifdef FEAT_BEVAL
6899 gui_mch_new_tooltip_colors();
6900# endif
6901# ifdef FEAT_MENU
6902 gui_mch_new_menu_font();
6903# endif
6904 }
6905# endif
6906
6907 /* Ok, we're done allocating the new default graphics items.
6908 * The screen should already be refreshed at this point.
6909 * It is now Ok to clear out the old data.
6910 */
6911#endif
6912#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006913 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006914#endif
6915 restore_cterm_colors();
6916
6917 /*
6918 * Clear all default highlight groups and load the defaults.
6919 */
6920 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6921 highlight_clear(idx);
6922 init_highlight(TRUE, TRUE);
6923#ifdef FEAT_GUI
6924 if (gui.in_use)
6925 highlight_gui_started();
6926#endif
6927 highlight_changed();
6928 redraw_later_clear();
6929 return;
6930 }
6931 name_end = skiptowhite(line);
6932 linep = skipwhite(name_end);
6933 }
6934
6935 /*
6936 * Find the group name in the table. If it does not exist yet, add it.
6937 */
6938 id = syn_check_group(line, (int)(name_end - line));
6939 if (id == 0) /* failed (out of memory) */
6940 return;
6941 idx = id - 1; /* index is ID minus one */
6942
6943 /* Return if "default" was used and the group already has settings. */
6944 if (dodefault && hl_has_settings(idx, TRUE))
6945 return;
6946
6947 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6948 is_normal_group = TRUE;
6949#ifdef FEAT_GUI_X11
6950 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6951 is_menu_group = TRUE;
6952 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6953 is_scrollbar_group = TRUE;
6954 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6955 is_tooltip_group = TRUE;
6956#endif
6957
6958 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6959 if (doclear || (forceit && init))
6960 {
6961 highlight_clear(idx);
6962 if (!doclear)
6963 HL_TABLE()[idx].sg_set = 0;
6964 }
6965
6966 if (!doclear)
6967 while (!ends_excmd(*linep))
6968 {
6969 key_start = linep;
6970 if (*linep == '=')
6971 {
6972 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6973 error = TRUE;
6974 break;
6975 }
6976
6977 /*
6978 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6979 * "guibg").
6980 */
6981 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6982 ++linep;
6983 vim_free(key);
6984 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6985 if (key == NULL)
6986 {
6987 error = TRUE;
6988 break;
6989 }
6990 linep = skipwhite(linep);
6991
6992 if (STRCMP(key, "NONE") == 0)
6993 {
6994 if (!init || HL_TABLE()[idx].sg_set == 0)
6995 {
6996 if (!init)
6997 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6998 highlight_clear(idx);
6999 }
7000 continue;
7001 }
7002
7003 /*
7004 * Check for the equal sign.
7005 */
7006 if (*linep != '=')
7007 {
7008 EMSG2(_("E416: missing equal sign: %s"), key_start);
7009 error = TRUE;
7010 break;
7011 }
7012 ++linep;
7013
7014 /*
7015 * Isolate the argument.
7016 */
7017 linep = skipwhite(linep);
7018 if (*linep == '\'') /* guifg='color name' */
7019 {
7020 arg_start = ++linep;
7021 linep = vim_strchr(linep, '\'');
7022 if (linep == NULL)
7023 {
7024 EMSG2(_(e_invarg2), key_start);
7025 error = TRUE;
7026 break;
7027 }
7028 }
7029 else
7030 {
7031 arg_start = linep;
7032 linep = skiptowhite(linep);
7033 }
7034 if (linep == arg_start)
7035 {
7036 EMSG2(_("E417: missing argument: %s"), key_start);
7037 error = TRUE;
7038 break;
7039 }
7040 vim_free(arg);
7041 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7042 if (arg == NULL)
7043 {
7044 error = TRUE;
7045 break;
7046 }
7047 if (*linep == '\'')
7048 ++linep;
7049
7050 /*
7051 * Store the argument.
7052 */
7053 if ( STRCMP(key, "TERM") == 0
7054 || STRCMP(key, "CTERM") == 0
7055 || STRCMP(key, "GUI") == 0)
7056 {
7057 attr = 0;
7058 off = 0;
7059 while (arg[off] != NUL)
7060 {
7061 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7062 {
7063 len = (int)STRLEN(hl_name_table[i]);
7064 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7065 {
7066 attr |= hl_attr_table[i];
7067 off += len;
7068 break;
7069 }
7070 }
7071 if (i < 0)
7072 {
7073 EMSG2(_("E418: Illegal value: %s"), arg);
7074 error = TRUE;
7075 break;
7076 }
7077 if (arg[off] == ',') /* another one follows */
7078 ++off;
7079 }
7080 if (error)
7081 break;
7082 if (*key == 'T')
7083 {
7084 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7085 {
7086 if (!init)
7087 HL_TABLE()[idx].sg_set |= SG_TERM;
7088 HL_TABLE()[idx].sg_term = attr;
7089 }
7090 }
7091 else if (*key == 'C')
7092 {
7093 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7094 {
7095 if (!init)
7096 HL_TABLE()[idx].sg_set |= SG_CTERM;
7097 HL_TABLE()[idx].sg_cterm = attr;
7098 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7099 }
7100 }
7101#ifdef FEAT_GUI
7102 else
7103 {
7104 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7105 {
7106 if (!init)
7107 HL_TABLE()[idx].sg_set |= SG_GUI;
7108 HL_TABLE()[idx].sg_gui = attr;
7109 }
7110 }
7111#endif
7112 }
7113 else if (STRCMP(key, "FONT") == 0)
7114 {
7115 /* in non-GUI fonts are simply ignored */
7116#ifdef FEAT_GUI
7117 if (!gui.shell_created)
7118 {
7119 /* GUI not started yet, always accept the name. */
7120 vim_free(HL_TABLE()[idx].sg_font_name);
7121 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7122 }
7123 else
7124 {
7125 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7126# ifdef FEAT_XFONTSET
7127 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7128# endif
7129 /* First, save the current font/fontset.
7130 * Then try to allocate the font/fontset.
7131 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7132 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7133 */
7134
7135 HL_TABLE()[idx].sg_font = NOFONT;
7136# ifdef FEAT_XFONTSET
7137 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7138# endif
7139 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7140 is_tooltip_group);
7141
7142# ifdef FEAT_XFONTSET
7143 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7144 {
7145 /* New fontset was accepted. Free the old one, if there was
7146 * one.
7147 */
7148 gui_mch_free_fontset(temp_sg_fontset);
7149 vim_free(HL_TABLE()[idx].sg_font_name);
7150 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7151 }
7152 else
7153 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7154# endif
7155 if (HL_TABLE()[idx].sg_font != NOFONT)
7156 {
7157 /* New font was accepted. Free the old one, if there was
7158 * one.
7159 */
7160 gui_mch_free_font(temp_sg_font);
7161 vim_free(HL_TABLE()[idx].sg_font_name);
7162 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7163 }
7164 else
7165 HL_TABLE()[idx].sg_font = temp_sg_font;
7166 }
7167#endif
7168 }
7169 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7170 {
7171 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7172 {
7173 if (!init)
7174 HL_TABLE()[idx].sg_set |= SG_CTERM;
7175
7176 /* When setting the foreground color, and previously the "bold"
7177 * flag was set for a light color, reset it now */
7178 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7179 {
7180 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7181 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7182 }
7183
7184 if (VIM_ISDIGIT(*arg))
7185 color = atoi((char *)arg);
7186 else if (STRICMP(arg, "fg") == 0)
7187 {
7188 if (cterm_normal_fg_color)
7189 color = cterm_normal_fg_color - 1;
7190 else
7191 {
7192 EMSG(_("E419: FG color unknown"));
7193 error = TRUE;
7194 break;
7195 }
7196 }
7197 else if (STRICMP(arg, "bg") == 0)
7198 {
7199 if (cterm_normal_bg_color > 0)
7200 color = cterm_normal_bg_color - 1;
7201 else
7202 {
7203 EMSG(_("E420: BG color unknown"));
7204 error = TRUE;
7205 break;
7206 }
7207 }
7208 else
7209 {
7210 static char *(color_names[28]) = {
7211 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7212 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7213 "Gray", "Grey",
7214 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7215 "Blue", "LightBlue", "Green", "LightGreen",
7216 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7217 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7218 static int color_numbers_16[28] = {0, 1, 2, 3,
7219 4, 5, 6, 6,
7220 7, 7,
7221 7, 7, 8, 8,
7222 9, 9, 10, 10,
7223 11, 11, 12, 12, 13,
7224 13, 14, 14, 15, -1};
7225 /* for xterm with 88 colors... */
7226 static int color_numbers_88[28] = {0, 4, 2, 6,
7227 1, 5, 32, 72,
7228 84, 84,
7229 7, 7, 82, 82,
7230 12, 43, 10, 61,
7231 14, 63, 9, 74, 13,
7232 75, 11, 78, 15, -1};
7233 /* for xterm with 256 colors... */
7234 static int color_numbers_256[28] = {0, 4, 2, 6,
7235 1, 5, 130, 130,
7236 248, 248,
7237 7, 7, 242, 242,
7238 12, 81, 10, 121,
7239 14, 159, 9, 224, 13,
7240 225, 11, 229, 15, -1};
7241 /* for terminals with less than 16 colors... */
7242 static int color_numbers_8[28] = {0, 4, 2, 6,
7243 1, 5, 3, 3,
7244 7, 7,
7245 7, 7, 0+8, 0+8,
7246 4+8, 4+8, 2+8, 2+8,
7247 6+8, 6+8, 1+8, 1+8, 5+8,
7248 5+8, 3+8, 3+8, 7+8, -1};
7249#if defined(__QNXNTO__)
7250 static int *color_numbers_8_qansi = color_numbers_8;
7251 /* On qnx, the 8 & 16 color arrays are the same */
7252 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7253 color_numbers_8_qansi = color_numbers_16;
7254#endif
7255
7256 /* reduce calls to STRICMP a bit, it can be slow */
7257 off = TOUPPER_ASC(*arg);
7258 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7259 if (off == color_names[i][0]
7260 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7261 break;
7262 if (i < 0)
7263 {
7264 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7265 error = TRUE;
7266 break;
7267 }
7268
7269 /* Use the _16 table to check if its a valid color name. */
7270 color = color_numbers_16[i];
7271 if (color >= 0)
7272 {
7273 if (t_colors == 8)
7274 {
7275 /* t_Co is 8: use the 8 colors table */
7276#if defined(__QNXNTO__)
7277 color = color_numbers_8_qansi[i];
7278#else
7279 color = color_numbers_8[i];
7280#endif
7281 if (key[5] == 'F')
7282 {
7283 /* set/reset bold attribute to get light foreground
7284 * colors (on some terminals, e.g. "linux") */
7285 if (color & 8)
7286 {
7287 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7288 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7289 }
7290 else
7291 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7292 }
7293 color &= 7; /* truncate to 8 colors */
7294 }
7295 else if (t_colors == 16 || t_colors == 88
7296 || t_colors == 256)
7297 {
7298 /*
7299 * Guess: if the termcap entry ends in 'm', it is
7300 * probably an xterm-like terminal. Use the changed
7301 * order for colors.
7302 */
7303 if (*T_CAF != NUL)
7304 p = T_CAF;
7305 else
7306 p = T_CSF;
7307 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7308 switch (t_colors)
7309 {
7310 case 16:
7311 color = color_numbers_8[i];
7312 break;
7313 case 88:
7314 color = color_numbers_88[i];
7315 break;
7316 case 256:
7317 color = color_numbers_256[i];
7318 break;
7319 }
7320 }
7321 }
7322 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007323 /* Add one to the argument, to avoid zero. Zero is used for
7324 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007325 if (key[5] == 'F')
7326 {
7327 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7328 if (is_normal_group)
7329 {
7330 cterm_normal_fg_color = color + 1;
7331 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7332#ifdef FEAT_GUI
7333 /* Don't do this if the GUI is used. */
7334 if (!gui.in_use && !gui.starting)
7335#endif
7336 {
7337 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007338 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007339 term_fg_color(color);
7340 }
7341 }
7342 }
7343 else
7344 {
7345 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7346 if (is_normal_group)
7347 {
7348 cterm_normal_bg_color = color + 1;
7349#ifdef FEAT_GUI
7350 /* Don't mess with 'background' if the GUI is used. */
7351 if (!gui.in_use && !gui.starting)
7352#endif
7353 {
7354 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007355 if (color >= 0)
7356 {
7357 if (termcap_active)
7358 term_bg_color(color);
7359 if (t_colors < 16)
7360 i = (color == 0 || color == 4);
7361 else
7362 i = (color < 7 || color == 8);
7363 /* Set the 'background' option if the value is
7364 * wrong. */
7365 if (i != (*p_bg == 'd'))
7366 set_option_value((char_u *)"bg", 0L,
7367 i ? (char_u *)"dark"
7368 : (char_u *)"light", 0);
7369 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007370 }
7371 }
7372 }
7373 }
7374 }
7375 else if (STRCMP(key, "GUIFG") == 0)
7376 {
7377#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007378 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007379 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007380 if (!init)
7381 HL_TABLE()[idx].sg_set |= SG_GUI;
7382
7383 i = color_name2handle(arg);
7384 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7385 {
7386 HL_TABLE()[idx].sg_gui_fg = i;
7387 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7388 if (STRCMP(arg, "NONE"))
7389 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7390 else
7391 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007392# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007393 if (is_menu_group)
7394 gui.menu_fg_pixel = i;
7395 if (is_scrollbar_group)
7396 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007397# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007398 if (is_tooltip_group)
7399 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007400# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007401 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007402# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007403 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007404 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007405#endif
7406 }
7407 else if (STRCMP(key, "GUIBG") == 0)
7408 {
7409#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007410 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007411 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007412 if (!init)
7413 HL_TABLE()[idx].sg_set |= SG_GUI;
7414
7415 i = color_name2handle(arg);
7416 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7417 {
7418 HL_TABLE()[idx].sg_gui_bg = i;
7419 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7420 if (STRCMP(arg, "NONE") != 0)
7421 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7422 else
7423 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007424# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007425 if (is_menu_group)
7426 gui.menu_bg_pixel = i;
7427 if (is_scrollbar_group)
7428 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007429# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007430 if (is_tooltip_group)
7431 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007432# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007433 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007434# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007435 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007436 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007437#endif
7438 }
7439 else if (STRCMP(key, "GUISP") == 0)
7440 {
7441#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
7442 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7443 {
7444 if (!init)
7445 HL_TABLE()[idx].sg_set |= SG_GUI;
7446
7447 i = color_name2handle(arg);
7448 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7449 {
7450 HL_TABLE()[idx].sg_gui_sp = i;
7451 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7452 if (STRCMP(arg, "NONE") != 0)
7453 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7454 else
7455 HL_TABLE()[idx].sg_gui_sp_name = NULL;
7456 }
7457 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007458#endif
7459 }
7460 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7461 {
7462 char_u buf[100];
7463 char_u *tname;
7464
7465 if (!init)
7466 HL_TABLE()[idx].sg_set |= SG_TERM;
7467
7468 /*
7469 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007470 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007471 */
7472 if (STRNCMP(arg, "t_", 2) == 0)
7473 {
7474 off = 0;
7475 buf[0] = 0;
7476 while (arg[off] != NUL)
7477 {
7478 /* Isolate one termcap name */
7479 for (len = 0; arg[off + len] &&
7480 arg[off + len] != ','; ++len)
7481 ;
7482 tname = vim_strnsave(arg + off, len);
7483 if (tname == NULL) /* out of memory */
7484 {
7485 error = TRUE;
7486 break;
7487 }
7488 /* lookup the escape sequence for the item */
7489 p = get_term_code(tname);
7490 vim_free(tname);
7491 if (p == NULL) /* ignore non-existing things */
7492 p = (char_u *)"";
7493
7494 /* Append it to the already found stuff */
7495 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7496 {
7497 EMSG2(_("E422: terminal code too long: %s"), arg);
7498 error = TRUE;
7499 break;
7500 }
7501 STRCAT(buf, p);
7502
7503 /* Advance to the next item */
7504 off += len;
7505 if (arg[off] == ',') /* another one follows */
7506 ++off;
7507 }
7508 }
7509 else
7510 {
7511 /*
7512 * Copy characters from arg[] to buf[], translating <> codes.
7513 */
7514 for (p = arg, off = 0; off < 100 && *p; )
7515 {
7516 len = trans_special(&p, buf + off, FALSE);
7517 if (len) /* recognized special char */
7518 off += len;
7519 else /* copy as normal char */
7520 buf[off++] = *p++;
7521 }
7522 buf[off] = NUL;
7523 }
7524 if (error)
7525 break;
7526
7527 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7528 p = NULL;
7529 else
7530 p = vim_strsave(buf);
7531 if (key[2] == 'A')
7532 {
7533 vim_free(HL_TABLE()[idx].sg_start);
7534 HL_TABLE()[idx].sg_start = p;
7535 }
7536 else
7537 {
7538 vim_free(HL_TABLE()[idx].sg_stop);
7539 HL_TABLE()[idx].sg_stop = p;
7540 }
7541 }
7542 else
7543 {
7544 EMSG2(_("E423: Illegal argument: %s"), key_start);
7545 error = TRUE;
7546 break;
7547 }
7548
7549 /*
7550 * When highlighting has been given for a group, don't link it.
7551 */
7552 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7553 HL_TABLE()[idx].sg_link = 0;
7554
7555 /*
7556 * Continue with next argument.
7557 */
7558 linep = skipwhite(linep);
7559 }
7560
7561 /*
7562 * If there is an error, and it's a new entry, remove it from the table.
7563 */
7564 if (error && idx == highlight_ga.ga_len)
7565 syn_unadd_group();
7566 else
7567 {
7568 if (is_normal_group)
7569 {
7570 HL_TABLE()[idx].sg_term_attr = 0;
7571 HL_TABLE()[idx].sg_cterm_attr = 0;
7572#ifdef FEAT_GUI
7573 HL_TABLE()[idx].sg_gui_attr = 0;
7574 /*
7575 * Need to update all groups, because they might be using "bg"
7576 * and/or "fg", which have been changed now.
7577 */
7578 if (gui.in_use)
7579 highlight_gui_started();
7580#endif
7581 }
7582#ifdef FEAT_GUI_X11
7583# ifdef FEAT_MENU
7584 else if (is_menu_group)
7585 {
7586 if (gui.in_use && do_colors)
7587 gui_mch_new_menu_colors();
7588 }
7589# endif
7590 else if (is_scrollbar_group)
7591 {
7592 if (gui.in_use && do_colors)
7593 gui_new_scrollbar_colors();
7594 }
7595# ifdef FEAT_BEVAL
7596 else if (is_tooltip_group)
7597 {
7598 if (gui.in_use && do_colors)
7599 gui_mch_new_tooltip_colors();
7600 }
7601# endif
7602#endif
7603 else
7604 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007605#ifdef FEAT_EVAL
7606 HL_TABLE()[idx].sg_scriptID = current_SID;
7607#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007608 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007609 }
7610 vim_free(key);
7611 vim_free(arg);
7612
7613 /* Only call highlight_changed() once, after sourcing a syntax file */
7614 need_highlight_changed = TRUE;
7615}
7616
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007617#if defined(EXITFREE) || defined(PROTO)
7618 void
7619free_highlight()
7620{
7621 int i;
7622
7623 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007624 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007625 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007626 vim_free(HL_TABLE()[i].sg_name);
7627 vim_free(HL_TABLE()[i].sg_name_u);
7628 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007629 ga_clear(&highlight_ga);
7630}
7631#endif
7632
Bram Moolenaar071d4272004-06-13 20:20:40 +00007633/*
7634 * Reset the cterm colors to what they were before Vim was started, if
7635 * possible. Otherwise reset them to zero.
7636 */
7637 void
7638restore_cterm_colors()
7639{
7640#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7641 /* Since t_me has been set, this probably means that the user
7642 * wants to use this as default colors. Need to reset default
7643 * background/foreground colors. */
7644 mch_set_normal_colors();
7645#else
7646 cterm_normal_fg_color = 0;
7647 cterm_normal_fg_bold = 0;
7648 cterm_normal_bg_color = 0;
7649#endif
7650}
7651
7652/*
7653 * Return TRUE if highlight group "idx" has any settings.
7654 * When "check_link" is TRUE also check for an existing link.
7655 */
7656 static int
7657hl_has_settings(idx, check_link)
7658 int idx;
7659 int check_link;
7660{
7661 return ( HL_TABLE()[idx].sg_term_attr != 0
7662 || HL_TABLE()[idx].sg_cterm_attr != 0
7663#ifdef FEAT_GUI
7664 || HL_TABLE()[idx].sg_gui_attr != 0
7665#endif
7666 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7667}
7668
7669/*
7670 * Clear highlighting for one group.
7671 */
7672 static void
7673highlight_clear(idx)
7674 int idx;
7675{
7676 HL_TABLE()[idx].sg_term = 0;
7677 vim_free(HL_TABLE()[idx].sg_start);
7678 HL_TABLE()[idx].sg_start = NULL;
7679 vim_free(HL_TABLE()[idx].sg_stop);
7680 HL_TABLE()[idx].sg_stop = NULL;
7681 HL_TABLE()[idx].sg_term_attr = 0;
7682 HL_TABLE()[idx].sg_cterm = 0;
7683 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7684 HL_TABLE()[idx].sg_cterm_fg = 0;
7685 HL_TABLE()[idx].sg_cterm_bg = 0;
7686 HL_TABLE()[idx].sg_cterm_attr = 0;
7687#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7688 HL_TABLE()[idx].sg_gui = 0;
7689 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7690 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7691 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7692 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7693 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7694 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007695 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7696 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7697 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007698 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7699 HL_TABLE()[idx].sg_font = NOFONT;
7700# ifdef FEAT_XFONTSET
7701 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7702 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7703# endif
7704 vim_free(HL_TABLE()[idx].sg_font_name);
7705 HL_TABLE()[idx].sg_font_name = NULL;
7706 HL_TABLE()[idx].sg_gui_attr = 0;
7707#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007708#ifdef FEAT_EVAL
7709 /* Clear the script ID only when there is no link, since that is not
7710 * cleared. */
7711 if (HL_TABLE()[idx].sg_link == 0)
7712 HL_TABLE()[idx].sg_scriptID = 0;
7713#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007714}
7715
7716#if defined(FEAT_GUI) || defined(PROTO)
7717/*
7718 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007719 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007720 * "Tooltip" colors.
7721 */
7722 void
7723set_normal_colors()
7724{
7725 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007726 &gui.norm_pixel, &gui.back_pixel,
7727 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007728 {
7729 gui_mch_new_colors();
7730 must_redraw = CLEAR;
7731 }
7732#ifdef FEAT_GUI_X11
7733 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007734 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7735 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007736 {
7737# ifdef FEAT_MENU
7738 gui_mch_new_menu_colors();
7739# endif
7740 must_redraw = CLEAR;
7741 }
7742# ifdef FEAT_BEVAL
7743 if (set_group_colors((char_u *)"Tooltip",
7744 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7745 FALSE, FALSE, TRUE))
7746 {
7747# ifdef FEAT_TOOLBAR
7748 gui_mch_new_tooltip_colors();
7749# endif
7750 must_redraw = CLEAR;
7751 }
7752#endif
7753 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007754 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7755 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007756 {
7757 gui_new_scrollbar_colors();
7758 must_redraw = CLEAR;
7759 }
7760#endif
7761}
7762
7763/*
7764 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7765 */
7766 static int
7767set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7768 char_u *name;
7769 guicolor_T *fgp;
7770 guicolor_T *bgp;
7771 int do_menu;
7772 int use_norm;
7773 int do_tooltip;
7774{
7775 int idx;
7776
7777 idx = syn_name2id(name) - 1;
7778 if (idx >= 0)
7779 {
7780 gui_do_one_color(idx, do_menu, do_tooltip);
7781
7782 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7783 *fgp = HL_TABLE()[idx].sg_gui_fg;
7784 else if (use_norm)
7785 *fgp = gui.def_norm_pixel;
7786 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7787 *bgp = HL_TABLE()[idx].sg_gui_bg;
7788 else if (use_norm)
7789 *bgp = gui.def_back_pixel;
7790 return TRUE;
7791 }
7792 return FALSE;
7793}
7794
7795/*
7796 * Get the font of the "Normal" group.
7797 * Returns "" when it's not found or not set.
7798 */
7799 char_u *
7800hl_get_font_name()
7801{
7802 int id;
7803 char_u *s;
7804
7805 id = syn_name2id((char_u *)"Normal");
7806 if (id > 0)
7807 {
7808 s = HL_TABLE()[id - 1].sg_font_name;
7809 if (s != NULL)
7810 return s;
7811 }
7812 return (char_u *)"";
7813}
7814
7815/*
7816 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7817 * actually chosen to be used.
7818 */
7819 void
7820hl_set_font_name(font_name)
7821 char_u *font_name;
7822{
7823 int id;
7824
7825 id = syn_name2id((char_u *)"Normal");
7826 if (id > 0)
7827 {
7828 vim_free(HL_TABLE()[id - 1].sg_font_name);
7829 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7830 }
7831}
7832
7833/*
7834 * Set background color for "Normal" group. Called by gui_set_bg_color()
7835 * when the color is known.
7836 */
7837 void
7838hl_set_bg_color_name(name)
7839 char_u *name; /* must have been allocated */
7840{
7841 int id;
7842
7843 if (name != NULL)
7844 {
7845 id = syn_name2id((char_u *)"Normal");
7846 if (id > 0)
7847 {
7848 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7849 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7850 }
7851 }
7852}
7853
7854/*
7855 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7856 * when the color is known.
7857 */
7858 void
7859hl_set_fg_color_name(name)
7860 char_u *name; /* must have been allocated */
7861{
7862 int id;
7863
7864 if (name != NULL)
7865 {
7866 id = syn_name2id((char_u *)"Normal");
7867 if (id > 0)
7868 {
7869 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7870 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7871 }
7872 }
7873}
7874
7875/*
7876 * Return the handle for a color name.
7877 * Returns INVALCOLOR when failed.
7878 */
7879 static guicolor_T
7880color_name2handle(name)
7881 char_u *name;
7882{
7883 if (STRCMP(name, "NONE") == 0)
7884 return INVALCOLOR;
7885
7886 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7887 return gui.norm_pixel;
7888 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7889 return gui.back_pixel;
7890
7891 return gui_get_color(name);
7892}
7893
7894/*
7895 * Return the handle for a font name.
7896 * Returns NOFONT when failed.
7897 */
7898 static GuiFont
7899font_name2handle(name)
7900 char_u *name;
7901{
7902 if (STRCMP(name, "NONE") == 0)
7903 return NOFONT;
7904
7905 return gui_mch_get_font(name, TRUE);
7906}
7907
7908# ifdef FEAT_XFONTSET
7909/*
7910 * Return the handle for a fontset name.
7911 * Returns NOFONTSET when failed.
7912 */
7913 static GuiFontset
7914fontset_name2handle(name, fixed_width)
7915 char_u *name;
7916 int fixed_width;
7917{
7918 if (STRCMP(name, "NONE") == 0)
7919 return NOFONTSET;
7920
7921 return gui_mch_get_fontset(name, TRUE, fixed_width);
7922}
7923# endif
7924
7925/*
7926 * Get the font or fontset for one highlight group.
7927 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007928 static void
7929hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7930 int idx;
7931 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00007932 int do_normal; /* set normal font */
7933 int do_menu UNUSED; /* set menu font */
7934 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007935{
7936# ifdef FEAT_XFONTSET
7937 /* If 'guifontset' is not empty, first try using the name as a
7938 * fontset. If that doesn't work, use it as a font name. */
7939 if (*p_guifontset != NUL
7940# ifdef FONTSET_ALWAYS
7941 || do_menu
7942# endif
7943# ifdef FEAT_BEVAL_TIP
7944 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7945 || do_tooltip
7946# endif
7947 )
7948 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7949# ifdef FONTSET_ALWAYS
7950 || do_menu
7951# endif
7952# ifdef FEAT_BEVAL_TIP
7953 || do_tooltip
7954# endif
7955 );
7956 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7957 {
7958 /* If it worked and it's the Normal group, use it as the
7959 * normal fontset. Same for the Menu group. */
7960 if (do_normal)
7961 gui_init_font(arg, TRUE);
7962# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7963 if (do_menu)
7964 {
7965# ifdef FONTSET_ALWAYS
7966 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7967# else
7968 /* YIKES! This is a bug waiting to crash the program */
7969 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7970# endif
7971 gui_mch_new_menu_font();
7972 }
7973# ifdef FEAT_BEVAL
7974 if (do_tooltip)
7975 {
7976 /* The Athena widget set cannot currently handle switching between
7977 * displaying a single font and a fontset.
7978 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007979 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00007980 * XFontStruct is used.
7981 */
7982 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7983 gui_mch_new_tooltip_font();
7984 }
7985# endif
7986# endif
7987 }
7988 else
7989# endif
7990 {
7991 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7992 /* If it worked and it's the Normal group, use it as the
7993 * normal font. Same for the Menu group. */
7994 if (HL_TABLE()[idx].sg_font != NOFONT)
7995 {
7996 if (do_normal)
7997 gui_init_font(arg, FALSE);
7998#ifndef FONTSET_ALWAYS
7999# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8000 if (do_menu)
8001 {
8002 gui.menu_font = HL_TABLE()[idx].sg_font;
8003 gui_mch_new_menu_font();
8004 }
8005# endif
8006#endif
8007 }
8008 }
8009}
8010
8011#endif /* FEAT_GUI */
8012
8013/*
8014 * Table with the specifications for an attribute number.
8015 * Note that this table is used by ALL buffers. This is required because the
8016 * GUI can redraw at any time for any buffer.
8017 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008018static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008019
8020#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8021
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008022static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008023
8024#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8025
8026#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008027static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008028
8029#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8030#endif
8031
8032/*
8033 * Return the attr number for a set of colors and font.
8034 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8035 * if the combination is new.
8036 * Return 0 for error (no more room).
8037 */
8038 static int
8039get_attr_entry(table, aep)
8040 garray_T *table;
8041 attrentry_T *aep;
8042{
8043 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008044 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008045 static int recursive = FALSE;
8046
8047 /*
8048 * Init the table, in case it wasn't done yet.
8049 */
8050 table->ga_itemsize = sizeof(attrentry_T);
8051 table->ga_growsize = 7;
8052
8053 /*
8054 * Try to find an entry with the same specifications.
8055 */
8056 for (i = 0; i < table->ga_len; ++i)
8057 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008058 taep = &(((attrentry_T *)table->ga_data)[i]);
8059 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008060 && (
8061#ifdef FEAT_GUI
8062 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008063 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8064 && aep->ae_u.gui.bg_color
8065 == taep->ae_u.gui.bg_color
8066 && aep->ae_u.gui.sp_color
8067 == taep->ae_u.gui.sp_color
8068 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008069# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008070 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008071# endif
8072 ))
8073 ||
8074#endif
8075 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008076 && (aep->ae_u.term.start == NULL)
8077 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008078 && (aep->ae_u.term.start == NULL
8079 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008080 taep->ae_u.term.start) == 0)
8081 && (aep->ae_u.term.stop == NULL)
8082 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008083 && (aep->ae_u.term.stop == NULL
8084 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008085 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008086 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008087 && aep->ae_u.cterm.fg_color
8088 == taep->ae_u.cterm.fg_color
8089 && aep->ae_u.cterm.bg_color
8090 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008091 ))
8092
8093 return i + ATTR_OFF;
8094 }
8095
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008096 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097 {
8098 /*
8099 * Running out of attribute entries! remove all attributes, and
8100 * compute new ones for all groups.
8101 * When called recursively, we are really out of numbers.
8102 */
8103 if (recursive)
8104 {
8105 EMSG(_("E424: Too many different highlighting attributes in use"));
8106 return 0;
8107 }
8108 recursive = TRUE;
8109
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008110 clear_hl_tables();
8111
Bram Moolenaar071d4272004-06-13 20:20:40 +00008112 must_redraw = CLEAR;
8113
8114 for (i = 0; i < highlight_ga.ga_len; ++i)
8115 set_hl_attr(i);
8116
8117 recursive = FALSE;
8118 }
8119
8120 /*
8121 * This is a new combination of colors and font, add an entry.
8122 */
8123 if (ga_grow(table, 1) == FAIL)
8124 return 0;
8125
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008126 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8127 vim_memset(taep, 0, sizeof(attrentry_T));
8128 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008129#ifdef FEAT_GUI
8130 if (table == &gui_attr_table)
8131 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008132 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8133 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8134 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8135 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008136# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008137 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008138# endif
8139 }
8140#endif
8141 if (table == &term_attr_table)
8142 {
8143 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008144 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008146 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008147 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008148 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008150 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008151 }
8152 else if (table == &cterm_attr_table)
8153 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008154 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8155 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008156 }
8157 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008158 return (table->ga_len - 1 + ATTR_OFF);
8159}
8160
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008161/*
8162 * Clear all highlight tables.
8163 */
8164 void
8165clear_hl_tables()
8166{
8167 int i;
8168 attrentry_T *taep;
8169
8170#ifdef FEAT_GUI
8171 ga_clear(&gui_attr_table);
8172#endif
8173 for (i = 0; i < term_attr_table.ga_len; ++i)
8174 {
8175 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8176 vim_free(taep->ae_u.term.start);
8177 vim_free(taep->ae_u.term.stop);
8178 }
8179 ga_clear(&term_attr_table);
8180 ga_clear(&cterm_attr_table);
8181}
8182
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008183#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008184/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008185 * Combine special attributes (e.g., for spelling) with other attributes
8186 * (e.g., for syntax highlighting).
8187 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008188 * This creates a new group when required.
8189 * Since we expect there to be few spelling mistakes we don't cache the
8190 * result.
8191 * Return the resulting attributes.
8192 */
8193 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008194hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008195 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008196 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008197{
8198 attrentry_T *char_aep = NULL;
8199 attrentry_T *spell_aep;
8200 attrentry_T new_en;
8201
8202 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008203 return prim_attr;
8204 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8205 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008206#ifdef FEAT_GUI
8207 if (gui.in_use)
8208 {
8209 if (char_attr > HL_ALL)
8210 char_aep = syn_gui_attr2entry(char_attr);
8211 if (char_aep != NULL)
8212 new_en = *char_aep;
8213 else
8214 {
8215 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008216 new_en.ae_u.gui.fg_color = INVALCOLOR;
8217 new_en.ae_u.gui.bg_color = INVALCOLOR;
8218 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008219 if (char_attr <= HL_ALL)
8220 new_en.ae_attr = char_attr;
8221 }
8222
Bram Moolenaar30abd282005-06-22 22:35:10 +00008223 if (prim_attr <= HL_ALL)
8224 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008225 else
8226 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008227 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008228 if (spell_aep != NULL)
8229 {
8230 new_en.ae_attr |= spell_aep->ae_attr;
8231 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8232 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8233 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8234 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8235 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8236 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8237 if (spell_aep->ae_u.gui.font != NOFONT)
8238 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8239# ifdef FEAT_XFONTSET
8240 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8241 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8242# endif
8243 }
8244 }
8245 return get_attr_entry(&gui_attr_table, &new_en);
8246 }
8247#endif
8248
8249 if (t_colors > 1)
8250 {
8251 if (char_attr > HL_ALL)
8252 char_aep = syn_cterm_attr2entry(char_attr);
8253 if (char_aep != NULL)
8254 new_en = *char_aep;
8255 else
8256 {
8257 vim_memset(&new_en, 0, sizeof(new_en));
8258 if (char_attr <= HL_ALL)
8259 new_en.ae_attr = char_attr;
8260 }
8261
Bram Moolenaar30abd282005-06-22 22:35:10 +00008262 if (prim_attr <= HL_ALL)
8263 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008264 else
8265 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008266 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008267 if (spell_aep != NULL)
8268 {
8269 new_en.ae_attr |= spell_aep->ae_attr;
8270 if (spell_aep->ae_u.cterm.fg_color > 0)
8271 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8272 if (spell_aep->ae_u.cterm.bg_color > 0)
8273 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8274 }
8275 }
8276 return get_attr_entry(&cterm_attr_table, &new_en);
8277 }
8278
8279 if (char_attr > HL_ALL)
8280 char_aep = syn_term_attr2entry(char_attr);
8281 if (char_aep != NULL)
8282 new_en = *char_aep;
8283 else
8284 {
8285 vim_memset(&new_en, 0, sizeof(new_en));
8286 if (char_attr <= HL_ALL)
8287 new_en.ae_attr = char_attr;
8288 }
8289
Bram Moolenaar30abd282005-06-22 22:35:10 +00008290 if (prim_attr <= HL_ALL)
8291 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008292 else
8293 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008294 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008295 if (spell_aep != NULL)
8296 {
8297 new_en.ae_attr |= spell_aep->ae_attr;
8298 if (spell_aep->ae_u.term.start != NULL)
8299 {
8300 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8301 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8302 }
8303 }
8304 }
8305 return get_attr_entry(&term_attr_table, &new_en);
8306}
8307#endif
8308
Bram Moolenaar071d4272004-06-13 20:20:40 +00008309#ifdef FEAT_GUI
8310
8311 attrentry_T *
8312syn_gui_attr2entry(attr)
8313 int attr;
8314{
8315 attr -= ATTR_OFF;
8316 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8317 return NULL;
8318 return &(GUI_ATTR_ENTRY(attr));
8319}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008320#endif /* FEAT_GUI */
8321
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008322/*
8323 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8324 * Only to be used when "attr" > HL_ALL.
8325 */
8326 int
8327syn_attr2attr(attr)
8328 int attr;
8329{
8330 attrentry_T *aep;
8331
8332#ifdef FEAT_GUI
8333 if (gui.in_use)
8334 aep = syn_gui_attr2entry(attr);
8335 else
8336#endif
8337 if (t_colors > 1)
8338 aep = syn_cterm_attr2entry(attr);
8339 else
8340 aep = syn_term_attr2entry(attr);
8341
8342 if (aep == NULL) /* highlighting not set */
8343 return 0;
8344 return aep->ae_attr;
8345}
8346
8347
Bram Moolenaar071d4272004-06-13 20:20:40 +00008348 attrentry_T *
8349syn_term_attr2entry(attr)
8350 int attr;
8351{
8352 attr -= ATTR_OFF;
8353 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8354 return NULL;
8355 return &(TERM_ATTR_ENTRY(attr));
8356}
8357
8358 attrentry_T *
8359syn_cterm_attr2entry(attr)
8360 int attr;
8361{
8362 attr -= ATTR_OFF;
8363 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8364 return NULL;
8365 return &(CTERM_ATTR_ENTRY(attr));
8366}
8367
8368#define LIST_ATTR 1
8369#define LIST_STRING 2
8370#define LIST_INT 3
8371
8372 static void
8373highlight_list_one(id)
8374 int id;
8375{
8376 struct hl_group *sgp;
8377 int didh = FALSE;
8378
8379 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8380
8381 didh = highlight_list_arg(id, didh, LIST_ATTR,
8382 sgp->sg_term, NULL, "term");
8383 didh = highlight_list_arg(id, didh, LIST_STRING,
8384 0, sgp->sg_start, "start");
8385 didh = highlight_list_arg(id, didh, LIST_STRING,
8386 0, sgp->sg_stop, "stop");
8387
8388 didh = highlight_list_arg(id, didh, LIST_ATTR,
8389 sgp->sg_cterm, NULL, "cterm");
8390 didh = highlight_list_arg(id, didh, LIST_INT,
8391 sgp->sg_cterm_fg, NULL, "ctermfg");
8392 didh = highlight_list_arg(id, didh, LIST_INT,
8393 sgp->sg_cterm_bg, NULL, "ctermbg");
8394
8395#ifdef FEAT_GUI
8396 didh = highlight_list_arg(id, didh, LIST_ATTR,
8397 sgp->sg_gui, NULL, "gui");
8398 didh = highlight_list_arg(id, didh, LIST_STRING,
8399 0, sgp->sg_gui_fg_name, "guifg");
8400 didh = highlight_list_arg(id, didh, LIST_STRING,
8401 0, sgp->sg_gui_bg_name, "guibg");
8402 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008403 0, sgp->sg_gui_sp_name, "guisp");
8404 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008405 0, sgp->sg_font_name, "font");
8406#endif
8407
Bram Moolenaar661b1822005-07-28 22:36:45 +00008408 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008409 {
8410 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008411 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008412 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8413 msg_putchar(' ');
8414 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8415 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008416
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008417 if (!didh)
8418 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008419#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008420 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008421 last_set_msg(sgp->sg_scriptID);
8422#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008423}
8424
8425 static int
8426highlight_list_arg(id, didh, type, iarg, sarg, name)
8427 int id;
8428 int didh;
8429 int type;
8430 int iarg;
8431 char_u *sarg;
8432 char *name;
8433{
8434 char_u buf[100];
8435 char_u *ts;
8436 int i;
8437
Bram Moolenaar661b1822005-07-28 22:36:45 +00008438 if (got_int)
8439 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008440 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8441 {
8442 ts = buf;
8443 if (type == LIST_INT)
8444 sprintf((char *)buf, "%d", iarg - 1);
8445 else if (type == LIST_STRING)
8446 ts = sarg;
8447 else /* type == LIST_ATTR */
8448 {
8449 buf[0] = NUL;
8450 for (i = 0; hl_attr_table[i] != 0; ++i)
8451 {
8452 if (iarg & hl_attr_table[i])
8453 {
8454 if (buf[0] != NUL)
8455 STRCAT(buf, ",");
8456 STRCAT(buf, hl_name_table[i]);
8457 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8458 }
8459 }
8460 }
8461
8462 (void)syn_list_header(didh,
8463 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8464 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008465 if (!got_int)
8466 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008467 if (*name != NUL)
8468 {
8469 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8470 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8471 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008472 msg_outtrans(ts);
8473 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008474 }
8475 return didh;
8476}
8477
8478#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8479/*
8480 * Return "1" if highlight group "id" has attribute "flag".
8481 * Return NULL otherwise.
8482 */
8483 char_u *
8484highlight_has_attr(id, flag, modec)
8485 int id;
8486 int flag;
8487 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8488{
8489 int attr;
8490
8491 if (id <= 0 || id > highlight_ga.ga_len)
8492 return NULL;
8493
8494#ifdef FEAT_GUI
8495 if (modec == 'g')
8496 attr = HL_TABLE()[id - 1].sg_gui;
8497 else
8498#endif
8499 if (modec == 'c')
8500 attr = HL_TABLE()[id - 1].sg_cterm;
8501 else
8502 attr = HL_TABLE()[id - 1].sg_term;
8503
8504 if (attr & flag)
8505 return (char_u *)"1";
8506 return NULL;
8507}
8508#endif
8509
8510#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8511/*
8512 * Return color name of highlight group "id".
8513 */
8514 char_u *
8515highlight_color(id, what, modec)
8516 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008517 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8519{
8520 static char_u name[20];
8521 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008522 int fg = FALSE;
8523# ifdef FEAT_GUI
8524 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008525 int font = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008526# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008527
8528 if (id <= 0 || id > highlight_ga.ga_len)
8529 return NULL;
8530
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008531 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008532 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008533# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008534 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
8535 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
8536 font = TRUE;
8537 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008538 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008539 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8540 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008541 if (modec == 'g')
8542 {
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008543 /* return font name */
8544 if (font)
8545 return HL_TABLE()[id - 1].sg_font_name;
8546
Bram Moolenaar071d4272004-06-13 20:20:40 +00008547 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008548 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008549 {
8550 guicolor_T color;
8551 long_u rgb;
8552 static char_u buf[10];
8553
8554 if (fg)
8555 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008556 else if (sp)
8557 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008558 else
8559 color = HL_TABLE()[id - 1].sg_gui_bg;
8560 if (color == INVALCOLOR)
8561 return NULL;
8562 rgb = gui_mch_get_rgb(color);
8563 sprintf((char *)buf, "#%02x%02x%02x",
8564 (unsigned)(rgb >> 16),
8565 (unsigned)(rgb >> 8) & 255,
8566 (unsigned)rgb & 255);
8567 return buf;
8568 }
8569 if (fg)
8570 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008571 if (sp)
8572 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008573 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8574 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008575 if (font || sp)
8576 return NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008577# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008578 if (modec == 'c')
8579 {
8580 if (fg)
8581 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8582 else
8583 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8584 sprintf((char *)name, "%d", n);
8585 return name;
8586 }
8587 /* term doesn't have color */
8588 return NULL;
8589}
8590#endif
8591
8592#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8593 || defined(PROTO)
8594/*
8595 * Return color name of highlight group "id" as RGB value.
8596 */
8597 long_u
8598highlight_gui_color_rgb(id, fg)
8599 int id;
8600 int fg; /* TRUE = fg, FALSE = bg */
8601{
8602 guicolor_T color;
8603
8604 if (id <= 0 || id > highlight_ga.ga_len)
8605 return 0L;
8606
8607 if (fg)
8608 color = HL_TABLE()[id - 1].sg_gui_fg;
8609 else
8610 color = HL_TABLE()[id - 1].sg_gui_bg;
8611
8612 if (color == INVALCOLOR)
8613 return 0L;
8614
8615 return gui_mch_get_rgb(color);
8616}
8617#endif
8618
8619/*
8620 * Output the syntax list header.
8621 * Return TRUE when started a new line.
8622 */
8623 static int
8624syn_list_header(did_header, outlen, id)
8625 int did_header; /* did header already */
8626 int outlen; /* length of string that comes */
8627 int id; /* highlight group id */
8628{
8629 int endcol = 19;
8630 int newline = TRUE;
8631
8632 if (!did_header)
8633 {
8634 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008635 if (got_int)
8636 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008637 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8638 endcol = 15;
8639 }
8640 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008641 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008642 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008643 if (got_int)
8644 return TRUE;
8645 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008646 else
8647 {
8648 if (msg_col >= endcol) /* wrap around is like starting a new line */
8649 newline = FALSE;
8650 }
8651
8652 if (msg_col >= endcol) /* output at least one space */
8653 endcol = msg_col + 1;
8654 if (Columns <= endcol) /* avoid hang for tiny window */
8655 endcol = Columns - 1;
8656
8657 msg_advance(endcol);
8658
8659 /* Show "xxx" with the attributes. */
8660 if (!did_header)
8661 {
8662 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8663 msg_putchar(' ');
8664 }
8665
8666 return newline;
8667}
8668
8669/*
8670 * Set the attribute numbers for a highlight group.
8671 * Called after one of the attributes has changed.
8672 */
8673 static void
8674set_hl_attr(idx)
8675 int idx; /* index in array */
8676{
8677 attrentry_T at_en;
8678 struct hl_group *sgp = HL_TABLE() + idx;
8679
8680 /* The "Normal" group doesn't need an attribute number */
8681 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8682 return;
8683
8684#ifdef FEAT_GUI
8685 /*
8686 * For the GUI mode: If there are other than "normal" highlighting
8687 * attributes, need to allocate an attr number.
8688 */
8689 if (sgp->sg_gui_fg == INVALCOLOR
8690 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008691 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008692 && sgp->sg_font == NOFONT
8693# ifdef FEAT_XFONTSET
8694 && sgp->sg_fontset == NOFONTSET
8695# endif
8696 )
8697 {
8698 sgp->sg_gui_attr = sgp->sg_gui;
8699 }
8700 else
8701 {
8702 at_en.ae_attr = sgp->sg_gui;
8703 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8704 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008705 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008706 at_en.ae_u.gui.font = sgp->sg_font;
8707# ifdef FEAT_XFONTSET
8708 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8709# endif
8710 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8711 }
8712#endif
8713 /*
8714 * For the term mode: If there are other than "normal" highlighting
8715 * attributes, need to allocate an attr number.
8716 */
8717 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8718 sgp->sg_term_attr = sgp->sg_term;
8719 else
8720 {
8721 at_en.ae_attr = sgp->sg_term;
8722 at_en.ae_u.term.start = sgp->sg_start;
8723 at_en.ae_u.term.stop = sgp->sg_stop;
8724 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8725 }
8726
8727 /*
8728 * For the color term mode: If there are other than "normal"
8729 * highlighting attributes, need to allocate an attr number.
8730 */
8731 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8732 sgp->sg_cterm_attr = sgp->sg_cterm;
8733 else
8734 {
8735 at_en.ae_attr = sgp->sg_cterm;
8736 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8737 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8738 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8739 }
8740}
8741
8742/*
8743 * Lookup a highlight group name and return it's ID.
8744 * If it is not found, 0 is returned.
8745 */
8746 int
8747syn_name2id(name)
8748 char_u *name;
8749{
8750 int i;
8751 char_u name_u[200];
8752
8753 /* Avoid using stricmp() too much, it's slow on some systems */
8754 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8755 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008756 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008757 vim_strup(name_u);
8758 for (i = highlight_ga.ga_len; --i >= 0; )
8759 if (HL_TABLE()[i].sg_name_u != NULL
8760 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8761 break;
8762 return i + 1;
8763}
8764
8765#if defined(FEAT_EVAL) || defined(PROTO)
8766/*
8767 * Return TRUE if highlight group "name" exists.
8768 */
8769 int
8770highlight_exists(name)
8771 char_u *name;
8772{
8773 return (syn_name2id(name) > 0);
8774}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008775
8776# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8777/*
8778 * Return the name of highlight group "id".
8779 * When not a valid ID return an empty string.
8780 */
8781 char_u *
8782syn_id2name(id)
8783 int id;
8784{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008785 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008786 return (char_u *)"";
8787 return HL_TABLE()[id - 1].sg_name;
8788}
8789# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008790#endif
8791
8792/*
8793 * Like syn_name2id(), but take a pointer + length argument.
8794 */
8795 int
8796syn_namen2id(linep, len)
8797 char_u *linep;
8798 int len;
8799{
8800 char_u *name;
8801 int id = 0;
8802
8803 name = vim_strnsave(linep, len);
8804 if (name != NULL)
8805 {
8806 id = syn_name2id(name);
8807 vim_free(name);
8808 }
8809 return id;
8810}
8811
8812/*
8813 * Find highlight group name in the table and return it's ID.
8814 * The argument is a pointer to the name and the length of the name.
8815 * If it doesn't exist yet, a new entry is created.
8816 * Return 0 for failure.
8817 */
8818 int
8819syn_check_group(pp, len)
8820 char_u *pp;
8821 int len;
8822{
8823 int id;
8824 char_u *name;
8825
8826 name = vim_strnsave(pp, len);
8827 if (name == NULL)
8828 return 0;
8829
8830 id = syn_name2id(name);
8831 if (id == 0) /* doesn't exist yet */
8832 id = syn_add_group(name);
8833 else
8834 vim_free(name);
8835 return id;
8836}
8837
8838/*
8839 * Add new highlight group and return it's ID.
8840 * "name" must be an allocated string, it will be consumed.
8841 * Return 0 for failure.
8842 */
8843 static int
8844syn_add_group(name)
8845 char_u *name;
8846{
8847 char_u *p;
8848
8849 /* Check that the name is ASCII letters, digits and underscore. */
8850 for (p = name; *p != NUL; ++p)
8851 {
8852 if (!vim_isprintc(*p))
8853 {
8854 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008855 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008856 return 0;
8857 }
8858 else if (!ASCII_ISALNUM(*p) && *p != '_')
8859 {
8860 /* This is an error, but since there previously was no check only
8861 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008862 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008863 MSG(_("W18: Invalid character in group name"));
8864 break;
8865 }
8866 }
8867
8868 /*
8869 * First call for this growarray: init growing array.
8870 */
8871 if (highlight_ga.ga_data == NULL)
8872 {
8873 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8874 highlight_ga.ga_growsize = 10;
8875 }
8876
8877 /*
8878 * Make room for at least one other syntax_highlight entry.
8879 */
8880 if (ga_grow(&highlight_ga, 1) == FAIL)
8881 {
8882 vim_free(name);
8883 return 0;
8884 }
8885
8886 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8887 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8888 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8889#ifdef FEAT_GUI
8890 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8891 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008892 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008893#endif
8894 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008895
8896 return highlight_ga.ga_len; /* ID is index plus one */
8897}
8898
8899/*
8900 * When, just after calling syn_add_group(), an error is discovered, this
8901 * function deletes the new name.
8902 */
8903 static void
8904syn_unadd_group()
8905{
8906 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008907 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8908 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8909}
8910
8911/*
8912 * Translate a group ID to highlight attributes.
8913 */
8914 int
8915syn_id2attr(hl_id)
8916 int hl_id;
8917{
8918 int attr;
8919 struct hl_group *sgp;
8920
8921 hl_id = syn_get_final_id(hl_id);
8922 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8923
8924#ifdef FEAT_GUI
8925 /*
8926 * Only use GUI attr when the GUI is being used.
8927 */
8928 if (gui.in_use)
8929 attr = sgp->sg_gui_attr;
8930 else
8931#endif
8932 if (t_colors > 1)
8933 attr = sgp->sg_cterm_attr;
8934 else
8935 attr = sgp->sg_term_attr;
8936
8937 return attr;
8938}
8939
8940#ifdef FEAT_GUI
8941/*
8942 * Get the GUI colors and attributes for a group ID.
8943 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8944 */
8945 int
8946syn_id2colors(hl_id, fgp, bgp)
8947 int hl_id;
8948 guicolor_T *fgp;
8949 guicolor_T *bgp;
8950{
8951 struct hl_group *sgp;
8952
8953 hl_id = syn_get_final_id(hl_id);
8954 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8955
8956 *fgp = sgp->sg_gui_fg;
8957 *bgp = sgp->sg_gui_bg;
8958 return sgp->sg_gui;
8959}
8960#endif
8961
8962/*
8963 * Translate a group ID to the final group ID (following links).
8964 */
8965 int
8966syn_get_final_id(hl_id)
8967 int hl_id;
8968{
8969 int count;
8970 struct hl_group *sgp;
8971
8972 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8973 return 0; /* Can be called from eval!! */
8974
8975 /*
8976 * Follow links until there is no more.
8977 * Look out for loops! Break after 100 links.
8978 */
8979 for (count = 100; --count >= 0; )
8980 {
8981 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8982 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8983 break;
8984 hl_id = sgp->sg_link;
8985 }
8986
8987 return hl_id;
8988}
8989
8990#ifdef FEAT_GUI
8991/*
8992 * Call this function just after the GUI has started.
8993 * It finds the font and color handles for the highlighting groups.
8994 */
8995 void
8996highlight_gui_started()
8997{
8998 int idx;
8999
9000 /* First get the colors from the "Normal" and "Menu" group, if set */
9001 set_normal_colors();
9002
9003 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9004 gui_do_one_color(idx, FALSE, FALSE);
9005
9006 highlight_changed();
9007}
9008
9009 static void
9010gui_do_one_color(idx, do_menu, do_tooltip)
9011 int idx;
9012 int do_menu; /* TRUE: might set the menu font */
9013 int do_tooltip; /* TRUE: might set the tooltip font */
9014{
9015 int didit = FALSE;
9016
9017 if (HL_TABLE()[idx].sg_font_name != NULL)
9018 {
9019 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9020 do_tooltip);
9021 didit = TRUE;
9022 }
9023 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9024 {
9025 HL_TABLE()[idx].sg_gui_fg =
9026 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9027 didit = TRUE;
9028 }
9029 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9030 {
9031 HL_TABLE()[idx].sg_gui_bg =
9032 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9033 didit = TRUE;
9034 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009035 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9036 {
9037 HL_TABLE()[idx].sg_gui_sp =
9038 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9039 didit = TRUE;
9040 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009041 if (didit) /* need to get a new attr number */
9042 set_hl_attr(idx);
9043}
9044
9045#endif
9046
9047/*
9048 * Translate the 'highlight' option into attributes in highlight_attr[] and
9049 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9050 * corresponding highlights to use on top of HLF_SNC is computed.
9051 * Called only when the 'highlight' option has been changed and upon first
9052 * screen redraw after any :highlight command.
9053 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9054 */
9055 int
9056highlight_changed()
9057{
9058 int hlf;
9059 int i;
9060 char_u *p;
9061 int attr;
9062 char_u *end;
9063 int id;
9064#ifdef USER_HIGHLIGHT
9065 char_u userhl[10];
9066# ifdef FEAT_STL_OPT
9067 int id_SNC = -1;
9068 int id_S = -1;
9069 int hlcnt;
9070# endif
9071#endif
9072 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9073
9074 need_highlight_changed = FALSE;
9075
9076 /*
9077 * Clear all attributes.
9078 */
9079 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9080 highlight_attr[hlf] = 0;
9081
9082 /*
9083 * First set all attributes to their default value.
9084 * Then use the attributes from the 'highlight' option.
9085 */
9086 for (i = 0; i < 2; ++i)
9087 {
9088 if (i)
9089 p = p_hl;
9090 else
9091 p = get_highlight_default();
9092 if (p == NULL) /* just in case */
9093 continue;
9094
9095 while (*p)
9096 {
9097 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9098 if (hl_flags[hlf] == *p)
9099 break;
9100 ++p;
9101 if (hlf == (int)HLF_COUNT || *p == NUL)
9102 return FAIL;
9103
9104 /*
9105 * Allow several hl_flags to be combined, like "bu" for
9106 * bold-underlined.
9107 */
9108 attr = 0;
9109 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9110 {
9111 if (vim_iswhite(*p)) /* ignore white space */
9112 continue;
9113
9114 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9115 return FAIL;
9116
9117 switch (*p)
9118 {
9119 case 'b': attr |= HL_BOLD;
9120 break;
9121 case 'i': attr |= HL_ITALIC;
9122 break;
9123 case '-':
9124 case 'n': /* no highlighting */
9125 break;
9126 case 'r': attr |= HL_INVERSE;
9127 break;
9128 case 's': attr |= HL_STANDOUT;
9129 break;
9130 case 'u': attr |= HL_UNDERLINE;
9131 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009132 case 'c': attr |= HL_UNDERCURL;
9133 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009134 case ':': ++p; /* highlight group name */
9135 if (attr || *p == NUL) /* no combinations */
9136 return FAIL;
9137 end = vim_strchr(p, ',');
9138 if (end == NULL)
9139 end = p + STRLEN(p);
9140 id = syn_check_group(p, (int)(end - p));
9141 if (id == 0)
9142 return FAIL;
9143 attr = syn_id2attr(id);
9144 p = end - 1;
9145#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9146 if (hlf == (int)HLF_SNC)
9147 id_SNC = syn_get_final_id(id);
9148 else if (hlf == (int)HLF_S)
9149 id_S = syn_get_final_id(id);
9150#endif
9151 break;
9152 default: return FAIL;
9153 }
9154 }
9155 highlight_attr[hlf] = attr;
9156
9157 p = skip_to_option_part(p); /* skip comma and spaces */
9158 }
9159 }
9160
9161#ifdef USER_HIGHLIGHT
9162 /* Setup the user highlights
9163 *
9164 * Temporarily utilize 10 more hl entries. Have to be in there
9165 * simultaneously in case of table overflows in get_attr_entry()
9166 */
9167# ifdef FEAT_STL_OPT
9168 if (ga_grow(&highlight_ga, 10) == FAIL)
9169 return FAIL;
9170 hlcnt = highlight_ga.ga_len;
9171 if (id_S == 0)
9172 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009173 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009174 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9175 id_S = hlcnt + 10;
9176 }
9177# endif
9178 for (i = 0; i < 9; i++)
9179 {
9180 sprintf((char *)userhl, "User%d", i + 1);
9181 id = syn_name2id(userhl);
9182 if (id == 0)
9183 {
9184 highlight_user[i] = 0;
9185# ifdef FEAT_STL_OPT
9186 highlight_stlnc[i] = 0;
9187# endif
9188 }
9189 else
9190 {
9191# ifdef FEAT_STL_OPT
9192 struct hl_group *hlt = HL_TABLE();
9193# endif
9194
9195 highlight_user[i] = syn_id2attr(id);
9196# ifdef FEAT_STL_OPT
9197 if (id_SNC == 0)
9198 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009199 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009200 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9201 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
9202# ifdef FEAT_GUI
9203 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9204# endif
9205 }
9206 else
9207 mch_memmove(&hlt[hlcnt + i],
9208 &hlt[id_SNC - 1],
9209 sizeof(struct hl_group));
9210 hlt[hlcnt + i].sg_link = 0;
9211
9212 /* Apply difference between UserX and HLF_S to HLF_SNC */
9213 hlt[hlcnt + i].sg_term ^=
9214 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9215 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9216 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9217 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9218 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9219 hlt[hlcnt + i].sg_cterm ^=
9220 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9221 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9222 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9223 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9224 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
9225# ifdef FEAT_GUI
9226 hlt[hlcnt + i].sg_gui ^=
9227 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
9228 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9229 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9230 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9231 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009232 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9233 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009234 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9235 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9236# ifdef FEAT_XFONTSET
9237 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9238 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9239# endif
9240# endif
9241 highlight_ga.ga_len = hlcnt + i + 1;
9242 set_hl_attr(hlcnt + i); /* At long last we can apply */
9243 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9244# endif
9245 }
9246 }
9247# ifdef FEAT_STL_OPT
9248 highlight_ga.ga_len = hlcnt;
9249# endif
9250
9251#endif /* USER_HIGHLIGHT */
9252
9253 return OK;
9254}
9255
Bram Moolenaar4f688582007-07-24 12:34:30 +00009256#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009257
9258static void highlight_list __ARGS((void));
9259static void highlight_list_two __ARGS((int cnt, int attr));
9260
9261/*
9262 * Handle command line completion for :highlight command.
9263 */
9264 void
9265set_context_in_highlight_cmd(xp, arg)
9266 expand_T *xp;
9267 char_u *arg;
9268{
9269 char_u *p;
9270
9271 /* Default: expand group names */
9272 xp->xp_context = EXPAND_HIGHLIGHT;
9273 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009274 include_link = 2;
9275 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009276
9277 /* (part of) subcommand already typed */
9278 if (*arg != NUL)
9279 {
9280 p = skiptowhite(arg);
9281 if (*p != NUL) /* past "default" or group name */
9282 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009283 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009284 if (STRNCMP("default", arg, p - arg) == 0)
9285 {
9286 arg = skipwhite(p);
9287 xp->xp_pattern = arg;
9288 p = skiptowhite(arg);
9289 }
9290 if (*p != NUL) /* past group name */
9291 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009292 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009293 if (arg[1] == 'i' && arg[0] == 'N')
9294 highlight_list();
9295 if (STRNCMP("link", arg, p - arg) == 0
9296 || STRNCMP("clear", arg, p - arg) == 0)
9297 {
9298 xp->xp_pattern = skipwhite(p);
9299 p = skiptowhite(xp->xp_pattern);
9300 if (*p != NUL) /* past first group name */
9301 {
9302 xp->xp_pattern = skipwhite(p);
9303 p = skiptowhite(xp->xp_pattern);
9304 }
9305 }
9306 if (*p != NUL) /* past group name(s) */
9307 xp->xp_context = EXPAND_NOTHING;
9308 }
9309 }
9310 }
9311}
9312
9313/*
9314 * List highlighting matches in a nice way.
9315 */
9316 static void
9317highlight_list()
9318{
9319 int i;
9320
9321 for (i = 10; --i >= 0; )
9322 highlight_list_two(i, hl_attr(HLF_D));
9323 for (i = 40; --i >= 0; )
9324 highlight_list_two(99, 0);
9325}
9326
9327 static void
9328highlight_list_two(cnt, attr)
9329 int cnt;
9330 int attr;
9331{
9332 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9333 msg_clr_eos();
9334 out_flush();
9335 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9336}
9337
9338#endif /* FEAT_CMDL_COMPL */
9339
9340#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9341 || defined(FEAT_SIGNS) || defined(PROTO)
9342/*
9343 * Function given to ExpandGeneric() to obtain the list of group names.
9344 * Also used for synIDattr() function.
9345 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009346 char_u *
9347get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009348 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009349 int idx;
9350{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009351#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009352 if (idx == highlight_ga.ga_len && include_none != 0)
9353 return (char_u *)"none";
9354 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009356 if (idx == highlight_ga.ga_len + include_none + include_default
9357 && include_link != 0)
9358 return (char_u *)"link";
9359 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9360 && include_link != 0)
9361 return (char_u *)"clear";
9362#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009363 if (idx < 0 || idx >= highlight_ga.ga_len)
9364 return NULL;
9365 return HL_TABLE()[idx].sg_name;
9366}
9367#endif
9368
Bram Moolenaar4f688582007-07-24 12:34:30 +00009369#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009370/*
9371 * Free all the highlight group fonts.
9372 * Used when quitting for systems which need it.
9373 */
9374 void
9375free_highlight_fonts()
9376{
9377 int idx;
9378
9379 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9380 {
9381 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9382 HL_TABLE()[idx].sg_font = NOFONT;
9383# ifdef FEAT_XFONTSET
9384 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9385 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9386# endif
9387 }
9388
9389 gui_mch_free_font(gui.norm_font);
9390# ifdef FEAT_XFONTSET
9391 gui_mch_free_fontset(gui.fontset);
9392# endif
9393# ifndef HAVE_GTK2
9394 gui_mch_free_font(gui.bold_font);
9395 gui_mch_free_font(gui.ital_font);
9396 gui_mch_free_font(gui.boldital_font);
9397# endif
9398}
9399#endif
9400
9401/**************************************
9402 * End of Highlighting stuff *
9403 **************************************/