blob: 57fab0c9a6ec902ad330ff2603993b089ad231ed [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
36#ifdef FEAT_GUI
37/* for when using the GUI */
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020071#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
72
Bram Moolenaar071d4272004-06-13 20:20:40 +000073#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000074/* Flags to indicate an additional string for highlight name completion. */
75static int include_none = 0; /* when 1 include "None" */
76static int include_default = 0; /* when 1 include "default" */
77static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000078#endif
79
80/*
81 * The "term", "cterm" and "gui" arguments can be any combination of the
82 * following names, separated by commas (but no spaces!).
83 */
84static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000085 {"bold", "standout", "underline", "undercurl",
86 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
90static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
91static void syn_unadd_group __ARGS((void));
92static void set_hl_attr __ARGS((int idx));
93static void highlight_list_one __ARGS((int id));
94static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
95static int syn_add_group __ARGS((char_u *name));
96static int syn_list_header __ARGS((int did_header, int outlen, int id));
97static int hl_has_settings __ARGS((int idx, int check_link));
98static void highlight_clear __ARGS((int idx));
99
100#ifdef FEAT_GUI
101static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
102static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
103static guicolor_T color_name2handle __ARGS((char_u *name));
104static GuiFont font_name2handle __ARGS((char_u *name));
105# ifdef FEAT_XFONTSET
106static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
107# endif
108static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
109#endif
110
111/*
112 * An attribute number is the index in attr_table plus ATTR_OFF.
113 */
114#define ATTR_OFF (HL_ALL + 1)
115
116#if defined(FEAT_SYN_HL) || defined(PROTO)
117
118#define SYN_NAMELEN 50 /* maximum length of a syntax name */
119
120/* different types of offsets that are possible */
121#define SPO_MS_OFF 0 /* match start offset */
122#define SPO_ME_OFF 1 /* match end offset */
123#define SPO_HS_OFF 2 /* highl. start offset */
124#define SPO_HE_OFF 3 /* highl. end offset */
125#define SPO_RS_OFF 4 /* region start offset */
126#define SPO_RE_OFF 5 /* region end offset */
127#define SPO_LC_OFF 6 /* leading context offset */
128#define SPO_COUNT 7
129
130static char *(spo_name_tab[SPO_COUNT]) =
131 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
132
133/*
134 * The patterns that are being searched for are stored in a syn_pattern.
135 * A match item consists of one pattern.
136 * A start/end item consists of n start patterns and m end patterns.
137 * A start/skip/end item consists of n start patterns, one skip pattern and m
138 * end patterns.
139 * For the latter two, the patterns are always consecutive: start-skip-end.
140 *
141 * A character offset can be given for the matched text (_m_start and _m_end)
142 * and for the actually highlighted text (_h_start and _h_end).
143 */
144typedef struct syn_pattern
145{
146 char sp_type; /* see SPTYPE_ defines below */
147 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200148 int sp_flags; /* see HL_ defines below */
149#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200150 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 struct sp_syn sp_syn; /* struct passed to in_id_list() */
153 short sp_syn_match_id; /* highlight group ID of pattern */
154 char_u *sp_pattern; /* regexp to match, pattern */
155 regprog_T *sp_prog; /* regexp to match, program */
156 int sp_ic; /* ignore-case flag for sp_prog */
157 short sp_off_flags; /* see below */
158 int sp_offsets[SPO_COUNT]; /* offsets */
159 short *sp_cont_list; /* cont. group IDs, if non-zero */
160 short *sp_next_list; /* next group IDs, if non-zero */
161 int sp_sync_idx; /* sync item index (syncing only) */
162 int sp_line_id; /* ID of last line where tried */
163 int sp_startcol; /* next match in sp_line_id line */
164} synpat_T;
165
166/* The sp_off_flags are computed like this:
167 * offset from the start of the matched text: (1 << SPO_XX_OFF)
168 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
169 * When both are present, only one is used.
170 */
171
172#define SPTYPE_MATCH 1 /* match keyword with this group ID */
173#define SPTYPE_START 2 /* match a regexp, start of item */
174#define SPTYPE_END 3 /* match a regexp, end of item */
175#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
176
Bram Moolenaar071d4272004-06-13 20:20:40 +0000177
178#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
179
180#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
181
182/*
183 * Flags for b_syn_sync_flags:
184 */
185#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
186#define SF_MATCH 0x02 /* sync by matching a pattern */
187
188#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
189
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190#define MAXKEYWLEN 80 /* maximum length of a keyword */
191
192/*
193 * The attributes of the syntax item that has been recognized.
194 */
195static int current_attr = 0; /* attr of current syntax word */
196#ifdef FEAT_EVAL
197static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000198static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200200#ifdef FEAT_CONCEAL
201static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200202static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200203static int current_sub_char = 0;
204#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205
Bram Moolenaar217ad922005-03-20 22:37:15 +0000206typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207{
208 char_u *scl_name; /* syntax cluster name */
209 char_u *scl_name_u; /* uppercase of scl_name */
210 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000211} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212
213/*
214 * Methods of combining two clusters
215 */
216#define CLUSTER_REPLACE 1 /* replace first list with second */
217#define CLUSTER_ADD 2 /* add second list to first */
218#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
219
Bram Moolenaar217ad922005-03-20 22:37:15 +0000220#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221
222/*
223 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200224 * 0 - 19999 normal syntax groups
225 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
226 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
227 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
228 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200230#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200231#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
232#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
233#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
234
Bram Moolenaar42431a72011-04-01 14:44:59 +0200235#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
236#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237
238/*
239 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
240 * expand_filename(). Most of the other syntax commands don't need it, so
241 * instead of passing it to them, we stow it here.
242 */
243static char_u **syn_cmdlinep;
244
245/*
246 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200247 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 * rules in each ":syn include"'d file.
249 */
250static int current_syn_inc_tag = 0;
251static int running_syn_inc_tag = 0;
252
253/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000254 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
255 * This avoids adding a pointer to the hashtable item.
256 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
257 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
258 * HI2KE() converts a hashitem pointer to a var pointer.
259 */
260static keyentry_T dumkey;
261#define KE2HIKEY(kp) ((kp)->keyword)
262#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
263#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
264
265/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266 * To reduce the time spent in keepend(), remember at which level in the state
267 * stack the first item with "keepend" is present. When "-1", there is no
268 * "keepend" on the stack.
269 */
270static int keepend_level = -1;
271
272/*
273 * For the current state we need to remember more than just the idx.
274 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
275 * (The end positions have the column number of the next char)
276 */
277typedef struct state_item
278{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000279 int si_idx; /* index of syntax pattern or
280 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000282 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 int si_m_lnum; /* lnum of the match */
284 int si_m_startcol; /* starting column of the match */
285 lpos_T si_m_endpos; /* just after end posn of the match */
286 lpos_T si_h_startpos; /* start position of the highlighting */
287 lpos_T si_h_endpos; /* end position of the highlighting */
288 lpos_T si_eoe_pos; /* end position of end pattern */
289 int si_end_idx; /* group ID for end pattern or zero */
290 int si_ends; /* if match ends before si_m_endpos */
291 int si_attr; /* attributes in this state */
292 long si_flags; /* HL_HAS_EOL flag in this state, and
293 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200294#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200295 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200296 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200297#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298 short *si_cont_list; /* list of contained groups */
299 short *si_next_list; /* nextgroup IDs after this item ends */
300 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
301 * pattern */
302} stateitem_T;
303
304#define KEYWORD_IDX -1 /* value of si_idx for keywords */
305#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
306 but contained groups */
307
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200308#ifdef FEAT_CONCEAL
309static int next_seqnr = 0; /* value to use for si_seqnr */
310#endif
311
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000313 * Struct to reduce the number of arguments to get_syn_options(), it's used
314 * very often.
315 */
316typedef struct
317{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000318 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000319 int keyword; /* TRUE for ":syn keyword" */
320 int *sync_idx; /* syntax item for "grouphere" argument, NULL
321 if not allowed */
322 char has_cont_list; /* TRUE if "cont_list" can be used */
323 short *cont_list; /* group IDs for "contains" argument */
324 short *cont_in_list; /* group IDs for "containedin" argument */
325 short *next_list; /* group IDs for "nextgroup" argument */
326} syn_opt_arg_T;
327
328/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329 * The next possible match in the current line for any pattern is remembered,
330 * to avoid having to try for a match in each column.
331 * If next_match_idx == -1, not tried (in this line) yet.
332 * If next_match_col == MAXCOL, no match found in this line.
333 * (All end positions have the column of the char after the end)
334 */
335static int next_match_col; /* column for start of next match */
336static lpos_T next_match_m_endpos; /* position for end of next match */
337static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
338static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
339static int next_match_idx; /* index of matched item */
340static long next_match_flags; /* flags for next match */
341static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
342static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
343static int next_match_end_idx; /* ID of group for end pattn or zero */
344static reg_extmatch_T *next_match_extmatch = NULL;
345
346/*
347 * A state stack is an array of integers or stateitem_T, stored in a
348 * garray_T. A state stack is invalid if it's itemsize entry is zero.
349 */
350#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
351#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
352
353/*
354 * The current state (within the line) of the recognition engine.
355 * When current_state.ga_itemsize is 0 the current state is invalid.
356 */
357static win_T *syn_win; /* current window for highlighting */
358static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200359static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360static linenr_T current_lnum = 0; /* lnum of current state */
361static colnr_T current_col = 0; /* column of current state */
362static int current_state_stored = 0; /* TRUE if stored current state
363 * after setting current_finished */
364static int current_finished = 0; /* current line has been finished */
365static garray_T current_state /* current stack of state_items */
366 = {0, 0, 0, 0, NULL};
367static short *current_next_list = NULL; /* when non-zero, nextgroup list */
368static int current_next_flags = 0; /* flags for current_next_list */
369static int current_line_id = 0; /* unique number for current line */
370
371#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
372
373static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
374static int syn_match_linecont __ARGS((linenr_T lnum));
375static void syn_start_line __ARGS((void));
376static void syn_update_ends __ARGS((int startofline));
377static void syn_stack_alloc __ARGS((void));
378static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200379static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000381static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000382static void load_current_state __ARGS((synstate_T *from));
383static void invalidate_current_state __ARGS((void));
384static int syn_stack_equal __ARGS((synstate_T *sp));
385static void validate_current_state __ARGS((void));
386static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000387static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000388static int did_match_already __ARGS((int idx, garray_T *gap));
389static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
390static void check_state_ends __ARGS((void));
391static void update_si_attr __ARGS((int idx));
392static void check_keepend __ARGS((void));
393static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
394static short *copy_id_list __ARGS((short *list));
395static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
396static int push_current_state __ARGS((int idx));
397static void pop_current_state __ARGS((void));
398
Bram Moolenaar860cae12010-06-05 23:22:07 +0200399static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400static 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));
401static void clear_syn_state __ARGS((synstate_T *p));
402static void clear_current_state __ARGS((void));
403
404static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
405static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
406static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
407static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
408static char_u *syn_getcurline __ARGS((void));
409static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200410static 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 +0000411static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000412static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200414static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
415static void syn_clear_pattern __ARGS((synblock_T *block, int i));
416static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200418static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419static void syn_clear_one __ARGS((int id, int syncing));
420static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
421static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
422static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
423static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
424static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
425static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
426static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
427static void syn_lines_msg __ARGS((void));
428static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200429static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430static void syn_list_one __ARGS((int id, int syncing, int link_only));
431static void syn_list_cluster __ARGS((int id));
432static void put_id_list __ARGS((char_u *name, short *list, int attr));
433static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000434static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
435static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
436static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200437static 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 +0000438static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200439static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
441static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
442static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
443static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
444#ifdef __BORLANDC__
445static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
446#else
447static int syn_compare_stub __ARGS((const void *v1, const void *v2));
448#endif
449static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
450static int syn_scl_name2id __ARGS((char_u *name));
451static int syn_scl_namen2id __ARGS((char_u *linep, int len));
452static int syn_check_cluster __ARGS((char_u *pp, int len));
453static int syn_add_cluster __ARGS((char_u *name));
454static void init_syn_patterns __ARGS((void));
455static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
456static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
457static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
458static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
459static void syn_incl_toplevel __ARGS((int id, int *flagsp));
460
461/*
462 * Start the syntax recognition for a line. This function is normally called
463 * from the screen updating, once for each displayed line.
464 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
465 * it. Careful: curbuf and curwin are likely to point to another buffer and
466 * window.
467 */
468 void
469syntax_start(wp, lnum)
470 win_T *wp;
471 linenr_T lnum;
472{
473 synstate_T *p;
474 synstate_T *last_valid = NULL;
475 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000476 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 linenr_T parsed_lnum;
478 linenr_T first_stored;
479 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000480 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200482#ifdef FEAT_CONCEAL
483 current_sub_char = NUL;
484#endif
485
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486 /*
487 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000488 * Also do this when a change was made, the current state may be invalid
489 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200491 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 {
493 invalidate_current_state();
494 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200495 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000496 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000497 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 syn_win = wp;
499
500 /*
501 * Allocate syntax stack when needed.
502 */
503 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200504 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000505 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200506 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507
508 /*
509 * If the state of the end of the previous line is useful, store it.
510 */
511 if (VALID_STATE(&current_state)
512 && current_lnum < lnum
513 && current_lnum < syn_buf->b_ml.ml_line_count)
514 {
515 (void)syn_finish_line(FALSE);
516 if (!current_state_stored)
517 {
518 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000519 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 }
521
522 /*
523 * If the current_lnum is now the same as "lnum", keep the current
524 * state (this happens very often!). Otherwise invalidate
525 * current_state and figure it out below.
526 */
527 if (current_lnum != lnum)
528 invalidate_current_state();
529 }
530 else
531 invalidate_current_state();
532
533 /*
534 * Try to synchronize from a saved state in b_sst_array[].
535 * Only do this if lnum is not before and not to far beyond a saved state.
536 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200537 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538 {
539 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 {
542 if (p->sst_lnum > lnum)
543 break;
544 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
545 {
546 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200547 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 last_min_valid = p;
549 }
550 }
551 if (last_min_valid != NULL)
552 load_current_state(last_min_valid);
553 }
554
555 /*
556 * If "lnum" is before or far beyond a line with a saved state, need to
557 * re-synchronize.
558 */
559 if (INVALID_STATE(&current_state))
560 {
561 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200562 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000563 }
564 else
565 first_stored = current_lnum;
566
567 /*
568 * Advance from the sync point or saved state until the current line.
569 * Save some entries for syncing with later on.
570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200571 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000572 dist = 999999;
573 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 while (current_lnum < lnum)
576 {
577 syn_start_line();
578 (void)syn_finish_line(FALSE);
579 ++current_lnum;
580
581 /* If we parsed at least "minlines" lines or started at a valid
582 * state, the current state is considered valid. */
583 if (current_lnum >= first_stored)
584 {
585 /* Check if the saved state entry is for the current line and is
586 * equal to the current state. If so, then validate all saved
587 * states that depended on a change before the parsed line. */
588 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000589 prev = syn_stack_find_entry(current_lnum - 1);
590 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200591 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000593 sp = prev;
594 while (sp != NULL && sp->sst_lnum < current_lnum)
595 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 if (sp != NULL
597 && sp->sst_lnum == current_lnum
598 && syn_stack_equal(sp))
599 {
600 parsed_lnum = current_lnum;
601 prev = sp;
602 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
603 {
604 if (sp->sst_lnum <= lnum)
605 /* valid state before desired line, use this one */
606 prev = sp;
607 else if (sp->sst_change_lnum == 0)
608 /* past saved states depending on change, break here. */
609 break;
610 sp->sst_change_lnum = 0;
611 sp = sp->sst_next;
612 }
613 load_current_state(prev);
614 }
615 /* Store the state at this line when it's the first one, the line
616 * where we start parsing, or some distance from the previously
617 * saved state. But only when parsed at least 'minlines'. */
618 else if (prev == NULL
619 || current_lnum == lnum
620 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000621 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 }
623
624 /* This can take a long time: break when CTRL-C pressed. The current
625 * state will be wrong then. */
626 line_breakcheck();
627 if (got_int)
628 {
629 current_lnum = lnum;
630 break;
631 }
632 }
633
634 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635}
636
637/*
638 * We cannot simply discard growarrays full of state_items or buf_states; we
639 * have to manually release their extmatch pointers first.
640 */
641 static void
642clear_syn_state(p)
643 synstate_T *p;
644{
645 int i;
646 garray_T *gap;
647
648 if (p->sst_stacksize > SST_FIX_STATES)
649 {
650 gap = &(p->sst_union.sst_ga);
651 for (i = 0; i < gap->ga_len; i++)
652 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
653 ga_clear(gap);
654 }
655 else
656 {
657 for (i = 0; i < p->sst_stacksize; i++)
658 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
659 }
660}
661
662/*
663 * Cleanup the current_state stack.
664 */
665 static void
666clear_current_state()
667{
668 int i;
669 stateitem_T *sip;
670
671 sip = (stateitem_T *)(current_state.ga_data);
672 for (i = 0; i < current_state.ga_len; i++)
673 unref_extmatch(sip[i].si_extmatch);
674 ga_clear(&current_state);
675}
676
677/*
678 * Try to find a synchronisation point for line "lnum".
679 *
680 * This sets current_lnum and the current state. One of three methods is
681 * used:
682 * 1. Search backwards for the end of a C-comment.
683 * 2. Search backwards for given sync patterns.
684 * 3. Simply start on a given number of lines above "lnum".
685 */
686 static void
687syn_sync(wp, start_lnum, last_valid)
688 win_T *wp;
689 linenr_T start_lnum;
690 synstate_T *last_valid;
691{
692 buf_T *curbuf_save;
693 win_T *curwin_save;
694 pos_T cursor_save;
695 int idx;
696 linenr_T lnum;
697 linenr_T end_lnum;
698 linenr_T break_lnum;
699 int had_sync_point;
700 stateitem_T *cur_si;
701 synpat_T *spp;
702 char_u *line;
703 int found_flags = 0;
704 int found_match_idx = 0;
705 linenr_T found_current_lnum = 0;
706 int found_current_col= 0;
707 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000708 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709
710 /*
711 * Clear any current state that might be hanging around.
712 */
713 invalidate_current_state();
714
715 /*
716 * Start at least "minlines" back. Default starting point for parsing is
717 * there.
718 * Start further back, to avoid that scrolling backwards will result in
719 * resyncing for every line. Now it resyncs only one out of N lines,
720 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
721 * Watch out for overflow when minlines is MAXLNUM.
722 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200723 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000724 start_lnum = 1;
725 else
726 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200727 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200729 else if (syn_block->b_syn_sync_minlines < 10)
730 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200732 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
733 if (syn_block->b_syn_sync_maxlines != 0
734 && lnum > syn_block->b_syn_sync_maxlines)
735 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 if (lnum >= start_lnum)
737 start_lnum = 1;
738 else
739 start_lnum -= lnum;
740 }
741 current_lnum = start_lnum;
742
743 /*
744 * 1. Search backwards for the end of a C-style comment.
745 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200746 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747 {
748 /* Need to make syn_buf the current buffer for a moment, to be able to
749 * use find_start_comment(). */
750 curwin_save = curwin;
751 curwin = wp;
752 curbuf_save = curbuf;
753 curbuf = syn_buf;
754
755 /*
756 * Skip lines that end in a backslash.
757 */
758 for ( ; start_lnum > 1; --start_lnum)
759 {
760 line = ml_get(start_lnum - 1);
761 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
762 break;
763 }
764 current_lnum = start_lnum;
765
766 /* set cursor to start of search */
767 cursor_save = wp->w_cursor;
768 wp->w_cursor.lnum = start_lnum;
769 wp->w_cursor.col = 0;
770
771 /*
772 * If the line is inside a comment, need to find the syntax item that
773 * defines the comment.
774 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
775 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200776 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200778 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
779 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
780 == syn_block->b_syn_sync_id
781 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 {
783 validate_current_state();
784 if (push_current_state(idx) == OK)
785 update_si_attr(current_state.ga_len - 1);
786 break;
787 }
788 }
789
790 /* restore cursor and buffer */
791 wp->w_cursor = cursor_save;
792 curwin = curwin_save;
793 curbuf = curbuf_save;
794 }
795
796 /*
797 * 2. Search backwards for given sync patterns.
798 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200799 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200801 if (syn_block->b_syn_sync_maxlines != 0
802 && start_lnum > syn_block->b_syn_sync_maxlines)
803 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 else
805 break_lnum = 0;
806
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000807 found_m_endpos.lnum = 0;
808 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 end_lnum = start_lnum;
810 lnum = start_lnum;
811 while (--lnum > break_lnum)
812 {
813 /* This can take a long time: break when CTRL-C pressed. */
814 line_breakcheck();
815 if (got_int)
816 {
817 invalidate_current_state();
818 current_lnum = start_lnum;
819 break;
820 }
821
822 /* Check if we have run into a valid saved state stack now. */
823 if (last_valid != NULL && lnum == last_valid->sst_lnum)
824 {
825 load_current_state(last_valid);
826 break;
827 }
828
829 /*
830 * Check if the previous line has the line-continuation pattern.
831 */
832 if (lnum > 1 && syn_match_linecont(lnum - 1))
833 continue;
834
835 /*
836 * Start with nothing on the state stack
837 */
838 validate_current_state();
839
840 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
841 {
842 syn_start_line();
843 for (;;)
844 {
845 had_sync_point = syn_finish_line(TRUE);
846 /*
847 * When a sync point has been found, remember where, and
848 * continue to look for another one, further on in the line.
849 */
850 if (had_sync_point && current_state.ga_len)
851 {
852 cur_si = &CUR_STATE(current_state.ga_len - 1);
853 if (cur_si->si_m_endpos.lnum > start_lnum)
854 {
855 /* ignore match that goes to after where started */
856 current_lnum = end_lnum;
857 break;
858 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000859 if (cur_si->si_idx < 0)
860 {
861 /* Cannot happen? */
862 found_flags = 0;
863 found_match_idx = KEYWORD_IDX;
864 }
865 else
866 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200867 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000868 found_flags = spp->sp_flags;
869 found_match_idx = spp->sp_sync_idx;
870 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871 found_current_lnum = current_lnum;
872 found_current_col = current_col;
873 found_m_endpos = cur_si->si_m_endpos;
874 /*
875 * Continue after the match (be aware of a zero-length
876 * match).
877 */
878 if (found_m_endpos.lnum > current_lnum)
879 {
880 current_lnum = found_m_endpos.lnum;
881 current_col = found_m_endpos.col;
882 if (current_lnum >= end_lnum)
883 break;
884 }
885 else if (found_m_endpos.col > current_col)
886 current_col = found_m_endpos.col;
887 else
888 ++current_col;
889
890 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000891 * an item that ends here, need to do that now. Be
892 * careful not to go past the NUL. */
893 prev_current_col = current_col;
894 if (syn_getcurline()[current_col] != NUL)
895 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000897 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000898 }
899 else
900 break;
901 }
902 }
903
904 /*
905 * If a sync point was encountered, break here.
906 */
907 if (found_flags)
908 {
909 /*
910 * Put the item that was specified by the sync point on the
911 * state stack. If there was no item specified, make the
912 * state stack empty.
913 */
914 clear_current_state();
915 if (found_match_idx >= 0
916 && push_current_state(found_match_idx) == OK)
917 update_si_attr(current_state.ga_len - 1);
918
919 /*
920 * When using "grouphere", continue from the sync point
921 * match, until the end of the line. Parsing starts at
922 * the next line.
923 * For "groupthere" the parsing starts at start_lnum.
924 */
925 if (found_flags & HL_SYNC_HERE)
926 {
927 if (current_state.ga_len)
928 {
929 cur_si = &CUR_STATE(current_state.ga_len - 1);
930 cur_si->si_h_startpos.lnum = found_current_lnum;
931 cur_si->si_h_startpos.col = found_current_col;
932 update_si_end(cur_si, (int)current_col, TRUE);
933 check_keepend();
934 }
935 current_col = found_m_endpos.col;
936 current_lnum = found_m_endpos.lnum;
937 (void)syn_finish_line(FALSE);
938 ++current_lnum;
939 }
940 else
941 current_lnum = start_lnum;
942
943 break;
944 }
945
946 end_lnum = lnum;
947 invalidate_current_state();
948 }
949
950 /* Ran into start of the file or exceeded maximum number of lines */
951 if (lnum <= break_lnum)
952 {
953 invalidate_current_state();
954 current_lnum = break_lnum + 1;
955 }
956 }
957
958 validate_current_state();
959}
960
961/*
962 * Return TRUE if the line-continuation pattern matches in line "lnum".
963 */
964 static int
965syn_match_linecont(lnum)
966 linenr_T lnum;
967{
968 regmmatch_T regmatch;
969
Bram Moolenaar860cae12010-06-05 23:22:07 +0200970 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200972 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
973 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000974 return syn_regexec(&regmatch, lnum, (colnr_T)0);
975 }
976 return FALSE;
977}
978
979/*
980 * Prepare the current state for the start of a line.
981 */
982 static void
983syn_start_line()
984{
985 current_finished = FALSE;
986 current_col = 0;
987
988 /*
989 * Need to update the end of a start/skip/end that continues from the
990 * previous line and regions that have "keepend".
991 */
992 if (current_state.ga_len > 0)
993 syn_update_ends(TRUE);
994
995 next_match_idx = -1;
996 ++current_line_id;
997}
998
999/*
1000 * Check for items in the stack that need their end updated.
1001 * When "startofline" is TRUE the last item is always updated.
1002 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1003 */
1004 static void
1005syn_update_ends(startofline)
1006 int startofline;
1007{
1008 stateitem_T *cur_si;
1009 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001010 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011
1012 if (startofline)
1013 {
1014 /* Check for a match carried over from a previous line with a
1015 * contained region. The match ends as soon as the region ends. */
1016 for (i = 0; i < current_state.ga_len; ++i)
1017 {
1018 cur_si = &CUR_STATE(i);
1019 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001020 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021 == SPTYPE_MATCH
1022 && cur_si->si_m_endpos.lnum < current_lnum)
1023 {
1024 cur_si->si_flags |= HL_MATCHCONT;
1025 cur_si->si_m_endpos.lnum = 0;
1026 cur_si->si_m_endpos.col = 0;
1027 cur_si->si_h_endpos = cur_si->si_m_endpos;
1028 cur_si->si_ends = TRUE;
1029 }
1030 }
1031 }
1032
1033 /*
1034 * Need to update the end of a start/skip/end that continues from the
1035 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001036 * influence contained items. If we've just removed "extend"
1037 * (startofline == 0) then we should update ends of normal regions
1038 * contained inside "keepend" because "extend" could have extended
1039 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 * Then check for items ending in column 0.
1041 */
1042 i = current_state.ga_len - 1;
1043 if (keepend_level >= 0)
1044 for ( ; i > keepend_level; --i)
1045 if (CUR_STATE(i).si_flags & HL_EXTEND)
1046 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001047
1048 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 for ( ; i < current_state.ga_len; ++i)
1050 {
1051 cur_si = &CUR_STATE(i);
1052 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001053 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054 || (i == current_state.ga_len - 1 && startofline))
1055 {
1056 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1057 cur_si->si_h_startpos.lnum = current_lnum;
1058
1059 if (!(cur_si->si_flags & HL_MATCHCONT))
1060 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001061
1062 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1063 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064 }
1065 }
1066 check_keepend();
1067 check_state_ends();
1068}
1069
1070/****************************************
1071 * Handling of the state stack cache.
1072 */
1073
1074/*
1075 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1076 *
1077 * To speed up syntax highlighting, the state stack for the start of some
1078 * lines is cached. These entries can be used to start parsing at that point.
1079 *
1080 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1081 * valid entries. b_sst_first points to the first one, then follow sst_next.
1082 * The entries are sorted on line number. The first entry is often for line 2
1083 * (line 1 always starts with an empty stack).
1084 * There is also a list for free entries. This construction is used to avoid
1085 * having to allocate and free memory blocks too often.
1086 *
1087 * When making changes to the buffer, this is logged in b_mod_*. When calling
1088 * update_screen() to update the display, it will call
1089 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1090 * entries. The entries which are inside the changed area are removed,
1091 * because they must be recomputed. Entries below the changed have their line
1092 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1093 * set to indicate that a check must be made if the changed lines would change
1094 * the cached entry.
1095 *
1096 * When later displaying lines, an entry is stored for each line. Displayed
1097 * lines are likely to be displayed again, in which case the state at the
1098 * start of the line is needed.
1099 * For not displayed lines, an entry is stored for every so many lines. These
1100 * entries will be used e.g., when scrolling backwards. The distance between
1101 * entries depends on the number of lines in the buffer. For small buffers
1102 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1103 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1104 */
1105
Bram Moolenaar860cae12010-06-05 23:22:07 +02001106 static void
1107syn_stack_free_block(block)
1108 synblock_T *block;
1109{
1110 synstate_T *p;
1111
1112 if (block->b_sst_array != NULL)
1113 {
1114 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1115 clear_syn_state(p);
1116 vim_free(block->b_sst_array);
1117 block->b_sst_array = NULL;
1118 block->b_sst_len = 0;
1119 }
1120}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121/*
1122 * Free b_sst_array[] for buffer "buf".
1123 * Used when syntax items changed to force resyncing everywhere.
1124 */
1125 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126syn_stack_free_all(block)
1127 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 win_T *wp;
1130
Bram Moolenaar860cae12010-06-05 23:22:07 +02001131 syn_stack_free_block(block);
1132
1133
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134#ifdef FEAT_FOLDING
1135 /* When using "syntax" fold method, must update all folds. */
1136 FOR_ALL_WINDOWS(wp)
1137 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001138 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001139 foldUpdateAll(wp);
1140 }
1141#endif
1142}
1143
1144/*
1145 * Allocate the syntax state stack for syn_buf when needed.
1146 * If the number of entries in b_sst_array[] is much too big or a bit too
1147 * small, reallocate it.
1148 * Also used to allocate b_sst_array[] for the first time.
1149 */
1150 static void
1151syn_stack_alloc()
1152{
1153 long len;
1154 synstate_T *to, *from;
1155 synstate_T *sstp;
1156
1157 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1158 if (len < SST_MIN_ENTRIES)
1159 len = SST_MIN_ENTRIES;
1160 else if (len > SST_MAX_ENTRIES)
1161 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001162 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163 {
1164 /* Allocate 50% too much, to avoid reallocating too often. */
1165 len = syn_buf->b_ml.ml_line_count;
1166 len = (len + len / 2) / SST_DIST + Rows * 2;
1167 if (len < SST_MIN_ENTRIES)
1168 len = SST_MIN_ENTRIES;
1169 else if (len > SST_MAX_ENTRIES)
1170 len = SST_MAX_ENTRIES;
1171
Bram Moolenaar860cae12010-06-05 23:22:07 +02001172 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001173 {
1174 /* When shrinking the array, cleanup the existing stack.
1175 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001176 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001177 && syn_stack_cleanup())
1178 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001179 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1180 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 }
1182
1183 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1184 if (sstp == NULL) /* out of memory! */
1185 return;
1186
1187 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001188 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 {
1190 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001191 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192 from = from->sst_next)
1193 {
1194 ++to;
1195 *to = *from;
1196 to->sst_next = to + 1;
1197 }
1198 }
1199 if (to != sstp - 1)
1200 {
1201 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 syn_block->b_sst_first = sstp;
1203 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 }
1205 else
1206 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 syn_block->b_sst_first = NULL;
1208 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209 }
1210
1211 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001212 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213 while (++to < sstp + len)
1214 to->sst_next = to + 1;
1215 (sstp + len - 1)->sst_next = NULL;
1216
Bram Moolenaar860cae12010-06-05 23:22:07 +02001217 vim_free(syn_block->b_sst_array);
1218 syn_block->b_sst_array = sstp;
1219 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 }
1221}
1222
1223/*
1224 * Check for changes in a buffer to affect stored syntax states. Uses the
1225 * b_mod_* fields.
1226 * Called from update_screen(), before screen is being updated, once for each
1227 * displayed buffer.
1228 */
1229 void
1230syn_stack_apply_changes(buf)
1231 buf_T *buf;
1232{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001233 win_T *wp;
1234
1235 syn_stack_apply_changes_block(&buf->b_s, buf);
1236
1237 FOR_ALL_WINDOWS(wp)
1238 {
1239 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1240 syn_stack_apply_changes_block(wp->w_s, buf);
1241 }
1242}
1243
1244 static void
1245syn_stack_apply_changes_block(block, buf)
1246 synblock_T *block;
1247 buf_T *buf;
1248{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 synstate_T *p, *prev, *np;
1250 linenr_T n;
1251
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 return;
1254
1255 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 {
1260 n = p->sst_lnum + buf->b_mod_xlines;
1261 if (n <= buf->b_mod_bot)
1262 {
1263 /* this state is inside the changed area, remove it */
1264 np = p->sst_next;
1265 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001266 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 else
1268 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 p = np;
1271 continue;
1272 }
1273 /* This state is below the changed area. Remember the line
1274 * that needs to be parsed before this entry can be made valid
1275 * again. */
1276 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1277 {
1278 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1279 p->sst_change_lnum += buf->b_mod_xlines;
1280 else
1281 p->sst_change_lnum = buf->b_mod_top;
1282 }
1283 if (p->sst_change_lnum == 0
1284 || p->sst_change_lnum < buf->b_mod_bot)
1285 p->sst_change_lnum = buf->b_mod_bot;
1286
1287 p->sst_lnum = n;
1288 }
1289 prev = p;
1290 p = p->sst_next;
1291 }
1292}
1293
1294/*
1295 * Reduce the number of entries in the state stack for syn_buf.
1296 * Returns TRUE if at least one entry was freed.
1297 */
1298 static int
1299syn_stack_cleanup()
1300{
1301 synstate_T *p, *prev;
1302 disptick_T tick;
1303 int above;
1304 int dist;
1305 int retval = FALSE;
1306
Bram Moolenaar860cae12010-06-05 23:22:07 +02001307 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 return retval;
1309
1310 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001312 dist = 999999;
1313 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001314 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315
1316 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001317 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 * be removed. Set "above" when the "tick" for the oldest entry is above
1319 * "b_sst_lasttick" (the display tick wraps around).
1320 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001321 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1325 {
1326 if (prev->sst_lnum + dist > p->sst_lnum)
1327 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 {
1330 if (!above || p->sst_tick < tick)
1331 tick = p->sst_tick;
1332 above = TRUE;
1333 }
1334 else if (!above && p->sst_tick < tick)
1335 tick = p->sst_tick;
1336 }
1337 }
1338
1339 /*
1340 * Go through the list to make the entries for the oldest tick at an
1341 * interval of several lines.
1342 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1345 {
1346 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1347 {
1348 /* Move this entry from used list to free list */
1349 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 p = prev;
1352 retval = TRUE;
1353 }
1354 }
1355 return retval;
1356}
1357
1358/*
1359 * Free the allocated memory for a syn_state item.
1360 * Move the entry into the free list.
1361 */
1362 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001363syn_stack_free_entry(block, p)
1364 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365 synstate_T *p;
1366{
1367 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001368 p->sst_next = block->b_sst_firstfree;
1369 block->b_sst_firstfree = p;
1370 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371}
1372
1373/*
1374 * Find an entry in the list of state stacks at or before "lnum".
1375 * Returns NULL when there is no entry or the first entry is after "lnum".
1376 */
1377 static synstate_T *
1378syn_stack_find_entry(lnum)
1379 linenr_T lnum;
1380{
1381 synstate_T *p, *prev;
1382
1383 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001384 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385 {
1386 if (p->sst_lnum == lnum)
1387 return p;
1388 if (p->sst_lnum > lnum)
1389 break;
1390 }
1391 return prev;
1392}
1393
1394/*
1395 * Try saving the current state in b_sst_array[].
1396 * The current state must be valid for the start of the current_lnum line!
1397 */
1398 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001399store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400{
1401 int i;
1402 synstate_T *p;
1403 bufstate_T *bp;
1404 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001405 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406
1407 /*
1408 * If the current state contains a start or end pattern that continues
1409 * from the previous line, we can't use it. Don't store it then.
1410 */
1411 for (i = current_state.ga_len - 1; i >= 0; --i)
1412 {
1413 cur_si = &CUR_STATE(i);
1414 if (cur_si->si_h_startpos.lnum >= current_lnum
1415 || cur_si->si_m_endpos.lnum >= current_lnum
1416 || cur_si->si_h_endpos.lnum >= current_lnum
1417 || (cur_si->si_end_idx
1418 && cur_si->si_eoe_pos.lnum >= current_lnum))
1419 break;
1420 }
1421 if (i >= 0)
1422 {
1423 if (sp != NULL)
1424 {
1425 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001426 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001427 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001428 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429 else
1430 {
1431 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001432 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433 if (p->sst_next == sp)
1434 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001435 if (p != NULL) /* just in case */
1436 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001438 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001439 sp = NULL;
1440 }
1441 }
1442 else if (sp == NULL || sp->sst_lnum != current_lnum)
1443 {
1444 /*
1445 * Add a new entry
1446 */
1447 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001448 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449 {
1450 (void)syn_stack_cleanup();
1451 /* "sp" may have been moved to the freelist now */
1452 sp = syn_stack_find_entry(current_lnum);
1453 }
1454 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001455 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 sp = NULL;
1457 else
1458 {
1459 /* Take the first item from the free list and put it in the used
1460 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001461 p = syn_block->b_sst_firstfree;
1462 syn_block->b_sst_firstfree = p->sst_next;
1463 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 if (sp == NULL)
1465 {
1466 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001467 p->sst_next = syn_block->b_sst_first;
1468 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469 }
1470 else
1471 {
1472 /* insert in list after *sp */
1473 p->sst_next = sp->sst_next;
1474 sp->sst_next = p;
1475 }
1476 sp = p;
1477 sp->sst_stacksize = 0;
1478 sp->sst_lnum = current_lnum;
1479 }
1480 }
1481 if (sp != NULL)
1482 {
1483 /* When overwriting an existing state stack, clear it first */
1484 clear_syn_state(sp);
1485 sp->sst_stacksize = current_state.ga_len;
1486 if (current_state.ga_len > SST_FIX_STATES)
1487 {
1488 /* Need to clear it, might be something remaining from when the
1489 * length was less than SST_FIX_STATES. */
1490 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1491 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1492 sp->sst_stacksize = 0;
1493 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1496 }
1497 else
1498 bp = sp->sst_union.sst_stack;
1499 for (i = 0; i < sp->sst_stacksize; ++i)
1500 {
1501 bp[i].bs_idx = CUR_STATE(i).si_idx;
1502 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001503#ifdef FEAT_CONCEAL
1504 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1505 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1506#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1508 }
1509 sp->sst_next_flags = current_next_flags;
1510 sp->sst_next_list = current_next_list;
1511 sp->sst_tick = display_tick;
1512 sp->sst_change_lnum = 0;
1513 }
1514 current_state_stored = TRUE;
1515 return sp;
1516}
1517
1518/*
1519 * Copy a state stack from "from" in b_sst_array[] to current_state;
1520 */
1521 static void
1522load_current_state(from)
1523 synstate_T *from;
1524{
1525 int i;
1526 bufstate_T *bp;
1527
1528 clear_current_state();
1529 validate_current_state();
1530 keepend_level = -1;
1531 if (from->sst_stacksize
1532 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1533 {
1534 if (from->sst_stacksize > SST_FIX_STATES)
1535 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1536 else
1537 bp = from->sst_union.sst_stack;
1538 for (i = 0; i < from->sst_stacksize; ++i)
1539 {
1540 CUR_STATE(i).si_idx = bp[i].bs_idx;
1541 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001542#ifdef FEAT_CONCEAL
1543 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1544 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1545#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1547 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1548 keepend_level = i;
1549 CUR_STATE(i).si_ends = FALSE;
1550 CUR_STATE(i).si_m_lnum = 0;
1551 if (CUR_STATE(i).si_idx >= 0)
1552 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001553 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 else
1555 CUR_STATE(i).si_next_list = NULL;
1556 update_si_attr(i);
1557 }
1558 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001559 }
1560 current_next_list = from->sst_next_list;
1561 current_next_flags = from->sst_next_flags;
1562 current_lnum = from->sst_lnum;
1563}
1564
1565/*
1566 * Compare saved state stack "*sp" with the current state.
1567 * Return TRUE when they are equal.
1568 */
1569 static int
1570syn_stack_equal(sp)
1571 synstate_T *sp;
1572{
1573 int i, j;
1574 bufstate_T *bp;
1575 reg_extmatch_T *six, *bsx;
1576
1577 /* First a quick check if the stacks have the same size end nextlist. */
1578 if (sp->sst_stacksize == current_state.ga_len
1579 && sp->sst_next_list == current_next_list)
1580 {
1581 /* Need to compare all states on both stacks. */
1582 if (sp->sst_stacksize > SST_FIX_STATES)
1583 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1584 else
1585 bp = sp->sst_union.sst_stack;
1586
1587 for (i = current_state.ga_len; --i >= 0; )
1588 {
1589 /* If the item has another index the state is different. */
1590 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1591 break;
1592 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1593 {
1594 /* When the extmatch pointers are different, the strings in
1595 * them can still be the same. Check if the extmatch
1596 * references are equal. */
1597 bsx = bp[i].bs_extmatch;
1598 six = CUR_STATE(i).si_extmatch;
1599 /* If one of the extmatch pointers is NULL the states are
1600 * different. */
1601 if (bsx == NULL || six == NULL)
1602 break;
1603 for (j = 0; j < NSUBEXP; ++j)
1604 {
1605 /* Check each referenced match string. They must all be
1606 * equal. */
1607 if (bsx->matches[j] != six->matches[j])
1608 {
1609 /* If the pointer is different it can still be the
1610 * same text. Compare the strings, ignore case when
1611 * the start item has the sp_ic flag set. */
1612 if (bsx->matches[j] == NULL
1613 || six->matches[j] == NULL)
1614 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001615 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 ? MB_STRICMP(bsx->matches[j],
1617 six->matches[j]) != 0
1618 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1619 break;
1620 }
1621 }
1622 if (j != NSUBEXP)
1623 break;
1624 }
1625 }
1626 if (i < 0)
1627 return TRUE;
1628 }
1629 return FALSE;
1630}
1631
1632/*
1633 * We stop parsing syntax above line "lnum". If the stored state at or below
1634 * this line depended on a change before it, it now depends on the line below
1635 * the last parsed line.
1636 * The window looks like this:
1637 * line which changed
1638 * displayed line
1639 * displayed line
1640 * lnum -> line below window
1641 */
1642 void
1643syntax_end_parsing(lnum)
1644 linenr_T lnum;
1645{
1646 synstate_T *sp;
1647
1648 sp = syn_stack_find_entry(lnum);
1649 if (sp != NULL && sp->sst_lnum < lnum)
1650 sp = sp->sst_next;
1651
1652 if (sp != NULL && sp->sst_change_lnum != 0)
1653 sp->sst_change_lnum = lnum;
1654}
1655
1656/*
1657 * End of handling of the state stack.
1658 ****************************************/
1659
1660 static void
1661invalidate_current_state()
1662{
1663 clear_current_state();
1664 current_state.ga_itemsize = 0; /* mark current_state invalid */
1665 current_next_list = NULL;
1666 keepend_level = -1;
1667}
1668
1669 static void
1670validate_current_state()
1671{
1672 current_state.ga_itemsize = sizeof(stateitem_T);
1673 current_state.ga_growsize = 3;
1674}
1675
1676/*
1677 * Return TRUE if the syntax at start of lnum changed since last time.
1678 * This will only be called just after get_syntax_attr() for the previous
1679 * line, to check if the next line needs to be redrawn too.
1680 */
1681 int
1682syntax_check_changed(lnum)
1683 linenr_T lnum;
1684{
1685 int retval = TRUE;
1686 synstate_T *sp;
1687
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688 /*
1689 * Check the state stack when:
1690 * - lnum is just below the previously syntaxed line.
1691 * - lnum is not before the lines with saved states.
1692 * - lnum is not past the lines with saved states.
1693 * - lnum is at or before the last changed line.
1694 */
1695 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1696 {
1697 sp = syn_stack_find_entry(lnum);
1698 if (sp != NULL && sp->sst_lnum == lnum)
1699 {
1700 /*
1701 * finish the previous line (needed when not all of the line was
1702 * drawn)
1703 */
1704 (void)syn_finish_line(FALSE);
1705
1706 /*
1707 * Compare the current state with the previously saved state of
1708 * the line.
1709 */
1710 if (syn_stack_equal(sp))
1711 retval = FALSE;
1712
1713 /*
1714 * Store the current state in b_sst_array[] for later use.
1715 */
1716 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001717 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 }
1719 }
1720
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721 return retval;
1722}
1723
1724/*
1725 * Finish the current line.
1726 * This doesn't return any attributes, it only gets the state at the end of
1727 * the line. It can start anywhere in the line, as long as the current state
1728 * is valid.
1729 */
1730 static int
1731syn_finish_line(syncing)
1732 int syncing; /* called for syncing */
1733{
1734 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001735 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736
1737 if (!current_finished)
1738 {
1739 while (!current_finished)
1740 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001741 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742 /*
1743 * When syncing, and found some item, need to check the item.
1744 */
1745 if (syncing && current_state.ga_len)
1746 {
1747 /*
1748 * Check for match with sync item.
1749 */
1750 cur_si = &CUR_STATE(current_state.ga_len - 1);
1751 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001752 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1754 return TRUE;
1755
1756 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001757 * that ends here, need to do that now. Be careful not to go
1758 * past the NUL. */
1759 prev_current_col = current_col;
1760 if (syn_getcurline()[current_col] != NUL)
1761 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001763 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 }
1765 ++current_col;
1766 }
1767 }
1768 return FALSE;
1769}
1770
1771/*
1772 * Return highlight attributes for next character.
1773 * Must first call syntax_start() once for the line.
1774 * "col" is normally 0 for the first use in a line, and increments by one each
1775 * time. It's allowed to skip characters and to stop before the end of the
1776 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001777 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1778 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779 */
1780 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001781get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001783 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001784 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785{
1786 int attr = 0;
1787
Bram Moolenaar349955a2007-08-14 21:07:36 +00001788 if (can_spell != NULL)
1789 /* Default: Only do spelling when there is no @Spell cluster or when
1790 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001791 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1792 ? (syn_block->b_spell_cluster_id == 0)
1793 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001794
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001796 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797 return 0;
1798
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001799 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001800 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001801 {
1802 clear_current_state();
1803#ifdef FEAT_EVAL
1804 current_id = 0;
1805 current_trans_id = 0;
1806#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001807#ifdef FEAT_CONCEAL
1808 current_flags = 0;
1809#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001810 return 0;
1811 }
1812
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 /* Make sure current_state is valid */
1814 if (INVALID_STATE(&current_state))
1815 validate_current_state();
1816
1817 /*
1818 * Skip from the current column to "col", get the attributes for "col".
1819 */
1820 while (current_col <= col)
1821 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001822 attr = syn_current_attr(FALSE, TRUE, can_spell,
1823 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824 ++current_col;
1825 }
1826
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 return attr;
1828}
1829
1830/*
1831 * Get syntax attributes for current_lnum, current_col.
1832 */
1833 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001834syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835 int syncing; /* When 1: called for syncing */
1836 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001837 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001838 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839{
1840 int syn_id;
1841 lpos_T endpos; /* was: char_u *endp; */
1842 lpos_T hl_startpos; /* was: int hl_startcol; */
1843 lpos_T hl_endpos;
1844 lpos_T eos_pos; /* end-of-start match (start region) */
1845 lpos_T eoe_pos; /* end-of-end pattern */
1846 int end_idx; /* group ID for end pattern */
1847 int idx;
1848 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001849 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 int startcol;
1851 int endcol;
1852 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001853 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 short *next_list;
1855 int found_match; /* found usable match */
1856 static int try_next_column = FALSE; /* must try in next col */
1857 int do_keywords;
1858 regmmatch_T regmatch;
1859 lpos_T pos;
1860 int lc_col;
1861 reg_extmatch_T *cur_extmatch = NULL;
1862 char_u *line; /* current line. NOTE: becomes invalid after
1863 looking for a pattern match! */
1864
1865 /* variables for zero-width matches that have a "nextgroup" argument */
1866 int keep_next_list;
1867 int zero_width_next_list = FALSE;
1868 garray_T zero_width_next_ga;
1869
1870 /*
1871 * No character, no attributes! Past end of line?
1872 * Do try matching with an empty line (could be the start of a region).
1873 */
1874 line = syn_getcurline();
1875 if (line[current_col] == NUL && current_col != 0)
1876 {
1877 /*
1878 * If we found a match after the last column, use it.
1879 */
1880 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1881 && next_match_col != MAXCOL)
1882 (void)push_next_match(NULL);
1883
1884 current_finished = TRUE;
1885 current_state_stored = FALSE;
1886 return 0;
1887 }
1888
1889 /* if the current or next character is NUL, we will finish the line now */
1890 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1891 {
1892 current_finished = TRUE;
1893 current_state_stored = FALSE;
1894 }
1895
1896 /*
1897 * When in the previous column there was a match but it could not be used
1898 * (empty match or already matched in this column) need to try again in
1899 * the next column.
1900 */
1901 if (try_next_column)
1902 {
1903 next_match_idx = -1;
1904 try_next_column = FALSE;
1905 }
1906
1907 /* Only check for keywords when not syncing and there are some. */
1908 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001909 && (syn_block->b_keywtab.ht_used > 0
1910 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911
1912 /* Init the list of zero-width matches with a nextlist. This is used to
1913 * avoid matching the same item in the same position twice. */
1914 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1915
1916 /*
1917 * Repeat matching keywords and patterns, to find contained items at the
1918 * same column. This stops when there are no extra matches at the current
1919 * column.
1920 */
1921 do
1922 {
1923 found_match = FALSE;
1924 keep_next_list = FALSE;
1925 syn_id = 0;
1926
1927 /*
1928 * 1. Check for a current state.
1929 * Only when there is no current state, or if the current state may
1930 * contain other things, we need to check for keywords and patterns.
1931 * Always need to check for contained items if some item has the
1932 * "containedin" argument (takes extra time!).
1933 */
1934 if (current_state.ga_len)
1935 cur_si = &CUR_STATE(current_state.ga_len - 1);
1936 else
1937 cur_si = NULL;
1938
Bram Moolenaar860cae12010-06-05 23:22:07 +02001939 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 || cur_si->si_cont_list != NULL)
1941 {
1942 /*
1943 * 2. Check for keywords, if on a keyword char after a non-keyword
1944 * char. Don't do this when syncing.
1945 */
1946 if (do_keywords)
1947 {
1948 line = syn_getcurline();
1949 if (vim_iswordc_buf(line + current_col, syn_buf)
1950 && (current_col == 0
1951 || !vim_iswordc_buf(line + current_col - 1
1952#ifdef FEAT_MBYTE
1953 - (has_mbyte
1954 ? (*mb_head_off)(line, line + current_col - 1)
1955 : 0)
1956#endif
1957 , syn_buf)))
1958 {
1959 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001960 &endcol, &flags, &next_list, cur_si,
1961 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001962 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963 {
1964 if (push_current_state(KEYWORD_IDX) == OK)
1965 {
1966 cur_si = &CUR_STATE(current_state.ga_len - 1);
1967 cur_si->si_m_startcol = current_col;
1968 cur_si->si_h_startpos.lnum = current_lnum;
1969 cur_si->si_h_startpos.col = 0; /* starts right away */
1970 cur_si->si_m_endpos.lnum = current_lnum;
1971 cur_si->si_m_endpos.col = endcol;
1972 cur_si->si_h_endpos.lnum = current_lnum;
1973 cur_si->si_h_endpos.col = endcol;
1974 cur_si->si_ends = TRUE;
1975 cur_si->si_end_idx = 0;
1976 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001977#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001978 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001979 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001980 if (current_state.ga_len > 1)
1981 cur_si->si_flags |=
1982 CUR_STATE(current_state.ga_len - 2).si_flags
1983 & HL_CONCEAL;
1984#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 cur_si->si_id = syn_id;
1986 cur_si->si_trans_id = syn_id;
1987 if (flags & HL_TRANSP)
1988 {
1989 if (current_state.ga_len < 2)
1990 {
1991 cur_si->si_attr = 0;
1992 cur_si->si_trans_id = 0;
1993 }
1994 else
1995 {
1996 cur_si->si_attr = CUR_STATE(
1997 current_state.ga_len - 2).si_attr;
1998 cur_si->si_trans_id = CUR_STATE(
1999 current_state.ga_len - 2).si_trans_id;
2000 }
2001 }
2002 else
2003 cur_si->si_attr = syn_id2attr(syn_id);
2004 cur_si->si_cont_list = NULL;
2005 cur_si->si_next_list = next_list;
2006 check_keepend();
2007 }
2008 else
2009 vim_free(next_list);
2010 }
2011 }
2012 }
2013
2014 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002015 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002017 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018 {
2019 /*
2020 * If we didn't check for a match yet, or we are past it, check
2021 * for any match with a pattern.
2022 */
2023 if (next_match_idx < 0 || next_match_col < (int)current_col)
2024 {
2025 /*
2026 * Check all relevant patterns for a match at this
2027 * position. This is complicated, because matching with a
2028 * pattern takes quite a bit of time, thus we want to
2029 * avoid doing it when it's not needed.
2030 */
2031 next_match_idx = 0; /* no match in this line yet */
2032 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002033 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002034 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002035 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 if ( spp->sp_syncing == syncing
2037 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2038 && (spp->sp_type == SPTYPE_MATCH
2039 || spp->sp_type == SPTYPE_START)
2040 && (current_next_list != NULL
2041 ? in_id_list(NULL, current_next_list,
2042 &spp->sp_syn, 0)
2043 : (cur_si == NULL
2044 ? !(spp->sp_flags & HL_CONTAINED)
2045 : in_id_list(cur_si,
2046 cur_si->si_cont_list, &spp->sp_syn,
2047 spp->sp_flags & HL_CONTAINED))))
2048 {
2049 /* If we already tried matching in this line, and
2050 * there isn't a match before next_match_col, skip
2051 * this item. */
2052 if (spp->sp_line_id == current_line_id
2053 && spp->sp_startcol >= next_match_col)
2054 continue;
2055 spp->sp_line_id = current_line_id;
2056
2057 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2058 if (lc_col < 0)
2059 lc_col = 0;
2060
2061 regmatch.rmm_ic = spp->sp_ic;
2062 regmatch.regprog = spp->sp_prog;
2063 if (!syn_regexec(&regmatch, current_lnum,
2064 (colnr_T)lc_col))
2065 {
2066 /* no match in this line, try another one */
2067 spp->sp_startcol = MAXCOL;
2068 continue;
2069 }
2070
2071 /*
2072 * Compute the first column of the match.
2073 */
2074 syn_add_start_off(&pos, &regmatch,
2075 spp, SPO_MS_OFF, -1);
2076 if (pos.lnum > current_lnum)
2077 {
2078 /* must have used end of match in a next line,
2079 * we can't handle that */
2080 spp->sp_startcol = MAXCOL;
2081 continue;
2082 }
2083 startcol = pos.col;
2084
2085 /* remember the next column where this pattern
2086 * matches in the current line */
2087 spp->sp_startcol = startcol;
2088
2089 /*
2090 * If a previously found match starts at a lower
2091 * column number, don't use this one.
2092 */
2093 if (startcol >= next_match_col)
2094 continue;
2095
2096 /*
2097 * If we matched this pattern at this position
2098 * before, skip it. Must retry in the next
2099 * column, because it may match from there.
2100 */
2101 if (did_match_already(idx, &zero_width_next_ga))
2102 {
2103 try_next_column = TRUE;
2104 continue;
2105 }
2106
2107 endpos.lnum = regmatch.endpos[0].lnum;
2108 endpos.col = regmatch.endpos[0].col;
2109
2110 /* Compute the highlight start. */
2111 syn_add_start_off(&hl_startpos, &regmatch,
2112 spp, SPO_HS_OFF, -1);
2113
2114 /* Compute the region start. */
2115 /* Default is to use the end of the match. */
2116 syn_add_end_off(&eos_pos, &regmatch,
2117 spp, SPO_RS_OFF, 0);
2118
2119 /*
2120 * Grab the external submatches before they get
2121 * overwritten. Reference count doesn't change.
2122 */
2123 unref_extmatch(cur_extmatch);
2124 cur_extmatch = re_extmatch_out;
2125 re_extmatch_out = NULL;
2126
2127 flags = 0;
2128 eoe_pos.lnum = 0; /* avoid warning */
2129 eoe_pos.col = 0;
2130 end_idx = 0;
2131 hl_endpos.lnum = 0;
2132
2133 /*
2134 * For a "oneline" the end must be found in the
2135 * same line too. Search for it after the end of
2136 * the match with the start pattern. Set the
2137 * resulting end positions at the same time.
2138 */
2139 if (spp->sp_type == SPTYPE_START
2140 && (spp->sp_flags & HL_ONELINE))
2141 {
2142 lpos_T startpos;
2143
2144 startpos = endpos;
2145 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2146 &flags, &eoe_pos, &end_idx, cur_extmatch);
2147 if (endpos.lnum == 0)
2148 continue; /* not found */
2149 }
2150
2151 /*
2152 * For a "match" the size must be > 0 after the
2153 * end offset needs has been added. Except when
2154 * syncing.
2155 */
2156 else if (spp->sp_type == SPTYPE_MATCH)
2157 {
2158 syn_add_end_off(&hl_endpos, &regmatch, spp,
2159 SPO_HE_OFF, 0);
2160 syn_add_end_off(&endpos, &regmatch, spp,
2161 SPO_ME_OFF, 0);
2162 if (endpos.lnum == current_lnum
2163 && (int)endpos.col + syncing < startcol)
2164 {
2165 /*
2166 * If an empty string is matched, may need
2167 * to try matching again at next column.
2168 */
2169 if (regmatch.startpos[0].col
2170 == regmatch.endpos[0].col)
2171 try_next_column = TRUE;
2172 continue;
2173 }
2174 }
2175
2176 /*
2177 * keep the best match so far in next_match_*
2178 */
2179 /* Highlighting must start after startpos and end
2180 * before endpos. */
2181 if (hl_startpos.lnum == current_lnum
2182 && (int)hl_startpos.col < startcol)
2183 hl_startpos.col = startcol;
2184 limit_pos_zero(&hl_endpos, &endpos);
2185
2186 next_match_idx = idx;
2187 next_match_col = startcol;
2188 next_match_m_endpos = endpos;
2189 next_match_h_endpos = hl_endpos;
2190 next_match_h_startpos = hl_startpos;
2191 next_match_flags = flags;
2192 next_match_eos_pos = eos_pos;
2193 next_match_eoe_pos = eoe_pos;
2194 next_match_end_idx = end_idx;
2195 unref_extmatch(next_match_extmatch);
2196 next_match_extmatch = cur_extmatch;
2197 cur_extmatch = NULL;
2198 }
2199 }
2200 }
2201
2202 /*
2203 * If we found a match at the current column, use it.
2204 */
2205 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2206 {
2207 synpat_T *lspp;
2208
2209 /* When a zero-width item matched which has a nextgroup,
2210 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002211 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002212 if (next_match_m_endpos.lnum == current_lnum
2213 && next_match_m_endpos.col == current_col
2214 && lspp->sp_next_list != NULL)
2215 {
2216 current_next_list = lspp->sp_next_list;
2217 current_next_flags = lspp->sp_flags;
2218 keep_next_list = TRUE;
2219 zero_width_next_list = TRUE;
2220
2221 /* Add the index to a list, so that we can check
2222 * later that we don't match it again (and cause an
2223 * endless loop). */
2224 if (ga_grow(&zero_width_next_ga, 1) == OK)
2225 {
2226 ((int *)(zero_width_next_ga.ga_data))
2227 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228 }
2229 next_match_idx = -1;
2230 }
2231 else
2232 cur_si = push_next_match(cur_si);
2233 found_match = TRUE;
2234 }
2235 }
2236 }
2237
2238 /*
2239 * Handle searching for nextgroup match.
2240 */
2241 if (current_next_list != NULL && !keep_next_list)
2242 {
2243 /*
2244 * If a nextgroup was not found, continue looking for one if:
2245 * - this is an empty line and the "skipempty" option was given
2246 * - we are on white space and the "skipwhite" option was given
2247 */
2248 if (!found_match)
2249 {
2250 line = syn_getcurline();
2251 if (((current_next_flags & HL_SKIPWHITE)
2252 && vim_iswhite(line[current_col]))
2253 || ((current_next_flags & HL_SKIPEMPTY)
2254 && *line == NUL))
2255 break;
2256 }
2257
2258 /*
2259 * If a nextgroup was found: Use it, and continue looking for
2260 * contained matches.
2261 * If a nextgroup was not found: Continue looking for a normal
2262 * match.
2263 * When did set current_next_list for a zero-width item and no
2264 * match was found don't loop (would get stuck).
2265 */
2266 current_next_list = NULL;
2267 next_match_idx = -1;
2268 if (!zero_width_next_list)
2269 found_match = TRUE;
2270 }
2271
2272 } while (found_match);
2273
2274 /*
2275 * Use attributes from the current state, if within its highlighting.
2276 * If not, use attributes from the current-but-one state, etc.
2277 */
2278 current_attr = 0;
2279#ifdef FEAT_EVAL
2280 current_id = 0;
2281 current_trans_id = 0;
2282#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002283#ifdef FEAT_CONCEAL
2284 current_flags = 0;
2285#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 if (cur_si != NULL)
2287 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002288#ifndef FEAT_EVAL
2289 int current_trans_id = 0;
2290#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002291 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2292 {
2293 sip = &CUR_STATE(idx);
2294 if ((current_lnum > sip->si_h_startpos.lnum
2295 || (current_lnum == sip->si_h_startpos.lnum
2296 && current_col >= sip->si_h_startpos.col))
2297 && (sip->si_h_endpos.lnum == 0
2298 || current_lnum < sip->si_h_endpos.lnum
2299 || (current_lnum == sip->si_h_endpos.lnum
2300 && current_col < sip->si_h_endpos.col)))
2301 {
2302 current_attr = sip->si_attr;
2303#ifdef FEAT_EVAL
2304 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002306 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002307#ifdef FEAT_CONCEAL
2308 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002309 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002310 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002311#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 break;
2313 }
2314 }
2315
Bram Moolenaar217ad922005-03-20 22:37:15 +00002316 if (can_spell != NULL)
2317 {
2318 struct sp_syn sps;
2319
2320 /*
2321 * set "can_spell" to TRUE if spell checking is supposed to be
2322 * done in the current item.
2323 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002324 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002325 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002326 /* There is no @Spell cluster: Do spelling for items without
2327 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002328 if (syn_block->b_nospell_cluster_id == 0
2329 || current_trans_id == 0)
2330 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002331 else
2332 {
2333 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002334 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002335 sps.cont_in_list = NULL;
2336 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2337 }
2338 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002339 else
2340 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002341 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002342 * the @Spell cluster. But not when @NoSpell is also there.
2343 * At the toplevel only spell check when ":syn spell toplevel"
2344 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002345 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002346 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002347 else
2348 {
2349 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002350 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002351 sps.cont_in_list = NULL;
2352 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2353
Bram Moolenaar860cae12010-06-05 23:22:07 +02002354 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002355 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002356 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002357 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2358 *can_spell = FALSE;
2359 }
2360 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002361 }
2362 }
2363
2364
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 /*
2366 * Check for end of current state (and the states before it) at the
2367 * next column. Don't do this for syncing, because we would miss a
2368 * single character match.
2369 * First check if the current state ends at the current column. It
2370 * may be for an empty match and a containing item might end in the
2371 * current column.
2372 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002373 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374 {
2375 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002376 if (current_state.ga_len > 0
2377 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378 {
2379 ++current_col;
2380 check_state_ends();
2381 --current_col;
2382 }
2383 }
2384 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002386 /* Default: Only do spelling when there is no @Spell cluster or when
2387 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2389 ? (syn_block->b_spell_cluster_id == 0)
2390 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002392 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002393 if (current_next_list != NULL
2394 && syn_getcurline()[current_col + 1] == NUL
2395 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2396 current_next_list = NULL;
2397
2398 if (zero_width_next_ga.ga_len > 0)
2399 ga_clear(&zero_width_next_ga);
2400
2401 /* No longer need external matches. But keep next_match_extmatch. */
2402 unref_extmatch(re_extmatch_out);
2403 re_extmatch_out = NULL;
2404 unref_extmatch(cur_extmatch);
2405
2406 return current_attr;
2407}
2408
2409
2410/*
2411 * Check if we already matched pattern "idx" at the current column.
2412 */
2413 static int
2414did_match_already(idx, gap)
2415 int idx;
2416 garray_T *gap;
2417{
2418 int i;
2419
2420 for (i = current_state.ga_len; --i >= 0; )
2421 if (CUR_STATE(i).si_m_startcol == (int)current_col
2422 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2423 && CUR_STATE(i).si_idx == idx)
2424 return TRUE;
2425
2426 /* Zero-width matches with a nextgroup argument are not put on the syntax
2427 * stack, and can only be matched once anyway. */
2428 for (i = gap->ga_len; --i >= 0; )
2429 if (((int *)(gap->ga_data))[i] == idx)
2430 return TRUE;
2431
2432 return FALSE;
2433}
2434
2435/*
2436 * Push the next match onto the stack.
2437 */
2438 static stateitem_T *
2439push_next_match(cur_si)
2440 stateitem_T *cur_si;
2441{
2442 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002443#ifdef FEAT_CONCEAL
2444 int save_flags;
2445#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446
Bram Moolenaar860cae12010-06-05 23:22:07 +02002447 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448
2449 /*
2450 * Push the item in current_state stack;
2451 */
2452 if (push_current_state(next_match_idx) == OK)
2453 {
2454 /*
2455 * If it's a start-skip-end type that crosses lines, figure out how
2456 * much it continues in this line. Otherwise just fill in the length.
2457 */
2458 cur_si = &CUR_STATE(current_state.ga_len - 1);
2459 cur_si->si_h_startpos = next_match_h_startpos;
2460 cur_si->si_m_startcol = current_col;
2461 cur_si->si_m_lnum = current_lnum;
2462 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002463#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002464 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002465 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002466 if (current_state.ga_len > 1)
2467 cur_si->si_flags |=
2468 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2469#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 cur_si->si_next_list = spp->sp_next_list;
2471 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2472 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2473 {
2474 /* Try to find the end pattern in the current line */
2475 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2476 check_keepend();
2477 }
2478 else
2479 {
2480 cur_si->si_m_endpos = next_match_m_endpos;
2481 cur_si->si_h_endpos = next_match_h_endpos;
2482 cur_si->si_ends = TRUE;
2483 cur_si->si_flags |= next_match_flags;
2484 cur_si->si_eoe_pos = next_match_eoe_pos;
2485 cur_si->si_end_idx = next_match_end_idx;
2486 }
2487 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2488 keepend_level = current_state.ga_len - 1;
2489 check_keepend();
2490 update_si_attr(current_state.ga_len - 1);
2491
Bram Moolenaar860cae12010-06-05 23:22:07 +02002492#ifdef FEAT_CONCEAL
2493 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2494#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002495 /*
2496 * If the start pattern has another highlight group, push another item
2497 * on the stack for the start pattern.
2498 */
2499 if ( spp->sp_type == SPTYPE_START
2500 && spp->sp_syn_match_id != 0
2501 && push_current_state(next_match_idx) == OK)
2502 {
2503 cur_si = &CUR_STATE(current_state.ga_len - 1);
2504 cur_si->si_h_startpos = next_match_h_startpos;
2505 cur_si->si_m_startcol = current_col;
2506 cur_si->si_m_lnum = current_lnum;
2507 cur_si->si_m_endpos = next_match_eos_pos;
2508 cur_si->si_h_endpos = next_match_eos_pos;
2509 cur_si->si_ends = TRUE;
2510 cur_si->si_end_idx = 0;
2511 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002512#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002513 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002514 cur_si->si_flags |= save_flags;
2515 if (cur_si->si_flags & HL_CONCEALENDS)
2516 cur_si->si_flags |= HL_CONCEAL;
2517#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518 cur_si->si_next_list = NULL;
2519 check_keepend();
2520 update_si_attr(current_state.ga_len - 1);
2521 }
2522 }
2523
2524 next_match_idx = -1; /* try other match next time */
2525
2526 return cur_si;
2527}
2528
2529/*
2530 * Check for end of current state (and the states before it).
2531 */
2532 static void
2533check_state_ends()
2534{
2535 stateitem_T *cur_si;
2536 int had_extend = FALSE;
2537
2538 cur_si = &CUR_STATE(current_state.ga_len - 1);
2539 for (;;)
2540 {
2541 if (cur_si->si_ends
2542 && (cur_si->si_m_endpos.lnum < current_lnum
2543 || (cur_si->si_m_endpos.lnum == current_lnum
2544 && cur_si->si_m_endpos.col <= current_col)))
2545 {
2546 /*
2547 * If there is an end pattern group ID, highlight the end pattern
2548 * now. No need to pop the current item from the stack.
2549 * Only do this if the end pattern continues beyond the current
2550 * position.
2551 */
2552 if (cur_si->si_end_idx
2553 && (cur_si->si_eoe_pos.lnum > current_lnum
2554 || (cur_si->si_eoe_pos.lnum == current_lnum
2555 && cur_si->si_eoe_pos.col > current_col)))
2556 {
2557 cur_si->si_idx = cur_si->si_end_idx;
2558 cur_si->si_end_idx = 0;
2559 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2560 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2561 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002562#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002563 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002564 if (cur_si->si_flags & HL_CONCEALENDS)
2565 cur_si->si_flags |= HL_CONCEAL;
2566#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002568
2569 /* what matches next may be different now, clear it */
2570 next_match_idx = 0;
2571 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572 break;
2573 }
2574 else
2575 {
2576 /* handle next_list, unless at end of line and no "skipnl" or
2577 * "skipempty" */
2578 current_next_list = cur_si->si_next_list;
2579 current_next_flags = cur_si->si_flags;
2580 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2581 && syn_getcurline()[current_col] == NUL)
2582 current_next_list = NULL;
2583
2584 /* When the ended item has "extend", another item with
2585 * "keepend" now needs to check for its end. */
2586 if (cur_si->si_flags & HL_EXTEND)
2587 had_extend = TRUE;
2588
2589 pop_current_state();
2590
2591 if (current_state.ga_len == 0)
2592 break;
2593
Bram Moolenaar81993f42008-01-11 20:27:45 +00002594 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002595 {
2596 syn_update_ends(FALSE);
2597 if (current_state.ga_len == 0)
2598 break;
2599 }
2600
2601 cur_si = &CUR_STATE(current_state.ga_len - 1);
2602
2603 /*
2604 * Only for a region the search for the end continues after
2605 * the end of the contained item. If the contained match
2606 * included the end-of-line, break here, the region continues.
2607 * Don't do this when:
2608 * - "keepend" is used for the contained item
2609 * - not at the end of the line (could be end="x$"me=e-1).
2610 * - "excludenl" is used (HL_HAS_EOL won't be set)
2611 */
2612 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002613 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614 == SPTYPE_START
2615 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2616 {
2617 update_si_end(cur_si, (int)current_col, TRUE);
2618 check_keepend();
2619 if ((current_next_flags & HL_HAS_EOL)
2620 && keepend_level < 0
2621 && syn_getcurline()[current_col] == NUL)
2622 break;
2623 }
2624 }
2625 }
2626 else
2627 break;
2628 }
2629}
2630
2631/*
2632 * Update an entry in the current_state stack for a match or region. This
2633 * fills in si_attr, si_next_list and si_cont_list.
2634 */
2635 static void
2636update_si_attr(idx)
2637 int idx;
2638{
2639 stateitem_T *sip = &CUR_STATE(idx);
2640 synpat_T *spp;
2641
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002642 /* This should not happen... */
2643 if (sip->si_idx < 0)
2644 return;
2645
Bram Moolenaar860cae12010-06-05 23:22:07 +02002646 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 if (sip->si_flags & HL_MATCH)
2648 sip->si_id = spp->sp_syn_match_id;
2649 else
2650 sip->si_id = spp->sp_syn.id;
2651 sip->si_attr = syn_id2attr(sip->si_id);
2652 sip->si_trans_id = sip->si_id;
2653 if (sip->si_flags & HL_MATCH)
2654 sip->si_cont_list = NULL;
2655 else
2656 sip->si_cont_list = spp->sp_cont_list;
2657
2658 /*
2659 * For transparent items, take attr from outer item.
2660 * Also take cont_list, if there is none.
2661 * Don't do this for the matchgroup of a start or end pattern.
2662 */
2663 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2664 {
2665 if (idx == 0)
2666 {
2667 sip->si_attr = 0;
2668 sip->si_trans_id = 0;
2669 if (sip->si_cont_list == NULL)
2670 sip->si_cont_list = ID_LIST_ALL;
2671 }
2672 else
2673 {
2674 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2675 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002676 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2677 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002678 if (sip->si_cont_list == NULL)
2679 {
2680 sip->si_flags |= HL_TRANS_CONT;
2681 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2682 }
2683 }
2684 }
2685}
2686
2687/*
2688 * Check the current stack for patterns with "keepend" flag.
2689 * Propagate the match-end to contained items, until a "skipend" item is found.
2690 */
2691 static void
2692check_keepend()
2693{
2694 int i;
2695 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002696 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 stateitem_T *sip;
2698
2699 /*
2700 * This check can consume a lot of time; only do it from the level where
2701 * there really is a keepend.
2702 */
2703 if (keepend_level < 0)
2704 return;
2705
2706 /*
2707 * Find the last index of an "extend" item. "keepend" items before that
2708 * won't do anything. If there is no "extend" item "i" will be
2709 * "keepend_level" and all "keepend" items will work normally.
2710 */
2711 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2712 if (CUR_STATE(i).si_flags & HL_EXTEND)
2713 break;
2714
2715 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002716 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002717 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002718 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719 for ( ; i < current_state.ga_len; ++i)
2720 {
2721 sip = &CUR_STATE(i);
2722 if (maxpos.lnum != 0)
2723 {
2724 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002725 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2727 sip->si_ends = TRUE;
2728 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002729 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2730 {
2731 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002732 || maxpos.lnum > sip->si_m_endpos.lnum
2733 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002734 && maxpos.col > sip->si_m_endpos.col))
2735 maxpos = sip->si_m_endpos;
2736 if (maxpos_h.lnum == 0
2737 || maxpos_h.lnum > sip->si_h_endpos.lnum
2738 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2739 && maxpos_h.col > sip->si_h_endpos.col))
2740 maxpos_h = sip->si_h_endpos;
2741 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742 }
2743}
2744
2745/*
2746 * Update an entry in the current_state stack for a start-skip-end pattern.
2747 * This finds the end of the current item, if it's in the current line.
2748 *
2749 * Return the flags for the matched END.
2750 */
2751 static void
2752update_si_end(sip, startcol, force)
2753 stateitem_T *sip;
2754 int startcol; /* where to start searching for the end */
2755 int force; /* when TRUE overrule a previous end */
2756{
2757 lpos_T startpos;
2758 lpos_T endpos;
2759 lpos_T hl_endpos;
2760 lpos_T end_endpos;
2761 int end_idx;
2762
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002763 /* return quickly for a keyword */
2764 if (sip->si_idx < 0)
2765 return;
2766
Bram Moolenaar071d4272004-06-13 20:20:40 +00002767 /* Don't update when it's already done. Can be a match of an end pattern
2768 * that started in a previous line. Watch out: can also be a "keepend"
2769 * from a containing item. */
2770 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2771 return;
2772
2773 /*
2774 * We need to find the end of the region. It may continue in the next
2775 * line.
2776 */
2777 end_idx = 0;
2778 startpos.lnum = current_lnum;
2779 startpos.col = startcol;
2780 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2781 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2782
2783 if (endpos.lnum == 0)
2784 {
2785 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002786 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002787 {
2788 /* a "oneline" never continues in the next line */
2789 sip->si_ends = TRUE;
2790 sip->si_m_endpos.lnum = current_lnum;
2791 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2792 }
2793 else
2794 {
2795 /* continues in the next line */
2796 sip->si_ends = FALSE;
2797 sip->si_m_endpos.lnum = 0;
2798 }
2799 sip->si_h_endpos = sip->si_m_endpos;
2800 }
2801 else
2802 {
2803 /* match within this line */
2804 sip->si_m_endpos = endpos;
2805 sip->si_h_endpos = hl_endpos;
2806 sip->si_eoe_pos = end_endpos;
2807 sip->si_ends = TRUE;
2808 sip->si_end_idx = end_idx;
2809 }
2810}
2811
2812/*
2813 * Add a new state to the current state stack.
2814 * It is cleared and the index set to "idx".
2815 * Return FAIL if it's not possible (out of memory).
2816 */
2817 static int
2818push_current_state(idx)
2819 int idx;
2820{
2821 if (ga_grow(&current_state, 1) == FAIL)
2822 return FAIL;
2823 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2824 CUR_STATE(current_state.ga_len).si_idx = idx;
2825 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826 return OK;
2827}
2828
2829/*
2830 * Remove a state from the current_state stack.
2831 */
2832 static void
2833pop_current_state()
2834{
2835 if (current_state.ga_len)
2836 {
2837 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2838 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002839 }
2840 /* after the end of a pattern, try matching a keyword or pattern */
2841 next_match_idx = -1;
2842
2843 /* if first state with "keepend" is popped, reset keepend_level */
2844 if (keepend_level >= current_state.ga_len)
2845 keepend_level = -1;
2846}
2847
2848/*
2849 * Find the end of a start/skip/end syntax region after "startpos".
2850 * Only checks one line.
2851 * Also handles a match item that continued from a previous line.
2852 * If not found, the syntax item continues in the next line. m_endpos->lnum
2853 * will be 0.
2854 * If found, the end of the region and the end of the highlighting is
2855 * computed.
2856 */
2857 static void
2858find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2859 end_idx, start_ext)
2860 int idx; /* index of the pattern */
2861 lpos_T *startpos; /* where to start looking for an END match */
2862 lpos_T *m_endpos; /* return: end of match */
2863 lpos_T *hl_endpos; /* return: end of highlighting */
2864 long *flagsp; /* return: flags of matching END */
2865 lpos_T *end_endpos; /* return: end of end pattern match */
2866 int *end_idx; /* return: group ID for end pat. match, or 0 */
2867 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2868{
2869 colnr_T matchcol;
2870 synpat_T *spp, *spp_skip;
2871 int start_idx;
2872 int best_idx;
2873 regmmatch_T regmatch;
2874 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2875 lpos_T pos;
2876 char_u *line;
2877 int had_match = FALSE;
2878
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002879 /* just in case we are invoked for a keyword */
2880 if (idx < 0)
2881 return;
2882
Bram Moolenaar071d4272004-06-13 20:20:40 +00002883 /*
2884 * Check for being called with a START pattern.
2885 * Can happen with a match that continues to the next line, because it
2886 * contained a region.
2887 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002888 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889 if (spp->sp_type != SPTYPE_START)
2890 {
2891 *hl_endpos = *startpos;
2892 return;
2893 }
2894
2895 /*
2896 * Find the SKIP or first END pattern after the last START pattern.
2897 */
2898 for (;;)
2899 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002900 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002901 if (spp->sp_type != SPTYPE_START)
2902 break;
2903 ++idx;
2904 }
2905
2906 /*
2907 * Lookup the SKIP pattern (if present)
2908 */
2909 if (spp->sp_type == SPTYPE_SKIP)
2910 {
2911 spp_skip = spp;
2912 ++idx;
2913 }
2914 else
2915 spp_skip = NULL;
2916
2917 /* Setup external matches for syn_regexec(). */
2918 unref_extmatch(re_extmatch_in);
2919 re_extmatch_in = ref_extmatch(start_ext);
2920
2921 matchcol = startpos->col; /* start looking for a match at sstart */
2922 start_idx = idx; /* remember the first END pattern. */
2923 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2924 for (;;)
2925 {
2926 /*
2927 * Find end pattern that matches first after "matchcol".
2928 */
2929 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002930 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002931 {
2932 int lc_col = matchcol;
2933
Bram Moolenaar860cae12010-06-05 23:22:07 +02002934 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2936 break;
2937 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2938 if (lc_col < 0)
2939 lc_col = 0;
2940
2941 regmatch.rmm_ic = spp->sp_ic;
2942 regmatch.regprog = spp->sp_prog;
2943 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2944 {
2945 if (best_idx == -1 || regmatch.startpos[0].col
2946 < best_regmatch.startpos[0].col)
2947 {
2948 best_idx = idx;
2949 best_regmatch.startpos[0] = regmatch.startpos[0];
2950 best_regmatch.endpos[0] = regmatch.endpos[0];
2951 }
2952 }
2953 }
2954
2955 /*
2956 * If all end patterns have been tried, and there is no match, the
2957 * item continues until end-of-line.
2958 */
2959 if (best_idx == -1)
2960 break;
2961
2962 /*
2963 * If the skip pattern matches before the end pattern,
2964 * continue searching after the skip pattern.
2965 */
2966 if (spp_skip != NULL)
2967 {
2968 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2969
2970 if (lc_col < 0)
2971 lc_col = 0;
2972 regmatch.rmm_ic = spp_skip->sp_ic;
2973 regmatch.regprog = spp_skip->sp_prog;
2974 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2975 && regmatch.startpos[0].col
2976 <= best_regmatch.startpos[0].col)
2977 {
2978 /* Add offset to skip pattern match */
2979 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2980
2981 /* If the skip pattern goes on to the next line, there is no
2982 * match with an end pattern in this line. */
2983 if (pos.lnum > startpos->lnum)
2984 break;
2985
2986 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2987
2988 /* take care of an empty match or negative offset */
2989 if (pos.col <= matchcol)
2990 ++matchcol;
2991 else if (pos.col <= regmatch.endpos[0].col)
2992 matchcol = pos.col;
2993 else
2994 /* Be careful not to jump over the NUL at the end-of-line */
2995 for (matchcol = regmatch.endpos[0].col;
2996 line[matchcol] != NUL && matchcol < pos.col;
2997 ++matchcol)
2998 ;
2999
3000 /* if the skip pattern includes end-of-line, break here */
3001 if (line[matchcol] == NUL)
3002 break;
3003
3004 continue; /* start with first end pattern again */
3005 }
3006 }
3007
3008 /*
3009 * Match from start pattern to end pattern.
3010 * Correct for match and highlight offset of end pattern.
3011 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003012 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3014 /* can't end before the start */
3015 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3016 m_endpos->col = startpos->col;
3017
3018 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3019 /* can't end before the start */
3020 if (end_endpos->lnum == startpos->lnum
3021 && end_endpos->col < startpos->col)
3022 end_endpos->col = startpos->col;
3023 /* can't end after the match */
3024 limit_pos(end_endpos, m_endpos);
3025
3026 /*
3027 * If the end group is highlighted differently, adjust the pointers.
3028 */
3029 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3030 {
3031 *end_idx = best_idx;
3032 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3033 {
3034 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3035 hl_endpos->col = best_regmatch.endpos[0].col;
3036 }
3037 else
3038 {
3039 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3040 hl_endpos->col = best_regmatch.startpos[0].col;
3041 }
3042 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3043
3044 /* can't end before the start */
3045 if (hl_endpos->lnum == startpos->lnum
3046 && hl_endpos->col < startpos->col)
3047 hl_endpos->col = startpos->col;
3048 limit_pos(hl_endpos, m_endpos);
3049
3050 /* now the match ends where the highlighting ends, it is turned
3051 * into the matchgroup for the end */
3052 *m_endpos = *hl_endpos;
3053 }
3054 else
3055 {
3056 *end_idx = 0;
3057 *hl_endpos = *end_endpos;
3058 }
3059
3060 *flagsp = spp->sp_flags;
3061
3062 had_match = TRUE;
3063 break;
3064 }
3065
3066 /* no match for an END pattern in this line */
3067 if (!had_match)
3068 m_endpos->lnum = 0;
3069
3070 /* Remove external matches. */
3071 unref_extmatch(re_extmatch_in);
3072 re_extmatch_in = NULL;
3073}
3074
3075/*
3076 * Limit "pos" not to be after "limit".
3077 */
3078 static void
3079limit_pos(pos, limit)
3080 lpos_T *pos;
3081 lpos_T *limit;
3082{
3083 if (pos->lnum > limit->lnum)
3084 *pos = *limit;
3085 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3086 pos->col = limit->col;
3087}
3088
3089/*
3090 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3091 */
3092 static void
3093limit_pos_zero(pos, limit)
3094 lpos_T *pos;
3095 lpos_T *limit;
3096{
3097 if (pos->lnum == 0)
3098 *pos = *limit;
3099 else
3100 limit_pos(pos, limit);
3101}
3102
3103/*
3104 * Add offset to matched text for end of match or highlight.
3105 */
3106 static void
3107syn_add_end_off(result, regmatch, spp, idx, extra)
3108 lpos_T *result; /* returned position */
3109 regmmatch_T *regmatch; /* start/end of match */
3110 synpat_T *spp; /* matched pattern */
3111 int idx; /* index of offset */
3112 int extra; /* extra chars for offset to start */
3113{
3114 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003115 int off;
3116 char_u *base;
3117 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003118
3119 if (spp->sp_off_flags & (1 << idx))
3120 {
3121 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003122 col = regmatch->startpos[0].col;
3123 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003124 }
3125 else
3126 {
3127 result->lnum = regmatch->endpos[0].lnum;
3128 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003129 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003130 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003131 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3132 * is a matchgroup. Watch out for match with last NL in the buffer. */
3133 if (result->lnum > syn_buf->b_ml.ml_line_count)
3134 col = 0;
3135 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003136 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003137 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3138 p = base + col;
3139 if (off > 0)
3140 {
3141 while (off-- > 0 && *p != NUL)
3142 mb_ptr_adv(p);
3143 }
3144 else if (off < 0)
3145 {
3146 while (off++ < 0 && base < p)
3147 mb_ptr_back(base, p);
3148 }
3149 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003150 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003151 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152}
3153
3154/*
3155 * Add offset to matched text for start of match or highlight.
3156 * Avoid resulting column to become negative.
3157 */
3158 static void
3159syn_add_start_off(result, regmatch, spp, idx, extra)
3160 lpos_T *result; /* returned position */
3161 regmmatch_T *regmatch; /* start/end of match */
3162 synpat_T *spp;
3163 int idx;
3164 int extra; /* extra chars for offset to end */
3165{
3166 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003167 int off;
3168 char_u *base;
3169 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170
3171 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3172 {
3173 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003174 col = regmatch->endpos[0].col;
3175 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176 }
3177 else
3178 {
3179 result->lnum = regmatch->startpos[0].lnum;
3180 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003181 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003182 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003183 if (result->lnum > syn_buf->b_ml.ml_line_count)
3184 {
3185 /* a "\n" at the end of the pattern may take us below the last line */
3186 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003187 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003188 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003189 if (off != 0)
3190 {
3191 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3192 p = base + col;
3193 if (off > 0)
3194 {
3195 while (off-- && *p != NUL)
3196 mb_ptr_adv(p);
3197 }
3198 else if (off < 0)
3199 {
3200 while (off++ && base < p)
3201 mb_ptr_back(base, p);
3202 }
3203 col = (int)(p - base);
3204 }
3205 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003206}
3207
3208/*
3209 * Get current line in syntax buffer.
3210 */
3211 static char_u *
3212syn_getcurline()
3213{
3214 return ml_get_buf(syn_buf, current_lnum, FALSE);
3215}
3216
3217/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003218 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 * Returns TRUE when there is a match.
3220 */
3221 static int
3222syn_regexec(rmp, lnum, col)
3223 regmmatch_T *rmp;
3224 linenr_T lnum;
3225 colnr_T col;
3226{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003227 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003228 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 {
3230 rmp->startpos[0].lnum += lnum;
3231 rmp->endpos[0].lnum += lnum;
3232 return TRUE;
3233 }
3234 return FALSE;
3235}
3236
3237/*
3238 * Check one position in a line for a matching keyword.
3239 * The caller must check if a keyword can start at startcol.
3240 * Return it's ID if found, 0 otherwise.
3241 */
3242 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003243check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003244 char_u *line;
3245 int startcol; /* position in line to check for keyword */
3246 int *endcolp; /* return: character after found keyword */
3247 long *flagsp; /* return: flags of matching keyword */
3248 short **next_listp; /* return: next_list of matching keyword */
3249 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003250 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003251{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003252 keyentry_T *kp;
3253 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003255 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003257 hashtab_T *ht;
3258 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259
3260 /* Find first character after the keyword. First character was already
3261 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003262 kwp = line + startcol;
3263 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 do
3265 {
3266#ifdef FEAT_MBYTE
3267 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003268 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269 else
3270#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003271 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003273 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274
Bram Moolenaardad6b692005-01-25 22:14:34 +00003275 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276 return 0;
3277
3278 /*
3279 * Must make a copy of the keyword, so we can add a NUL and make it
3280 * lowercase.
3281 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003282 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003283
3284 /*
3285 * Try twice:
3286 * 1. matching case
3287 * 2. ignoring case
3288 */
3289 for (round = 1; round <= 2; ++round)
3290 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003291 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003292 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003294 if (round == 2) /* ignore case */
3295 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296
3297 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003298 * Find keywords that match. There can be several with different
3299 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300 * When current_next_list is non-zero accept only that group, otherwise:
3301 * Accept a not-contained keyword at toplevel.
3302 * Accept a keyword at other levels only if it is in the contains list.
3303 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003304 hi = hash_find(ht, keyword);
3305 if (!HASHITEM_EMPTY(hi))
3306 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003308 if (current_next_list != 0
3309 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3310 : (cur_si == NULL
3311 ? !(kp->flags & HL_CONTAINED)
3312 : in_id_list(cur_si, cur_si->si_cont_list,
3313 &kp->k_syn, kp->flags & HL_CONTAINED)))
3314 {
3315 *endcolp = startcol + kwlen;
3316 *flagsp = kp->flags;
3317 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003318#ifdef FEAT_CONCEAL
3319 *ccharp = kp->k_char;
3320#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003321 return kp->k_syn.id;
3322 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323 }
3324 }
3325 return 0;
3326}
3327
3328/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003329 * Handle ":syntax conceal" command.
3330 */
3331 static void
3332syn_cmd_conceal(eap, syncing)
3333 exarg_T *eap UNUSED;
3334 int syncing UNUSED;
3335{
3336#ifdef FEAT_CONCEAL
3337 char_u *arg = eap->arg;
3338 char_u *next;
3339
3340 eap->nextcmd = find_nextcmd(arg);
3341 if (eap->skip)
3342 return;
3343
3344 next = skiptowhite(arg);
3345 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3346 curwin->w_s->b_syn_conceal = TRUE;
3347 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3348 curwin->w_s->b_syn_conceal = FALSE;
3349 else
3350 EMSG2(_("E390: Illegal argument: %s"), arg);
3351#endif
3352}
3353
3354/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355 * Handle ":syntax case" command.
3356 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 static void
3358syn_cmd_case(eap, syncing)
3359 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003360 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361{
3362 char_u *arg = eap->arg;
3363 char_u *next;
3364
3365 eap->nextcmd = find_nextcmd(arg);
3366 if (eap->skip)
3367 return;
3368
3369 next = skiptowhite(arg);
3370 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003371 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003373 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 else
3375 EMSG2(_("E390: Illegal argument: %s"), arg);
3376}
3377
3378/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003379 * Handle ":syntax spell" command.
3380 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003381 static void
3382syn_cmd_spell(eap, syncing)
3383 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003384 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003385{
3386 char_u *arg = eap->arg;
3387 char_u *next;
3388
3389 eap->nextcmd = find_nextcmd(arg);
3390 if (eap->skip)
3391 return;
3392
3393 next = skiptowhite(arg);
3394 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003395 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003396 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003397 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003398 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003399 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003400 else
3401 EMSG2(_("E390: Illegal argument: %s"), arg);
3402}
3403
3404/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003405 * Clear all syntax info for one buffer.
3406 */
3407 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003408syntax_clear(block)
3409 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003410{
3411 int i;
3412
Bram Moolenaar860cae12010-06-05 23:22:07 +02003413 block->b_syn_error = FALSE; /* clear previous error */
3414 block->b_syn_ic = FALSE; /* Use case, by default */
3415 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3416 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003417
3418 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003419 clear_keywtab(&block->b_keywtab);
3420 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421
3422 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3424 syn_clear_pattern(block, i);
3425 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003426
3427 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003428 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3429 syn_clear_cluster(block, i);
3430 ga_clear(&block->b_syn_clusters);
3431 block->b_spell_cluster_id = 0;
3432 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433
Bram Moolenaar860cae12010-06-05 23:22:07 +02003434 block->b_syn_sync_flags = 0;
3435 block->b_syn_sync_minlines = 0;
3436 block->b_syn_sync_maxlines = 0;
3437 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003438
Bram Moolenaar860cae12010-06-05 23:22:07 +02003439 vim_free(block->b_syn_linecont_prog);
3440 block->b_syn_linecont_prog = NULL;
3441 vim_free(block->b_syn_linecont_pat);
3442 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003444 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445#endif
3446
3447 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003448 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003450
3451 /* Reset the counter for ":syn include" */
3452 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453}
3454
3455/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003456 * Get rid of ownsyntax for window "wp".
3457 */
3458 void
3459reset_synblock(wp)
3460 win_T *wp;
3461{
3462 if (wp->w_s != &wp->w_buffer->b_s)
3463 {
3464 syntax_clear(wp->w_s);
3465 vim_free(wp->w_s);
3466 wp->w_s = &wp->w_buffer->b_s;
3467 }
3468}
3469
3470/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 * Clear syncing info for one buffer.
3472 */
3473 static void
3474syntax_sync_clear()
3475{
3476 int i;
3477
3478 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003479 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3480 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3481 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482
Bram Moolenaar860cae12010-06-05 23:22:07 +02003483 curwin->w_s->b_syn_sync_flags = 0;
3484 curwin->w_s->b_syn_sync_minlines = 0;
3485 curwin->w_s->b_syn_sync_maxlines = 0;
3486 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488 vim_free(curwin->w_s->b_syn_linecont_prog);
3489 curwin->w_s->b_syn_linecont_prog = NULL;
3490 vim_free(curwin->w_s->b_syn_linecont_pat);
3491 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494}
3495
3496/*
3497 * Remove one pattern from the buffer's pattern list.
3498 */
3499 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500syn_remove_pattern(block, idx)
3501 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502 int idx;
3503{
3504 synpat_T *spp;
3505
Bram Moolenaar860cae12010-06-05 23:22:07 +02003506 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003507#ifdef FEAT_FOLDING
3508 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003513 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3514 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515}
3516
3517/*
3518 * Clear and free one syntax pattern. When clearing all, must be called from
3519 * last to first!
3520 */
3521 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003522syn_clear_pattern(block, i)
3523 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003524 int i;
3525{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003526 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3527 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003531 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3532 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3533 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534 }
3535}
3536
3537/*
3538 * Clear and free one syntax cluster.
3539 */
3540 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003541syn_clear_cluster(block, i)
3542 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003543 int i;
3544{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003545 vim_free(SYN_CLSTR(block)[i].scl_name);
3546 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3547 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548}
3549
3550/*
3551 * Handle ":syntax clear" command.
3552 */
3553 static void
3554syn_cmd_clear(eap, syncing)
3555 exarg_T *eap;
3556 int syncing;
3557{
3558 char_u *arg = eap->arg;
3559 char_u *arg_end;
3560 int id;
3561
3562 eap->nextcmd = find_nextcmd(arg);
3563 if (eap->skip)
3564 return;
3565
3566 /*
3567 * We have to disable this within ":syn include @group filename",
3568 * because otherwise @group would get deleted.
3569 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3570 * clear".
3571 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003572 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003573 return;
3574
3575 if (ends_excmd(*arg))
3576 {
3577 /*
3578 * No argument: Clear all syntax items.
3579 */
3580 if (syncing)
3581 syntax_sync_clear();
3582 else
3583 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003584 syntax_clear(curwin->w_s);
3585 if (curwin->w_s == &curwin->w_buffer->b_s)
3586 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003587 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003588 }
3589 }
3590 else
3591 {
3592 /*
3593 * Clear the group IDs that are in the argument.
3594 */
3595 while (!ends_excmd(*arg))
3596 {
3597 arg_end = skiptowhite(arg);
3598 if (*arg == '@')
3599 {
3600 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3601 if (id == 0)
3602 {
3603 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3604 break;
3605 }
3606 else
3607 {
3608 /*
3609 * We can't physically delete a cluster without changing
3610 * the IDs of other clusters, so we do the next best thing
3611 * and make it empty.
3612 */
3613 short scl_id = id - SYNID_CLUSTER;
3614
Bram Moolenaar860cae12010-06-05 23:22:07 +02003615 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3616 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003617 }
3618 }
3619 else
3620 {
3621 id = syn_namen2id(arg, (int)(arg_end - arg));
3622 if (id == 0)
3623 {
3624 EMSG2(_(e_nogroup), arg);
3625 break;
3626 }
3627 else
3628 syn_clear_one(id, syncing);
3629 }
3630 arg = skipwhite(arg_end);
3631 }
3632 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003633 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003634 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003635}
3636
3637/*
3638 * Clear one syntax group for the current buffer.
3639 */
3640 static void
3641syn_clear_one(id, syncing)
3642 int id;
3643 int syncing;
3644{
3645 synpat_T *spp;
3646 int idx;
3647
3648 /* Clear keywords only when not ":syn sync clear group-name" */
3649 if (!syncing)
3650 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003651 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3652 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653 }
3654
3655 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003656 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003658 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003659 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3660 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003661 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662 }
3663}
3664
3665/*
3666 * Handle ":syntax on" command.
3667 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668 static void
3669syn_cmd_on(eap, syncing)
3670 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003671 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672{
3673 syn_cmd_onoff(eap, "syntax");
3674}
3675
3676/*
3677 * Handle ":syntax enable" command.
3678 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003679 static void
3680syn_cmd_enable(eap, syncing)
3681 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003682 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683{
3684 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3685 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003686 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687}
3688
3689/*
3690 * Handle ":syntax reset" command.
3691 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003692 static void
3693syn_cmd_reset(eap, syncing)
3694 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003695 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696{
3697 eap->nextcmd = check_nextcmd(eap->arg);
3698 if (!eap->skip)
3699 {
3700 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3701 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003702 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703 }
3704}
3705
3706/*
3707 * Handle ":syntax manual" command.
3708 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003709 static void
3710syn_cmd_manual(eap, syncing)
3711 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003712 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713{
3714 syn_cmd_onoff(eap, "manual");
3715}
3716
3717/*
3718 * Handle ":syntax off" command.
3719 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003720 static void
3721syn_cmd_off(eap, syncing)
3722 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003723 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724{
3725 syn_cmd_onoff(eap, "nosyntax");
3726}
3727
3728 static void
3729syn_cmd_onoff(eap, name)
3730 exarg_T *eap;
3731 char *name;
3732{
3733 char_u buf[100];
3734
3735 eap->nextcmd = check_nextcmd(eap->arg);
3736 if (!eap->skip)
3737 {
3738 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003739 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003740 do_cmdline_cmd(buf);
3741 }
3742}
3743
3744/*
3745 * Handle ":syntax [list]" command: list current syntax words.
3746 */
3747 static void
3748syn_cmd_list(eap, syncing)
3749 exarg_T *eap;
3750 int syncing; /* when TRUE: list syncing items */
3751{
3752 char_u *arg = eap->arg;
3753 int id;
3754 char_u *arg_end;
3755
3756 eap->nextcmd = find_nextcmd(arg);
3757 if (eap->skip)
3758 return;
3759
Bram Moolenaar860cae12010-06-05 23:22:07 +02003760 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003761 {
3762 MSG(_("No Syntax items defined for this buffer"));
3763 return;
3764 }
3765
3766 if (syncing)
3767 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003768 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 {
3770 MSG_PUTS(_("syncing on C-style comments"));
3771 syn_lines_msg();
3772 syn_match_msg();
3773 return;
3774 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003775 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003777 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003778 MSG_PUTS(_("no syncing"));
3779 else
3780 {
3781 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003782 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783 MSG_PUTS(_(" lines before top line"));
3784 syn_match_msg();
3785 }
3786 return;
3787 }
3788 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003789 if (curwin->w_s->b_syn_sync_minlines > 0
3790 || curwin->w_s->b_syn_sync_maxlines > 0
3791 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 {
3793 MSG_PUTS(_("\nsyncing on items"));
3794 syn_lines_msg();
3795 syn_match_msg();
3796 }
3797 }
3798 else
3799 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3800 if (ends_excmd(*arg))
3801 {
3802 /*
3803 * No argument: List all group IDs and all syntax clusters.
3804 */
3805 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3806 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003807 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003808 syn_list_cluster(id);
3809 }
3810 else
3811 {
3812 /*
3813 * List the group IDs and syntax clusters that are in the argument.
3814 */
3815 while (!ends_excmd(*arg) && !got_int)
3816 {
3817 arg_end = skiptowhite(arg);
3818 if (*arg == '@')
3819 {
3820 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3821 if (id == 0)
3822 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3823 else
3824 syn_list_cluster(id - SYNID_CLUSTER);
3825 }
3826 else
3827 {
3828 id = syn_namen2id(arg, (int)(arg_end - arg));
3829 if (id == 0)
3830 EMSG2(_(e_nogroup), arg);
3831 else
3832 syn_list_one(id, syncing, TRUE);
3833 }
3834 arg = skipwhite(arg_end);
3835 }
3836 }
3837 eap->nextcmd = check_nextcmd(arg);
3838}
3839
3840 static void
3841syn_lines_msg()
3842{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003843 if (curwin->w_s->b_syn_sync_maxlines > 0
3844 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 {
3846 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003847 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 {
3849 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003850 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3851 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852 MSG_PUTS(", ");
3853 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003854 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 {
3856 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003857 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 }
3859 MSG_PUTS(_(" lines before top line"));
3860 }
3861}
3862
3863 static void
3864syn_match_msg()
3865{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003866 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 {
3868 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003869 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 MSG_PUTS(_(" line breaks"));
3871 }
3872}
3873
3874static int last_matchgroup;
3875
3876struct name_list
3877{
3878 int flag;
3879 char *name;
3880};
3881
3882static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3883
3884/*
3885 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3886 */
3887 static void
3888syn_list_one(id, syncing, link_only)
3889 int id;
3890 int syncing; /* when TRUE: list syncing items */
3891 int link_only; /* when TRUE; list link-only too */
3892{
3893 int attr;
3894 int idx;
3895 int did_header = FALSE;
3896 synpat_T *spp;
3897 static struct name_list namelist1[] =
3898 {
3899 {HL_DISPLAY, "display"},
3900 {HL_CONTAINED, "contained"},
3901 {HL_ONELINE, "oneline"},
3902 {HL_KEEPEND, "keepend"},
3903 {HL_EXTEND, "extend"},
3904 {HL_EXCLUDENL, "excludenl"},
3905 {HL_TRANSP, "transparent"},
3906 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003907#ifdef FEAT_CONCEAL
3908 {HL_CONCEAL, "conceal"},
3909 {HL_CONCEALENDS, "concealends"},
3910#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 {0, NULL}
3912 };
3913 static struct name_list namelist2[] =
3914 {
3915 {HL_SKIPWHITE, "skipwhite"},
3916 {HL_SKIPNL, "skipnl"},
3917 {HL_SKIPEMPTY, "skipempty"},
3918 {0, NULL}
3919 };
3920
3921 attr = hl_attr(HLF_D); /* highlight like directories */
3922
3923 /* list the keywords for "id" */
3924 if (!syncing)
3925 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003926 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3927 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 did_header, attr);
3929 }
3930
3931 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003932 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003933 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003934 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3936 continue;
3937
3938 (void)syn_list_header(did_header, 999, id);
3939 did_header = TRUE;
3940 last_matchgroup = 0;
3941 if (spp->sp_type == SPTYPE_MATCH)
3942 {
3943 put_pattern("match", ' ', spp, attr);
3944 msg_putchar(' ');
3945 }
3946 else if (spp->sp_type == SPTYPE_START)
3947 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003948 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3949 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3950 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3951 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3952 while (idx < curwin->w_s->b_syn_patterns.ga_len
3953 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3954 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 --idx;
3956 msg_putchar(' ');
3957 }
3958 syn_list_flags(namelist1, spp->sp_flags, attr);
3959
3960 if (spp->sp_cont_list != NULL)
3961 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3962
3963 if (spp->sp_syn.cont_in_list != NULL)
3964 put_id_list((char_u *)"containedin",
3965 spp->sp_syn.cont_in_list, attr);
3966
3967 if (spp->sp_next_list != NULL)
3968 {
3969 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3970 syn_list_flags(namelist2, spp->sp_flags, attr);
3971 }
3972 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3973 {
3974 if (spp->sp_flags & HL_SYNC_HERE)
3975 msg_puts_attr((char_u *)"grouphere", attr);
3976 else
3977 msg_puts_attr((char_u *)"groupthere", attr);
3978 msg_putchar(' ');
3979 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003980 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3982 else
3983 MSG_PUTS("NONE");
3984 msg_putchar(' ');
3985 }
3986 }
3987
3988 /* list the link, if there is one */
3989 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3990 {
3991 (void)syn_list_header(did_header, 999, id);
3992 msg_puts_attr((char_u *)"links to", attr);
3993 msg_putchar(' ');
3994 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3995 }
3996}
3997
3998 static void
3999syn_list_flags(nl, flags, attr)
4000 struct name_list *nl;
4001 int flags;
4002 int attr;
4003{
4004 int i;
4005
4006 for (i = 0; nl[i].flag != 0; ++i)
4007 if (flags & nl[i].flag)
4008 {
4009 msg_puts_attr((char_u *)nl[i].name, attr);
4010 msg_putchar(' ');
4011 }
4012}
4013
4014/*
4015 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4016 */
4017 static void
4018syn_list_cluster(id)
4019 int id;
4020{
4021 int endcol = 15;
4022
4023 /* slight hack: roughly duplicate the guts of syn_list_header() */
4024 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004025 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026
4027 if (msg_col >= endcol) /* output at least one space */
4028 endcol = msg_col + 1;
4029 if (Columns <= endcol) /* avoid hang for tiny window */
4030 endcol = Columns - 1;
4031
4032 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004033 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004035 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004036 hl_attr(HLF_D));
4037 }
4038 else
4039 {
4040 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4041 msg_puts((char_u *)"=NONE");
4042 }
4043}
4044
4045 static void
4046put_id_list(name, list, attr)
4047 char_u *name;
4048 short *list;
4049 int attr;
4050{
4051 short *p;
4052
4053 msg_puts_attr(name, attr);
4054 msg_putchar('=');
4055 for (p = list; *p; ++p)
4056 {
4057 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4058 {
4059 if (p[1])
4060 MSG_PUTS("ALLBUT");
4061 else
4062 MSG_PUTS("ALL");
4063 }
4064 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4065 {
4066 MSG_PUTS("TOP");
4067 }
4068 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4069 {
4070 MSG_PUTS("CONTAINED");
4071 }
4072 else if (*p >= SYNID_CLUSTER)
4073 {
4074 short scl_id = *p - SYNID_CLUSTER;
4075
4076 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004077 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 }
4079 else
4080 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4081 if (p[1])
4082 msg_putchar(',');
4083 }
4084 msg_putchar(' ');
4085}
4086
4087 static void
4088put_pattern(s, c, spp, attr)
4089 char *s;
4090 int c;
4091 synpat_T *spp;
4092 int attr;
4093{
4094 long n;
4095 int mask;
4096 int first;
4097 static char *sepchars = "/+=-#@\"|'^&";
4098 int i;
4099
4100 /* May have to write "matchgroup=group" */
4101 if (last_matchgroup != spp->sp_syn_match_id)
4102 {
4103 last_matchgroup = spp->sp_syn_match_id;
4104 msg_puts_attr((char_u *)"matchgroup", attr);
4105 msg_putchar('=');
4106 if (last_matchgroup == 0)
4107 msg_outtrans((char_u *)"NONE");
4108 else
4109 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4110 msg_putchar(' ');
4111 }
4112
4113 /* output the name of the pattern and an '=' or ' ' */
4114 msg_puts_attr((char_u *)s, attr);
4115 msg_putchar(c);
4116
4117 /* output the pattern, in between a char that is not in the pattern */
4118 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4119 if (sepchars[++i] == NUL)
4120 {
4121 i = 0; /* no good char found, just use the first one */
4122 break;
4123 }
4124 msg_putchar(sepchars[i]);
4125 msg_outtrans(spp->sp_pattern);
4126 msg_putchar(sepchars[i]);
4127
4128 /* output any pattern options */
4129 first = TRUE;
4130 for (i = 0; i < SPO_COUNT; ++i)
4131 {
4132 mask = (1 << i);
4133 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4134 {
4135 if (!first)
4136 msg_putchar(','); /* separate with commas */
4137 msg_puts((char_u *)spo_name_tab[i]);
4138 n = spp->sp_offsets[i];
4139 if (i != SPO_LC_OFF)
4140 {
4141 if (spp->sp_off_flags & mask)
4142 msg_putchar('s');
4143 else
4144 msg_putchar('e');
4145 if (n > 0)
4146 msg_putchar('+');
4147 }
4148 if (n || i == SPO_LC_OFF)
4149 msg_outnum(n);
4150 first = FALSE;
4151 }
4152 }
4153 msg_putchar(' ');
4154}
4155
4156/*
4157 * List or clear the keywords for one syntax group.
4158 * Return TRUE if the header has been printed.
4159 */
4160 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004161syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004163 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004164 int did_header; /* header has already been printed */
4165 int attr;
4166{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004168 hashitem_T *hi;
4169 keyentry_T *kp;
4170 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 int prev_contained = 0;
4172 short *prev_next_list = NULL;
4173 short *prev_cont_in_list = NULL;
4174 int prev_skipnl = 0;
4175 int prev_skipwhite = 0;
4176 int prev_skipempty = 0;
4177
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 /*
4179 * Unfortunately, this list of keywords is not sorted on alphabet but on
4180 * hash value...
4181 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004182 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004183 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004185 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004187 --todo;
4188 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004190 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004191 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004192 if (prev_contained != (kp->flags & HL_CONTAINED)
4193 || prev_skipnl != (kp->flags & HL_SKIPNL)
4194 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4195 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4196 || prev_cont_in_list != kp->k_syn.cont_in_list
4197 || prev_next_list != kp->next_list)
4198 outlen = 9999;
4199 else
4200 outlen = (int)STRLEN(kp->keyword);
4201 /* output "contained" and "nextgroup" on each line */
4202 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004203 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004204 prev_contained = 0;
4205 prev_next_list = NULL;
4206 prev_cont_in_list = NULL;
4207 prev_skipnl = 0;
4208 prev_skipwhite = 0;
4209 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004211 did_header = TRUE;
4212 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004214 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004216 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004218 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004220 put_id_list((char_u *)"containedin",
4221 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004223 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004224 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004225 if (kp->next_list != prev_next_list)
4226 {
4227 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4228 msg_putchar(' ');
4229 prev_next_list = kp->next_list;
4230 if (kp->flags & HL_SKIPNL)
4231 {
4232 msg_puts_attr((char_u *)"skipnl", attr);
4233 msg_putchar(' ');
4234 prev_skipnl = (kp->flags & HL_SKIPNL);
4235 }
4236 if (kp->flags & HL_SKIPWHITE)
4237 {
4238 msg_puts_attr((char_u *)"skipwhite", attr);
4239 msg_putchar(' ');
4240 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4241 }
4242 if (kp->flags & HL_SKIPEMPTY)
4243 {
4244 msg_puts_attr((char_u *)"skipempty", attr);
4245 msg_putchar(' ');
4246 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4247 }
4248 }
4249 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 }
4252 }
4253 }
4254
4255 return did_header;
4256}
4257
4258 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004259syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004261 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 hashitem_T *hi;
4264 keyentry_T *kp;
4265 keyentry_T *kp_prev;
4266 keyentry_T *kp_next;
4267 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268
Bram Moolenaardad6b692005-01-25 22:14:34 +00004269 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004270 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004271 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 --todo;
4276 kp_prev = NULL;
4277 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 if (kp->k_syn.id == id)
4280 {
4281 kp_next = kp->ke_next;
4282 if (kp_prev == NULL)
4283 {
4284 if (kp_next == NULL)
4285 hash_remove(ht, hi);
4286 else
4287 hi->hi_key = KE2HIKEY(kp_next);
4288 }
4289 else
4290 kp_prev->ke_next = kp_next;
4291 vim_free(kp->next_list);
4292 vim_free(kp->k_syn.cont_in_list);
4293 vim_free(kp);
4294 kp = kp_next;
4295 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004296 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004297 {
4298 kp_prev = kp;
4299 kp = kp->ke_next;
4300 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 }
4302 }
4303 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004304 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305}
4306
4307/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004308 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 */
4310 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311clear_keywtab(ht)
4312 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004314 hashitem_T *hi;
4315 int todo;
4316 keyentry_T *kp;
4317 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004319 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004320 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004322 if (!HASHITEM_EMPTY(hi))
4323 {
4324 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004326 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004327 kp_next = kp->ke_next;
4328 vim_free(kp->next_list);
4329 vim_free(kp->k_syn.cont_in_list);
4330 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 hash_clear(ht);
4335 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336}
4337
4338/*
4339 * Add a keyword to the list of keywords.
4340 */
4341 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004342add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343 char_u *name; /* name of keyword */
4344 int id; /* group ID for this keyword */
4345 int flags; /* flags for this keyword */
4346 short *cont_in_list; /* containedin for this keyword */
4347 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004348 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004350 keyentry_T *kp;
4351 hashtab_T *ht;
4352 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004353 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004355 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004356
Bram Moolenaar860cae12010-06-05 23:22:07 +02004357 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004358 name_ic = str_foldcase(name, (int)STRLEN(name),
4359 name_folded, MAXKEYWLEN + 1);
4360 else
4361 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004362 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4363 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 kp->k_syn.id = id;
4367 kp->k_syn.inc_tag = current_syn_inc_tag;
4368 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004369 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004372 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374
Bram Moolenaar860cae12010-06-05 23:22:07 +02004375 if (curwin->w_s->b_syn_ic)
4376 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004378 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 hash = hash_hash(kp->keyword);
4381 hi = hash_lookup(ht, kp->keyword, hash);
4382 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 /* new keyword, add to hashtable */
4385 kp->ke_next = NULL;
4386 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 /* keyword already exists, prepend to list */
4391 kp->ke_next = HI2KE(hi);
4392 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394}
4395
4396/*
4397 * Get the start and end of the group name argument.
4398 * Return a pointer to the first argument.
4399 * Return NULL if the end of the command was found instead of further args.
4400 */
4401 static char_u *
4402get_group_name(arg, name_end)
4403 char_u *arg; /* start of the argument */
4404 char_u **name_end; /* pointer to end of the name */
4405{
4406 char_u *rest;
4407
4408 *name_end = skiptowhite(arg);
4409 rest = skipwhite(*name_end);
4410
4411 /*
4412 * Check if there are enough arguments. The first argument may be a
4413 * pattern, where '|' is allowed, so only check for NUL.
4414 */
4415 if (ends_excmd(*arg) || *rest == NUL)
4416 return NULL;
4417 return rest;
4418}
4419
4420/*
4421 * Check for syntax command option arguments.
4422 * This can be called at any place in the list of arguments, and just picks
4423 * out the arguments that are known. Can be called several times in a row to
4424 * collect all options in between other arguments.
4425 * Return a pointer to the next argument (which isn't an option).
4426 * Return NULL for any error;
4427 */
4428 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004429get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004430 char_u *arg; /* next argument to be checked */
4431 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004432 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 char_u *gname_start, *gname;
4435 int syn_id;
4436 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004437 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438 int i;
4439 int fidx;
4440 static struct flag
4441 {
4442 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004443 int argtype;
4444 int flags;
4445 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4446 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4447 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4448 {"eExXtTeEnNdD", 0, HL_EXTEND},
4449 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4450 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4451 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4452 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4453 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4454 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4455 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4456 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4457 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004458 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4459 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4460 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004461 {"cCoOnNtTaAiInNsS", 1, 0},
4462 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4463 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004465 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466
4467 if (arg == NULL) /* already detected error */
4468 return NULL;
4469
Bram Moolenaar860cae12010-06-05 23:22:07 +02004470#ifdef FEAT_CONCEAL
4471 if (curwin->w_s->b_syn_conceal)
4472 opt->flags |= HL_CONCEAL;
4473#endif
4474
Bram Moolenaar071d4272004-06-13 20:20:40 +00004475 for (;;)
4476 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004477 /*
4478 * This is used very often when a large number of keywords is defined.
4479 * Need to skip quickly when no option name is found.
4480 * Also avoid tolower(), it's slow.
4481 */
4482 if (strchr(first_letters, *arg) == NULL)
4483 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484
4485 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4486 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004487 p = flagtab[fidx].name;
4488 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4489 if (arg[len] != p[i] && arg[len] != p[i + 1])
4490 break;
4491 if (p[i] == NUL && (vim_iswhite(arg[len])
4492 || (flagtab[fidx].argtype > 0
4493 ? arg[len] == '='
4494 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004496 if (opt->keyword
4497 && (flagtab[fidx].flags == HL_DISPLAY
4498 || flagtab[fidx].flags == HL_FOLD
4499 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500 /* treat "display", "fold" and "extend" as a keyword */
4501 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502 break;
4503 }
4504 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004505 if (fidx < 0) /* no match found */
4506 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004507
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004508 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004510 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 {
4512 EMSG(_("E395: contains argument not accepted here"));
4513 return NULL;
4514 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004516 return NULL;
4517 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004518 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004520 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521 return NULL;
4522 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004525 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 return NULL;
4527 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004528 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4529 {
4530#ifdef FEAT_MBYTE
4531 /* cchar=? */
4532 if (has_mbyte)
4533 {
4534# ifdef FEAT_CONCEAL
4535 *conceal_char = mb_ptr2char(arg + 6);
4536# endif
4537 arg += mb_ptr2len(arg + 6) - 1;
4538 }
4539 else
4540#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004541 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004542#ifdef FEAT_CONCEAL
4543 *conceal_char = arg[6];
4544#else
4545 ;
4546#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004547 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004548#ifdef FEAT_CONCEAL
4549 if (!vim_isprintc_strict(*conceal_char))
4550 {
4551 EMSG(_("E844: invalid cchar value"));
4552 return NULL;
4553 }
4554#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004555 arg = skipwhite(arg + 7);
4556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004557 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004558 {
4559 opt->flags |= flagtab[fidx].flags;
4560 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004562 if (flagtab[fidx].flags == HL_SYNC_HERE
4563 || flagtab[fidx].flags == HL_SYNC_THERE)
4564 {
4565 if (opt->sync_idx == NULL)
4566 {
4567 EMSG(_("E393: group[t]here not accepted here"));
4568 return NULL;
4569 }
4570 gname_start = arg;
4571 arg = skiptowhite(arg);
4572 if (gname_start == arg)
4573 return NULL;
4574 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4575 if (gname == NULL)
4576 return NULL;
4577 if (STRCMP(gname, "NONE") == 0)
4578 *opt->sync_idx = NONE_IDX;
4579 else
4580 {
4581 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004582 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4583 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4584 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004585 {
4586 *opt->sync_idx = i;
4587 break;
4588 }
4589 if (i < 0)
4590 {
4591 EMSG2(_("E394: Didn't find region item for %s"), gname);
4592 vim_free(gname);
4593 return NULL;
4594 }
4595 }
4596
4597 vim_free(gname);
4598 arg = skipwhite(arg);
4599 }
4600#ifdef FEAT_FOLDING
4601 else if (flagtab[fidx].flags == HL_FOLD
4602 && foldmethodIsSyntax(curwin))
4603 /* Need to update folds later. */
4604 foldUpdateAll(curwin);
4605#endif
4606 }
4607 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004608
4609 return arg;
4610}
4611
4612/*
4613 * Adjustments to syntax item when declared in a ":syn include"'d file.
4614 * Set the contained flag, and if the item is not already contained, add it
4615 * to the specified top-level group, if any.
4616 */
4617 static void
4618syn_incl_toplevel(id, flagsp)
4619 int id;
4620 int *flagsp;
4621{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004622 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004623 return;
4624 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004625 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626 {
4627 /* We have to alloc this, because syn_combine_list() will free it. */
4628 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004629 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004630
4631 if (grp_list != NULL)
4632 {
4633 grp_list[0] = id;
4634 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004635 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 CLUSTER_ADD);
4637 }
4638 }
4639}
4640
4641/*
4642 * Handle ":syntax include [@{group-name}] filename" command.
4643 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004644 static void
4645syn_cmd_include(eap, syncing)
4646 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004647 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648{
4649 char_u *arg = eap->arg;
4650 int sgl_id = 1;
4651 char_u *group_name_end;
4652 char_u *rest;
4653 char_u *errormsg = NULL;
4654 int prev_toplvl_grp;
4655 int prev_syn_inc_tag;
4656 int source = FALSE;
4657
4658 eap->nextcmd = find_nextcmd(arg);
4659 if (eap->skip)
4660 return;
4661
4662 if (arg[0] == '@')
4663 {
4664 ++arg;
4665 rest = get_group_name(arg, &group_name_end);
4666 if (rest == NULL)
4667 {
4668 EMSG((char_u *)_("E397: Filename required"));
4669 return;
4670 }
4671 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004672 if (sgl_id == 0)
4673 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674 /* separate_nextcmd() and expand_filename() depend on this */
4675 eap->arg = rest;
4676 }
4677
4678 /*
4679 * Everything that's left, up to the next command, should be the
4680 * filename to include.
4681 */
4682 eap->argt |= (XFILE | NOSPC);
4683 separate_nextcmd(eap);
4684 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4685 {
4686 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4687 * file. Need to expand the file name first. In other cases
4688 * ":runtime!" is used. */
4689 source = TRUE;
4690 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4691 {
4692 if (errormsg != NULL)
4693 EMSG(errormsg);
4694 return;
4695 }
4696 }
4697
4698 /*
4699 * Save and restore the existing top-level grouplist id and ":syn
4700 * include" tag around the actual inclusion.
4701 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004702 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4703 {
4704 EMSG((char_u *)_("E847: Too many syntax includes"));
4705 return;
4706 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707 prev_syn_inc_tag = current_syn_inc_tag;
4708 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004709 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4710 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004711 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4712 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004714 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004715 current_syn_inc_tag = prev_syn_inc_tag;
4716}
4717
4718/*
4719 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4720 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721 static void
4722syn_cmd_keyword(eap, syncing)
4723 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004724 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725{
4726 char_u *arg = eap->arg;
4727 char_u *group_name_end;
4728 int syn_id;
4729 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004730 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004732 char_u *kw;
4733 syn_opt_arg_T syn_opt_arg;
4734 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004735 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004736
4737 rest = get_group_name(arg, &group_name_end);
4738
4739 if (rest != NULL)
4740 {
4741 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004742 if (syn_id != 0)
4743 /* allocate a buffer, for removing backslashes in the keyword */
4744 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004745 if (keyword_copy != NULL)
4746 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004747 syn_opt_arg.flags = 0;
4748 syn_opt_arg.keyword = TRUE;
4749 syn_opt_arg.sync_idx = NULL;
4750 syn_opt_arg.has_cont_list = FALSE;
4751 syn_opt_arg.cont_in_list = NULL;
4752 syn_opt_arg.next_list = NULL;
4753
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754 /*
4755 * The options given apply to ALL keywords, so all options must be
4756 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004757 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004759 cnt = 0;
4760 p = keyword_copy;
4761 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004763 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004764 if (rest == NULL || ends_excmd(*rest))
4765 break;
4766 /* Copy the keyword, removing backslashes, and add a NUL. */
4767 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004769 if (*rest == '\\' && rest[1] != NUL)
4770 ++rest;
4771 *p++ = *rest++;
4772 }
4773 *p++ = NUL;
4774 ++cnt;
4775 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004777 if (!eap->skip)
4778 {
4779 /* Adjust flags for use of ":syn include". */
4780 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4781
4782 /*
4783 * 2: Add an entry for each keyword.
4784 */
4785 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4786 {
4787 for (p = vim_strchr(kw, '['); ; )
4788 {
4789 if (p != NULL)
4790 *p = NUL;
4791 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004792 syn_opt_arg.cont_in_list,
4793 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004794 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004795 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004796 if (p[1] == NUL)
4797 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004798 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004799 kw = p + 2; /* skip over the NUL */
4800 break;
4801 }
4802 if (p[1] == ']')
4803 {
4804 kw = p + 1; /* skip over the "]" */
4805 break;
4806 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004807#ifdef FEAT_MBYTE
4808 if (has_mbyte)
4809 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004810 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004811
4812 mch_memmove(p, p + 1, l);
4813 p += l;
4814 }
4815 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004817 {
4818 p[0] = p[1];
4819 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820 }
4821 }
4822 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004824
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004826 vim_free(syn_opt_arg.cont_in_list);
4827 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 }
4829 }
4830
4831 if (rest != NULL)
4832 eap->nextcmd = check_nextcmd(rest);
4833 else
4834 EMSG2(_(e_invarg2), arg);
4835
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004836 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004837 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838}
4839
4840/*
4841 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4842 *
4843 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4844 */
4845 static void
4846syn_cmd_match(eap, syncing)
4847 exarg_T *eap;
4848 int syncing; /* TRUE for ":syntax sync match .. " */
4849{
4850 char_u *arg = eap->arg;
4851 char_u *group_name_end;
4852 char_u *rest;
4853 synpat_T item; /* the item found in the line */
4854 int syn_id;
4855 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004856 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004857 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004858 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004859
4860 /* Isolate the group name, check for validity */
4861 rest = get_group_name(arg, &group_name_end);
4862
4863 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004864 syn_opt_arg.flags = 0;
4865 syn_opt_arg.keyword = FALSE;
4866 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4867 syn_opt_arg.has_cont_list = TRUE;
4868 syn_opt_arg.cont_list = NULL;
4869 syn_opt_arg.cont_in_list = NULL;
4870 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004871 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004872
4873 /* get the pattern. */
4874 init_syn_patterns();
4875 vim_memset(&item, 0, sizeof(item));
4876 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004877 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4878 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879
4880 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004881 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882
4883 if (rest != NULL) /* all arguments are valid */
4884 {
4885 /*
4886 * Check for trailing command and illegal trailing arguments.
4887 */
4888 eap->nextcmd = check_nextcmd(rest);
4889 if (!ends_excmd(*rest) || eap->skip)
4890 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892 && (syn_id = syn_check_group(arg,
4893 (int)(group_name_end - arg))) != 0)
4894 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004895 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896 /*
4897 * Store the pattern in the syn_items list
4898 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004899 idx = curwin->w_s->b_syn_patterns.ga_len;
4900 SYN_ITEMS(curwin->w_s)[idx] = item;
4901 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4902 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4903 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4904 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4905 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4906 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4907 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4908 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004909 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004910#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004911 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004912#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004913 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004914 curwin->w_s->b_syn_containedin = TRUE;
4915 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4916 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917
4918 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004919 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004920 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004922 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004923 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924#endif
4925
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004926 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004927 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928 return; /* don't free the progs and patterns now */
4929 }
4930 }
4931
4932 /*
4933 * Something failed, free the allocated memory.
4934 */
4935 vim_free(item.sp_prog);
4936 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004937 vim_free(syn_opt_arg.cont_list);
4938 vim_free(syn_opt_arg.cont_in_list);
4939 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004940
4941 if (rest == NULL)
4942 EMSG2(_(e_invarg2), arg);
4943}
4944
4945/*
4946 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4947 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4948 */
4949 static void
4950syn_cmd_region(eap, syncing)
4951 exarg_T *eap;
4952 int syncing; /* TRUE for ":syntax sync region .." */
4953{
4954 char_u *arg = eap->arg;
4955 char_u *group_name_end;
4956 char_u *rest; /* next arg, NULL on error */
4957 char_u *key_end;
4958 char_u *key = NULL;
4959 char_u *p;
4960 int item;
4961#define ITEM_START 0
4962#define ITEM_SKIP 1
4963#define ITEM_END 2
4964#define ITEM_MATCHGROUP 3
4965 struct pat_ptr
4966 {
4967 synpat_T *pp_synp; /* pointer to syn_pattern */
4968 int pp_matchgroup_id; /* matchgroup ID */
4969 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4970 } *(pat_ptrs[3]);
4971 /* patterns found in the line */
4972 struct pat_ptr *ppp;
4973 struct pat_ptr *ppp_next;
4974 int pat_count = 0; /* nr of syn_patterns found */
4975 int syn_id;
4976 int matchgroup_id = 0;
4977 int not_enough = FALSE; /* not enough arguments */
4978 int illegal = FALSE; /* illegal arguments */
4979 int success = FALSE;
4980 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004981 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004982 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004983
4984 /* Isolate the group name, check for validity */
4985 rest = get_group_name(arg, &group_name_end);
4986
4987 pat_ptrs[0] = NULL;
4988 pat_ptrs[1] = NULL;
4989 pat_ptrs[2] = NULL;
4990
4991 init_syn_patterns();
4992
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004993 syn_opt_arg.flags = 0;
4994 syn_opt_arg.keyword = FALSE;
4995 syn_opt_arg.sync_idx = NULL;
4996 syn_opt_arg.has_cont_list = TRUE;
4997 syn_opt_arg.cont_list = NULL;
4998 syn_opt_arg.cont_in_list = NULL;
4999 syn_opt_arg.next_list = NULL;
5000
Bram Moolenaar071d4272004-06-13 20:20:40 +00005001 /*
5002 * get the options, patterns and matchgroup.
5003 */
5004 while (rest != NULL && !ends_excmd(*rest))
5005 {
5006 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005007 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005008 if (rest == NULL || ends_excmd(*rest))
5009 break;
5010
5011 /* must be a pattern or matchgroup then */
5012 key_end = rest;
5013 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5014 ++key_end;
5015 vim_free(key);
5016 key = vim_strnsave_up(rest, (int)(key_end - rest));
5017 if (key == NULL) /* out of memory */
5018 {
5019 rest = NULL;
5020 break;
5021 }
5022 if (STRCMP(key, "MATCHGROUP") == 0)
5023 item = ITEM_MATCHGROUP;
5024 else if (STRCMP(key, "START") == 0)
5025 item = ITEM_START;
5026 else if (STRCMP(key, "END") == 0)
5027 item = ITEM_END;
5028 else if (STRCMP(key, "SKIP") == 0)
5029 {
5030 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5031 {
5032 illegal = TRUE;
5033 break;
5034 }
5035 item = ITEM_SKIP;
5036 }
5037 else
5038 break;
5039 rest = skipwhite(key_end);
5040 if (*rest != '=')
5041 {
5042 rest = NULL;
5043 EMSG2(_("E398: Missing '=': %s"), arg);
5044 break;
5045 }
5046 rest = skipwhite(rest + 1);
5047 if (*rest == NUL)
5048 {
5049 not_enough = TRUE;
5050 break;
5051 }
5052
5053 if (item == ITEM_MATCHGROUP)
5054 {
5055 p = skiptowhite(rest);
5056 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5057 matchgroup_id = 0;
5058 else
5059 {
5060 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5061 if (matchgroup_id == 0)
5062 {
5063 illegal = TRUE;
5064 break;
5065 }
5066 }
5067 rest = skipwhite(p);
5068 }
5069 else
5070 {
5071 /*
5072 * Allocate room for a syn_pattern, and link it in the list of
5073 * syn_patterns for this item, at the start (because the list is
5074 * used from end to start).
5075 */
5076 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5077 if (ppp == NULL)
5078 {
5079 rest = NULL;
5080 break;
5081 }
5082 ppp->pp_next = pat_ptrs[item];
5083 pat_ptrs[item] = ppp;
5084 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5085 if (ppp->pp_synp == NULL)
5086 {
5087 rest = NULL;
5088 break;
5089 }
5090
5091 /*
5092 * Get the syntax pattern and the following offset(s).
5093 */
5094 /* Enable the appropriate \z specials. */
5095 if (item == ITEM_START)
5096 reg_do_extmatch = REX_SET;
5097 else if (item == ITEM_SKIP || item == ITEM_END)
5098 reg_do_extmatch = REX_USE;
5099 rest = get_syn_pattern(rest, ppp->pp_synp);
5100 reg_do_extmatch = 0;
5101 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005102 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005103 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5104 ppp->pp_matchgroup_id = matchgroup_id;
5105 ++pat_count;
5106 }
5107 }
5108 vim_free(key);
5109 if (illegal || not_enough)
5110 rest = NULL;
5111
5112 /*
5113 * Must have a "start" and "end" pattern.
5114 */
5115 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5116 pat_ptrs[ITEM_END] == NULL))
5117 {
5118 not_enough = TRUE;
5119 rest = NULL;
5120 }
5121
5122 if (rest != NULL)
5123 {
5124 /*
5125 * Check for trailing garbage or command.
5126 * If OK, add the item.
5127 */
5128 eap->nextcmd = check_nextcmd(rest);
5129 if (!ends_excmd(*rest) || eap->skip)
5130 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005131 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005132 && (syn_id = syn_check_group(arg,
5133 (int)(group_name_end - arg))) != 0)
5134 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005135 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005136 /*
5137 * Store the start/skip/end in the syn_items list
5138 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005139 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 for (item = ITEM_START; item <= ITEM_END; ++item)
5141 {
5142 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5143 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005144 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5145 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5146 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005147 (item == ITEM_START) ? SPTYPE_START :
5148 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005149 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5150 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005151 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5152 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005153 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005154 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005155#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005156 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005157#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 if (item == ITEM_START)
5159 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005160 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005161 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005162 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005163 syn_opt_arg.cont_in_list;
5164 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005165 curwin->w_s->b_syn_containedin = TRUE;
5166 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005167 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005169 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005170 ++idx;
5171#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005172 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005173 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005174#endif
5175 }
5176 }
5177
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005178 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005179 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005180 success = TRUE; /* don't free the progs and patterns now */
5181 }
5182 }
5183
5184 /*
5185 * Free the allocated memory.
5186 */
5187 for (item = ITEM_START; item <= ITEM_END; ++item)
5188 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5189 {
5190 if (!success)
5191 {
5192 vim_free(ppp->pp_synp->sp_prog);
5193 vim_free(ppp->pp_synp->sp_pattern);
5194 }
5195 vim_free(ppp->pp_synp);
5196 ppp_next = ppp->pp_next;
5197 vim_free(ppp);
5198 }
5199
5200 if (!success)
5201 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005202 vim_free(syn_opt_arg.cont_list);
5203 vim_free(syn_opt_arg.cont_in_list);
5204 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005205 if (not_enough)
5206 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5207 else if (illegal || rest == NULL)
5208 EMSG2(_(e_invarg2), arg);
5209 }
5210}
5211
5212/*
5213 * A simple syntax group ID comparison function suitable for use in qsort()
5214 */
5215 static int
5216#ifdef __BORLANDC__
5217_RTLENTRYF
5218#endif
5219syn_compare_stub(v1, v2)
5220 const void *v1;
5221 const void *v2;
5222{
5223 const short *s1 = v1;
5224 const short *s2 = v2;
5225
5226 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5227}
5228
5229/*
5230 * Combines lists of syntax clusters.
5231 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5232 */
5233 static void
5234syn_combine_list(clstr1, clstr2, list_op)
5235 short **clstr1;
5236 short **clstr2;
5237 int list_op;
5238{
5239 int count1 = 0;
5240 int count2 = 0;
5241 short *g1;
5242 short *g2;
5243 short *clstr = NULL;
5244 int count;
5245 int round;
5246
5247 /*
5248 * Handle degenerate cases.
5249 */
5250 if (*clstr2 == NULL)
5251 return;
5252 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5253 {
5254 if (list_op == CLUSTER_REPLACE)
5255 vim_free(*clstr1);
5256 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5257 *clstr1 = *clstr2;
5258 else
5259 vim_free(*clstr2);
5260 return;
5261 }
5262
5263 for (g1 = *clstr1; *g1; g1++)
5264 ++count1;
5265 for (g2 = *clstr2; *g2; g2++)
5266 ++count2;
5267
5268 /*
5269 * For speed purposes, sort both lists.
5270 */
5271 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5272 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5273
5274 /*
5275 * We proceed in two passes; in round 1, we count the elements to place
5276 * in the new list, and in round 2, we allocate and populate the new
5277 * list. For speed, we use a mergesort-like method, adding the smaller
5278 * of the current elements in each list to the new list.
5279 */
5280 for (round = 1; round <= 2; round++)
5281 {
5282 g1 = *clstr1;
5283 g2 = *clstr2;
5284 count = 0;
5285
5286 /*
5287 * First, loop through the lists until one of them is empty.
5288 */
5289 while (*g1 && *g2)
5290 {
5291 /*
5292 * We always want to add from the first list.
5293 */
5294 if (*g1 < *g2)
5295 {
5296 if (round == 2)
5297 clstr[count] = *g1;
5298 count++;
5299 g1++;
5300 continue;
5301 }
5302 /*
5303 * We only want to add from the second list if we're adding the
5304 * lists.
5305 */
5306 if (list_op == CLUSTER_ADD)
5307 {
5308 if (round == 2)
5309 clstr[count] = *g2;
5310 count++;
5311 }
5312 if (*g1 == *g2)
5313 g1++;
5314 g2++;
5315 }
5316
5317 /*
5318 * Now add the leftovers from whichever list didn't get finished
5319 * first. As before, we only want to add from the second list if
5320 * we're adding the lists.
5321 */
5322 for (; *g1; g1++, count++)
5323 if (round == 2)
5324 clstr[count] = *g1;
5325 if (list_op == CLUSTER_ADD)
5326 for (; *g2; g2++, count++)
5327 if (round == 2)
5328 clstr[count] = *g2;
5329
5330 if (round == 1)
5331 {
5332 /*
5333 * If the group ended up empty, we don't need to allocate any
5334 * space for it.
5335 */
5336 if (count == 0)
5337 {
5338 clstr = NULL;
5339 break;
5340 }
5341 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5342 if (clstr == NULL)
5343 break;
5344 clstr[count] = 0;
5345 }
5346 }
5347
5348 /*
5349 * Finally, put the new list in place.
5350 */
5351 vim_free(*clstr1);
5352 vim_free(*clstr2);
5353 *clstr1 = clstr;
5354}
5355
5356/*
5357 * Lookup a syntax cluster name and return it's ID.
5358 * If it is not found, 0 is returned.
5359 */
5360 static int
5361syn_scl_name2id(name)
5362 char_u *name;
5363{
5364 int i;
5365 char_u *name_u;
5366
5367 /* Avoid using stricmp() too much, it's slow on some systems */
5368 name_u = vim_strsave_up(name);
5369 if (name_u == NULL)
5370 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005371 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5372 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5373 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005374 break;
5375 vim_free(name_u);
5376 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5377}
5378
5379/*
5380 * Like syn_scl_name2id(), but take a pointer + length argument.
5381 */
5382 static int
5383syn_scl_namen2id(linep, len)
5384 char_u *linep;
5385 int len;
5386{
5387 char_u *name;
5388 int id = 0;
5389
5390 name = vim_strnsave(linep, len);
5391 if (name != NULL)
5392 {
5393 id = syn_scl_name2id(name);
5394 vim_free(name);
5395 }
5396 return id;
5397}
5398
5399/*
5400 * Find syntax cluster name in the table and return it's ID.
5401 * The argument is a pointer to the name and the length of the name.
5402 * If it doesn't exist yet, a new entry is created.
5403 * Return 0 for failure.
5404 */
5405 static int
5406syn_check_cluster(pp, len)
5407 char_u *pp;
5408 int len;
5409{
5410 int id;
5411 char_u *name;
5412
5413 name = vim_strnsave(pp, len);
5414 if (name == NULL)
5415 return 0;
5416
5417 id = syn_scl_name2id(name);
5418 if (id == 0) /* doesn't exist yet */
5419 id = syn_add_cluster(name);
5420 else
5421 vim_free(name);
5422 return id;
5423}
5424
5425/*
5426 * Add new syntax cluster and return it's ID.
5427 * "name" must be an allocated string, it will be consumed.
5428 * Return 0 for failure.
5429 */
5430 static int
5431syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005432 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005434 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005435
5436 /*
5437 * First call for this growarray: init growing array.
5438 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005439 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005441 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5442 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443 }
5444
Bram Moolenaar42431a72011-04-01 14:44:59 +02005445 len = curwin->w_s->b_syn_clusters.ga_len;
5446 if (len >= MAX_CLUSTER_ID)
5447 {
5448 EMSG((char_u *)_("E848: Too many syntax clusters"));
5449 vim_free(name);
5450 return 0;
5451 }
5452
Bram Moolenaar071d4272004-06-13 20:20:40 +00005453 /*
5454 * Make room for at least one other cluster entry.
5455 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005456 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457 {
5458 vim_free(name);
5459 return 0;
5460 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461
Bram Moolenaar860cae12010-06-05 23:22:07 +02005462 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5463 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5464 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5465 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5466 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005467
Bram Moolenaar217ad922005-03-20 22:37:15 +00005468 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005469 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005470 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005471 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005472
Bram Moolenaar071d4272004-06-13 20:20:40 +00005473 return len + SYNID_CLUSTER;
5474}
5475
5476/*
5477 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5478 * [add={groupname},..] [remove={groupname},..]".
5479 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005480 static void
5481syn_cmd_cluster(eap, syncing)
5482 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005483 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005484{
5485 char_u *arg = eap->arg;
5486 char_u *group_name_end;
5487 char_u *rest;
5488 int scl_id;
5489 short *clstr_list;
5490 int got_clstr = FALSE;
5491 int opt_len;
5492 int list_op;
5493
5494 eap->nextcmd = find_nextcmd(arg);
5495 if (eap->skip)
5496 return;
5497
5498 rest = get_group_name(arg, &group_name_end);
5499
5500 if (rest != NULL)
5501 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005502 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5503 if (scl_id == 0)
5504 return;
5505 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005506
5507 for (;;)
5508 {
5509 if (STRNICMP(rest, "add", 3) == 0
5510 && (vim_iswhite(rest[3]) || rest[3] == '='))
5511 {
5512 opt_len = 3;
5513 list_op = CLUSTER_ADD;
5514 }
5515 else if (STRNICMP(rest, "remove", 6) == 0
5516 && (vim_iswhite(rest[6]) || rest[6] == '='))
5517 {
5518 opt_len = 6;
5519 list_op = CLUSTER_SUBTRACT;
5520 }
5521 else if (STRNICMP(rest, "contains", 8) == 0
5522 && (vim_iswhite(rest[8]) || rest[8] == '='))
5523 {
5524 opt_len = 8;
5525 list_op = CLUSTER_REPLACE;
5526 }
5527 else
5528 break;
5529
5530 clstr_list = NULL;
5531 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5532 {
5533 EMSG2(_(e_invarg2), rest);
5534 break;
5535 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005536 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005537 &clstr_list, list_op);
5538 got_clstr = TRUE;
5539 }
5540
5541 if (got_clstr)
5542 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005543 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005544 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005545 }
5546 }
5547
5548 if (!got_clstr)
5549 EMSG(_("E400: No cluster specified"));
5550 if (rest == NULL || !ends_excmd(*rest))
5551 EMSG2(_(e_invarg2), arg);
5552}
5553
5554/*
5555 * On first call for current buffer: Init growing array.
5556 */
5557 static void
5558init_syn_patterns()
5559{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005560 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5561 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005562}
5563
5564/*
5565 * Get one pattern for a ":syntax match" or ":syntax region" command.
5566 * Stores the pattern and program in a synpat_T.
5567 * Returns a pointer to the next argument, or NULL in case of an error.
5568 */
5569 static char_u *
5570get_syn_pattern(arg, ci)
5571 char_u *arg;
5572 synpat_T *ci;
5573{
5574 char_u *end;
5575 int *p;
5576 int idx;
5577 char_u *cpo_save;
5578
5579 /* need at least three chars */
5580 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5581 return NULL;
5582
5583 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5584 if (*end != *arg) /* end delimiter not found */
5585 {
5586 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5587 return NULL;
5588 }
5589 /* store the pattern and compiled regexp program */
5590 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5591 return NULL;
5592
5593 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5594 cpo_save = p_cpo;
5595 p_cpo = (char_u *)"";
5596 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5597 p_cpo = cpo_save;
5598
5599 if (ci->sp_prog == NULL)
5600 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005601 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005602
5603 /*
5604 * Check for a match, highlight or region offset.
5605 */
5606 ++end;
5607 do
5608 {
5609 for (idx = SPO_COUNT; --idx >= 0; )
5610 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5611 break;
5612 if (idx >= 0)
5613 {
5614 p = &(ci->sp_offsets[idx]);
5615 if (idx != SPO_LC_OFF)
5616 switch (end[3])
5617 {
5618 case 's': break;
5619 case 'b': break;
5620 case 'e': idx += SPO_COUNT; break;
5621 default: idx = -1; break;
5622 }
5623 if (idx >= 0)
5624 {
5625 ci->sp_off_flags |= (1 << idx);
5626 if (idx == SPO_LC_OFF) /* lc=99 */
5627 {
5628 end += 3;
5629 *p = getdigits(&end);
5630
5631 /* "lc=" offset automatically sets "ms=" offset */
5632 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5633 {
5634 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5635 ci->sp_offsets[SPO_MS_OFF] = *p;
5636 }
5637 }
5638 else /* yy=x+99 */
5639 {
5640 end += 4;
5641 if (*end == '+')
5642 {
5643 ++end;
5644 *p = getdigits(&end); /* positive offset */
5645 }
5646 else if (*end == '-')
5647 {
5648 ++end;
5649 *p = -getdigits(&end); /* negative offset */
5650 }
5651 }
5652 if (*end != ',')
5653 break;
5654 ++end;
5655 }
5656 }
5657 } while (idx >= 0);
5658
5659 if (!ends_excmd(*end) && !vim_iswhite(*end))
5660 {
5661 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5662 return NULL;
5663 }
5664 return skipwhite(end);
5665}
5666
5667/*
5668 * Handle ":syntax sync .." command.
5669 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005670 static void
5671syn_cmd_sync(eap, syncing)
5672 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005673 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005674{
5675 char_u *arg_start = eap->arg;
5676 char_u *arg_end;
5677 char_u *key = NULL;
5678 char_u *next_arg;
5679 int illegal = FALSE;
5680 int finished = FALSE;
5681 long n;
5682 char_u *cpo_save;
5683
5684 if (ends_excmd(*arg_start))
5685 {
5686 syn_cmd_list(eap, TRUE);
5687 return;
5688 }
5689
5690 while (!ends_excmd(*arg_start))
5691 {
5692 arg_end = skiptowhite(arg_start);
5693 next_arg = skipwhite(arg_end);
5694 vim_free(key);
5695 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5696 if (STRCMP(key, "CCOMMENT") == 0)
5697 {
5698 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005699 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 if (!ends_excmd(*next_arg))
5701 {
5702 arg_end = skiptowhite(next_arg);
5703 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005704 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005705 (int)(arg_end - next_arg));
5706 next_arg = skipwhite(arg_end);
5707 }
5708 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005709 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005710 }
5711 else if ( STRNCMP(key, "LINES", 5) == 0
5712 || STRNCMP(key, "MINLINES", 8) == 0
5713 || STRNCMP(key, "MAXLINES", 8) == 0
5714 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5715 {
5716 if (key[4] == 'S')
5717 arg_end = key + 6;
5718 else if (key[0] == 'L')
5719 arg_end = key + 11;
5720 else
5721 arg_end = key + 9;
5722 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5723 {
5724 illegal = TRUE;
5725 break;
5726 }
5727 n = getdigits(&arg_end);
5728 if (!eap->skip)
5729 {
5730 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005731 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005733 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005735 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736 }
5737 }
5738 else if (STRCMP(key, "FROMSTART") == 0)
5739 {
5740 if (!eap->skip)
5741 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005742 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5743 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 }
5745 }
5746 else if (STRCMP(key, "LINECONT") == 0)
5747 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005748 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005749 {
5750 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5751 finished = TRUE;
5752 break;
5753 }
5754 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5755 if (*arg_end != *next_arg) /* end delimiter not found */
5756 {
5757 illegal = TRUE;
5758 break;
5759 }
5760
5761 if (!eap->skip)
5762 {
5763 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005764 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005765 (int)(arg_end - next_arg - 1))) == NULL)
5766 {
5767 finished = TRUE;
5768 break;
5769 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005770 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771
5772 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5773 cpo_save = p_cpo;
5774 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005775 curwin->w_s->b_syn_linecont_prog =
5776 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005777 p_cpo = cpo_save;
5778
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005781 vim_free(curwin->w_s->b_syn_linecont_pat);
5782 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005783 finished = TRUE;
5784 break;
5785 }
5786 }
5787 next_arg = skipwhite(arg_end + 1);
5788 }
5789 else
5790 {
5791 eap->arg = next_arg;
5792 if (STRCMP(key, "MATCH") == 0)
5793 syn_cmd_match(eap, TRUE);
5794 else if (STRCMP(key, "REGION") == 0)
5795 syn_cmd_region(eap, TRUE);
5796 else if (STRCMP(key, "CLEAR") == 0)
5797 syn_cmd_clear(eap, TRUE);
5798 else
5799 illegal = TRUE;
5800 finished = TRUE;
5801 break;
5802 }
5803 arg_start = next_arg;
5804 }
5805 vim_free(key);
5806 if (illegal)
5807 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5808 else if (!finished)
5809 {
5810 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005811 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005812 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005813 }
5814}
5815
5816/*
5817 * Convert a line of highlight group names into a list of group ID numbers.
5818 * "arg" should point to the "contains" or "nextgroup" keyword.
5819 * "arg" is advanced to after the last group name.
5820 * Careful: the argument is modified (NULs added).
5821 * returns FAIL for some error, OK for success.
5822 */
5823 static int
5824get_id_list(arg, keylen, list)
5825 char_u **arg;
5826 int keylen; /* length of keyword */
5827 short **list; /* where to store the resulting list, if not
5828 NULL, the list is silently skipped! */
5829{
5830 char_u *p = NULL;
5831 char_u *end;
5832 int round;
5833 int count;
5834 int total_count = 0;
5835 short *retval = NULL;
5836 char_u *name;
5837 regmatch_T regmatch;
5838 int id;
5839 int i;
5840 int failed = FALSE;
5841
5842 /*
5843 * We parse the list twice:
5844 * round == 1: count the number of items, allocate the array.
5845 * round == 2: fill the array with the items.
5846 * In round 1 new groups may be added, causing the number of items to
5847 * grow when a regexp is used. In that case round 1 is done once again.
5848 */
5849 for (round = 1; round <= 2; ++round)
5850 {
5851 /*
5852 * skip "contains"
5853 */
5854 p = skipwhite(*arg + keylen);
5855 if (*p != '=')
5856 {
5857 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5858 break;
5859 }
5860 p = skipwhite(p + 1);
5861 if (ends_excmd(*p))
5862 {
5863 EMSG2(_("E406: Empty argument: %s"), *arg);
5864 break;
5865 }
5866
5867 /*
5868 * parse the arguments after "contains"
5869 */
5870 count = 0;
5871 while (!ends_excmd(*p))
5872 {
5873 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5874 ;
5875 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5876 if (name == NULL)
5877 {
5878 failed = TRUE;
5879 break;
5880 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005881 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005882 if ( STRCMP(name + 1, "ALLBUT") == 0
5883 || STRCMP(name + 1, "ALL") == 0
5884 || STRCMP(name + 1, "TOP") == 0
5885 || STRCMP(name + 1, "CONTAINED") == 0)
5886 {
5887 if (TOUPPER_ASC(**arg) != 'C')
5888 {
5889 EMSG2(_("E407: %s not allowed here"), name + 1);
5890 failed = TRUE;
5891 vim_free(name);
5892 break;
5893 }
5894 if (count != 0)
5895 {
5896 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5897 failed = TRUE;
5898 vim_free(name);
5899 break;
5900 }
5901 if (name[1] == 'A')
5902 id = SYNID_ALLBUT;
5903 else if (name[1] == 'T')
5904 id = SYNID_TOP;
5905 else
5906 id = SYNID_CONTAINED;
5907 id += current_syn_inc_tag;
5908 }
5909 else if (name[1] == '@')
5910 {
5911 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5912 }
5913 else
5914 {
5915 /*
5916 * Handle full group name.
5917 */
5918 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5919 id = syn_check_group(name + 1, (int)(end - p));
5920 else
5921 {
5922 /*
5923 * Handle match of regexp with group names.
5924 */
5925 *name = '^';
5926 STRCAT(name, "$");
5927 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5928 if (regmatch.regprog == NULL)
5929 {
5930 failed = TRUE;
5931 vim_free(name);
5932 break;
5933 }
5934
5935 regmatch.rm_ic = TRUE;
5936 id = 0;
5937 for (i = highlight_ga.ga_len; --i >= 0; )
5938 {
5939 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5940 (colnr_T)0))
5941 {
5942 if (round == 2)
5943 {
5944 /* Got more items than expected; can happen
5945 * when adding items that match:
5946 * "contains=a.*b,axb".
5947 * Go back to first round */
5948 if (count >= total_count)
5949 {
5950 vim_free(retval);
5951 round = 1;
5952 }
5953 else
5954 retval[count] = i + 1;
5955 }
5956 ++count;
5957 id = -1; /* remember that we found one */
5958 }
5959 }
5960 vim_free(regmatch.regprog);
5961 }
5962 }
5963 vim_free(name);
5964 if (id == 0)
5965 {
5966 EMSG2(_("E409: Unknown group name: %s"), p);
5967 failed = TRUE;
5968 break;
5969 }
5970 if (id > 0)
5971 {
5972 if (round == 2)
5973 {
5974 /* Got more items than expected, go back to first round */
5975 if (count >= total_count)
5976 {
5977 vim_free(retval);
5978 round = 1;
5979 }
5980 else
5981 retval[count] = id;
5982 }
5983 ++count;
5984 }
5985 p = skipwhite(end);
5986 if (*p != ',')
5987 break;
5988 p = skipwhite(p + 1); /* skip comma in between arguments */
5989 }
5990 if (failed)
5991 break;
5992 if (round == 1)
5993 {
5994 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5995 if (retval == NULL)
5996 break;
5997 retval[count] = 0; /* zero means end of the list */
5998 total_count = count;
5999 }
6000 }
6001
6002 *arg = p;
6003 if (failed || retval == NULL)
6004 {
6005 vim_free(retval);
6006 return FAIL;
6007 }
6008
6009 if (*list == NULL)
6010 *list = retval;
6011 else
6012 vim_free(retval); /* list already found, don't overwrite it */
6013
6014 return OK;
6015}
6016
6017/*
6018 * Make a copy of an ID list.
6019 */
6020 static short *
6021copy_id_list(list)
6022 short *list;
6023{
6024 int len;
6025 int count;
6026 short *retval;
6027
6028 if (list == NULL)
6029 return NULL;
6030
6031 for (count = 0; list[count]; ++count)
6032 ;
6033 len = (count + 1) * sizeof(short);
6034 retval = (short *)alloc((unsigned)len);
6035 if (retval != NULL)
6036 mch_memmove(retval, list, (size_t)len);
6037
6038 return retval;
6039}
6040
6041/*
6042 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6043 * "cur_si" can be NULL if not checking the "containedin" list.
6044 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6045 * the current item.
6046 * This function is called very often, keep it fast!!
6047 */
6048 static int
6049in_id_list(cur_si, list, ssp, contained)
6050 stateitem_T *cur_si; /* current item or NULL */
6051 short *list; /* id list */
6052 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6053 int contained; /* group id is contained */
6054{
6055 int retval;
6056 short *scl_list;
6057 short item;
6058 short id = ssp->id;
6059 static int depth = 0;
6060 int r;
6061
6062 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006063 if (cur_si != NULL && ssp->cont_in_list != NULL
6064 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006065 {
6066 /* Ignore transparent items without a contains argument. Double check
6067 * that we don't go back past the first one. */
6068 while ((cur_si->si_flags & HL_TRANS_CONT)
6069 && cur_si > (stateitem_T *)(current_state.ga_data))
6070 --cur_si;
6071 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6072 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006073 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6074 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006075 return TRUE;
6076 }
6077
6078 if (list == NULL)
6079 return FALSE;
6080
6081 /*
6082 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6083 * inside anything. Only allow not-contained groups.
6084 */
6085 if (list == ID_LIST_ALL)
6086 return !contained;
6087
6088 /*
6089 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6090 * contains list. We also require that "id" is at the same ":syn include"
6091 * level as the list.
6092 */
6093 item = *list;
6094 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6095 {
6096 if (item < SYNID_TOP)
6097 {
6098 /* ALL or ALLBUT: accept all groups in the same file */
6099 if (item - SYNID_ALLBUT != ssp->inc_tag)
6100 return FALSE;
6101 }
6102 else if (item < SYNID_CONTAINED)
6103 {
6104 /* TOP: accept all not-contained groups in the same file */
6105 if (item - SYNID_TOP != ssp->inc_tag || contained)
6106 return FALSE;
6107 }
6108 else
6109 {
6110 /* CONTAINED: accept all contained groups in the same file */
6111 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6112 return FALSE;
6113 }
6114 item = *++list;
6115 retval = FALSE;
6116 }
6117 else
6118 retval = TRUE;
6119
6120 /*
6121 * Return "retval" if id is in the contains list.
6122 */
6123 while (item != 0)
6124 {
6125 if (item == id)
6126 return retval;
6127 if (item >= SYNID_CLUSTER)
6128 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006129 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006130 /* restrict recursiveness to 30 to avoid an endless loop for a
6131 * cluster that includes itself (indirectly) */
6132 if (scl_list != NULL && depth < 30)
6133 {
6134 ++depth;
6135 r = in_id_list(NULL, scl_list, ssp, contained);
6136 --depth;
6137 if (r)
6138 return retval;
6139 }
6140 }
6141 item = *++list;
6142 }
6143 return !retval;
6144}
6145
6146struct subcommand
6147{
6148 char *name; /* subcommand name */
6149 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6150};
6151
6152static struct subcommand subcommands[] =
6153{
6154 {"case", syn_cmd_case},
6155 {"clear", syn_cmd_clear},
6156 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006157 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006158 {"enable", syn_cmd_enable},
6159 {"include", syn_cmd_include},
6160 {"keyword", syn_cmd_keyword},
6161 {"list", syn_cmd_list},
6162 {"manual", syn_cmd_manual},
6163 {"match", syn_cmd_match},
6164 {"on", syn_cmd_on},
6165 {"off", syn_cmd_off},
6166 {"region", syn_cmd_region},
6167 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006168 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006169 {"sync", syn_cmd_sync},
6170 {"", syn_cmd_list},
6171 {NULL, NULL}
6172};
6173
6174/*
6175 * ":syntax".
6176 * This searches the subcommands[] table for the subcommand name, and calls a
6177 * syntax_subcommand() function to do the rest.
6178 */
6179 void
6180ex_syntax(eap)
6181 exarg_T *eap;
6182{
6183 char_u *arg = eap->arg;
6184 char_u *subcmd_end;
6185 char_u *subcmd_name;
6186 int i;
6187
6188 syn_cmdlinep = eap->cmdlinep;
6189
6190 /* isolate subcommand name */
6191 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6192 ;
6193 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6194 if (subcmd_name != NULL)
6195 {
6196 if (eap->skip) /* skip error messages for all subcommands */
6197 ++emsg_skip;
6198 for (i = 0; ; ++i)
6199 {
6200 if (subcommands[i].name == NULL)
6201 {
6202 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6203 break;
6204 }
6205 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6206 {
6207 eap->arg = skipwhite(subcmd_end);
6208 (subcommands[i].func)(eap, FALSE);
6209 break;
6210 }
6211 }
6212 vim_free(subcmd_name);
6213 if (eap->skip)
6214 --emsg_skip;
6215 }
6216}
6217
Bram Moolenaar860cae12010-06-05 23:22:07 +02006218 void
6219ex_ownsyntax(eap)
6220 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006221{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006222 char_u *old_value;
6223 char_u *new_value;
6224
Bram Moolenaar860cae12010-06-05 23:22:07 +02006225 if (curwin->w_s == &curwin->w_buffer->b_s)
6226 {
6227 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6228 memset(curwin->w_s, 0, sizeof(synblock_T));
6229#ifdef FEAT_SPELL
6230 curwin->w_p_spell = FALSE; /* No spell checking */
6231 clear_string_option(&curwin->w_s->b_p_spc);
6232 clear_string_option(&curwin->w_s->b_p_spf);
6233 vim_free(curwin->w_s->b_cap_prog);
6234 curwin->w_s->b_cap_prog = NULL;
6235 clear_string_option(&curwin->w_s->b_p_spl);
6236#endif
6237 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006238
6239 /* save value of b:current_syntax */
6240 old_value = get_var_value((char_u *)"b:current_syntax");
6241 if (old_value != NULL)
6242 old_value = vim_strsave(old_value);
6243
6244 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6245 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006246 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006247
6248 /* move value of b:current_syntax to w:current_syntax */
6249 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006250 if (new_value != NULL)
6251 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006252
6253 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006254 if (old_value == NULL)
6255 do_unlet((char_u *)"b:current_syntax", TRUE);
6256 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006257 {
6258 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6259 vim_free(old_value);
6260 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006261}
6262
6263 int
6264syntax_present(win)
6265 win_T *win;
6266{
6267 return (win->w_s->b_syn_patterns.ga_len != 0
6268 || win->w_s->b_syn_clusters.ga_len != 0
6269 || win->w_s->b_keywtab.ht_used > 0
6270 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006271}
6272
6273#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6274
6275static enum
6276{
6277 EXP_SUBCMD, /* expand ":syn" sub-commands */
6278 EXP_CASE /* expand ":syn case" arguments */
6279} expand_what;
6280
Bram Moolenaar4f688582007-07-24 12:34:30 +00006281/*
6282 * Reset include_link, include_default, include_none to 0.
6283 * Called when we are done expanding.
6284 */
6285 void
6286reset_expand_highlight()
6287{
6288 include_link = include_default = include_none = 0;
6289}
6290
6291/*
6292 * Handle command line completion for :match and :echohl command: Add "None"
6293 * as highlight group.
6294 */
6295 void
6296set_context_in_echohl_cmd(xp, arg)
6297 expand_T *xp;
6298 char_u *arg;
6299{
6300 xp->xp_context = EXPAND_HIGHLIGHT;
6301 xp->xp_pattern = arg;
6302 include_none = 1;
6303}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006304
6305/*
6306 * Handle command line completion for :syntax command.
6307 */
6308 void
6309set_context_in_syntax_cmd(xp, arg)
6310 expand_T *xp;
6311 char_u *arg;
6312{
6313 char_u *p;
6314
6315 /* Default: expand subcommands */
6316 xp->xp_context = EXPAND_SYNTAX;
6317 expand_what = EXP_SUBCMD;
6318 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006319 include_link = 0;
6320 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006321
6322 /* (part of) subcommand already typed */
6323 if (*arg != NUL)
6324 {
6325 p = skiptowhite(arg);
6326 if (*p != NUL) /* past first word */
6327 {
6328 xp->xp_pattern = skipwhite(p);
6329 if (*skiptowhite(xp->xp_pattern) != NUL)
6330 xp->xp_context = EXPAND_NOTHING;
6331 else if (STRNICMP(arg, "case", p - arg) == 0)
6332 expand_what = EXP_CASE;
6333 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6334 || STRNICMP(arg, "region", p - arg) == 0
6335 || STRNICMP(arg, "match", p - arg) == 0
6336 || STRNICMP(arg, "list", p - arg) == 0)
6337 xp->xp_context = EXPAND_HIGHLIGHT;
6338 else
6339 xp->xp_context = EXPAND_NOTHING;
6340 }
6341 }
6342}
6343
6344static char *(case_args[]) = {"match", "ignore", NULL};
6345
6346/*
6347 * Function given to ExpandGeneric() to obtain the list syntax names for
6348 * expansion.
6349 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006350 char_u *
6351get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006352 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353 int idx;
6354{
6355 if (expand_what == EXP_SUBCMD)
6356 return (char_u *)subcommands[idx].name;
6357 return (char_u *)case_args[idx];
6358}
6359
6360#endif /* FEAT_CMDL_COMPL */
6361
Bram Moolenaar071d4272004-06-13 20:20:40 +00006362/*
6363 * Function called for expression evaluation: get syntax ID at file position.
6364 */
6365 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006366syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006367 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006368 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006369 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006370 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006371 int *spellp; /* return: can do spell checking */
6372 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006373{
6374 /* When the position is not after the current position and in the same
6375 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006376 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006377 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006378 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006379 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006380
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006381 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382
6383 return (trans ? current_trans_id : current_id);
6384}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006385
Bram Moolenaar860cae12010-06-05 23:22:07 +02006386#if defined(FEAT_CONCEAL) || defined(PROTO)
6387/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006388 * Get extra information about the syntax item. Must be called right after
6389 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006390 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006391 * Returns the current flags.
6392 */
6393 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006394get_syntax_info(seqnrp)
6395 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006396{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006397 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006398 return current_flags;
6399}
6400
6401/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006402 * Return conceal substitution character
6403 */
6404 int
6405syn_get_sub_char()
6406{
6407 return current_sub_char;
6408}
6409#endif
6410
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006411#if defined(FEAT_EVAL) || defined(PROTO)
6412/*
6413 * Return the syntax ID at position "i" in the current stack.
6414 * The caller must have called syn_get_id() before to fill the stack.
6415 * Returns -1 when "i" is out of range.
6416 */
6417 int
6418syn_get_stack_item(i)
6419 int i;
6420{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006421 if (i >= current_state.ga_len)
6422 {
6423 /* Need to invalidate the state, because we didn't properly finish it
6424 * for the last character, "keep_state" was TRUE. */
6425 invalidate_current_state();
6426 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006427 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006428 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006429 return CUR_STATE(i).si_id;
6430}
6431#endif
6432
Bram Moolenaar071d4272004-06-13 20:20:40 +00006433#if defined(FEAT_FOLDING) || defined(PROTO)
6434/*
6435 * Function called to get folding level for line "lnum" in window "wp".
6436 */
6437 int
6438syn_get_foldlevel(wp, lnum)
6439 win_T *wp;
6440 long lnum;
6441{
6442 int level = 0;
6443 int i;
6444
6445 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006446 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006447 {
6448 syntax_start(wp, lnum);
6449
6450 for (i = 0; i < current_state.ga_len; ++i)
6451 if (CUR_STATE(i).si_flags & HL_FOLD)
6452 ++level;
6453 }
6454 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006455 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006456 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006457 if (level < 0)
6458 level = 0;
6459 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460 return level;
6461}
6462#endif
6463
6464#endif /* FEAT_SYN_HL */
6465
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466/**************************************
6467 * Highlighting stuff *
6468 **************************************/
6469
6470/*
6471 * The default highlight groups. These are compiled-in for fast startup and
6472 * they still work when the runtime files can't be found.
6473 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006474 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6475 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006476 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006477#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006478# define CENT(a, b) b
6479#else
6480# define CENT(a, b) a
6481#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482static char *(highlight_init_both[]) =
6483 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006484 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6485 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6486 CENT("IncSearch term=reverse cterm=reverse",
6487 "IncSearch term=reverse cterm=reverse gui=reverse"),
6488 CENT("ModeMsg term=bold cterm=bold",
6489 "ModeMsg term=bold cterm=bold gui=bold"),
6490 CENT("NonText term=bold ctermfg=Blue",
6491 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6492 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6493 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6494 CENT("StatusLineNC term=reverse cterm=reverse",
6495 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006496#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006497 CENT("VertSplit term=reverse cterm=reverse",
6498 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006499#endif
6500#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006501 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6502 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006503#endif
6504#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006505 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6506 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006507#endif
6508#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006509 CENT("PmenuThumb cterm=reverse",
6510 "PmenuThumb cterm=reverse gui=reverse"),
6511 CENT("PmenuSbar ctermbg=Grey",
6512 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006513#endif
6514#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006515 CENT("TabLineSel term=bold cterm=bold",
6516 "TabLineSel term=bold cterm=bold gui=bold"),
6517 CENT("TabLineFill term=reverse cterm=reverse",
6518 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006519#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006520#ifdef FEAT_GUI
6521 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006522 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524 NULL
6525 };
6526
6527static char *(highlight_init_light[]) =
6528 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006529 CENT("Directory term=bold ctermfg=DarkBlue",
6530 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6531 CENT("LineNr term=underline ctermfg=Brown",
6532 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6533 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6534 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6535 CENT("Question term=standout ctermfg=DarkGreen",
6536 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6537 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6538 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006539#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006540 CENT("SpellBad term=reverse ctermbg=LightRed",
6541 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6542 CENT("SpellCap term=reverse ctermbg=LightBlue",
6543 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6544 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6545 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6546 CENT("SpellLocal term=underline ctermbg=Cyan",
6547 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006548#endif
6549#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006550 CENT("Pmenu ctermbg=LightMagenta",
6551 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6552 CENT("PmenuSel ctermbg=LightGrey",
6553 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006554#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006555 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6556 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6557 CENT("Title term=bold ctermfg=DarkMagenta",
6558 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6559 CENT("WarningMsg term=standout ctermfg=DarkRed",
6560 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006561#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006562 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6563 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006564#endif
6565#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006566 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6567 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6568 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6569 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006570#endif
6571#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006572 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6573 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006574#endif
6575#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006576 CENT("Visual term=reverse",
6577 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006578#endif
6579#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006580 CENT("DiffAdd term=bold ctermbg=LightBlue",
6581 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6582 CENT("DiffChange term=bold ctermbg=LightMagenta",
6583 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6584 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6585 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006586#endif
6587#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006588 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6589 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006590#endif
6591#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006592 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006593 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006594 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006595 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006596 CENT("ColorColumn term=reverse ctermbg=LightRed",
6597 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006598#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006599#ifdef FEAT_CONCEAL
6600 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6601 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6602#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006603#ifdef FEAT_AUTOCMD
6604 CENT("MatchParen term=reverse ctermbg=Cyan",
6605 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6606#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006607#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006608 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006609#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006610 NULL
6611 };
6612
6613static char *(highlight_init_dark[]) =
6614 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006615 CENT("Directory term=bold ctermfg=LightCyan",
6616 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6617 CENT("LineNr term=underline ctermfg=Yellow",
6618 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6619 CENT("MoreMsg term=bold ctermfg=LightGreen",
6620 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6621 CENT("Question term=standout ctermfg=LightGreen",
6622 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6623 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6624 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6625 CENT("SpecialKey term=bold ctermfg=LightBlue",
6626 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006627#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006628 CENT("SpellBad term=reverse ctermbg=Red",
6629 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6630 CENT("SpellCap term=reverse ctermbg=Blue",
6631 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6632 CENT("SpellRare term=reverse ctermbg=Magenta",
6633 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6634 CENT("SpellLocal term=underline ctermbg=Cyan",
6635 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006636#endif
6637#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006638 CENT("Pmenu ctermbg=Magenta",
6639 "Pmenu ctermbg=Magenta guibg=Magenta"),
6640 CENT("PmenuSel ctermbg=DarkGrey",
6641 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006642#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006643 CENT("Title term=bold ctermfg=LightMagenta",
6644 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6645 CENT("WarningMsg term=standout ctermfg=LightRed",
6646 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006647#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006648 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6649 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006650#endif
6651#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006652 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6653 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6654 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6655 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006656#endif
6657#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006658 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6659 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006660#endif
6661#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006662 CENT("Visual term=reverse",
6663 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006664#endif
6665#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006666 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6667 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6668 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6669 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6670 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6671 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006672#endif
6673#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006674 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6675 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006676#endif
6677#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006678 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006679 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006680 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006681 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006682 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6683 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006684#endif
6685#ifdef FEAT_AUTOCMD
6686 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6687 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006688#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006689#ifdef FEAT_CONCEAL
6690 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6691 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6692#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006693#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006694 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006695#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006696 NULL
6697 };
6698
6699 void
6700init_highlight(both, reset)
6701 int both; /* include groups where 'bg' doesn't matter */
6702 int reset; /* clear group first */
6703{
6704 int i;
6705 char **pp;
6706 static int had_both = FALSE;
6707#ifdef FEAT_EVAL
6708 char_u *p;
6709
6710 /*
6711 * Try finding the color scheme file. Used when a color file was loaded
6712 * and 'background' or 't_Co' is changed.
6713 */
6714 p = get_var_value((char_u *)"g:colors_name");
6715 if (p != NULL && load_colors(p) == OK)
6716 return;
6717#endif
6718
6719 /*
6720 * Didn't use a color file, use the compiled-in colors.
6721 */
6722 if (both)
6723 {
6724 had_both = TRUE;
6725 pp = highlight_init_both;
6726 for (i = 0; pp[i] != NULL; ++i)
6727 do_highlight((char_u *)pp[i], reset, TRUE);
6728 }
6729 else if (!had_both)
6730 /* Don't do anything before the call with both == TRUE from main().
6731 * Not everything has been setup then, and that call will overrule
6732 * everything anyway. */
6733 return;
6734
6735 if (*p_bg == 'l')
6736 pp = highlight_init_light;
6737 else
6738 pp = highlight_init_dark;
6739 for (i = 0; pp[i] != NULL; ++i)
6740 do_highlight((char_u *)pp[i], reset, TRUE);
6741
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006742 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006743 * depend on the number of colors available.
6744 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006745 * to avoid Statement highlighted text disappears.
6746 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006747 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006748 do_highlight((char_u *)(*p_bg == 'l'
6749 ? "Visual cterm=NONE ctermbg=LightGrey"
6750 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006751 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006752 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006753 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6754 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006755 if (*p_bg == 'l')
6756 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6757 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006758
Bram Moolenaar071d4272004-06-13 20:20:40 +00006759#ifdef FEAT_SYN_HL
6760 /*
6761 * If syntax highlighting is enabled load the highlighting for it.
6762 */
6763 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006764 {
6765 static int recursive = 0;
6766
6767 if (recursive >= 5)
6768 EMSG(_("E679: recursive loop loading syncolor.vim"));
6769 else
6770 {
6771 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006772 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006773 --recursive;
6774 }
6775 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006776#endif
6777}
6778
6779/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006780 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006781 * Return OK for success, FAIL for failure.
6782 */
6783 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006784load_colors(name)
6785 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006786{
6787 char_u *buf;
6788 int retval = FAIL;
6789 static int recursive = FALSE;
6790
6791 /* When being called recursively, this is probably because setting
6792 * 'background' caused the highlighting to be reloaded. This means it is
6793 * working, thus we should return OK. */
6794 if (recursive)
6795 return OK;
6796
6797 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006798 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006799 if (buf != NULL)
6800 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006801 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006802 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006803 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006804#ifdef FEAT_AUTOCMD
6805 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6806#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006807 }
6808 recursive = FALSE;
6809
6810 return retval;
6811}
6812
6813/*
6814 * Handle the ":highlight .." command.
6815 * When using ":hi clear" this is called recursively for each group with
6816 * "forceit" and "init" both TRUE.
6817 */
6818 void
6819do_highlight(line, forceit, init)
6820 char_u *line;
6821 int forceit;
6822 int init; /* TRUE when called for initializing */
6823{
6824 char_u *name_end;
6825 char_u *p;
6826 char_u *linep;
6827 char_u *key_start;
6828 char_u *arg_start;
6829 char_u *key = NULL, *arg = NULL;
6830 long i;
6831 int off;
6832 int len;
6833 int attr;
6834 int id;
6835 int idx;
6836 int dodefault = FALSE;
6837 int doclear = FALSE;
6838 int dolink = FALSE;
6839 int error = FALSE;
6840 int color;
6841 int is_normal_group = FALSE; /* "Normal" group */
6842#ifdef FEAT_GUI_X11
6843 int is_menu_group = FALSE; /* "Menu" group */
6844 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6845 int is_tooltip_group = FALSE; /* "Tooltip" group */
6846 int do_colors = FALSE; /* need to update colors? */
6847#else
6848# define is_menu_group 0
6849# define is_tooltip_group 0
6850#endif
6851
6852 /*
6853 * If no argument, list current highlighting.
6854 */
6855 if (ends_excmd(*line))
6856 {
6857 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6858 /* TODO: only call when the group has attributes set */
6859 highlight_list_one((int)i);
6860 return;
6861 }
6862
6863 /*
6864 * Isolate the name.
6865 */
6866 name_end = skiptowhite(line);
6867 linep = skipwhite(name_end);
6868
6869 /*
6870 * Check for "default" argument.
6871 */
6872 if (STRNCMP(line, "default", name_end - line) == 0)
6873 {
6874 dodefault = TRUE;
6875 line = linep;
6876 name_end = skiptowhite(line);
6877 linep = skipwhite(name_end);
6878 }
6879
6880 /*
6881 * Check for "clear" or "link" argument.
6882 */
6883 if (STRNCMP(line, "clear", name_end - line) == 0)
6884 doclear = TRUE;
6885 if (STRNCMP(line, "link", name_end - line) == 0)
6886 dolink = TRUE;
6887
6888 /*
6889 * ":highlight {group-name}": list highlighting for one group.
6890 */
6891 if (!doclear && !dolink && ends_excmd(*linep))
6892 {
6893 id = syn_namen2id(line, (int)(name_end - line));
6894 if (id == 0)
6895 EMSG2(_("E411: highlight group not found: %s"), line);
6896 else
6897 highlight_list_one(id);
6898 return;
6899 }
6900
6901 /*
6902 * Handle ":highlight link {from} {to}" command.
6903 */
6904 if (dolink)
6905 {
6906 char_u *from_start = linep;
6907 char_u *from_end;
6908 char_u *to_start;
6909 char_u *to_end;
6910 int from_id;
6911 int to_id;
6912
6913 from_end = skiptowhite(from_start);
6914 to_start = skipwhite(from_end);
6915 to_end = skiptowhite(to_start);
6916
6917 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6918 {
6919 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6920 from_start);
6921 return;
6922 }
6923
6924 if (!ends_excmd(*skipwhite(to_end)))
6925 {
6926 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6927 return;
6928 }
6929
6930 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6931 if (STRNCMP(to_start, "NONE", 4) == 0)
6932 to_id = 0;
6933 else
6934 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6935
6936 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6937 {
6938 /*
6939 * Don't allow a link when there already is some highlighting
6940 * for the group, unless '!' is used
6941 */
6942 if (to_id > 0 && !forceit && !init
6943 && hl_has_settings(from_id - 1, dodefault))
6944 {
6945 if (sourcing_name == NULL && !dodefault)
6946 EMSG(_("E414: group has settings, highlight link ignored"));
6947 }
6948 else
6949 {
6950 if (!init)
6951 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6952 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006953#ifdef FEAT_EVAL
6954 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6955#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006956 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006957 }
6958 }
6959
6960 /* Only call highlight_changed() once, after sourcing a syntax file */
6961 need_highlight_changed = TRUE;
6962
6963 return;
6964 }
6965
6966 if (doclear)
6967 {
6968 /*
6969 * ":highlight clear [group]" command.
6970 */
6971 line = linep;
6972 if (ends_excmd(*line))
6973 {
6974#ifdef FEAT_GUI
6975 /* First, we do not destroy the old values, but allocate the new
6976 * ones and update the display. THEN we destroy the old values.
6977 * If we destroy the old values first, then the old values
6978 * (such as GuiFont's or GuiFontset's) will still be displayed but
6979 * invalid because they were free'd.
6980 */
6981 if (gui.in_use)
6982 {
6983# ifdef FEAT_BEVAL_TIP
6984 gui_init_tooltip_font();
6985# endif
6986# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6987 gui_init_menu_font();
6988# endif
6989 }
6990# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6991 gui_mch_def_colors();
6992# endif
6993# ifdef FEAT_GUI_X11
6994# ifdef FEAT_MENU
6995
6996 /* This only needs to be done when there is no Menu highlight
6997 * group defined by default, which IS currently the case.
6998 */
6999 gui_mch_new_menu_colors();
7000# endif
7001 if (gui.in_use)
7002 {
7003 gui_new_scrollbar_colors();
7004# ifdef FEAT_BEVAL
7005 gui_mch_new_tooltip_colors();
7006# endif
7007# ifdef FEAT_MENU
7008 gui_mch_new_menu_font();
7009# endif
7010 }
7011# endif
7012
7013 /* Ok, we're done allocating the new default graphics items.
7014 * The screen should already be refreshed at this point.
7015 * It is now Ok to clear out the old data.
7016 */
7017#endif
7018#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007019 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007020#endif
7021 restore_cterm_colors();
7022
7023 /*
7024 * Clear all default highlight groups and load the defaults.
7025 */
7026 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7027 highlight_clear(idx);
7028 init_highlight(TRUE, TRUE);
7029#ifdef FEAT_GUI
7030 if (gui.in_use)
7031 highlight_gui_started();
7032#endif
7033 highlight_changed();
7034 redraw_later_clear();
7035 return;
7036 }
7037 name_end = skiptowhite(line);
7038 linep = skipwhite(name_end);
7039 }
7040
7041 /*
7042 * Find the group name in the table. If it does not exist yet, add it.
7043 */
7044 id = syn_check_group(line, (int)(name_end - line));
7045 if (id == 0) /* failed (out of memory) */
7046 return;
7047 idx = id - 1; /* index is ID minus one */
7048
7049 /* Return if "default" was used and the group already has settings. */
7050 if (dodefault && hl_has_settings(idx, TRUE))
7051 return;
7052
7053 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7054 is_normal_group = TRUE;
7055#ifdef FEAT_GUI_X11
7056 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7057 is_menu_group = TRUE;
7058 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7059 is_scrollbar_group = TRUE;
7060 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7061 is_tooltip_group = TRUE;
7062#endif
7063
7064 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7065 if (doclear || (forceit && init))
7066 {
7067 highlight_clear(idx);
7068 if (!doclear)
7069 HL_TABLE()[idx].sg_set = 0;
7070 }
7071
7072 if (!doclear)
7073 while (!ends_excmd(*linep))
7074 {
7075 key_start = linep;
7076 if (*linep == '=')
7077 {
7078 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7079 error = TRUE;
7080 break;
7081 }
7082
7083 /*
7084 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7085 * "guibg").
7086 */
7087 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7088 ++linep;
7089 vim_free(key);
7090 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7091 if (key == NULL)
7092 {
7093 error = TRUE;
7094 break;
7095 }
7096 linep = skipwhite(linep);
7097
7098 if (STRCMP(key, "NONE") == 0)
7099 {
7100 if (!init || HL_TABLE()[idx].sg_set == 0)
7101 {
7102 if (!init)
7103 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7104 highlight_clear(idx);
7105 }
7106 continue;
7107 }
7108
7109 /*
7110 * Check for the equal sign.
7111 */
7112 if (*linep != '=')
7113 {
7114 EMSG2(_("E416: missing equal sign: %s"), key_start);
7115 error = TRUE;
7116 break;
7117 }
7118 ++linep;
7119
7120 /*
7121 * Isolate the argument.
7122 */
7123 linep = skipwhite(linep);
7124 if (*linep == '\'') /* guifg='color name' */
7125 {
7126 arg_start = ++linep;
7127 linep = vim_strchr(linep, '\'');
7128 if (linep == NULL)
7129 {
7130 EMSG2(_(e_invarg2), key_start);
7131 error = TRUE;
7132 break;
7133 }
7134 }
7135 else
7136 {
7137 arg_start = linep;
7138 linep = skiptowhite(linep);
7139 }
7140 if (linep == arg_start)
7141 {
7142 EMSG2(_("E417: missing argument: %s"), key_start);
7143 error = TRUE;
7144 break;
7145 }
7146 vim_free(arg);
7147 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7148 if (arg == NULL)
7149 {
7150 error = TRUE;
7151 break;
7152 }
7153 if (*linep == '\'')
7154 ++linep;
7155
7156 /*
7157 * Store the argument.
7158 */
7159 if ( STRCMP(key, "TERM") == 0
7160 || STRCMP(key, "CTERM") == 0
7161 || STRCMP(key, "GUI") == 0)
7162 {
7163 attr = 0;
7164 off = 0;
7165 while (arg[off] != NUL)
7166 {
7167 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7168 {
7169 len = (int)STRLEN(hl_name_table[i]);
7170 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7171 {
7172 attr |= hl_attr_table[i];
7173 off += len;
7174 break;
7175 }
7176 }
7177 if (i < 0)
7178 {
7179 EMSG2(_("E418: Illegal value: %s"), arg);
7180 error = TRUE;
7181 break;
7182 }
7183 if (arg[off] == ',') /* another one follows */
7184 ++off;
7185 }
7186 if (error)
7187 break;
7188 if (*key == 'T')
7189 {
7190 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7191 {
7192 if (!init)
7193 HL_TABLE()[idx].sg_set |= SG_TERM;
7194 HL_TABLE()[idx].sg_term = attr;
7195 }
7196 }
7197 else if (*key == 'C')
7198 {
7199 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7200 {
7201 if (!init)
7202 HL_TABLE()[idx].sg_set |= SG_CTERM;
7203 HL_TABLE()[idx].sg_cterm = attr;
7204 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7205 }
7206 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007207#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007208 else
7209 {
7210 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7211 {
7212 if (!init)
7213 HL_TABLE()[idx].sg_set |= SG_GUI;
7214 HL_TABLE()[idx].sg_gui = attr;
7215 }
7216 }
7217#endif
7218 }
7219 else if (STRCMP(key, "FONT") == 0)
7220 {
7221 /* in non-GUI fonts are simply ignored */
7222#ifdef FEAT_GUI
7223 if (!gui.shell_created)
7224 {
7225 /* GUI not started yet, always accept the name. */
7226 vim_free(HL_TABLE()[idx].sg_font_name);
7227 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7228 }
7229 else
7230 {
7231 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7232# ifdef FEAT_XFONTSET
7233 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7234# endif
7235 /* First, save the current font/fontset.
7236 * Then try to allocate the font/fontset.
7237 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7238 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7239 */
7240
7241 HL_TABLE()[idx].sg_font = NOFONT;
7242# ifdef FEAT_XFONTSET
7243 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7244# endif
7245 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7246 is_tooltip_group);
7247
7248# ifdef FEAT_XFONTSET
7249 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7250 {
7251 /* New fontset was accepted. Free the old one, if there was
7252 * one.
7253 */
7254 gui_mch_free_fontset(temp_sg_fontset);
7255 vim_free(HL_TABLE()[idx].sg_font_name);
7256 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7257 }
7258 else
7259 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7260# endif
7261 if (HL_TABLE()[idx].sg_font != NOFONT)
7262 {
7263 /* New font was accepted. Free the old one, if there was
7264 * one.
7265 */
7266 gui_mch_free_font(temp_sg_font);
7267 vim_free(HL_TABLE()[idx].sg_font_name);
7268 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7269 }
7270 else
7271 HL_TABLE()[idx].sg_font = temp_sg_font;
7272 }
7273#endif
7274 }
7275 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7276 {
7277 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7278 {
7279 if (!init)
7280 HL_TABLE()[idx].sg_set |= SG_CTERM;
7281
7282 /* When setting the foreground color, and previously the "bold"
7283 * flag was set for a light color, reset it now */
7284 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7285 {
7286 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7287 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7288 }
7289
7290 if (VIM_ISDIGIT(*arg))
7291 color = atoi((char *)arg);
7292 else if (STRICMP(arg, "fg") == 0)
7293 {
7294 if (cterm_normal_fg_color)
7295 color = cterm_normal_fg_color - 1;
7296 else
7297 {
7298 EMSG(_("E419: FG color unknown"));
7299 error = TRUE;
7300 break;
7301 }
7302 }
7303 else if (STRICMP(arg, "bg") == 0)
7304 {
7305 if (cterm_normal_bg_color > 0)
7306 color = cterm_normal_bg_color - 1;
7307 else
7308 {
7309 EMSG(_("E420: BG color unknown"));
7310 error = TRUE;
7311 break;
7312 }
7313 }
7314 else
7315 {
7316 static char *(color_names[28]) = {
7317 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7318 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7319 "Gray", "Grey",
7320 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7321 "Blue", "LightBlue", "Green", "LightGreen",
7322 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7323 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7324 static int color_numbers_16[28] = {0, 1, 2, 3,
7325 4, 5, 6, 6,
7326 7, 7,
7327 7, 7, 8, 8,
7328 9, 9, 10, 10,
7329 11, 11, 12, 12, 13,
7330 13, 14, 14, 15, -1};
7331 /* for xterm with 88 colors... */
7332 static int color_numbers_88[28] = {0, 4, 2, 6,
7333 1, 5, 32, 72,
7334 84, 84,
7335 7, 7, 82, 82,
7336 12, 43, 10, 61,
7337 14, 63, 9, 74, 13,
7338 75, 11, 78, 15, -1};
7339 /* for xterm with 256 colors... */
7340 static int color_numbers_256[28] = {0, 4, 2, 6,
7341 1, 5, 130, 130,
7342 248, 248,
7343 7, 7, 242, 242,
7344 12, 81, 10, 121,
7345 14, 159, 9, 224, 13,
7346 225, 11, 229, 15, -1};
7347 /* for terminals with less than 16 colors... */
7348 static int color_numbers_8[28] = {0, 4, 2, 6,
7349 1, 5, 3, 3,
7350 7, 7,
7351 7, 7, 0+8, 0+8,
7352 4+8, 4+8, 2+8, 2+8,
7353 6+8, 6+8, 1+8, 1+8, 5+8,
7354 5+8, 3+8, 3+8, 7+8, -1};
7355#if defined(__QNXNTO__)
7356 static int *color_numbers_8_qansi = color_numbers_8;
7357 /* On qnx, the 8 & 16 color arrays are the same */
7358 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7359 color_numbers_8_qansi = color_numbers_16;
7360#endif
7361
7362 /* reduce calls to STRICMP a bit, it can be slow */
7363 off = TOUPPER_ASC(*arg);
7364 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7365 if (off == color_names[i][0]
7366 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7367 break;
7368 if (i < 0)
7369 {
7370 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7371 error = TRUE;
7372 break;
7373 }
7374
7375 /* Use the _16 table to check if its a valid color name. */
7376 color = color_numbers_16[i];
7377 if (color >= 0)
7378 {
7379 if (t_colors == 8)
7380 {
7381 /* t_Co is 8: use the 8 colors table */
7382#if defined(__QNXNTO__)
7383 color = color_numbers_8_qansi[i];
7384#else
7385 color = color_numbers_8[i];
7386#endif
7387 if (key[5] == 'F')
7388 {
7389 /* set/reset bold attribute to get light foreground
7390 * colors (on some terminals, e.g. "linux") */
7391 if (color & 8)
7392 {
7393 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7394 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7395 }
7396 else
7397 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7398 }
7399 color &= 7; /* truncate to 8 colors */
7400 }
7401 else if (t_colors == 16 || t_colors == 88
7402 || t_colors == 256)
7403 {
7404 /*
7405 * Guess: if the termcap entry ends in 'm', it is
7406 * probably an xterm-like terminal. Use the changed
7407 * order for colors.
7408 */
7409 if (*T_CAF != NUL)
7410 p = T_CAF;
7411 else
7412 p = T_CSF;
7413 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7414 switch (t_colors)
7415 {
7416 case 16:
7417 color = color_numbers_8[i];
7418 break;
7419 case 88:
7420 color = color_numbers_88[i];
7421 break;
7422 case 256:
7423 color = color_numbers_256[i];
7424 break;
7425 }
7426 }
7427 }
7428 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007429 /* Add one to the argument, to avoid zero. Zero is used for
7430 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007431 if (key[5] == 'F')
7432 {
7433 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7434 if (is_normal_group)
7435 {
7436 cterm_normal_fg_color = color + 1;
7437 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7438#ifdef FEAT_GUI
7439 /* Don't do this if the GUI is used. */
7440 if (!gui.in_use && !gui.starting)
7441#endif
7442 {
7443 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007444 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007445 term_fg_color(color);
7446 }
7447 }
7448 }
7449 else
7450 {
7451 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7452 if (is_normal_group)
7453 {
7454 cterm_normal_bg_color = color + 1;
7455#ifdef FEAT_GUI
7456 /* Don't mess with 'background' if the GUI is used. */
7457 if (!gui.in_use && !gui.starting)
7458#endif
7459 {
7460 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007461 if (color >= 0)
7462 {
7463 if (termcap_active)
7464 term_bg_color(color);
7465 if (t_colors < 16)
7466 i = (color == 0 || color == 4);
7467 else
7468 i = (color < 7 || color == 8);
7469 /* Set the 'background' option if the value is
7470 * wrong. */
7471 if (i != (*p_bg == 'd'))
7472 set_option_value((char_u *)"bg", 0L,
7473 i ? (char_u *)"dark"
7474 : (char_u *)"light", 0);
7475 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007476 }
7477 }
7478 }
7479 }
7480 }
7481 else if (STRCMP(key, "GUIFG") == 0)
7482 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007483#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007484 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007485 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007486 if (!init)
7487 HL_TABLE()[idx].sg_set |= SG_GUI;
7488
Bram Moolenaar61623362010-07-14 22:04:22 +02007489# ifdef FEAT_GUI
7490 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007491 i = color_name2handle(arg);
7492 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7493 {
7494 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007495# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007496 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7497 if (STRCMP(arg, "NONE"))
7498 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7499 else
7500 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007501# ifdef FEAT_GUI
7502# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007503 if (is_menu_group)
7504 gui.menu_fg_pixel = i;
7505 if (is_scrollbar_group)
7506 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007507# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007508 if (is_tooltip_group)
7509 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007510# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007511 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007512# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007513 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007514# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007515 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007516#endif
7517 }
7518 else if (STRCMP(key, "GUIBG") == 0)
7519 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007520#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007521 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007522 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007523 if (!init)
7524 HL_TABLE()[idx].sg_set |= SG_GUI;
7525
Bram Moolenaar61623362010-07-14 22:04:22 +02007526# ifdef FEAT_GUI
7527 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007528 i = color_name2handle(arg);
7529 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7530 {
7531 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007532# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007533 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7534 if (STRCMP(arg, "NONE") != 0)
7535 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7536 else
7537 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007538# ifdef FEAT_GUI
7539# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007540 if (is_menu_group)
7541 gui.menu_bg_pixel = i;
7542 if (is_scrollbar_group)
7543 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007544# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007545 if (is_tooltip_group)
7546 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007547# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007548 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007549# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007550 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007551# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007552 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007553#endif
7554 }
7555 else if (STRCMP(key, "GUISP") == 0)
7556 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007557#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007558 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7559 {
7560 if (!init)
7561 HL_TABLE()[idx].sg_set |= SG_GUI;
7562
Bram Moolenaar61623362010-07-14 22:04:22 +02007563# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007564 i = color_name2handle(arg);
7565 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7566 {
7567 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007568# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007569 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7570 if (STRCMP(arg, "NONE") != 0)
7571 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7572 else
7573 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007574# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007575 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007576# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007577 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007578#endif
7579 }
7580 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7581 {
7582 char_u buf[100];
7583 char_u *tname;
7584
7585 if (!init)
7586 HL_TABLE()[idx].sg_set |= SG_TERM;
7587
7588 /*
7589 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007590 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007591 */
7592 if (STRNCMP(arg, "t_", 2) == 0)
7593 {
7594 off = 0;
7595 buf[0] = 0;
7596 while (arg[off] != NUL)
7597 {
7598 /* Isolate one termcap name */
7599 for (len = 0; arg[off + len] &&
7600 arg[off + len] != ','; ++len)
7601 ;
7602 tname = vim_strnsave(arg + off, len);
7603 if (tname == NULL) /* out of memory */
7604 {
7605 error = TRUE;
7606 break;
7607 }
7608 /* lookup the escape sequence for the item */
7609 p = get_term_code(tname);
7610 vim_free(tname);
7611 if (p == NULL) /* ignore non-existing things */
7612 p = (char_u *)"";
7613
7614 /* Append it to the already found stuff */
7615 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7616 {
7617 EMSG2(_("E422: terminal code too long: %s"), arg);
7618 error = TRUE;
7619 break;
7620 }
7621 STRCAT(buf, p);
7622
7623 /* Advance to the next item */
7624 off += len;
7625 if (arg[off] == ',') /* another one follows */
7626 ++off;
7627 }
7628 }
7629 else
7630 {
7631 /*
7632 * Copy characters from arg[] to buf[], translating <> codes.
7633 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007634 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007635 {
7636 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007637 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007638 off += len;
7639 else /* copy as normal char */
7640 buf[off++] = *p++;
7641 }
7642 buf[off] = NUL;
7643 }
7644 if (error)
7645 break;
7646
7647 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7648 p = NULL;
7649 else
7650 p = vim_strsave(buf);
7651 if (key[2] == 'A')
7652 {
7653 vim_free(HL_TABLE()[idx].sg_start);
7654 HL_TABLE()[idx].sg_start = p;
7655 }
7656 else
7657 {
7658 vim_free(HL_TABLE()[idx].sg_stop);
7659 HL_TABLE()[idx].sg_stop = p;
7660 }
7661 }
7662 else
7663 {
7664 EMSG2(_("E423: Illegal argument: %s"), key_start);
7665 error = TRUE;
7666 break;
7667 }
7668
7669 /*
7670 * When highlighting has been given for a group, don't link it.
7671 */
7672 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7673 HL_TABLE()[idx].sg_link = 0;
7674
7675 /*
7676 * Continue with next argument.
7677 */
7678 linep = skipwhite(linep);
7679 }
7680
7681 /*
7682 * If there is an error, and it's a new entry, remove it from the table.
7683 */
7684 if (error && idx == highlight_ga.ga_len)
7685 syn_unadd_group();
7686 else
7687 {
7688 if (is_normal_group)
7689 {
7690 HL_TABLE()[idx].sg_term_attr = 0;
7691 HL_TABLE()[idx].sg_cterm_attr = 0;
7692#ifdef FEAT_GUI
7693 HL_TABLE()[idx].sg_gui_attr = 0;
7694 /*
7695 * Need to update all groups, because they might be using "bg"
7696 * and/or "fg", which have been changed now.
7697 */
7698 if (gui.in_use)
7699 highlight_gui_started();
7700#endif
7701 }
7702#ifdef FEAT_GUI_X11
7703# ifdef FEAT_MENU
7704 else if (is_menu_group)
7705 {
7706 if (gui.in_use && do_colors)
7707 gui_mch_new_menu_colors();
7708 }
7709# endif
7710 else if (is_scrollbar_group)
7711 {
7712 if (gui.in_use && do_colors)
7713 gui_new_scrollbar_colors();
7714 }
7715# ifdef FEAT_BEVAL
7716 else if (is_tooltip_group)
7717 {
7718 if (gui.in_use && do_colors)
7719 gui_mch_new_tooltip_colors();
7720 }
7721# endif
7722#endif
7723 else
7724 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007725#ifdef FEAT_EVAL
7726 HL_TABLE()[idx].sg_scriptID = current_SID;
7727#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007728 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007729 }
7730 vim_free(key);
7731 vim_free(arg);
7732
7733 /* Only call highlight_changed() once, after sourcing a syntax file */
7734 need_highlight_changed = TRUE;
7735}
7736
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007737#if defined(EXITFREE) || defined(PROTO)
7738 void
7739free_highlight()
7740{
7741 int i;
7742
7743 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007744 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007745 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007746 vim_free(HL_TABLE()[i].sg_name);
7747 vim_free(HL_TABLE()[i].sg_name_u);
7748 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007749 ga_clear(&highlight_ga);
7750}
7751#endif
7752
Bram Moolenaar071d4272004-06-13 20:20:40 +00007753/*
7754 * Reset the cterm colors to what they were before Vim was started, if
7755 * possible. Otherwise reset them to zero.
7756 */
7757 void
7758restore_cterm_colors()
7759{
7760#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7761 /* Since t_me has been set, this probably means that the user
7762 * wants to use this as default colors. Need to reset default
7763 * background/foreground colors. */
7764 mch_set_normal_colors();
7765#else
7766 cterm_normal_fg_color = 0;
7767 cterm_normal_fg_bold = 0;
7768 cterm_normal_bg_color = 0;
7769#endif
7770}
7771
7772/*
7773 * Return TRUE if highlight group "idx" has any settings.
7774 * When "check_link" is TRUE also check for an existing link.
7775 */
7776 static int
7777hl_has_settings(idx, check_link)
7778 int idx;
7779 int check_link;
7780{
7781 return ( HL_TABLE()[idx].sg_term_attr != 0
7782 || HL_TABLE()[idx].sg_cterm_attr != 0
7783#ifdef FEAT_GUI
7784 || HL_TABLE()[idx].sg_gui_attr != 0
7785#endif
7786 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7787}
7788
7789/*
7790 * Clear highlighting for one group.
7791 */
7792 static void
7793highlight_clear(idx)
7794 int idx;
7795{
7796 HL_TABLE()[idx].sg_term = 0;
7797 vim_free(HL_TABLE()[idx].sg_start);
7798 HL_TABLE()[idx].sg_start = NULL;
7799 vim_free(HL_TABLE()[idx].sg_stop);
7800 HL_TABLE()[idx].sg_stop = NULL;
7801 HL_TABLE()[idx].sg_term_attr = 0;
7802 HL_TABLE()[idx].sg_cterm = 0;
7803 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7804 HL_TABLE()[idx].sg_cterm_fg = 0;
7805 HL_TABLE()[idx].sg_cterm_bg = 0;
7806 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007807#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007808 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007809 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7810 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7812 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007813 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7814 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007815#endif
7816#ifdef FEAT_GUI
7817 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7818 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7819 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7821 HL_TABLE()[idx].sg_font = NOFONT;
7822# ifdef FEAT_XFONTSET
7823 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7824 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7825# endif
7826 vim_free(HL_TABLE()[idx].sg_font_name);
7827 HL_TABLE()[idx].sg_font_name = NULL;
7828 HL_TABLE()[idx].sg_gui_attr = 0;
7829#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007830#ifdef FEAT_EVAL
7831 /* Clear the script ID only when there is no link, since that is not
7832 * cleared. */
7833 if (HL_TABLE()[idx].sg_link == 0)
7834 HL_TABLE()[idx].sg_scriptID = 0;
7835#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836}
7837
7838#if defined(FEAT_GUI) || defined(PROTO)
7839/*
7840 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007841 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007842 * "Tooltip" colors.
7843 */
7844 void
7845set_normal_colors()
7846{
7847 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007848 &gui.norm_pixel, &gui.back_pixel,
7849 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 {
7851 gui_mch_new_colors();
7852 must_redraw = CLEAR;
7853 }
7854#ifdef FEAT_GUI_X11
7855 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007856 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7857 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 {
7859# ifdef FEAT_MENU
7860 gui_mch_new_menu_colors();
7861# endif
7862 must_redraw = CLEAR;
7863 }
7864# ifdef FEAT_BEVAL
7865 if (set_group_colors((char_u *)"Tooltip",
7866 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7867 FALSE, FALSE, TRUE))
7868 {
7869# ifdef FEAT_TOOLBAR
7870 gui_mch_new_tooltip_colors();
7871# endif
7872 must_redraw = CLEAR;
7873 }
7874#endif
7875 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007876 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7877 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007878 {
7879 gui_new_scrollbar_colors();
7880 must_redraw = CLEAR;
7881 }
7882#endif
7883}
7884
7885/*
7886 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7887 */
7888 static int
7889set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7890 char_u *name;
7891 guicolor_T *fgp;
7892 guicolor_T *bgp;
7893 int do_menu;
7894 int use_norm;
7895 int do_tooltip;
7896{
7897 int idx;
7898
7899 idx = syn_name2id(name) - 1;
7900 if (idx >= 0)
7901 {
7902 gui_do_one_color(idx, do_menu, do_tooltip);
7903
7904 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7905 *fgp = HL_TABLE()[idx].sg_gui_fg;
7906 else if (use_norm)
7907 *fgp = gui.def_norm_pixel;
7908 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7909 *bgp = HL_TABLE()[idx].sg_gui_bg;
7910 else if (use_norm)
7911 *bgp = gui.def_back_pixel;
7912 return TRUE;
7913 }
7914 return FALSE;
7915}
7916
7917/*
7918 * Get the font of the "Normal" group.
7919 * Returns "" when it's not found or not set.
7920 */
7921 char_u *
7922hl_get_font_name()
7923{
7924 int id;
7925 char_u *s;
7926
7927 id = syn_name2id((char_u *)"Normal");
7928 if (id > 0)
7929 {
7930 s = HL_TABLE()[id - 1].sg_font_name;
7931 if (s != NULL)
7932 return s;
7933 }
7934 return (char_u *)"";
7935}
7936
7937/*
7938 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7939 * actually chosen to be used.
7940 */
7941 void
7942hl_set_font_name(font_name)
7943 char_u *font_name;
7944{
7945 int id;
7946
7947 id = syn_name2id((char_u *)"Normal");
7948 if (id > 0)
7949 {
7950 vim_free(HL_TABLE()[id - 1].sg_font_name);
7951 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7952 }
7953}
7954
7955/*
7956 * Set background color for "Normal" group. Called by gui_set_bg_color()
7957 * when the color is known.
7958 */
7959 void
7960hl_set_bg_color_name(name)
7961 char_u *name; /* must have been allocated */
7962{
7963 int id;
7964
7965 if (name != NULL)
7966 {
7967 id = syn_name2id((char_u *)"Normal");
7968 if (id > 0)
7969 {
7970 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7971 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7972 }
7973 }
7974}
7975
7976/*
7977 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7978 * when the color is known.
7979 */
7980 void
7981hl_set_fg_color_name(name)
7982 char_u *name; /* must have been allocated */
7983{
7984 int id;
7985
7986 if (name != NULL)
7987 {
7988 id = syn_name2id((char_u *)"Normal");
7989 if (id > 0)
7990 {
7991 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7992 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7993 }
7994 }
7995}
7996
7997/*
7998 * Return the handle for a color name.
7999 * Returns INVALCOLOR when failed.
8000 */
8001 static guicolor_T
8002color_name2handle(name)
8003 char_u *name;
8004{
8005 if (STRCMP(name, "NONE") == 0)
8006 return INVALCOLOR;
8007
8008 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8009 return gui.norm_pixel;
8010 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8011 return gui.back_pixel;
8012
8013 return gui_get_color(name);
8014}
8015
8016/*
8017 * Return the handle for a font name.
8018 * Returns NOFONT when failed.
8019 */
8020 static GuiFont
8021font_name2handle(name)
8022 char_u *name;
8023{
8024 if (STRCMP(name, "NONE") == 0)
8025 return NOFONT;
8026
8027 return gui_mch_get_font(name, TRUE);
8028}
8029
8030# ifdef FEAT_XFONTSET
8031/*
8032 * Return the handle for a fontset name.
8033 * Returns NOFONTSET when failed.
8034 */
8035 static GuiFontset
8036fontset_name2handle(name, fixed_width)
8037 char_u *name;
8038 int fixed_width;
8039{
8040 if (STRCMP(name, "NONE") == 0)
8041 return NOFONTSET;
8042
8043 return gui_mch_get_fontset(name, TRUE, fixed_width);
8044}
8045# endif
8046
8047/*
8048 * Get the font or fontset for one highlight group.
8049 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008050 static void
8051hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8052 int idx;
8053 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008054 int do_normal; /* set normal font */
8055 int do_menu UNUSED; /* set menu font */
8056 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008057{
8058# ifdef FEAT_XFONTSET
8059 /* If 'guifontset' is not empty, first try using the name as a
8060 * fontset. If that doesn't work, use it as a font name. */
8061 if (*p_guifontset != NUL
8062# ifdef FONTSET_ALWAYS
8063 || do_menu
8064# endif
8065# ifdef FEAT_BEVAL_TIP
8066 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8067 || do_tooltip
8068# endif
8069 )
8070 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8071# ifdef FONTSET_ALWAYS
8072 || do_menu
8073# endif
8074# ifdef FEAT_BEVAL_TIP
8075 || do_tooltip
8076# endif
8077 );
8078 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8079 {
8080 /* If it worked and it's the Normal group, use it as the
8081 * normal fontset. Same for the Menu group. */
8082 if (do_normal)
8083 gui_init_font(arg, TRUE);
8084# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8085 if (do_menu)
8086 {
8087# ifdef FONTSET_ALWAYS
8088 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8089# else
8090 /* YIKES! This is a bug waiting to crash the program */
8091 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8092# endif
8093 gui_mch_new_menu_font();
8094 }
8095# ifdef FEAT_BEVAL
8096 if (do_tooltip)
8097 {
8098 /* The Athena widget set cannot currently handle switching between
8099 * displaying a single font and a fontset.
8100 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008101 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008102 * XFontStruct is used.
8103 */
8104 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8105 gui_mch_new_tooltip_font();
8106 }
8107# endif
8108# endif
8109 }
8110 else
8111# endif
8112 {
8113 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8114 /* If it worked and it's the Normal group, use it as the
8115 * normal font. Same for the Menu group. */
8116 if (HL_TABLE()[idx].sg_font != NOFONT)
8117 {
8118 if (do_normal)
8119 gui_init_font(arg, FALSE);
8120#ifndef FONTSET_ALWAYS
8121# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8122 if (do_menu)
8123 {
8124 gui.menu_font = HL_TABLE()[idx].sg_font;
8125 gui_mch_new_menu_font();
8126 }
8127# endif
8128#endif
8129 }
8130 }
8131}
8132
8133#endif /* FEAT_GUI */
8134
8135/*
8136 * Table with the specifications for an attribute number.
8137 * Note that this table is used by ALL buffers. This is required because the
8138 * GUI can redraw at any time for any buffer.
8139 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008140static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141
8142#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8143
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008144static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145
8146#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8147
8148#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008149static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008150
8151#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8152#endif
8153
8154/*
8155 * Return the attr number for a set of colors and font.
8156 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8157 * if the combination is new.
8158 * Return 0 for error (no more room).
8159 */
8160 static int
8161get_attr_entry(table, aep)
8162 garray_T *table;
8163 attrentry_T *aep;
8164{
8165 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008166 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008167 static int recursive = FALSE;
8168
8169 /*
8170 * Init the table, in case it wasn't done yet.
8171 */
8172 table->ga_itemsize = sizeof(attrentry_T);
8173 table->ga_growsize = 7;
8174
8175 /*
8176 * Try to find an entry with the same specifications.
8177 */
8178 for (i = 0; i < table->ga_len; ++i)
8179 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008180 taep = &(((attrentry_T *)table->ga_data)[i]);
8181 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182 && (
8183#ifdef FEAT_GUI
8184 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008185 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8186 && aep->ae_u.gui.bg_color
8187 == taep->ae_u.gui.bg_color
8188 && aep->ae_u.gui.sp_color
8189 == taep->ae_u.gui.sp_color
8190 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008191# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008192 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193# endif
8194 ))
8195 ||
8196#endif
8197 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008198 && (aep->ae_u.term.start == NULL)
8199 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008200 && (aep->ae_u.term.start == NULL
8201 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008202 taep->ae_u.term.start) == 0)
8203 && (aep->ae_u.term.stop == NULL)
8204 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008205 && (aep->ae_u.term.stop == NULL
8206 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008207 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008209 && aep->ae_u.cterm.fg_color
8210 == taep->ae_u.cterm.fg_color
8211 && aep->ae_u.cterm.bg_color
8212 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008213 ))
8214
8215 return i + ATTR_OFF;
8216 }
8217
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008218 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219 {
8220 /*
8221 * Running out of attribute entries! remove all attributes, and
8222 * compute new ones for all groups.
8223 * When called recursively, we are really out of numbers.
8224 */
8225 if (recursive)
8226 {
8227 EMSG(_("E424: Too many different highlighting attributes in use"));
8228 return 0;
8229 }
8230 recursive = TRUE;
8231
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008232 clear_hl_tables();
8233
Bram Moolenaar071d4272004-06-13 20:20:40 +00008234 must_redraw = CLEAR;
8235
8236 for (i = 0; i < highlight_ga.ga_len; ++i)
8237 set_hl_attr(i);
8238
8239 recursive = FALSE;
8240 }
8241
8242 /*
8243 * This is a new combination of colors and font, add an entry.
8244 */
8245 if (ga_grow(table, 1) == FAIL)
8246 return 0;
8247
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008248 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8249 vim_memset(taep, 0, sizeof(attrentry_T));
8250 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008251#ifdef FEAT_GUI
8252 if (table == &gui_attr_table)
8253 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008254 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8255 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8256 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8257 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008258# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008259 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008260# endif
8261 }
8262#endif
8263 if (table == &term_attr_table)
8264 {
8265 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008266 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008267 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008268 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008269 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008270 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008271 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008272 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008273 }
8274 else if (table == &cterm_attr_table)
8275 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008276 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8277 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008278 }
8279 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008280 return (table->ga_len - 1 + ATTR_OFF);
8281}
8282
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008283/*
8284 * Clear all highlight tables.
8285 */
8286 void
8287clear_hl_tables()
8288{
8289 int i;
8290 attrentry_T *taep;
8291
8292#ifdef FEAT_GUI
8293 ga_clear(&gui_attr_table);
8294#endif
8295 for (i = 0; i < term_attr_table.ga_len; ++i)
8296 {
8297 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8298 vim_free(taep->ae_u.term.start);
8299 vim_free(taep->ae_u.term.stop);
8300 }
8301 ga_clear(&term_attr_table);
8302 ga_clear(&cterm_attr_table);
8303}
8304
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008305#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008306/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008307 * Combine special attributes (e.g., for spelling) with other attributes
8308 * (e.g., for syntax highlighting).
8309 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008310 * This creates a new group when required.
8311 * Since we expect there to be few spelling mistakes we don't cache the
8312 * result.
8313 * Return the resulting attributes.
8314 */
8315 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008316hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008317 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008318 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008319{
8320 attrentry_T *char_aep = NULL;
8321 attrentry_T *spell_aep;
8322 attrentry_T new_en;
8323
8324 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008325 return prim_attr;
8326 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8327 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008328#ifdef FEAT_GUI
8329 if (gui.in_use)
8330 {
8331 if (char_attr > HL_ALL)
8332 char_aep = syn_gui_attr2entry(char_attr);
8333 if (char_aep != NULL)
8334 new_en = *char_aep;
8335 else
8336 {
8337 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008338 new_en.ae_u.gui.fg_color = INVALCOLOR;
8339 new_en.ae_u.gui.bg_color = INVALCOLOR;
8340 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008341 if (char_attr <= HL_ALL)
8342 new_en.ae_attr = char_attr;
8343 }
8344
Bram Moolenaar30abd282005-06-22 22:35:10 +00008345 if (prim_attr <= HL_ALL)
8346 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008347 else
8348 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008349 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008350 if (spell_aep != NULL)
8351 {
8352 new_en.ae_attr |= spell_aep->ae_attr;
8353 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8354 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8355 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8356 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8357 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8358 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8359 if (spell_aep->ae_u.gui.font != NOFONT)
8360 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8361# ifdef FEAT_XFONTSET
8362 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8363 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8364# endif
8365 }
8366 }
8367 return get_attr_entry(&gui_attr_table, &new_en);
8368 }
8369#endif
8370
8371 if (t_colors > 1)
8372 {
8373 if (char_attr > HL_ALL)
8374 char_aep = syn_cterm_attr2entry(char_attr);
8375 if (char_aep != NULL)
8376 new_en = *char_aep;
8377 else
8378 {
8379 vim_memset(&new_en, 0, sizeof(new_en));
8380 if (char_attr <= HL_ALL)
8381 new_en.ae_attr = char_attr;
8382 }
8383
Bram Moolenaar30abd282005-06-22 22:35:10 +00008384 if (prim_attr <= HL_ALL)
8385 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008386 else
8387 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008388 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008389 if (spell_aep != NULL)
8390 {
8391 new_en.ae_attr |= spell_aep->ae_attr;
8392 if (spell_aep->ae_u.cterm.fg_color > 0)
8393 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8394 if (spell_aep->ae_u.cterm.bg_color > 0)
8395 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8396 }
8397 }
8398 return get_attr_entry(&cterm_attr_table, &new_en);
8399 }
8400
8401 if (char_attr > HL_ALL)
8402 char_aep = syn_term_attr2entry(char_attr);
8403 if (char_aep != NULL)
8404 new_en = *char_aep;
8405 else
8406 {
8407 vim_memset(&new_en, 0, sizeof(new_en));
8408 if (char_attr <= HL_ALL)
8409 new_en.ae_attr = char_attr;
8410 }
8411
Bram Moolenaar30abd282005-06-22 22:35:10 +00008412 if (prim_attr <= HL_ALL)
8413 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008414 else
8415 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008416 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008417 if (spell_aep != NULL)
8418 {
8419 new_en.ae_attr |= spell_aep->ae_attr;
8420 if (spell_aep->ae_u.term.start != NULL)
8421 {
8422 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8423 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8424 }
8425 }
8426 }
8427 return get_attr_entry(&term_attr_table, &new_en);
8428}
8429#endif
8430
Bram Moolenaar071d4272004-06-13 20:20:40 +00008431#ifdef FEAT_GUI
8432
8433 attrentry_T *
8434syn_gui_attr2entry(attr)
8435 int attr;
8436{
8437 attr -= ATTR_OFF;
8438 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8439 return NULL;
8440 return &(GUI_ATTR_ENTRY(attr));
8441}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008442#endif /* FEAT_GUI */
8443
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008444/*
8445 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8446 * Only to be used when "attr" > HL_ALL.
8447 */
8448 int
8449syn_attr2attr(attr)
8450 int attr;
8451{
8452 attrentry_T *aep;
8453
8454#ifdef FEAT_GUI
8455 if (gui.in_use)
8456 aep = syn_gui_attr2entry(attr);
8457 else
8458#endif
8459 if (t_colors > 1)
8460 aep = syn_cterm_attr2entry(attr);
8461 else
8462 aep = syn_term_attr2entry(attr);
8463
8464 if (aep == NULL) /* highlighting not set */
8465 return 0;
8466 return aep->ae_attr;
8467}
8468
8469
Bram Moolenaar071d4272004-06-13 20:20:40 +00008470 attrentry_T *
8471syn_term_attr2entry(attr)
8472 int attr;
8473{
8474 attr -= ATTR_OFF;
8475 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8476 return NULL;
8477 return &(TERM_ATTR_ENTRY(attr));
8478}
8479
8480 attrentry_T *
8481syn_cterm_attr2entry(attr)
8482 int attr;
8483{
8484 attr -= ATTR_OFF;
8485 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8486 return NULL;
8487 return &(CTERM_ATTR_ENTRY(attr));
8488}
8489
8490#define LIST_ATTR 1
8491#define LIST_STRING 2
8492#define LIST_INT 3
8493
8494 static void
8495highlight_list_one(id)
8496 int id;
8497{
8498 struct hl_group *sgp;
8499 int didh = FALSE;
8500
8501 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8502
8503 didh = highlight_list_arg(id, didh, LIST_ATTR,
8504 sgp->sg_term, NULL, "term");
8505 didh = highlight_list_arg(id, didh, LIST_STRING,
8506 0, sgp->sg_start, "start");
8507 didh = highlight_list_arg(id, didh, LIST_STRING,
8508 0, sgp->sg_stop, "stop");
8509
8510 didh = highlight_list_arg(id, didh, LIST_ATTR,
8511 sgp->sg_cterm, NULL, "cterm");
8512 didh = highlight_list_arg(id, didh, LIST_INT,
8513 sgp->sg_cterm_fg, NULL, "ctermfg");
8514 didh = highlight_list_arg(id, didh, LIST_INT,
8515 sgp->sg_cterm_bg, NULL, "ctermbg");
8516
Bram Moolenaar61623362010-07-14 22:04:22 +02008517#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 didh = highlight_list_arg(id, didh, LIST_ATTR,
8519 sgp->sg_gui, NULL, "gui");
8520 didh = highlight_list_arg(id, didh, LIST_STRING,
8521 0, sgp->sg_gui_fg_name, "guifg");
8522 didh = highlight_list_arg(id, didh, LIST_STRING,
8523 0, sgp->sg_gui_bg_name, "guibg");
8524 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008525 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008526#endif
8527#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008528 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008529 0, sgp->sg_font_name, "font");
8530#endif
8531
Bram Moolenaar661b1822005-07-28 22:36:45 +00008532 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008533 {
8534 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008535 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008536 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8537 msg_putchar(' ');
8538 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8539 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008540
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008541 if (!didh)
8542 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008543#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008544 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008545 last_set_msg(sgp->sg_scriptID);
8546#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008547}
8548
8549 static int
8550highlight_list_arg(id, didh, type, iarg, sarg, name)
8551 int id;
8552 int didh;
8553 int type;
8554 int iarg;
8555 char_u *sarg;
8556 char *name;
8557{
8558 char_u buf[100];
8559 char_u *ts;
8560 int i;
8561
Bram Moolenaar661b1822005-07-28 22:36:45 +00008562 if (got_int)
8563 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008564 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8565 {
8566 ts = buf;
8567 if (type == LIST_INT)
8568 sprintf((char *)buf, "%d", iarg - 1);
8569 else if (type == LIST_STRING)
8570 ts = sarg;
8571 else /* type == LIST_ATTR */
8572 {
8573 buf[0] = NUL;
8574 for (i = 0; hl_attr_table[i] != 0; ++i)
8575 {
8576 if (iarg & hl_attr_table[i])
8577 {
8578 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008579 vim_strcat(buf, (char_u *)",", 100);
8580 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008581 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8582 }
8583 }
8584 }
8585
8586 (void)syn_list_header(didh,
8587 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8588 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008589 if (!got_int)
8590 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008591 if (*name != NUL)
8592 {
8593 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8594 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8595 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008596 msg_outtrans(ts);
8597 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008598 }
8599 return didh;
8600}
8601
8602#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8603/*
8604 * Return "1" if highlight group "id" has attribute "flag".
8605 * Return NULL otherwise.
8606 */
8607 char_u *
8608highlight_has_attr(id, flag, modec)
8609 int id;
8610 int flag;
8611 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8612{
8613 int attr;
8614
8615 if (id <= 0 || id > highlight_ga.ga_len)
8616 return NULL;
8617
Bram Moolenaar61623362010-07-14 22:04:22 +02008618#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008619 if (modec == 'g')
8620 attr = HL_TABLE()[id - 1].sg_gui;
8621 else
8622#endif
8623 if (modec == 'c')
8624 attr = HL_TABLE()[id - 1].sg_cterm;
8625 else
8626 attr = HL_TABLE()[id - 1].sg_term;
8627
8628 if (attr & flag)
8629 return (char_u *)"1";
8630 return NULL;
8631}
8632#endif
8633
8634#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8635/*
8636 * Return color name of highlight group "id".
8637 */
8638 char_u *
8639highlight_color(id, what, modec)
8640 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008641 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008642 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8643{
8644 static char_u name[20];
8645 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008646 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008647 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008648 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008649
8650 if (id <= 0 || id > highlight_ga.ga_len)
8651 return NULL;
8652
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008653 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008654 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008655 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008656 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008657 font = TRUE;
8658 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008659 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008660 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8661 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008662 if (modec == 'g')
8663 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008664# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008665 /* return font name */
8666 if (font)
8667 return HL_TABLE()[id - 1].sg_font_name;
8668
Bram Moolenaar071d4272004-06-13 20:20:40 +00008669 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008670 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008671 {
8672 guicolor_T color;
8673 long_u rgb;
8674 static char_u buf[10];
8675
8676 if (fg)
8677 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008678 else if (sp)
8679 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008680 else
8681 color = HL_TABLE()[id - 1].sg_gui_bg;
8682 if (color == INVALCOLOR)
8683 return NULL;
8684 rgb = gui_mch_get_rgb(color);
8685 sprintf((char *)buf, "#%02x%02x%02x",
8686 (unsigned)(rgb >> 16),
8687 (unsigned)(rgb >> 8) & 255,
8688 (unsigned)rgb & 255);
8689 return buf;
8690 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008691#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008692 if (fg)
8693 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008694 if (sp)
8695 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8697 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008698 if (font || sp)
8699 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008700 if (modec == 'c')
8701 {
8702 if (fg)
8703 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8704 else
8705 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8706 sprintf((char *)name, "%d", n);
8707 return name;
8708 }
8709 /* term doesn't have color */
8710 return NULL;
8711}
8712#endif
8713
8714#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8715 || defined(PROTO)
8716/*
8717 * Return color name of highlight group "id" as RGB value.
8718 */
8719 long_u
8720highlight_gui_color_rgb(id, fg)
8721 int id;
8722 int fg; /* TRUE = fg, FALSE = bg */
8723{
8724 guicolor_T color;
8725
8726 if (id <= 0 || id > highlight_ga.ga_len)
8727 return 0L;
8728
8729 if (fg)
8730 color = HL_TABLE()[id - 1].sg_gui_fg;
8731 else
8732 color = HL_TABLE()[id - 1].sg_gui_bg;
8733
8734 if (color == INVALCOLOR)
8735 return 0L;
8736
8737 return gui_mch_get_rgb(color);
8738}
8739#endif
8740
8741/*
8742 * Output the syntax list header.
8743 * Return TRUE when started a new line.
8744 */
8745 static int
8746syn_list_header(did_header, outlen, id)
8747 int did_header; /* did header already */
8748 int outlen; /* length of string that comes */
8749 int id; /* highlight group id */
8750{
8751 int endcol = 19;
8752 int newline = TRUE;
8753
8754 if (!did_header)
8755 {
8756 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008757 if (got_int)
8758 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008759 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8760 endcol = 15;
8761 }
8762 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008763 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008764 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008765 if (got_int)
8766 return TRUE;
8767 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008768 else
8769 {
8770 if (msg_col >= endcol) /* wrap around is like starting a new line */
8771 newline = FALSE;
8772 }
8773
8774 if (msg_col >= endcol) /* output at least one space */
8775 endcol = msg_col + 1;
8776 if (Columns <= endcol) /* avoid hang for tiny window */
8777 endcol = Columns - 1;
8778
8779 msg_advance(endcol);
8780
8781 /* Show "xxx" with the attributes. */
8782 if (!did_header)
8783 {
8784 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8785 msg_putchar(' ');
8786 }
8787
8788 return newline;
8789}
8790
8791/*
8792 * Set the attribute numbers for a highlight group.
8793 * Called after one of the attributes has changed.
8794 */
8795 static void
8796set_hl_attr(idx)
8797 int idx; /* index in array */
8798{
8799 attrentry_T at_en;
8800 struct hl_group *sgp = HL_TABLE() + idx;
8801
8802 /* The "Normal" group doesn't need an attribute number */
8803 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8804 return;
8805
8806#ifdef FEAT_GUI
8807 /*
8808 * For the GUI mode: If there are other than "normal" highlighting
8809 * attributes, need to allocate an attr number.
8810 */
8811 if (sgp->sg_gui_fg == INVALCOLOR
8812 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008813 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008814 && sgp->sg_font == NOFONT
8815# ifdef FEAT_XFONTSET
8816 && sgp->sg_fontset == NOFONTSET
8817# endif
8818 )
8819 {
8820 sgp->sg_gui_attr = sgp->sg_gui;
8821 }
8822 else
8823 {
8824 at_en.ae_attr = sgp->sg_gui;
8825 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8826 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008827 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008828 at_en.ae_u.gui.font = sgp->sg_font;
8829# ifdef FEAT_XFONTSET
8830 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8831# endif
8832 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8833 }
8834#endif
8835 /*
8836 * For the term mode: If there are other than "normal" highlighting
8837 * attributes, need to allocate an attr number.
8838 */
8839 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8840 sgp->sg_term_attr = sgp->sg_term;
8841 else
8842 {
8843 at_en.ae_attr = sgp->sg_term;
8844 at_en.ae_u.term.start = sgp->sg_start;
8845 at_en.ae_u.term.stop = sgp->sg_stop;
8846 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8847 }
8848
8849 /*
8850 * For the color term mode: If there are other than "normal"
8851 * highlighting attributes, need to allocate an attr number.
8852 */
8853 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8854 sgp->sg_cterm_attr = sgp->sg_cterm;
8855 else
8856 {
8857 at_en.ae_attr = sgp->sg_cterm;
8858 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8859 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8860 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8861 }
8862}
8863
8864/*
8865 * Lookup a highlight group name and return it's ID.
8866 * If it is not found, 0 is returned.
8867 */
8868 int
8869syn_name2id(name)
8870 char_u *name;
8871{
8872 int i;
8873 char_u name_u[200];
8874
8875 /* Avoid using stricmp() too much, it's slow on some systems */
8876 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8877 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008878 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008879 vim_strup(name_u);
8880 for (i = highlight_ga.ga_len; --i >= 0; )
8881 if (HL_TABLE()[i].sg_name_u != NULL
8882 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8883 break;
8884 return i + 1;
8885}
8886
8887#if defined(FEAT_EVAL) || defined(PROTO)
8888/*
8889 * Return TRUE if highlight group "name" exists.
8890 */
8891 int
8892highlight_exists(name)
8893 char_u *name;
8894{
8895 return (syn_name2id(name) > 0);
8896}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008897
8898# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8899/*
8900 * Return the name of highlight group "id".
8901 * When not a valid ID return an empty string.
8902 */
8903 char_u *
8904syn_id2name(id)
8905 int id;
8906{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008907 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008908 return (char_u *)"";
8909 return HL_TABLE()[id - 1].sg_name;
8910}
8911# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008912#endif
8913
8914/*
8915 * Like syn_name2id(), but take a pointer + length argument.
8916 */
8917 int
8918syn_namen2id(linep, len)
8919 char_u *linep;
8920 int len;
8921{
8922 char_u *name;
8923 int id = 0;
8924
8925 name = vim_strnsave(linep, len);
8926 if (name != NULL)
8927 {
8928 id = syn_name2id(name);
8929 vim_free(name);
8930 }
8931 return id;
8932}
8933
8934/*
8935 * Find highlight group name in the table and return it's ID.
8936 * The argument is a pointer to the name and the length of the name.
8937 * If it doesn't exist yet, a new entry is created.
8938 * Return 0 for failure.
8939 */
8940 int
8941syn_check_group(pp, len)
8942 char_u *pp;
8943 int len;
8944{
8945 int id;
8946 char_u *name;
8947
8948 name = vim_strnsave(pp, len);
8949 if (name == NULL)
8950 return 0;
8951
8952 id = syn_name2id(name);
8953 if (id == 0) /* doesn't exist yet */
8954 id = syn_add_group(name);
8955 else
8956 vim_free(name);
8957 return id;
8958}
8959
8960/*
8961 * Add new highlight group and return it's ID.
8962 * "name" must be an allocated string, it will be consumed.
8963 * Return 0 for failure.
8964 */
8965 static int
8966syn_add_group(name)
8967 char_u *name;
8968{
8969 char_u *p;
8970
8971 /* Check that the name is ASCII letters, digits and underscore. */
8972 for (p = name; *p != NUL; ++p)
8973 {
8974 if (!vim_isprintc(*p))
8975 {
8976 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008977 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008978 return 0;
8979 }
8980 else if (!ASCII_ISALNUM(*p) && *p != '_')
8981 {
8982 /* This is an error, but since there previously was no check only
8983 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008984 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008985 MSG(_("W18: Invalid character in group name"));
8986 break;
8987 }
8988 }
8989
8990 /*
8991 * First call for this growarray: init growing array.
8992 */
8993 if (highlight_ga.ga_data == NULL)
8994 {
8995 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8996 highlight_ga.ga_growsize = 10;
8997 }
8998
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02008999 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009000 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009001 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009002 vim_free(name);
9003 return 0;
9004 }
9005
Bram Moolenaar071d4272004-06-13 20:20:40 +00009006 /*
9007 * Make room for at least one other syntax_highlight entry.
9008 */
9009 if (ga_grow(&highlight_ga, 1) == FAIL)
9010 {
9011 vim_free(name);
9012 return 0;
9013 }
9014
9015 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9016 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9017 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9018#ifdef FEAT_GUI
9019 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9020 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009021 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009022#endif
9023 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009024
9025 return highlight_ga.ga_len; /* ID is index plus one */
9026}
9027
9028/*
9029 * When, just after calling syn_add_group(), an error is discovered, this
9030 * function deletes the new name.
9031 */
9032 static void
9033syn_unadd_group()
9034{
9035 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009036 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9037 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9038}
9039
9040/*
9041 * Translate a group ID to highlight attributes.
9042 */
9043 int
9044syn_id2attr(hl_id)
9045 int hl_id;
9046{
9047 int attr;
9048 struct hl_group *sgp;
9049
9050 hl_id = syn_get_final_id(hl_id);
9051 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9052
9053#ifdef FEAT_GUI
9054 /*
9055 * Only use GUI attr when the GUI is being used.
9056 */
9057 if (gui.in_use)
9058 attr = sgp->sg_gui_attr;
9059 else
9060#endif
9061 if (t_colors > 1)
9062 attr = sgp->sg_cterm_attr;
9063 else
9064 attr = sgp->sg_term_attr;
9065
9066 return attr;
9067}
9068
9069#ifdef FEAT_GUI
9070/*
9071 * Get the GUI colors and attributes for a group ID.
9072 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9073 */
9074 int
9075syn_id2colors(hl_id, fgp, bgp)
9076 int hl_id;
9077 guicolor_T *fgp;
9078 guicolor_T *bgp;
9079{
9080 struct hl_group *sgp;
9081
9082 hl_id = syn_get_final_id(hl_id);
9083 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9084
9085 *fgp = sgp->sg_gui_fg;
9086 *bgp = sgp->sg_gui_bg;
9087 return sgp->sg_gui;
9088}
9089#endif
9090
9091/*
9092 * Translate a group ID to the final group ID (following links).
9093 */
9094 int
9095syn_get_final_id(hl_id)
9096 int hl_id;
9097{
9098 int count;
9099 struct hl_group *sgp;
9100
9101 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9102 return 0; /* Can be called from eval!! */
9103
9104 /*
9105 * Follow links until there is no more.
9106 * Look out for loops! Break after 100 links.
9107 */
9108 for (count = 100; --count >= 0; )
9109 {
9110 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9111 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9112 break;
9113 hl_id = sgp->sg_link;
9114 }
9115
9116 return hl_id;
9117}
9118
9119#ifdef FEAT_GUI
9120/*
9121 * Call this function just after the GUI has started.
9122 * It finds the font and color handles for the highlighting groups.
9123 */
9124 void
9125highlight_gui_started()
9126{
9127 int idx;
9128
9129 /* First get the colors from the "Normal" and "Menu" group, if set */
9130 set_normal_colors();
9131
9132 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9133 gui_do_one_color(idx, FALSE, FALSE);
9134
9135 highlight_changed();
9136}
9137
9138 static void
9139gui_do_one_color(idx, do_menu, do_tooltip)
9140 int idx;
9141 int do_menu; /* TRUE: might set the menu font */
9142 int do_tooltip; /* TRUE: might set the tooltip font */
9143{
9144 int didit = FALSE;
9145
9146 if (HL_TABLE()[idx].sg_font_name != NULL)
9147 {
9148 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9149 do_tooltip);
9150 didit = TRUE;
9151 }
9152 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9153 {
9154 HL_TABLE()[idx].sg_gui_fg =
9155 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9156 didit = TRUE;
9157 }
9158 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9159 {
9160 HL_TABLE()[idx].sg_gui_bg =
9161 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9162 didit = TRUE;
9163 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009164 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9165 {
9166 HL_TABLE()[idx].sg_gui_sp =
9167 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9168 didit = TRUE;
9169 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009170 if (didit) /* need to get a new attr number */
9171 set_hl_attr(idx);
9172}
9173
9174#endif
9175
9176/*
9177 * Translate the 'highlight' option into attributes in highlight_attr[] and
9178 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9179 * corresponding highlights to use on top of HLF_SNC is computed.
9180 * Called only when the 'highlight' option has been changed and upon first
9181 * screen redraw after any :highlight command.
9182 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9183 */
9184 int
9185highlight_changed()
9186{
9187 int hlf;
9188 int i;
9189 char_u *p;
9190 int attr;
9191 char_u *end;
9192 int id;
9193#ifdef USER_HIGHLIGHT
9194 char_u userhl[10];
9195# ifdef FEAT_STL_OPT
9196 int id_SNC = -1;
9197 int id_S = -1;
9198 int hlcnt;
9199# endif
9200#endif
9201 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9202
9203 need_highlight_changed = FALSE;
9204
9205 /*
9206 * Clear all attributes.
9207 */
9208 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9209 highlight_attr[hlf] = 0;
9210
9211 /*
9212 * First set all attributes to their default value.
9213 * Then use the attributes from the 'highlight' option.
9214 */
9215 for (i = 0; i < 2; ++i)
9216 {
9217 if (i)
9218 p = p_hl;
9219 else
9220 p = get_highlight_default();
9221 if (p == NULL) /* just in case */
9222 continue;
9223
9224 while (*p)
9225 {
9226 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9227 if (hl_flags[hlf] == *p)
9228 break;
9229 ++p;
9230 if (hlf == (int)HLF_COUNT || *p == NUL)
9231 return FAIL;
9232
9233 /*
9234 * Allow several hl_flags to be combined, like "bu" for
9235 * bold-underlined.
9236 */
9237 attr = 0;
9238 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9239 {
9240 if (vim_iswhite(*p)) /* ignore white space */
9241 continue;
9242
9243 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9244 return FAIL;
9245
9246 switch (*p)
9247 {
9248 case 'b': attr |= HL_BOLD;
9249 break;
9250 case 'i': attr |= HL_ITALIC;
9251 break;
9252 case '-':
9253 case 'n': /* no highlighting */
9254 break;
9255 case 'r': attr |= HL_INVERSE;
9256 break;
9257 case 's': attr |= HL_STANDOUT;
9258 break;
9259 case 'u': attr |= HL_UNDERLINE;
9260 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009261 case 'c': attr |= HL_UNDERCURL;
9262 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009263 case ':': ++p; /* highlight group name */
9264 if (attr || *p == NUL) /* no combinations */
9265 return FAIL;
9266 end = vim_strchr(p, ',');
9267 if (end == NULL)
9268 end = p + STRLEN(p);
9269 id = syn_check_group(p, (int)(end - p));
9270 if (id == 0)
9271 return FAIL;
9272 attr = syn_id2attr(id);
9273 p = end - 1;
9274#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9275 if (hlf == (int)HLF_SNC)
9276 id_SNC = syn_get_final_id(id);
9277 else if (hlf == (int)HLF_S)
9278 id_S = syn_get_final_id(id);
9279#endif
9280 break;
9281 default: return FAIL;
9282 }
9283 }
9284 highlight_attr[hlf] = attr;
9285
9286 p = skip_to_option_part(p); /* skip comma and spaces */
9287 }
9288 }
9289
9290#ifdef USER_HIGHLIGHT
9291 /* Setup the user highlights
9292 *
9293 * Temporarily utilize 10 more hl entries. Have to be in there
9294 * simultaneously in case of table overflows in get_attr_entry()
9295 */
9296# ifdef FEAT_STL_OPT
9297 if (ga_grow(&highlight_ga, 10) == FAIL)
9298 return FAIL;
9299 hlcnt = highlight_ga.ga_len;
9300 if (id_S == 0)
9301 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009302 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009303 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9304 id_S = hlcnt + 10;
9305 }
9306# endif
9307 for (i = 0; i < 9; i++)
9308 {
9309 sprintf((char *)userhl, "User%d", i + 1);
9310 id = syn_name2id(userhl);
9311 if (id == 0)
9312 {
9313 highlight_user[i] = 0;
9314# ifdef FEAT_STL_OPT
9315 highlight_stlnc[i] = 0;
9316# endif
9317 }
9318 else
9319 {
9320# ifdef FEAT_STL_OPT
9321 struct hl_group *hlt = HL_TABLE();
9322# endif
9323
9324 highlight_user[i] = syn_id2attr(id);
9325# ifdef FEAT_STL_OPT
9326 if (id_SNC == 0)
9327 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009328 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009329 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9330 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009331# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009332 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9333# endif
9334 }
9335 else
9336 mch_memmove(&hlt[hlcnt + i],
9337 &hlt[id_SNC - 1],
9338 sizeof(struct hl_group));
9339 hlt[hlcnt + i].sg_link = 0;
9340
9341 /* Apply difference between UserX and HLF_S to HLF_SNC */
9342 hlt[hlcnt + i].sg_term ^=
9343 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9344 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9345 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9346 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9347 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9348 hlt[hlcnt + i].sg_cterm ^=
9349 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9350 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9351 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9352 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9353 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009354# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 hlt[hlcnt + i].sg_gui ^=
9356 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009357# endif
9358# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009359 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9360 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9361 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9362 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009363 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9364 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009365 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9366 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9367# ifdef FEAT_XFONTSET
9368 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9369 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9370# endif
9371# endif
9372 highlight_ga.ga_len = hlcnt + i + 1;
9373 set_hl_attr(hlcnt + i); /* At long last we can apply */
9374 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9375# endif
9376 }
9377 }
9378# ifdef FEAT_STL_OPT
9379 highlight_ga.ga_len = hlcnt;
9380# endif
9381
9382#endif /* USER_HIGHLIGHT */
9383
9384 return OK;
9385}
9386
Bram Moolenaar4f688582007-07-24 12:34:30 +00009387#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009388
9389static void highlight_list __ARGS((void));
9390static void highlight_list_two __ARGS((int cnt, int attr));
9391
9392/*
9393 * Handle command line completion for :highlight command.
9394 */
9395 void
9396set_context_in_highlight_cmd(xp, arg)
9397 expand_T *xp;
9398 char_u *arg;
9399{
9400 char_u *p;
9401
9402 /* Default: expand group names */
9403 xp->xp_context = EXPAND_HIGHLIGHT;
9404 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009405 include_link = 2;
9406 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407
9408 /* (part of) subcommand already typed */
9409 if (*arg != NUL)
9410 {
9411 p = skiptowhite(arg);
9412 if (*p != NUL) /* past "default" or group name */
9413 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009414 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009415 if (STRNCMP("default", arg, p - arg) == 0)
9416 {
9417 arg = skipwhite(p);
9418 xp->xp_pattern = arg;
9419 p = skiptowhite(arg);
9420 }
9421 if (*p != NUL) /* past group name */
9422 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009423 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009424 if (arg[1] == 'i' && arg[0] == 'N')
9425 highlight_list();
9426 if (STRNCMP("link", arg, p - arg) == 0
9427 || STRNCMP("clear", arg, p - arg) == 0)
9428 {
9429 xp->xp_pattern = skipwhite(p);
9430 p = skiptowhite(xp->xp_pattern);
9431 if (*p != NUL) /* past first group name */
9432 {
9433 xp->xp_pattern = skipwhite(p);
9434 p = skiptowhite(xp->xp_pattern);
9435 }
9436 }
9437 if (*p != NUL) /* past group name(s) */
9438 xp->xp_context = EXPAND_NOTHING;
9439 }
9440 }
9441 }
9442}
9443
9444/*
9445 * List highlighting matches in a nice way.
9446 */
9447 static void
9448highlight_list()
9449{
9450 int i;
9451
9452 for (i = 10; --i >= 0; )
9453 highlight_list_two(i, hl_attr(HLF_D));
9454 for (i = 40; --i >= 0; )
9455 highlight_list_two(99, 0);
9456}
9457
9458 static void
9459highlight_list_two(cnt, attr)
9460 int cnt;
9461 int attr;
9462{
9463 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9464 msg_clr_eos();
9465 out_flush();
9466 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9467}
9468
9469#endif /* FEAT_CMDL_COMPL */
9470
9471#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9472 || defined(FEAT_SIGNS) || defined(PROTO)
9473/*
9474 * Function given to ExpandGeneric() to obtain the list of group names.
9475 * Also used for synIDattr() function.
9476 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009477 char_u *
9478get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009479 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009480 int idx;
9481{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009482#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009483 if (idx == highlight_ga.ga_len && include_none != 0)
9484 return (char_u *)"none";
9485 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009486 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009487 if (idx == highlight_ga.ga_len + include_none + include_default
9488 && include_link != 0)
9489 return (char_u *)"link";
9490 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9491 && include_link != 0)
9492 return (char_u *)"clear";
9493#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009494 if (idx < 0 || idx >= highlight_ga.ga_len)
9495 return NULL;
9496 return HL_TABLE()[idx].sg_name;
9497}
9498#endif
9499
Bram Moolenaar4f688582007-07-24 12:34:30 +00009500#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009501/*
9502 * Free all the highlight group fonts.
9503 * Used when quitting for systems which need it.
9504 */
9505 void
9506free_highlight_fonts()
9507{
9508 int idx;
9509
9510 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9511 {
9512 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9513 HL_TABLE()[idx].sg_font = NOFONT;
9514# ifdef FEAT_XFONTSET
9515 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9516 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9517# endif
9518 }
9519
9520 gui_mch_free_font(gui.norm_font);
9521# ifdef FEAT_XFONTSET
9522 gui_mch_free_fontset(gui.fontset);
9523# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009524# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009525 gui_mch_free_font(gui.bold_font);
9526 gui_mch_free_font(gui.ital_font);
9527 gui_mch_free_font(gui.boldital_font);
9528# endif
9529}
9530#endif
9531
9532/**************************************
9533 * End of Highlighting stuff *
9534 **************************************/