blob: a9a2e862f34e24605fdd92fe59a14734aba0a90c [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)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200993 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000994 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200995 check_state_ends();
996 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997
998 next_match_idx = -1;
999 ++current_line_id;
1000}
1001
1002/*
1003 * Check for items in the stack that need their end updated.
1004 * When "startofline" is TRUE the last item is always updated.
1005 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1006 */
1007 static void
1008syn_update_ends(startofline)
1009 int startofline;
1010{
1011 stateitem_T *cur_si;
1012 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001013 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001014
1015 if (startofline)
1016 {
1017 /* Check for a match carried over from a previous line with a
1018 * contained region. The match ends as soon as the region ends. */
1019 for (i = 0; i < current_state.ga_len; ++i)
1020 {
1021 cur_si = &CUR_STATE(i);
1022 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001023 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024 == SPTYPE_MATCH
1025 && cur_si->si_m_endpos.lnum < current_lnum)
1026 {
1027 cur_si->si_flags |= HL_MATCHCONT;
1028 cur_si->si_m_endpos.lnum = 0;
1029 cur_si->si_m_endpos.col = 0;
1030 cur_si->si_h_endpos = cur_si->si_m_endpos;
1031 cur_si->si_ends = TRUE;
1032 }
1033 }
1034 }
1035
1036 /*
1037 * Need to update the end of a start/skip/end that continues from the
1038 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001039 * influence contained items. If we've just removed "extend"
1040 * (startofline == 0) then we should update ends of normal regions
1041 * contained inside "keepend" because "extend" could have extended
1042 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001043 * Then check for items ending in column 0.
1044 */
1045 i = current_state.ga_len - 1;
1046 if (keepend_level >= 0)
1047 for ( ; i > keepend_level; --i)
1048 if (CUR_STATE(i).si_flags & HL_EXTEND)
1049 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001050
1051 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052 for ( ; i < current_state.ga_len; ++i)
1053 {
1054 cur_si = &CUR_STATE(i);
1055 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001056 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057 || (i == current_state.ga_len - 1 && startofline))
1058 {
1059 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1060 cur_si->si_h_startpos.lnum = current_lnum;
1061
1062 if (!(cur_si->si_flags & HL_MATCHCONT))
1063 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001064
1065 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1066 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001067 }
1068 }
1069 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070}
1071
1072/****************************************
1073 * Handling of the state stack cache.
1074 */
1075
1076/*
1077 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1078 *
1079 * To speed up syntax highlighting, the state stack for the start of some
1080 * lines is cached. These entries can be used to start parsing at that point.
1081 *
1082 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1083 * valid entries. b_sst_first points to the first one, then follow sst_next.
1084 * The entries are sorted on line number. The first entry is often for line 2
1085 * (line 1 always starts with an empty stack).
1086 * There is also a list for free entries. This construction is used to avoid
1087 * having to allocate and free memory blocks too often.
1088 *
1089 * When making changes to the buffer, this is logged in b_mod_*. When calling
1090 * update_screen() to update the display, it will call
1091 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1092 * entries. The entries which are inside the changed area are removed,
1093 * because they must be recomputed. Entries below the changed have their line
1094 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1095 * set to indicate that a check must be made if the changed lines would change
1096 * the cached entry.
1097 *
1098 * When later displaying lines, an entry is stored for each line. Displayed
1099 * lines are likely to be displayed again, in which case the state at the
1100 * start of the line is needed.
1101 * For not displayed lines, an entry is stored for every so many lines. These
1102 * entries will be used e.g., when scrolling backwards. The distance between
1103 * entries depends on the number of lines in the buffer. For small buffers
1104 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1105 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1106 */
1107
Bram Moolenaar860cae12010-06-05 23:22:07 +02001108 static void
1109syn_stack_free_block(block)
1110 synblock_T *block;
1111{
1112 synstate_T *p;
1113
1114 if (block->b_sst_array != NULL)
1115 {
1116 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1117 clear_syn_state(p);
1118 vim_free(block->b_sst_array);
1119 block->b_sst_array = NULL;
1120 block->b_sst_len = 0;
1121 }
1122}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123/*
1124 * Free b_sst_array[] for buffer "buf".
1125 * Used when syntax items changed to force resyncing everywhere.
1126 */
1127 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001128syn_stack_free_all(block)
1129 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001131 win_T *wp;
1132
Bram Moolenaar860cae12010-06-05 23:22:07 +02001133 syn_stack_free_block(block);
1134
1135
Bram Moolenaar071d4272004-06-13 20:20:40 +00001136#ifdef FEAT_FOLDING
1137 /* When using "syntax" fold method, must update all folds. */
1138 FOR_ALL_WINDOWS(wp)
1139 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001140 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001141 foldUpdateAll(wp);
1142 }
1143#endif
1144}
1145
1146/*
1147 * Allocate the syntax state stack for syn_buf when needed.
1148 * If the number of entries in b_sst_array[] is much too big or a bit too
1149 * small, reallocate it.
1150 * Also used to allocate b_sst_array[] for the first time.
1151 */
1152 static void
1153syn_stack_alloc()
1154{
1155 long len;
1156 synstate_T *to, *from;
1157 synstate_T *sstp;
1158
1159 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1160 if (len < SST_MIN_ENTRIES)
1161 len = SST_MIN_ENTRIES;
1162 else if (len > SST_MAX_ENTRIES)
1163 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001164 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001165 {
1166 /* Allocate 50% too much, to avoid reallocating too often. */
1167 len = syn_buf->b_ml.ml_line_count;
1168 len = (len + len / 2) / SST_DIST + Rows * 2;
1169 if (len < SST_MIN_ENTRIES)
1170 len = SST_MIN_ENTRIES;
1171 else if (len > SST_MAX_ENTRIES)
1172 len = SST_MAX_ENTRIES;
1173
Bram Moolenaar860cae12010-06-05 23:22:07 +02001174 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001175 {
1176 /* When shrinking the array, cleanup the existing stack.
1177 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001178 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179 && syn_stack_cleanup())
1180 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001181 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1182 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001183 }
1184
1185 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1186 if (sstp == NULL) /* out of memory! */
1187 return;
1188
1189 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001190 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 {
1192 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001193 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001194 from = from->sst_next)
1195 {
1196 ++to;
1197 *to = *from;
1198 to->sst_next = to + 1;
1199 }
1200 }
1201 if (to != sstp - 1)
1202 {
1203 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001204 syn_block->b_sst_first = sstp;
1205 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 }
1207 else
1208 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001209 syn_block->b_sst_first = NULL;
1210 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001211 }
1212
1213 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001214 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001215 while (++to < sstp + len)
1216 to->sst_next = to + 1;
1217 (sstp + len - 1)->sst_next = NULL;
1218
Bram Moolenaar860cae12010-06-05 23:22:07 +02001219 vim_free(syn_block->b_sst_array);
1220 syn_block->b_sst_array = sstp;
1221 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222 }
1223}
1224
1225/*
1226 * Check for changes in a buffer to affect stored syntax states. Uses the
1227 * b_mod_* fields.
1228 * Called from update_screen(), before screen is being updated, once for each
1229 * displayed buffer.
1230 */
1231 void
1232syn_stack_apply_changes(buf)
1233 buf_T *buf;
1234{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001235 win_T *wp;
1236
1237 syn_stack_apply_changes_block(&buf->b_s, buf);
1238
1239 FOR_ALL_WINDOWS(wp)
1240 {
1241 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1242 syn_stack_apply_changes_block(wp->w_s, buf);
1243 }
1244}
1245
1246 static void
1247syn_stack_apply_changes_block(block, buf)
1248 synblock_T *block;
1249 buf_T *buf;
1250{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001251 synstate_T *p, *prev, *np;
1252 linenr_T n;
1253
Bram Moolenaar860cae12010-06-05 23:22:07 +02001254 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001255 return;
1256
1257 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001260 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 {
1262 n = p->sst_lnum + buf->b_mod_xlines;
1263 if (n <= buf->b_mod_bot)
1264 {
1265 /* this state is inside the changed area, remove it */
1266 np = p->sst_next;
1267 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001268 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 else
1270 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001271 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272 p = np;
1273 continue;
1274 }
1275 /* This state is below the changed area. Remember the line
1276 * that needs to be parsed before this entry can be made valid
1277 * again. */
1278 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1279 {
1280 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1281 p->sst_change_lnum += buf->b_mod_xlines;
1282 else
1283 p->sst_change_lnum = buf->b_mod_top;
1284 }
1285 if (p->sst_change_lnum == 0
1286 || p->sst_change_lnum < buf->b_mod_bot)
1287 p->sst_change_lnum = buf->b_mod_bot;
1288
1289 p->sst_lnum = n;
1290 }
1291 prev = p;
1292 p = p->sst_next;
1293 }
1294}
1295
1296/*
1297 * Reduce the number of entries in the state stack for syn_buf.
1298 * Returns TRUE if at least one entry was freed.
1299 */
1300 static int
1301syn_stack_cleanup()
1302{
1303 synstate_T *p, *prev;
1304 disptick_T tick;
1305 int above;
1306 int dist;
1307 int retval = FALSE;
1308
Bram Moolenaar860cae12010-06-05 23:22:07 +02001309 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 return retval;
1311
1312 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001313 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001314 dist = 999999;
1315 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001316 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001317
1318 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001319 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 * be removed. Set "above" when the "tick" for the oldest entry is above
1321 * "b_sst_lasttick" (the display tick wraps around).
1322 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001325 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1327 {
1328 if (prev->sst_lnum + dist > p->sst_lnum)
1329 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001330 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 {
1332 if (!above || p->sst_tick < tick)
1333 tick = p->sst_tick;
1334 above = TRUE;
1335 }
1336 else if (!above && p->sst_tick < tick)
1337 tick = p->sst_tick;
1338 }
1339 }
1340
1341 /*
1342 * Go through the list to make the entries for the oldest tick at an
1343 * interval of several lines.
1344 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001345 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1347 {
1348 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1349 {
1350 /* Move this entry from used list to free list */
1351 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001352 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353 p = prev;
1354 retval = TRUE;
1355 }
1356 }
1357 return retval;
1358}
1359
1360/*
1361 * Free the allocated memory for a syn_state item.
1362 * Move the entry into the free list.
1363 */
1364 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001365syn_stack_free_entry(block, p)
1366 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001367 synstate_T *p;
1368{
1369 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001370 p->sst_next = block->b_sst_firstfree;
1371 block->b_sst_firstfree = p;
1372 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373}
1374
1375/*
1376 * Find an entry in the list of state stacks at or before "lnum".
1377 * Returns NULL when there is no entry or the first entry is after "lnum".
1378 */
1379 static synstate_T *
1380syn_stack_find_entry(lnum)
1381 linenr_T lnum;
1382{
1383 synstate_T *p, *prev;
1384
1385 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001386 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387 {
1388 if (p->sst_lnum == lnum)
1389 return p;
1390 if (p->sst_lnum > lnum)
1391 break;
1392 }
1393 return prev;
1394}
1395
1396/*
1397 * Try saving the current state in b_sst_array[].
1398 * The current state must be valid for the start of the current_lnum line!
1399 */
1400 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001401store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001402{
1403 int i;
1404 synstate_T *p;
1405 bufstate_T *bp;
1406 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001407 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408
1409 /*
1410 * If the current state contains a start or end pattern that continues
1411 * from the previous line, we can't use it. Don't store it then.
1412 */
1413 for (i = current_state.ga_len - 1; i >= 0; --i)
1414 {
1415 cur_si = &CUR_STATE(i);
1416 if (cur_si->si_h_startpos.lnum >= current_lnum
1417 || cur_si->si_m_endpos.lnum >= current_lnum
1418 || cur_si->si_h_endpos.lnum >= current_lnum
1419 || (cur_si->si_end_idx
1420 && cur_si->si_eoe_pos.lnum >= current_lnum))
1421 break;
1422 }
1423 if (i >= 0)
1424 {
1425 if (sp != NULL)
1426 {
1427 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001428 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001430 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001431 else
1432 {
1433 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001434 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435 if (p->sst_next == sp)
1436 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001437 if (p != NULL) /* just in case */
1438 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001439 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 sp = NULL;
1442 }
1443 }
1444 else if (sp == NULL || sp->sst_lnum != current_lnum)
1445 {
1446 /*
1447 * Add a new entry
1448 */
1449 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001450 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451 {
1452 (void)syn_stack_cleanup();
1453 /* "sp" may have been moved to the freelist now */
1454 sp = syn_stack_find_entry(current_lnum);
1455 }
1456 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001457 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 sp = NULL;
1459 else
1460 {
1461 /* Take the first item from the free list and put it in the used
1462 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001463 p = syn_block->b_sst_firstfree;
1464 syn_block->b_sst_firstfree = p->sst_next;
1465 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001466 if (sp == NULL)
1467 {
1468 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001469 p->sst_next = syn_block->b_sst_first;
1470 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471 }
1472 else
1473 {
1474 /* insert in list after *sp */
1475 p->sst_next = sp->sst_next;
1476 sp->sst_next = p;
1477 }
1478 sp = p;
1479 sp->sst_stacksize = 0;
1480 sp->sst_lnum = current_lnum;
1481 }
1482 }
1483 if (sp != NULL)
1484 {
1485 /* When overwriting an existing state stack, clear it first */
1486 clear_syn_state(sp);
1487 sp->sst_stacksize = current_state.ga_len;
1488 if (current_state.ga_len > SST_FIX_STATES)
1489 {
1490 /* Need to clear it, might be something remaining from when the
1491 * length was less than SST_FIX_STATES. */
1492 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1493 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1494 sp->sst_stacksize = 0;
1495 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1498 }
1499 else
1500 bp = sp->sst_union.sst_stack;
1501 for (i = 0; i < sp->sst_stacksize; ++i)
1502 {
1503 bp[i].bs_idx = CUR_STATE(i).si_idx;
1504 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001505#ifdef FEAT_CONCEAL
1506 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1507 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1508#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001509 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1510 }
1511 sp->sst_next_flags = current_next_flags;
1512 sp->sst_next_list = current_next_list;
1513 sp->sst_tick = display_tick;
1514 sp->sst_change_lnum = 0;
1515 }
1516 current_state_stored = TRUE;
1517 return sp;
1518}
1519
1520/*
1521 * Copy a state stack from "from" in b_sst_array[] to current_state;
1522 */
1523 static void
1524load_current_state(from)
1525 synstate_T *from;
1526{
1527 int i;
1528 bufstate_T *bp;
1529
1530 clear_current_state();
1531 validate_current_state();
1532 keepend_level = -1;
1533 if (from->sst_stacksize
1534 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1535 {
1536 if (from->sst_stacksize > SST_FIX_STATES)
1537 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1538 else
1539 bp = from->sst_union.sst_stack;
1540 for (i = 0; i < from->sst_stacksize; ++i)
1541 {
1542 CUR_STATE(i).si_idx = bp[i].bs_idx;
1543 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001544#ifdef FEAT_CONCEAL
1545 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1546 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1547#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1549 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1550 keepend_level = i;
1551 CUR_STATE(i).si_ends = FALSE;
1552 CUR_STATE(i).si_m_lnum = 0;
1553 if (CUR_STATE(i).si_idx >= 0)
1554 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001555 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001556 else
1557 CUR_STATE(i).si_next_list = NULL;
1558 update_si_attr(i);
1559 }
1560 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001561 }
1562 current_next_list = from->sst_next_list;
1563 current_next_flags = from->sst_next_flags;
1564 current_lnum = from->sst_lnum;
1565}
1566
1567/*
1568 * Compare saved state stack "*sp" with the current state.
1569 * Return TRUE when they are equal.
1570 */
1571 static int
1572syn_stack_equal(sp)
1573 synstate_T *sp;
1574{
1575 int i, j;
1576 bufstate_T *bp;
1577 reg_extmatch_T *six, *bsx;
1578
1579 /* First a quick check if the stacks have the same size end nextlist. */
1580 if (sp->sst_stacksize == current_state.ga_len
1581 && sp->sst_next_list == current_next_list)
1582 {
1583 /* Need to compare all states on both stacks. */
1584 if (sp->sst_stacksize > SST_FIX_STATES)
1585 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1586 else
1587 bp = sp->sst_union.sst_stack;
1588
1589 for (i = current_state.ga_len; --i >= 0; )
1590 {
1591 /* If the item has another index the state is different. */
1592 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1593 break;
1594 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1595 {
1596 /* When the extmatch pointers are different, the strings in
1597 * them can still be the same. Check if the extmatch
1598 * references are equal. */
1599 bsx = bp[i].bs_extmatch;
1600 six = CUR_STATE(i).si_extmatch;
1601 /* If one of the extmatch pointers is NULL the states are
1602 * different. */
1603 if (bsx == NULL || six == NULL)
1604 break;
1605 for (j = 0; j < NSUBEXP; ++j)
1606 {
1607 /* Check each referenced match string. They must all be
1608 * equal. */
1609 if (bsx->matches[j] != six->matches[j])
1610 {
1611 /* If the pointer is different it can still be the
1612 * same text. Compare the strings, ignore case when
1613 * the start item has the sp_ic flag set. */
1614 if (bsx->matches[j] == NULL
1615 || six->matches[j] == NULL)
1616 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001617 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001618 ? MB_STRICMP(bsx->matches[j],
1619 six->matches[j]) != 0
1620 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1621 break;
1622 }
1623 }
1624 if (j != NSUBEXP)
1625 break;
1626 }
1627 }
1628 if (i < 0)
1629 return TRUE;
1630 }
1631 return FALSE;
1632}
1633
1634/*
1635 * We stop parsing syntax above line "lnum". If the stored state at or below
1636 * this line depended on a change before it, it now depends on the line below
1637 * the last parsed line.
1638 * The window looks like this:
1639 * line which changed
1640 * displayed line
1641 * displayed line
1642 * lnum -> line below window
1643 */
1644 void
1645syntax_end_parsing(lnum)
1646 linenr_T lnum;
1647{
1648 synstate_T *sp;
1649
1650 sp = syn_stack_find_entry(lnum);
1651 if (sp != NULL && sp->sst_lnum < lnum)
1652 sp = sp->sst_next;
1653
1654 if (sp != NULL && sp->sst_change_lnum != 0)
1655 sp->sst_change_lnum = lnum;
1656}
1657
1658/*
1659 * End of handling of the state stack.
1660 ****************************************/
1661
1662 static void
1663invalidate_current_state()
1664{
1665 clear_current_state();
1666 current_state.ga_itemsize = 0; /* mark current_state invalid */
1667 current_next_list = NULL;
1668 keepend_level = -1;
1669}
1670
1671 static void
1672validate_current_state()
1673{
1674 current_state.ga_itemsize = sizeof(stateitem_T);
1675 current_state.ga_growsize = 3;
1676}
1677
1678/*
1679 * Return TRUE if the syntax at start of lnum changed since last time.
1680 * This will only be called just after get_syntax_attr() for the previous
1681 * line, to check if the next line needs to be redrawn too.
1682 */
1683 int
1684syntax_check_changed(lnum)
1685 linenr_T lnum;
1686{
1687 int retval = TRUE;
1688 synstate_T *sp;
1689
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 /*
1691 * Check the state stack when:
1692 * - lnum is just below the previously syntaxed line.
1693 * - lnum is not before the lines with saved states.
1694 * - lnum is not past the lines with saved states.
1695 * - lnum is at or before the last changed line.
1696 */
1697 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1698 {
1699 sp = syn_stack_find_entry(lnum);
1700 if (sp != NULL && sp->sst_lnum == lnum)
1701 {
1702 /*
1703 * finish the previous line (needed when not all of the line was
1704 * drawn)
1705 */
1706 (void)syn_finish_line(FALSE);
1707
1708 /*
1709 * Compare the current state with the previously saved state of
1710 * the line.
1711 */
1712 if (syn_stack_equal(sp))
1713 retval = FALSE;
1714
1715 /*
1716 * Store the current state in b_sst_array[] for later use.
1717 */
1718 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001719 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 }
1721 }
1722
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723 return retval;
1724}
1725
1726/*
1727 * Finish the current line.
1728 * This doesn't return any attributes, it only gets the state at the end of
1729 * the line. It can start anywhere in the line, as long as the current state
1730 * is valid.
1731 */
1732 static int
1733syn_finish_line(syncing)
1734 int syncing; /* called for syncing */
1735{
1736 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001737 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001738
1739 if (!current_finished)
1740 {
1741 while (!current_finished)
1742 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001743 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744 /*
1745 * When syncing, and found some item, need to check the item.
1746 */
1747 if (syncing && current_state.ga_len)
1748 {
1749 /*
1750 * Check for match with sync item.
1751 */
1752 cur_si = &CUR_STATE(current_state.ga_len - 1);
1753 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001754 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001755 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1756 return TRUE;
1757
1758 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001759 * that ends here, need to do that now. Be careful not to go
1760 * past the NUL. */
1761 prev_current_col = current_col;
1762 if (syn_getcurline()[current_col] != NUL)
1763 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001765 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766 }
1767 ++current_col;
1768 }
1769 }
1770 return FALSE;
1771}
1772
1773/*
1774 * Return highlight attributes for next character.
1775 * Must first call syntax_start() once for the line.
1776 * "col" is normally 0 for the first use in a line, and increments by one each
1777 * time. It's allowed to skip characters and to stop before the end of the
1778 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001779 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1780 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781 */
1782 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001783get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001784 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001785 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001786 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001787{
1788 int attr = 0;
1789
Bram Moolenaar349955a2007-08-14 21:07:36 +00001790 if (can_spell != NULL)
1791 /* Default: Only do spelling when there is no @Spell cluster or when
1792 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001793 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1794 ? (syn_block->b_spell_cluster_id == 0)
1795 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001796
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001798 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001799 return 0;
1800
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001801 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001802 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001803 {
1804 clear_current_state();
1805#ifdef FEAT_EVAL
1806 current_id = 0;
1807 current_trans_id = 0;
1808#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001809#ifdef FEAT_CONCEAL
1810 current_flags = 0;
1811#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001812 return 0;
1813 }
1814
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815 /* Make sure current_state is valid */
1816 if (INVALID_STATE(&current_state))
1817 validate_current_state();
1818
1819 /*
1820 * Skip from the current column to "col", get the attributes for "col".
1821 */
1822 while (current_col <= col)
1823 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001824 attr = syn_current_attr(FALSE, TRUE, can_spell,
1825 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826 ++current_col;
1827 }
1828
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 return attr;
1830}
1831
1832/*
1833 * Get syntax attributes for current_lnum, current_col.
1834 */
1835 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001836syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001837 int syncing; /* When 1: called for syncing */
1838 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001839 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001840 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841{
1842 int syn_id;
1843 lpos_T endpos; /* was: char_u *endp; */
1844 lpos_T hl_startpos; /* was: int hl_startcol; */
1845 lpos_T hl_endpos;
1846 lpos_T eos_pos; /* end-of-start match (start region) */
1847 lpos_T eoe_pos; /* end-of-end pattern */
1848 int end_idx; /* group ID for end pattern */
1849 int idx;
1850 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001851 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001852 int startcol;
1853 int endcol;
1854 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001855 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 short *next_list;
1857 int found_match; /* found usable match */
1858 static int try_next_column = FALSE; /* must try in next col */
1859 int do_keywords;
1860 regmmatch_T regmatch;
1861 lpos_T pos;
1862 int lc_col;
1863 reg_extmatch_T *cur_extmatch = NULL;
1864 char_u *line; /* current line. NOTE: becomes invalid after
1865 looking for a pattern match! */
1866
1867 /* variables for zero-width matches that have a "nextgroup" argument */
1868 int keep_next_list;
1869 int zero_width_next_list = FALSE;
1870 garray_T zero_width_next_ga;
1871
1872 /*
1873 * No character, no attributes! Past end of line?
1874 * Do try matching with an empty line (could be the start of a region).
1875 */
1876 line = syn_getcurline();
1877 if (line[current_col] == NUL && current_col != 0)
1878 {
1879 /*
1880 * If we found a match after the last column, use it.
1881 */
1882 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1883 && next_match_col != MAXCOL)
1884 (void)push_next_match(NULL);
1885
1886 current_finished = TRUE;
1887 current_state_stored = FALSE;
1888 return 0;
1889 }
1890
1891 /* if the current or next character is NUL, we will finish the line now */
1892 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1893 {
1894 current_finished = TRUE;
1895 current_state_stored = FALSE;
1896 }
1897
1898 /*
1899 * When in the previous column there was a match but it could not be used
1900 * (empty match or already matched in this column) need to try again in
1901 * the next column.
1902 */
1903 if (try_next_column)
1904 {
1905 next_match_idx = -1;
1906 try_next_column = FALSE;
1907 }
1908
1909 /* Only check for keywords when not syncing and there are some. */
1910 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001911 && (syn_block->b_keywtab.ht_used > 0
1912 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001913
1914 /* Init the list of zero-width matches with a nextlist. This is used to
1915 * avoid matching the same item in the same position twice. */
1916 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1917
1918 /*
1919 * Repeat matching keywords and patterns, to find contained items at the
1920 * same column. This stops when there are no extra matches at the current
1921 * column.
1922 */
1923 do
1924 {
1925 found_match = FALSE;
1926 keep_next_list = FALSE;
1927 syn_id = 0;
1928
1929 /*
1930 * 1. Check for a current state.
1931 * Only when there is no current state, or if the current state may
1932 * contain other things, we need to check for keywords and patterns.
1933 * Always need to check for contained items if some item has the
1934 * "containedin" argument (takes extra time!).
1935 */
1936 if (current_state.ga_len)
1937 cur_si = &CUR_STATE(current_state.ga_len - 1);
1938 else
1939 cur_si = NULL;
1940
Bram Moolenaar860cae12010-06-05 23:22:07 +02001941 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001942 || cur_si->si_cont_list != NULL)
1943 {
1944 /*
1945 * 2. Check for keywords, if on a keyword char after a non-keyword
1946 * char. Don't do this when syncing.
1947 */
1948 if (do_keywords)
1949 {
1950 line = syn_getcurline();
1951 if (vim_iswordc_buf(line + current_col, syn_buf)
1952 && (current_col == 0
1953 || !vim_iswordc_buf(line + current_col - 1
1954#ifdef FEAT_MBYTE
1955 - (has_mbyte
1956 ? (*mb_head_off)(line, line + current_col - 1)
1957 : 0)
1958#endif
1959 , syn_buf)))
1960 {
1961 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001962 &endcol, &flags, &next_list, cur_si,
1963 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001964 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001965 {
1966 if (push_current_state(KEYWORD_IDX) == OK)
1967 {
1968 cur_si = &CUR_STATE(current_state.ga_len - 1);
1969 cur_si->si_m_startcol = current_col;
1970 cur_si->si_h_startpos.lnum = current_lnum;
1971 cur_si->si_h_startpos.col = 0; /* starts right away */
1972 cur_si->si_m_endpos.lnum = current_lnum;
1973 cur_si->si_m_endpos.col = endcol;
1974 cur_si->si_h_endpos.lnum = current_lnum;
1975 cur_si->si_h_endpos.col = endcol;
1976 cur_si->si_ends = TRUE;
1977 cur_si->si_end_idx = 0;
1978 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001979#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001980 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001981 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001982 if (current_state.ga_len > 1)
1983 cur_si->si_flags |=
1984 CUR_STATE(current_state.ga_len - 2).si_flags
1985 & HL_CONCEAL;
1986#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001987 cur_si->si_id = syn_id;
1988 cur_si->si_trans_id = syn_id;
1989 if (flags & HL_TRANSP)
1990 {
1991 if (current_state.ga_len < 2)
1992 {
1993 cur_si->si_attr = 0;
1994 cur_si->si_trans_id = 0;
1995 }
1996 else
1997 {
1998 cur_si->si_attr = CUR_STATE(
1999 current_state.ga_len - 2).si_attr;
2000 cur_si->si_trans_id = CUR_STATE(
2001 current_state.ga_len - 2).si_trans_id;
2002 }
2003 }
2004 else
2005 cur_si->si_attr = syn_id2attr(syn_id);
2006 cur_si->si_cont_list = NULL;
2007 cur_si->si_next_list = next_list;
2008 check_keepend();
2009 }
2010 else
2011 vim_free(next_list);
2012 }
2013 }
2014 }
2015
2016 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002017 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002019 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020 {
2021 /*
2022 * If we didn't check for a match yet, or we are past it, check
2023 * for any match with a pattern.
2024 */
2025 if (next_match_idx < 0 || next_match_col < (int)current_col)
2026 {
2027 /*
2028 * Check all relevant patterns for a match at this
2029 * position. This is complicated, because matching with a
2030 * pattern takes quite a bit of time, thus we want to
2031 * avoid doing it when it's not needed.
2032 */
2033 next_match_idx = 0; /* no match in this line yet */
2034 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002035 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002037 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038 if ( spp->sp_syncing == syncing
2039 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2040 && (spp->sp_type == SPTYPE_MATCH
2041 || spp->sp_type == SPTYPE_START)
2042 && (current_next_list != NULL
2043 ? in_id_list(NULL, current_next_list,
2044 &spp->sp_syn, 0)
2045 : (cur_si == NULL
2046 ? !(spp->sp_flags & HL_CONTAINED)
2047 : in_id_list(cur_si,
2048 cur_si->si_cont_list, &spp->sp_syn,
2049 spp->sp_flags & HL_CONTAINED))))
2050 {
2051 /* If we already tried matching in this line, and
2052 * there isn't a match before next_match_col, skip
2053 * this item. */
2054 if (spp->sp_line_id == current_line_id
2055 && spp->sp_startcol >= next_match_col)
2056 continue;
2057 spp->sp_line_id = current_line_id;
2058
2059 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2060 if (lc_col < 0)
2061 lc_col = 0;
2062
2063 regmatch.rmm_ic = spp->sp_ic;
2064 regmatch.regprog = spp->sp_prog;
2065 if (!syn_regexec(&regmatch, current_lnum,
2066 (colnr_T)lc_col))
2067 {
2068 /* no match in this line, try another one */
2069 spp->sp_startcol = MAXCOL;
2070 continue;
2071 }
2072
2073 /*
2074 * Compute the first column of the match.
2075 */
2076 syn_add_start_off(&pos, &regmatch,
2077 spp, SPO_MS_OFF, -1);
2078 if (pos.lnum > current_lnum)
2079 {
2080 /* must have used end of match in a next line,
2081 * we can't handle that */
2082 spp->sp_startcol = MAXCOL;
2083 continue;
2084 }
2085 startcol = pos.col;
2086
2087 /* remember the next column where this pattern
2088 * matches in the current line */
2089 spp->sp_startcol = startcol;
2090
2091 /*
2092 * If a previously found match starts at a lower
2093 * column number, don't use this one.
2094 */
2095 if (startcol >= next_match_col)
2096 continue;
2097
2098 /*
2099 * If we matched this pattern at this position
2100 * before, skip it. Must retry in the next
2101 * column, because it may match from there.
2102 */
2103 if (did_match_already(idx, &zero_width_next_ga))
2104 {
2105 try_next_column = TRUE;
2106 continue;
2107 }
2108
2109 endpos.lnum = regmatch.endpos[0].lnum;
2110 endpos.col = regmatch.endpos[0].col;
2111
2112 /* Compute the highlight start. */
2113 syn_add_start_off(&hl_startpos, &regmatch,
2114 spp, SPO_HS_OFF, -1);
2115
2116 /* Compute the region start. */
2117 /* Default is to use the end of the match. */
2118 syn_add_end_off(&eos_pos, &regmatch,
2119 spp, SPO_RS_OFF, 0);
2120
2121 /*
2122 * Grab the external submatches before they get
2123 * overwritten. Reference count doesn't change.
2124 */
2125 unref_extmatch(cur_extmatch);
2126 cur_extmatch = re_extmatch_out;
2127 re_extmatch_out = NULL;
2128
2129 flags = 0;
2130 eoe_pos.lnum = 0; /* avoid warning */
2131 eoe_pos.col = 0;
2132 end_idx = 0;
2133 hl_endpos.lnum = 0;
2134
2135 /*
2136 * For a "oneline" the end must be found in the
2137 * same line too. Search for it after the end of
2138 * the match with the start pattern. Set the
2139 * resulting end positions at the same time.
2140 */
2141 if (spp->sp_type == SPTYPE_START
2142 && (spp->sp_flags & HL_ONELINE))
2143 {
2144 lpos_T startpos;
2145
2146 startpos = endpos;
2147 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2148 &flags, &eoe_pos, &end_idx, cur_extmatch);
2149 if (endpos.lnum == 0)
2150 continue; /* not found */
2151 }
2152
2153 /*
2154 * For a "match" the size must be > 0 after the
2155 * end offset needs has been added. Except when
2156 * syncing.
2157 */
2158 else if (spp->sp_type == SPTYPE_MATCH)
2159 {
2160 syn_add_end_off(&hl_endpos, &regmatch, spp,
2161 SPO_HE_OFF, 0);
2162 syn_add_end_off(&endpos, &regmatch, spp,
2163 SPO_ME_OFF, 0);
2164 if (endpos.lnum == current_lnum
2165 && (int)endpos.col + syncing < startcol)
2166 {
2167 /*
2168 * If an empty string is matched, may need
2169 * to try matching again at next column.
2170 */
2171 if (regmatch.startpos[0].col
2172 == regmatch.endpos[0].col)
2173 try_next_column = TRUE;
2174 continue;
2175 }
2176 }
2177
2178 /*
2179 * keep the best match so far in next_match_*
2180 */
2181 /* Highlighting must start after startpos and end
2182 * before endpos. */
2183 if (hl_startpos.lnum == current_lnum
2184 && (int)hl_startpos.col < startcol)
2185 hl_startpos.col = startcol;
2186 limit_pos_zero(&hl_endpos, &endpos);
2187
2188 next_match_idx = idx;
2189 next_match_col = startcol;
2190 next_match_m_endpos = endpos;
2191 next_match_h_endpos = hl_endpos;
2192 next_match_h_startpos = hl_startpos;
2193 next_match_flags = flags;
2194 next_match_eos_pos = eos_pos;
2195 next_match_eoe_pos = eoe_pos;
2196 next_match_end_idx = end_idx;
2197 unref_extmatch(next_match_extmatch);
2198 next_match_extmatch = cur_extmatch;
2199 cur_extmatch = NULL;
2200 }
2201 }
2202 }
2203
2204 /*
2205 * If we found a match at the current column, use it.
2206 */
2207 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2208 {
2209 synpat_T *lspp;
2210
2211 /* When a zero-width item matched which has a nextgroup,
2212 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002213 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214 if (next_match_m_endpos.lnum == current_lnum
2215 && next_match_m_endpos.col == current_col
2216 && lspp->sp_next_list != NULL)
2217 {
2218 current_next_list = lspp->sp_next_list;
2219 current_next_flags = lspp->sp_flags;
2220 keep_next_list = TRUE;
2221 zero_width_next_list = TRUE;
2222
2223 /* Add the index to a list, so that we can check
2224 * later that we don't match it again (and cause an
2225 * endless loop). */
2226 if (ga_grow(&zero_width_next_ga, 1) == OK)
2227 {
2228 ((int *)(zero_width_next_ga.ga_data))
2229 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002230 }
2231 next_match_idx = -1;
2232 }
2233 else
2234 cur_si = push_next_match(cur_si);
2235 found_match = TRUE;
2236 }
2237 }
2238 }
2239
2240 /*
2241 * Handle searching for nextgroup match.
2242 */
2243 if (current_next_list != NULL && !keep_next_list)
2244 {
2245 /*
2246 * If a nextgroup was not found, continue looking for one if:
2247 * - this is an empty line and the "skipempty" option was given
2248 * - we are on white space and the "skipwhite" option was given
2249 */
2250 if (!found_match)
2251 {
2252 line = syn_getcurline();
2253 if (((current_next_flags & HL_SKIPWHITE)
2254 && vim_iswhite(line[current_col]))
2255 || ((current_next_flags & HL_SKIPEMPTY)
2256 && *line == NUL))
2257 break;
2258 }
2259
2260 /*
2261 * If a nextgroup was found: Use it, and continue looking for
2262 * contained matches.
2263 * If a nextgroup was not found: Continue looking for a normal
2264 * match.
2265 * When did set current_next_list for a zero-width item and no
2266 * match was found don't loop (would get stuck).
2267 */
2268 current_next_list = NULL;
2269 next_match_idx = -1;
2270 if (!zero_width_next_list)
2271 found_match = TRUE;
2272 }
2273
2274 } while (found_match);
2275
2276 /*
2277 * Use attributes from the current state, if within its highlighting.
2278 * If not, use attributes from the current-but-one state, etc.
2279 */
2280 current_attr = 0;
2281#ifdef FEAT_EVAL
2282 current_id = 0;
2283 current_trans_id = 0;
2284#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002285#ifdef FEAT_CONCEAL
2286 current_flags = 0;
2287#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002288 if (cur_si != NULL)
2289 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002290#ifndef FEAT_EVAL
2291 int current_trans_id = 0;
2292#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002293 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2294 {
2295 sip = &CUR_STATE(idx);
2296 if ((current_lnum > sip->si_h_startpos.lnum
2297 || (current_lnum == sip->si_h_startpos.lnum
2298 && current_col >= sip->si_h_startpos.col))
2299 && (sip->si_h_endpos.lnum == 0
2300 || current_lnum < sip->si_h_endpos.lnum
2301 || (current_lnum == sip->si_h_endpos.lnum
2302 && current_col < sip->si_h_endpos.col)))
2303 {
2304 current_attr = sip->si_attr;
2305#ifdef FEAT_EVAL
2306 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002307#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002308 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002309#ifdef FEAT_CONCEAL
2310 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002311 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002312 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002313#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002314 break;
2315 }
2316 }
2317
Bram Moolenaar217ad922005-03-20 22:37:15 +00002318 if (can_spell != NULL)
2319 {
2320 struct sp_syn sps;
2321
2322 /*
2323 * set "can_spell" to TRUE if spell checking is supposed to be
2324 * done in the current item.
2325 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002326 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002327 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002328 /* There is no @Spell cluster: Do spelling for items without
2329 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002330 if (syn_block->b_nospell_cluster_id == 0
2331 || current_trans_id == 0)
2332 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002333 else
2334 {
2335 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002336 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002337 sps.cont_in_list = NULL;
2338 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2339 }
2340 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002341 else
2342 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002343 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002344 * the @Spell cluster. But not when @NoSpell is also there.
2345 * At the toplevel only spell check when ":syn spell toplevel"
2346 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002347 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002348 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002349 else
2350 {
2351 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002352 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002353 sps.cont_in_list = NULL;
2354 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2355
Bram Moolenaar860cae12010-06-05 23:22:07 +02002356 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002357 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002358 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002359 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2360 *can_spell = FALSE;
2361 }
2362 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002363 }
2364 }
2365
2366
Bram Moolenaar071d4272004-06-13 20:20:40 +00002367 /*
2368 * Check for end of current state (and the states before it) at the
2369 * next column. Don't do this for syncing, because we would miss a
2370 * single character match.
2371 * First check if the current state ends at the current column. It
2372 * may be for an empty match and a containing item might end in the
2373 * current column.
2374 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002375 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002376 {
2377 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002378 if (current_state.ga_len > 0
2379 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380 {
2381 ++current_col;
2382 check_state_ends();
2383 --current_col;
2384 }
2385 }
2386 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002387 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002388 /* Default: Only do spelling when there is no @Spell cluster or when
2389 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002390 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2391 ? (syn_block->b_spell_cluster_id == 0)
2392 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002393
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002394 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395 if (current_next_list != NULL
2396 && syn_getcurline()[current_col + 1] == NUL
2397 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2398 current_next_list = NULL;
2399
2400 if (zero_width_next_ga.ga_len > 0)
2401 ga_clear(&zero_width_next_ga);
2402
2403 /* No longer need external matches. But keep next_match_extmatch. */
2404 unref_extmatch(re_extmatch_out);
2405 re_extmatch_out = NULL;
2406 unref_extmatch(cur_extmatch);
2407
2408 return current_attr;
2409}
2410
2411
2412/*
2413 * Check if we already matched pattern "idx" at the current column.
2414 */
2415 static int
2416did_match_already(idx, gap)
2417 int idx;
2418 garray_T *gap;
2419{
2420 int i;
2421
2422 for (i = current_state.ga_len; --i >= 0; )
2423 if (CUR_STATE(i).si_m_startcol == (int)current_col
2424 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2425 && CUR_STATE(i).si_idx == idx)
2426 return TRUE;
2427
2428 /* Zero-width matches with a nextgroup argument are not put on the syntax
2429 * stack, and can only be matched once anyway. */
2430 for (i = gap->ga_len; --i >= 0; )
2431 if (((int *)(gap->ga_data))[i] == idx)
2432 return TRUE;
2433
2434 return FALSE;
2435}
2436
2437/*
2438 * Push the next match onto the stack.
2439 */
2440 static stateitem_T *
2441push_next_match(cur_si)
2442 stateitem_T *cur_si;
2443{
2444 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002445#ifdef FEAT_CONCEAL
2446 int save_flags;
2447#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448
Bram Moolenaar860cae12010-06-05 23:22:07 +02002449 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450
2451 /*
2452 * Push the item in current_state stack;
2453 */
2454 if (push_current_state(next_match_idx) == OK)
2455 {
2456 /*
2457 * If it's a start-skip-end type that crosses lines, figure out how
2458 * much it continues in this line. Otherwise just fill in the length.
2459 */
2460 cur_si = &CUR_STATE(current_state.ga_len - 1);
2461 cur_si->si_h_startpos = next_match_h_startpos;
2462 cur_si->si_m_startcol = current_col;
2463 cur_si->si_m_lnum = current_lnum;
2464 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002465#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002466 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002467 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002468 if (current_state.ga_len > 1)
2469 cur_si->si_flags |=
2470 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2471#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002472 cur_si->si_next_list = spp->sp_next_list;
2473 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2474 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2475 {
2476 /* Try to find the end pattern in the current line */
2477 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2478 check_keepend();
2479 }
2480 else
2481 {
2482 cur_si->si_m_endpos = next_match_m_endpos;
2483 cur_si->si_h_endpos = next_match_h_endpos;
2484 cur_si->si_ends = TRUE;
2485 cur_si->si_flags |= next_match_flags;
2486 cur_si->si_eoe_pos = next_match_eoe_pos;
2487 cur_si->si_end_idx = next_match_end_idx;
2488 }
2489 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2490 keepend_level = current_state.ga_len - 1;
2491 check_keepend();
2492 update_si_attr(current_state.ga_len - 1);
2493
Bram Moolenaar860cae12010-06-05 23:22:07 +02002494#ifdef FEAT_CONCEAL
2495 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002497 /*
2498 * If the start pattern has another highlight group, push another item
2499 * on the stack for the start pattern.
2500 */
2501 if ( spp->sp_type == SPTYPE_START
2502 && spp->sp_syn_match_id != 0
2503 && push_current_state(next_match_idx) == OK)
2504 {
2505 cur_si = &CUR_STATE(current_state.ga_len - 1);
2506 cur_si->si_h_startpos = next_match_h_startpos;
2507 cur_si->si_m_startcol = current_col;
2508 cur_si->si_m_lnum = current_lnum;
2509 cur_si->si_m_endpos = next_match_eos_pos;
2510 cur_si->si_h_endpos = next_match_eos_pos;
2511 cur_si->si_ends = TRUE;
2512 cur_si->si_end_idx = 0;
2513 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002514#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002515 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002516 cur_si->si_flags |= save_flags;
2517 if (cur_si->si_flags & HL_CONCEALENDS)
2518 cur_si->si_flags |= HL_CONCEAL;
2519#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002520 cur_si->si_next_list = NULL;
2521 check_keepend();
2522 update_si_attr(current_state.ga_len - 1);
2523 }
2524 }
2525
2526 next_match_idx = -1; /* try other match next time */
2527
2528 return cur_si;
2529}
2530
2531/*
2532 * Check for end of current state (and the states before it).
2533 */
2534 static void
2535check_state_ends()
2536{
2537 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002538 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002539
2540 cur_si = &CUR_STATE(current_state.ga_len - 1);
2541 for (;;)
2542 {
2543 if (cur_si->si_ends
2544 && (cur_si->si_m_endpos.lnum < current_lnum
2545 || (cur_si->si_m_endpos.lnum == current_lnum
2546 && cur_si->si_m_endpos.col <= current_col)))
2547 {
2548 /*
2549 * If there is an end pattern group ID, highlight the end pattern
2550 * now. No need to pop the current item from the stack.
2551 * Only do this if the end pattern continues beyond the current
2552 * position.
2553 */
2554 if (cur_si->si_end_idx
2555 && (cur_si->si_eoe_pos.lnum > current_lnum
2556 || (cur_si->si_eoe_pos.lnum == current_lnum
2557 && cur_si->si_eoe_pos.col > current_col)))
2558 {
2559 cur_si->si_idx = cur_si->si_end_idx;
2560 cur_si->si_end_idx = 0;
2561 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2562 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2563 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002564#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002565 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002566 if (cur_si->si_flags & HL_CONCEALENDS)
2567 cur_si->si_flags |= HL_CONCEAL;
2568#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002570
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002571 /* nextgroup= should not match in the end pattern */
2572 current_next_list = NULL;
2573
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002574 /* what matches next may be different now, clear it */
2575 next_match_idx = 0;
2576 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577 break;
2578 }
2579 else
2580 {
2581 /* handle next_list, unless at end of line and no "skipnl" or
2582 * "skipempty" */
2583 current_next_list = cur_si->si_next_list;
2584 current_next_flags = cur_si->si_flags;
2585 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2586 && syn_getcurline()[current_col] == NUL)
2587 current_next_list = NULL;
2588
2589 /* When the ended item has "extend", another item with
2590 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002591 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002592
2593 pop_current_state();
2594
2595 if (current_state.ga_len == 0)
2596 break;
2597
Bram Moolenaar81993f42008-01-11 20:27:45 +00002598 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002599 {
2600 syn_update_ends(FALSE);
2601 if (current_state.ga_len == 0)
2602 break;
2603 }
2604
2605 cur_si = &CUR_STATE(current_state.ga_len - 1);
2606
2607 /*
2608 * Only for a region the search for the end continues after
2609 * the end of the contained item. If the contained match
2610 * included the end-of-line, break here, the region continues.
2611 * Don't do this when:
2612 * - "keepend" is used for the contained item
2613 * - not at the end of the line (could be end="x$"me=e-1).
2614 * - "excludenl" is used (HL_HAS_EOL won't be set)
2615 */
2616 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002617 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002618 == SPTYPE_START
2619 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2620 {
2621 update_si_end(cur_si, (int)current_col, TRUE);
2622 check_keepend();
2623 if ((current_next_flags & HL_HAS_EOL)
2624 && keepend_level < 0
2625 && syn_getcurline()[current_col] == NUL)
2626 break;
2627 }
2628 }
2629 }
2630 else
2631 break;
2632 }
2633}
2634
2635/*
2636 * Update an entry in the current_state stack for a match or region. This
2637 * fills in si_attr, si_next_list and si_cont_list.
2638 */
2639 static void
2640update_si_attr(idx)
2641 int idx;
2642{
2643 stateitem_T *sip = &CUR_STATE(idx);
2644 synpat_T *spp;
2645
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002646 /* This should not happen... */
2647 if (sip->si_idx < 0)
2648 return;
2649
Bram Moolenaar860cae12010-06-05 23:22:07 +02002650 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002651 if (sip->si_flags & HL_MATCH)
2652 sip->si_id = spp->sp_syn_match_id;
2653 else
2654 sip->si_id = spp->sp_syn.id;
2655 sip->si_attr = syn_id2attr(sip->si_id);
2656 sip->si_trans_id = sip->si_id;
2657 if (sip->si_flags & HL_MATCH)
2658 sip->si_cont_list = NULL;
2659 else
2660 sip->si_cont_list = spp->sp_cont_list;
2661
2662 /*
2663 * For transparent items, take attr from outer item.
2664 * Also take cont_list, if there is none.
2665 * Don't do this for the matchgroup of a start or end pattern.
2666 */
2667 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2668 {
2669 if (idx == 0)
2670 {
2671 sip->si_attr = 0;
2672 sip->si_trans_id = 0;
2673 if (sip->si_cont_list == NULL)
2674 sip->si_cont_list = ID_LIST_ALL;
2675 }
2676 else
2677 {
2678 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2679 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002680 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2681 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002682 if (sip->si_cont_list == NULL)
2683 {
2684 sip->si_flags |= HL_TRANS_CONT;
2685 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2686 }
2687 }
2688 }
2689}
2690
2691/*
2692 * Check the current stack for patterns with "keepend" flag.
2693 * Propagate the match-end to contained items, until a "skipend" item is found.
2694 */
2695 static void
2696check_keepend()
2697{
2698 int i;
2699 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002700 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 stateitem_T *sip;
2702
2703 /*
2704 * This check can consume a lot of time; only do it from the level where
2705 * there really is a keepend.
2706 */
2707 if (keepend_level < 0)
2708 return;
2709
2710 /*
2711 * Find the last index of an "extend" item. "keepend" items before that
2712 * won't do anything. If there is no "extend" item "i" will be
2713 * "keepend_level" and all "keepend" items will work normally.
2714 */
2715 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2716 if (CUR_STATE(i).si_flags & HL_EXTEND)
2717 break;
2718
2719 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002720 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002721 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002722 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 for ( ; i < current_state.ga_len; ++i)
2724 {
2725 sip = &CUR_STATE(i);
2726 if (maxpos.lnum != 0)
2727 {
2728 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002729 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002730 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2731 sip->si_ends = TRUE;
2732 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002733 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2734 {
2735 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 || maxpos.lnum > sip->si_m_endpos.lnum
2737 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002738 && maxpos.col > sip->si_m_endpos.col))
2739 maxpos = sip->si_m_endpos;
2740 if (maxpos_h.lnum == 0
2741 || maxpos_h.lnum > sip->si_h_endpos.lnum
2742 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2743 && maxpos_h.col > sip->si_h_endpos.col))
2744 maxpos_h = sip->si_h_endpos;
2745 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002746 }
2747}
2748
2749/*
2750 * Update an entry in the current_state stack for a start-skip-end pattern.
2751 * This finds the end of the current item, if it's in the current line.
2752 *
2753 * Return the flags for the matched END.
2754 */
2755 static void
2756update_si_end(sip, startcol, force)
2757 stateitem_T *sip;
2758 int startcol; /* where to start searching for the end */
2759 int force; /* when TRUE overrule a previous end */
2760{
2761 lpos_T startpos;
2762 lpos_T endpos;
2763 lpos_T hl_endpos;
2764 lpos_T end_endpos;
2765 int end_idx;
2766
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002767 /* return quickly for a keyword */
2768 if (sip->si_idx < 0)
2769 return;
2770
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771 /* Don't update when it's already done. Can be a match of an end pattern
2772 * that started in a previous line. Watch out: can also be a "keepend"
2773 * from a containing item. */
2774 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2775 return;
2776
2777 /*
2778 * We need to find the end of the region. It may continue in the next
2779 * line.
2780 */
2781 end_idx = 0;
2782 startpos.lnum = current_lnum;
2783 startpos.col = startcol;
2784 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2785 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2786
2787 if (endpos.lnum == 0)
2788 {
2789 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002790 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002791 {
2792 /* a "oneline" never continues in the next line */
2793 sip->si_ends = TRUE;
2794 sip->si_m_endpos.lnum = current_lnum;
2795 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2796 }
2797 else
2798 {
2799 /* continues in the next line */
2800 sip->si_ends = FALSE;
2801 sip->si_m_endpos.lnum = 0;
2802 }
2803 sip->si_h_endpos = sip->si_m_endpos;
2804 }
2805 else
2806 {
2807 /* match within this line */
2808 sip->si_m_endpos = endpos;
2809 sip->si_h_endpos = hl_endpos;
2810 sip->si_eoe_pos = end_endpos;
2811 sip->si_ends = TRUE;
2812 sip->si_end_idx = end_idx;
2813 }
2814}
2815
2816/*
2817 * Add a new state to the current state stack.
2818 * It is cleared and the index set to "idx".
2819 * Return FAIL if it's not possible (out of memory).
2820 */
2821 static int
2822push_current_state(idx)
2823 int idx;
2824{
2825 if (ga_grow(&current_state, 1) == FAIL)
2826 return FAIL;
2827 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2828 CUR_STATE(current_state.ga_len).si_idx = idx;
2829 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002830 return OK;
2831}
2832
2833/*
2834 * Remove a state from the current_state stack.
2835 */
2836 static void
2837pop_current_state()
2838{
2839 if (current_state.ga_len)
2840 {
2841 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2842 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002843 }
2844 /* after the end of a pattern, try matching a keyword or pattern */
2845 next_match_idx = -1;
2846
2847 /* if first state with "keepend" is popped, reset keepend_level */
2848 if (keepend_level >= current_state.ga_len)
2849 keepend_level = -1;
2850}
2851
2852/*
2853 * Find the end of a start/skip/end syntax region after "startpos".
2854 * Only checks one line.
2855 * Also handles a match item that continued from a previous line.
2856 * If not found, the syntax item continues in the next line. m_endpos->lnum
2857 * will be 0.
2858 * If found, the end of the region and the end of the highlighting is
2859 * computed.
2860 */
2861 static void
2862find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2863 end_idx, start_ext)
2864 int idx; /* index of the pattern */
2865 lpos_T *startpos; /* where to start looking for an END match */
2866 lpos_T *m_endpos; /* return: end of match */
2867 lpos_T *hl_endpos; /* return: end of highlighting */
2868 long *flagsp; /* return: flags of matching END */
2869 lpos_T *end_endpos; /* return: end of end pattern match */
2870 int *end_idx; /* return: group ID for end pat. match, or 0 */
2871 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2872{
2873 colnr_T matchcol;
2874 synpat_T *spp, *spp_skip;
2875 int start_idx;
2876 int best_idx;
2877 regmmatch_T regmatch;
2878 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2879 lpos_T pos;
2880 char_u *line;
2881 int had_match = FALSE;
2882
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002883 /* just in case we are invoked for a keyword */
2884 if (idx < 0)
2885 return;
2886
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 /*
2888 * Check for being called with a START pattern.
2889 * Can happen with a match that continues to the next line, because it
2890 * contained a region.
2891 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002892 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 if (spp->sp_type != SPTYPE_START)
2894 {
2895 *hl_endpos = *startpos;
2896 return;
2897 }
2898
2899 /*
2900 * Find the SKIP or first END pattern after the last START pattern.
2901 */
2902 for (;;)
2903 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002904 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 if (spp->sp_type != SPTYPE_START)
2906 break;
2907 ++idx;
2908 }
2909
2910 /*
2911 * Lookup the SKIP pattern (if present)
2912 */
2913 if (spp->sp_type == SPTYPE_SKIP)
2914 {
2915 spp_skip = spp;
2916 ++idx;
2917 }
2918 else
2919 spp_skip = NULL;
2920
2921 /* Setup external matches for syn_regexec(). */
2922 unref_extmatch(re_extmatch_in);
2923 re_extmatch_in = ref_extmatch(start_ext);
2924
2925 matchcol = startpos->col; /* start looking for a match at sstart */
2926 start_idx = idx; /* remember the first END pattern. */
2927 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2928 for (;;)
2929 {
2930 /*
2931 * Find end pattern that matches first after "matchcol".
2932 */
2933 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002934 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002935 {
2936 int lc_col = matchcol;
2937
Bram Moolenaar860cae12010-06-05 23:22:07 +02002938 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2940 break;
2941 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2942 if (lc_col < 0)
2943 lc_col = 0;
2944
2945 regmatch.rmm_ic = spp->sp_ic;
2946 regmatch.regprog = spp->sp_prog;
2947 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2948 {
2949 if (best_idx == -1 || regmatch.startpos[0].col
2950 < best_regmatch.startpos[0].col)
2951 {
2952 best_idx = idx;
2953 best_regmatch.startpos[0] = regmatch.startpos[0];
2954 best_regmatch.endpos[0] = regmatch.endpos[0];
2955 }
2956 }
2957 }
2958
2959 /*
2960 * If all end patterns have been tried, and there is no match, the
2961 * item continues until end-of-line.
2962 */
2963 if (best_idx == -1)
2964 break;
2965
2966 /*
2967 * If the skip pattern matches before the end pattern,
2968 * continue searching after the skip pattern.
2969 */
2970 if (spp_skip != NULL)
2971 {
2972 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2973
2974 if (lc_col < 0)
2975 lc_col = 0;
2976 regmatch.rmm_ic = spp_skip->sp_ic;
2977 regmatch.regprog = spp_skip->sp_prog;
2978 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2979 && regmatch.startpos[0].col
2980 <= best_regmatch.startpos[0].col)
2981 {
2982 /* Add offset to skip pattern match */
2983 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2984
2985 /* If the skip pattern goes on to the next line, there is no
2986 * match with an end pattern in this line. */
2987 if (pos.lnum > startpos->lnum)
2988 break;
2989
2990 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2991
2992 /* take care of an empty match or negative offset */
2993 if (pos.col <= matchcol)
2994 ++matchcol;
2995 else if (pos.col <= regmatch.endpos[0].col)
2996 matchcol = pos.col;
2997 else
2998 /* Be careful not to jump over the NUL at the end-of-line */
2999 for (matchcol = regmatch.endpos[0].col;
3000 line[matchcol] != NUL && matchcol < pos.col;
3001 ++matchcol)
3002 ;
3003
3004 /* if the skip pattern includes end-of-line, break here */
3005 if (line[matchcol] == NUL)
3006 break;
3007
3008 continue; /* start with first end pattern again */
3009 }
3010 }
3011
3012 /*
3013 * Match from start pattern to end pattern.
3014 * Correct for match and highlight offset of end pattern.
3015 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003016 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003017 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3018 /* can't end before the start */
3019 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3020 m_endpos->col = startpos->col;
3021
3022 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3023 /* can't end before the start */
3024 if (end_endpos->lnum == startpos->lnum
3025 && end_endpos->col < startpos->col)
3026 end_endpos->col = startpos->col;
3027 /* can't end after the match */
3028 limit_pos(end_endpos, m_endpos);
3029
3030 /*
3031 * If the end group is highlighted differently, adjust the pointers.
3032 */
3033 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3034 {
3035 *end_idx = best_idx;
3036 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3037 {
3038 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3039 hl_endpos->col = best_regmatch.endpos[0].col;
3040 }
3041 else
3042 {
3043 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3044 hl_endpos->col = best_regmatch.startpos[0].col;
3045 }
3046 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3047
3048 /* can't end before the start */
3049 if (hl_endpos->lnum == startpos->lnum
3050 && hl_endpos->col < startpos->col)
3051 hl_endpos->col = startpos->col;
3052 limit_pos(hl_endpos, m_endpos);
3053
3054 /* now the match ends where the highlighting ends, it is turned
3055 * into the matchgroup for the end */
3056 *m_endpos = *hl_endpos;
3057 }
3058 else
3059 {
3060 *end_idx = 0;
3061 *hl_endpos = *end_endpos;
3062 }
3063
3064 *flagsp = spp->sp_flags;
3065
3066 had_match = TRUE;
3067 break;
3068 }
3069
3070 /* no match for an END pattern in this line */
3071 if (!had_match)
3072 m_endpos->lnum = 0;
3073
3074 /* Remove external matches. */
3075 unref_extmatch(re_extmatch_in);
3076 re_extmatch_in = NULL;
3077}
3078
3079/*
3080 * Limit "pos" not to be after "limit".
3081 */
3082 static void
3083limit_pos(pos, limit)
3084 lpos_T *pos;
3085 lpos_T *limit;
3086{
3087 if (pos->lnum > limit->lnum)
3088 *pos = *limit;
3089 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3090 pos->col = limit->col;
3091}
3092
3093/*
3094 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3095 */
3096 static void
3097limit_pos_zero(pos, limit)
3098 lpos_T *pos;
3099 lpos_T *limit;
3100{
3101 if (pos->lnum == 0)
3102 *pos = *limit;
3103 else
3104 limit_pos(pos, limit);
3105}
3106
3107/*
3108 * Add offset to matched text for end of match or highlight.
3109 */
3110 static void
3111syn_add_end_off(result, regmatch, spp, idx, extra)
3112 lpos_T *result; /* returned position */
3113 regmmatch_T *regmatch; /* start/end of match */
3114 synpat_T *spp; /* matched pattern */
3115 int idx; /* index of offset */
3116 int extra; /* extra chars for offset to start */
3117{
3118 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003119 int off;
3120 char_u *base;
3121 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003122
3123 if (spp->sp_off_flags & (1 << idx))
3124 {
3125 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003126 col = regmatch->startpos[0].col;
3127 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003128 }
3129 else
3130 {
3131 result->lnum = regmatch->endpos[0].lnum;
3132 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003133 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003134 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003135 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3136 * is a matchgroup. Watch out for match with last NL in the buffer. */
3137 if (result->lnum > syn_buf->b_ml.ml_line_count)
3138 col = 0;
3139 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003140 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003141 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3142 p = base + col;
3143 if (off > 0)
3144 {
3145 while (off-- > 0 && *p != NUL)
3146 mb_ptr_adv(p);
3147 }
3148 else if (off < 0)
3149 {
3150 while (off++ < 0 && base < p)
3151 mb_ptr_back(base, p);
3152 }
3153 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003154 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003155 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156}
3157
3158/*
3159 * Add offset to matched text for start of match or highlight.
3160 * Avoid resulting column to become negative.
3161 */
3162 static void
3163syn_add_start_off(result, regmatch, spp, idx, extra)
3164 lpos_T *result; /* returned position */
3165 regmmatch_T *regmatch; /* start/end of match */
3166 synpat_T *spp;
3167 int idx;
3168 int extra; /* extra chars for offset to end */
3169{
3170 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003171 int off;
3172 char_u *base;
3173 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174
3175 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3176 {
3177 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003178 col = regmatch->endpos[0].col;
3179 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003180 }
3181 else
3182 {
3183 result->lnum = regmatch->startpos[0].lnum;
3184 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003185 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003187 if (result->lnum > syn_buf->b_ml.ml_line_count)
3188 {
3189 /* a "\n" at the end of the pattern may take us below the last line */
3190 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003191 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003192 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003193 if (off != 0)
3194 {
3195 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3196 p = base + col;
3197 if (off > 0)
3198 {
3199 while (off-- && *p != NUL)
3200 mb_ptr_adv(p);
3201 }
3202 else if (off < 0)
3203 {
3204 while (off++ && base < p)
3205 mb_ptr_back(base, p);
3206 }
3207 col = (int)(p - base);
3208 }
3209 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210}
3211
3212/*
3213 * Get current line in syntax buffer.
3214 */
3215 static char_u *
3216syn_getcurline()
3217{
3218 return ml_get_buf(syn_buf, current_lnum, FALSE);
3219}
3220
3221/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003222 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 * Returns TRUE when there is a match.
3224 */
3225 static int
3226syn_regexec(rmp, lnum, col)
3227 regmmatch_T *rmp;
3228 linenr_T lnum;
3229 colnr_T col;
3230{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003231 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003232 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003233 {
3234 rmp->startpos[0].lnum += lnum;
3235 rmp->endpos[0].lnum += lnum;
3236 return TRUE;
3237 }
3238 return FALSE;
3239}
3240
3241/*
3242 * Check one position in a line for a matching keyword.
3243 * The caller must check if a keyword can start at startcol.
3244 * Return it's ID if found, 0 otherwise.
3245 */
3246 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003247check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003248 char_u *line;
3249 int startcol; /* position in line to check for keyword */
3250 int *endcolp; /* return: character after found keyword */
3251 long *flagsp; /* return: flags of matching keyword */
3252 short **next_listp; /* return: next_list of matching keyword */
3253 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003254 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003255{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003256 keyentry_T *kp;
3257 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003259 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003261 hashtab_T *ht;
3262 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003263
3264 /* Find first character after the keyword. First character was already
3265 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003266 kwp = line + startcol;
3267 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003268 do
3269 {
3270#ifdef FEAT_MBYTE
3271 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003272 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 else
3274#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003275 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003277 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278
Bram Moolenaardad6b692005-01-25 22:14:34 +00003279 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 return 0;
3281
3282 /*
3283 * Must make a copy of the keyword, so we can add a NUL and make it
3284 * lowercase.
3285 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003286 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003287
3288 /*
3289 * Try twice:
3290 * 1. matching case
3291 * 2. ignoring case
3292 */
3293 for (round = 1; round <= 2; ++round)
3294 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003295 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003296 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003298 if (round == 2) /* ignore case */
3299 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300
3301 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003302 * Find keywords that match. There can be several with different
3303 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003304 * When current_next_list is non-zero accept only that group, otherwise:
3305 * Accept a not-contained keyword at toplevel.
3306 * Accept a keyword at other levels only if it is in the contains list.
3307 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003308 hi = hash_find(ht, keyword);
3309 if (!HASHITEM_EMPTY(hi))
3310 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003311 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003312 if (current_next_list != 0
3313 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3314 : (cur_si == NULL
3315 ? !(kp->flags & HL_CONTAINED)
3316 : in_id_list(cur_si, cur_si->si_cont_list,
3317 &kp->k_syn, kp->flags & HL_CONTAINED)))
3318 {
3319 *endcolp = startcol + kwlen;
3320 *flagsp = kp->flags;
3321 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003322#ifdef FEAT_CONCEAL
3323 *ccharp = kp->k_char;
3324#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003325 return kp->k_syn.id;
3326 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003327 }
3328 }
3329 return 0;
3330}
3331
3332/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003333 * Handle ":syntax conceal" command.
3334 */
3335 static void
3336syn_cmd_conceal(eap, syncing)
3337 exarg_T *eap UNUSED;
3338 int syncing UNUSED;
3339{
3340#ifdef FEAT_CONCEAL
3341 char_u *arg = eap->arg;
3342 char_u *next;
3343
3344 eap->nextcmd = find_nextcmd(arg);
3345 if (eap->skip)
3346 return;
3347
3348 next = skiptowhite(arg);
3349 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3350 curwin->w_s->b_syn_conceal = TRUE;
3351 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3352 curwin->w_s->b_syn_conceal = FALSE;
3353 else
3354 EMSG2(_("E390: Illegal argument: %s"), arg);
3355#endif
3356}
3357
3358/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 * Handle ":syntax case" command.
3360 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 static void
3362syn_cmd_case(eap, syncing)
3363 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003364 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365{
3366 char_u *arg = eap->arg;
3367 char_u *next;
3368
3369 eap->nextcmd = find_nextcmd(arg);
3370 if (eap->skip)
3371 return;
3372
3373 next = skiptowhite(arg);
3374 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003375 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003376 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003377 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378 else
3379 EMSG2(_("E390: Illegal argument: %s"), arg);
3380}
3381
3382/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003383 * Handle ":syntax spell" command.
3384 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003385 static void
3386syn_cmd_spell(eap, syncing)
3387 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003388 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003389{
3390 char_u *arg = eap->arg;
3391 char_u *next;
3392
3393 eap->nextcmd = find_nextcmd(arg);
3394 if (eap->skip)
3395 return;
3396
3397 next = skiptowhite(arg);
3398 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003399 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003400 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003401 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003402 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003403 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003404 else
3405 EMSG2(_("E390: Illegal argument: %s"), arg);
3406}
3407
3408/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409 * Clear all syntax info for one buffer.
3410 */
3411 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003412syntax_clear(block)
3413 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414{
3415 int i;
3416
Bram Moolenaar860cae12010-06-05 23:22:07 +02003417 block->b_syn_error = FALSE; /* clear previous error */
3418 block->b_syn_ic = FALSE; /* Use case, by default */
3419 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3420 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003421
3422 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423 clear_keywtab(&block->b_keywtab);
3424 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003425
3426 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003427 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3428 syn_clear_pattern(block, i);
3429 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430
3431 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003432 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3433 syn_clear_cluster(block, i);
3434 ga_clear(&block->b_syn_clusters);
3435 block->b_spell_cluster_id = 0;
3436 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003437
Bram Moolenaar860cae12010-06-05 23:22:07 +02003438 block->b_syn_sync_flags = 0;
3439 block->b_syn_sync_minlines = 0;
3440 block->b_syn_sync_maxlines = 0;
3441 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003442
Bram Moolenaar860cae12010-06-05 23:22:07 +02003443 vim_free(block->b_syn_linecont_prog);
3444 block->b_syn_linecont_prog = NULL;
3445 vim_free(block->b_syn_linecont_pat);
3446 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003447#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003448 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449#endif
3450
3451 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003452 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003454
3455 /* Reset the counter for ":syn include" */
3456 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003457}
3458
3459/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003460 * Get rid of ownsyntax for window "wp".
3461 */
3462 void
3463reset_synblock(wp)
3464 win_T *wp;
3465{
3466 if (wp->w_s != &wp->w_buffer->b_s)
3467 {
3468 syntax_clear(wp->w_s);
3469 vim_free(wp->w_s);
3470 wp->w_s = &wp->w_buffer->b_s;
3471 }
3472}
3473
3474/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003475 * Clear syncing info for one buffer.
3476 */
3477 static void
3478syntax_sync_clear()
3479{
3480 int i;
3481
3482 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003483 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3484 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3485 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486
Bram Moolenaar860cae12010-06-05 23:22:07 +02003487 curwin->w_s->b_syn_sync_flags = 0;
3488 curwin->w_s->b_syn_sync_minlines = 0;
3489 curwin->w_s->b_syn_sync_maxlines = 0;
3490 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003491
Bram Moolenaar860cae12010-06-05 23:22:07 +02003492 vim_free(curwin->w_s->b_syn_linecont_prog);
3493 curwin->w_s->b_syn_linecont_prog = NULL;
3494 vim_free(curwin->w_s->b_syn_linecont_pat);
3495 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496
Bram Moolenaar860cae12010-06-05 23:22:07 +02003497 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498}
3499
3500/*
3501 * Remove one pattern from the buffer's pattern list.
3502 */
3503 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003504syn_remove_pattern(block, idx)
3505 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003506 int idx;
3507{
3508 synpat_T *spp;
3509
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511#ifdef FEAT_FOLDING
3512 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003513 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003514#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003515 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003516 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003517 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3518 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519}
3520
3521/*
3522 * Clear and free one syntax pattern. When clearing all, must be called from
3523 * last to first!
3524 */
3525 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003526syn_clear_pattern(block, i)
3527 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003528 int i;
3529{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003530 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3531 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003533 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003535 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3536 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3537 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003538 }
3539}
3540
3541/*
3542 * Clear and free one syntax cluster.
3543 */
3544 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003545syn_clear_cluster(block, i)
3546 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003547 int i;
3548{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003549 vim_free(SYN_CLSTR(block)[i].scl_name);
3550 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3551 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003552}
3553
3554/*
3555 * Handle ":syntax clear" command.
3556 */
3557 static void
3558syn_cmd_clear(eap, syncing)
3559 exarg_T *eap;
3560 int syncing;
3561{
3562 char_u *arg = eap->arg;
3563 char_u *arg_end;
3564 int id;
3565
3566 eap->nextcmd = find_nextcmd(arg);
3567 if (eap->skip)
3568 return;
3569
3570 /*
3571 * We have to disable this within ":syn include @group filename",
3572 * because otherwise @group would get deleted.
3573 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3574 * clear".
3575 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003576 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003577 return;
3578
3579 if (ends_excmd(*arg))
3580 {
3581 /*
3582 * No argument: Clear all syntax items.
3583 */
3584 if (syncing)
3585 syntax_sync_clear();
3586 else
3587 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003588 syntax_clear(curwin->w_s);
3589 if (curwin->w_s == &curwin->w_buffer->b_s)
3590 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003591 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 }
3593 }
3594 else
3595 {
3596 /*
3597 * Clear the group IDs that are in the argument.
3598 */
3599 while (!ends_excmd(*arg))
3600 {
3601 arg_end = skiptowhite(arg);
3602 if (*arg == '@')
3603 {
3604 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3605 if (id == 0)
3606 {
3607 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3608 break;
3609 }
3610 else
3611 {
3612 /*
3613 * We can't physically delete a cluster without changing
3614 * the IDs of other clusters, so we do the next best thing
3615 * and make it empty.
3616 */
3617 short scl_id = id - SYNID_CLUSTER;
3618
Bram Moolenaar860cae12010-06-05 23:22:07 +02003619 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3620 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003621 }
3622 }
3623 else
3624 {
3625 id = syn_namen2id(arg, (int)(arg_end - arg));
3626 if (id == 0)
3627 {
3628 EMSG2(_(e_nogroup), arg);
3629 break;
3630 }
3631 else
3632 syn_clear_one(id, syncing);
3633 }
3634 arg = skipwhite(arg_end);
3635 }
3636 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003637 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003638 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639}
3640
3641/*
3642 * Clear one syntax group for the current buffer.
3643 */
3644 static void
3645syn_clear_one(id, syncing)
3646 int id;
3647 int syncing;
3648{
3649 synpat_T *spp;
3650 int idx;
3651
3652 /* Clear keywords only when not ":syn sync clear group-name" */
3653 if (!syncing)
3654 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003655 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3656 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003657 }
3658
3659 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003660 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003662 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3664 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003665 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003666 }
3667}
3668
3669/*
3670 * Handle ":syntax on" command.
3671 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672 static void
3673syn_cmd_on(eap, syncing)
3674 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003675 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676{
3677 syn_cmd_onoff(eap, "syntax");
3678}
3679
3680/*
3681 * Handle ":syntax enable" command.
3682 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003683 static void
3684syn_cmd_enable(eap, syncing)
3685 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003686 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003687{
3688 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3689 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003690 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003691}
3692
3693/*
3694 * Handle ":syntax reset" command.
3695 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003696 static void
3697syn_cmd_reset(eap, syncing)
3698 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003699 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003700{
3701 eap->nextcmd = check_nextcmd(eap->arg);
3702 if (!eap->skip)
3703 {
3704 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3705 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003706 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003707 }
3708}
3709
3710/*
3711 * Handle ":syntax manual" command.
3712 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713 static void
3714syn_cmd_manual(eap, syncing)
3715 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003716 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003717{
3718 syn_cmd_onoff(eap, "manual");
3719}
3720
3721/*
3722 * Handle ":syntax off" command.
3723 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003724 static void
3725syn_cmd_off(eap, syncing)
3726 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003727 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003728{
3729 syn_cmd_onoff(eap, "nosyntax");
3730}
3731
3732 static void
3733syn_cmd_onoff(eap, name)
3734 exarg_T *eap;
3735 char *name;
3736{
3737 char_u buf[100];
3738
3739 eap->nextcmd = check_nextcmd(eap->arg);
3740 if (!eap->skip)
3741 {
3742 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003743 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003744 do_cmdline_cmd(buf);
3745 }
3746}
3747
3748/*
3749 * Handle ":syntax [list]" command: list current syntax words.
3750 */
3751 static void
3752syn_cmd_list(eap, syncing)
3753 exarg_T *eap;
3754 int syncing; /* when TRUE: list syncing items */
3755{
3756 char_u *arg = eap->arg;
3757 int id;
3758 char_u *arg_end;
3759
3760 eap->nextcmd = find_nextcmd(arg);
3761 if (eap->skip)
3762 return;
3763
Bram Moolenaar860cae12010-06-05 23:22:07 +02003764 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765 {
3766 MSG(_("No Syntax items defined for this buffer"));
3767 return;
3768 }
3769
3770 if (syncing)
3771 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003772 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003773 {
3774 MSG_PUTS(_("syncing on C-style comments"));
3775 syn_lines_msg();
3776 syn_match_msg();
3777 return;
3778 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003779 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003781 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003782 MSG_PUTS(_("no syncing"));
3783 else
3784 {
3785 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003786 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003787 MSG_PUTS(_(" lines before top line"));
3788 syn_match_msg();
3789 }
3790 return;
3791 }
3792 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003793 if (curwin->w_s->b_syn_sync_minlines > 0
3794 || curwin->w_s->b_syn_sync_maxlines > 0
3795 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003796 {
3797 MSG_PUTS(_("\nsyncing on items"));
3798 syn_lines_msg();
3799 syn_match_msg();
3800 }
3801 }
3802 else
3803 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3804 if (ends_excmd(*arg))
3805 {
3806 /*
3807 * No argument: List all group IDs and all syntax clusters.
3808 */
3809 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3810 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003811 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003812 syn_list_cluster(id);
3813 }
3814 else
3815 {
3816 /*
3817 * List the group IDs and syntax clusters that are in the argument.
3818 */
3819 while (!ends_excmd(*arg) && !got_int)
3820 {
3821 arg_end = skiptowhite(arg);
3822 if (*arg == '@')
3823 {
3824 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3825 if (id == 0)
3826 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3827 else
3828 syn_list_cluster(id - SYNID_CLUSTER);
3829 }
3830 else
3831 {
3832 id = syn_namen2id(arg, (int)(arg_end - arg));
3833 if (id == 0)
3834 EMSG2(_(e_nogroup), arg);
3835 else
3836 syn_list_one(id, syncing, TRUE);
3837 }
3838 arg = skipwhite(arg_end);
3839 }
3840 }
3841 eap->nextcmd = check_nextcmd(arg);
3842}
3843
3844 static void
3845syn_lines_msg()
3846{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003847 if (curwin->w_s->b_syn_sync_maxlines > 0
3848 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 {
3850 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003851 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852 {
3853 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003854 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3855 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856 MSG_PUTS(", ");
3857 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003858 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 {
3860 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003861 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 }
3863 MSG_PUTS(_(" lines before top line"));
3864 }
3865}
3866
3867 static void
3868syn_match_msg()
3869{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003870 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871 {
3872 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003873 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003874 MSG_PUTS(_(" line breaks"));
3875 }
3876}
3877
3878static int last_matchgroup;
3879
3880struct name_list
3881{
3882 int flag;
3883 char *name;
3884};
3885
3886static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3887
3888/*
3889 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3890 */
3891 static void
3892syn_list_one(id, syncing, link_only)
3893 int id;
3894 int syncing; /* when TRUE: list syncing items */
3895 int link_only; /* when TRUE; list link-only too */
3896{
3897 int attr;
3898 int idx;
3899 int did_header = FALSE;
3900 synpat_T *spp;
3901 static struct name_list namelist1[] =
3902 {
3903 {HL_DISPLAY, "display"},
3904 {HL_CONTAINED, "contained"},
3905 {HL_ONELINE, "oneline"},
3906 {HL_KEEPEND, "keepend"},
3907 {HL_EXTEND, "extend"},
3908 {HL_EXCLUDENL, "excludenl"},
3909 {HL_TRANSP, "transparent"},
3910 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003911#ifdef FEAT_CONCEAL
3912 {HL_CONCEAL, "conceal"},
3913 {HL_CONCEALENDS, "concealends"},
3914#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003915 {0, NULL}
3916 };
3917 static struct name_list namelist2[] =
3918 {
3919 {HL_SKIPWHITE, "skipwhite"},
3920 {HL_SKIPNL, "skipnl"},
3921 {HL_SKIPEMPTY, "skipempty"},
3922 {0, NULL}
3923 };
3924
3925 attr = hl_attr(HLF_D); /* highlight like directories */
3926
3927 /* list the keywords for "id" */
3928 if (!syncing)
3929 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003930 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3931 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 did_header, attr);
3933 }
3934
3935 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003936 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003937 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003938 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003939 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3940 continue;
3941
3942 (void)syn_list_header(did_header, 999, id);
3943 did_header = TRUE;
3944 last_matchgroup = 0;
3945 if (spp->sp_type == SPTYPE_MATCH)
3946 {
3947 put_pattern("match", ' ', spp, attr);
3948 msg_putchar(' ');
3949 }
3950 else if (spp->sp_type == SPTYPE_START)
3951 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003952 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3953 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3954 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3955 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3956 while (idx < curwin->w_s->b_syn_patterns.ga_len
3957 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3958 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 --idx;
3960 msg_putchar(' ');
3961 }
3962 syn_list_flags(namelist1, spp->sp_flags, attr);
3963
3964 if (spp->sp_cont_list != NULL)
3965 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3966
3967 if (spp->sp_syn.cont_in_list != NULL)
3968 put_id_list((char_u *)"containedin",
3969 spp->sp_syn.cont_in_list, attr);
3970
3971 if (spp->sp_next_list != NULL)
3972 {
3973 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3974 syn_list_flags(namelist2, spp->sp_flags, attr);
3975 }
3976 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3977 {
3978 if (spp->sp_flags & HL_SYNC_HERE)
3979 msg_puts_attr((char_u *)"grouphere", attr);
3980 else
3981 msg_puts_attr((char_u *)"groupthere", attr);
3982 msg_putchar(' ');
3983 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003984 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3986 else
3987 MSG_PUTS("NONE");
3988 msg_putchar(' ');
3989 }
3990 }
3991
3992 /* list the link, if there is one */
3993 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3994 {
3995 (void)syn_list_header(did_header, 999, id);
3996 msg_puts_attr((char_u *)"links to", attr);
3997 msg_putchar(' ');
3998 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3999 }
4000}
4001
4002 static void
4003syn_list_flags(nl, flags, attr)
4004 struct name_list *nl;
4005 int flags;
4006 int attr;
4007{
4008 int i;
4009
4010 for (i = 0; nl[i].flag != 0; ++i)
4011 if (flags & nl[i].flag)
4012 {
4013 msg_puts_attr((char_u *)nl[i].name, attr);
4014 msg_putchar(' ');
4015 }
4016}
4017
4018/*
4019 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4020 */
4021 static void
4022syn_list_cluster(id)
4023 int id;
4024{
4025 int endcol = 15;
4026
4027 /* slight hack: roughly duplicate the guts of syn_list_header() */
4028 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004029 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004030
4031 if (msg_col >= endcol) /* output at least one space */
4032 endcol = msg_col + 1;
4033 if (Columns <= endcol) /* avoid hang for tiny window */
4034 endcol = Columns - 1;
4035
4036 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004037 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004039 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 hl_attr(HLF_D));
4041 }
4042 else
4043 {
4044 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4045 msg_puts((char_u *)"=NONE");
4046 }
4047}
4048
4049 static void
4050put_id_list(name, list, attr)
4051 char_u *name;
4052 short *list;
4053 int attr;
4054{
4055 short *p;
4056
4057 msg_puts_attr(name, attr);
4058 msg_putchar('=');
4059 for (p = list; *p; ++p)
4060 {
4061 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4062 {
4063 if (p[1])
4064 MSG_PUTS("ALLBUT");
4065 else
4066 MSG_PUTS("ALL");
4067 }
4068 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4069 {
4070 MSG_PUTS("TOP");
4071 }
4072 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4073 {
4074 MSG_PUTS("CONTAINED");
4075 }
4076 else if (*p >= SYNID_CLUSTER)
4077 {
4078 short scl_id = *p - SYNID_CLUSTER;
4079
4080 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004081 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004082 }
4083 else
4084 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4085 if (p[1])
4086 msg_putchar(',');
4087 }
4088 msg_putchar(' ');
4089}
4090
4091 static void
4092put_pattern(s, c, spp, attr)
4093 char *s;
4094 int c;
4095 synpat_T *spp;
4096 int attr;
4097{
4098 long n;
4099 int mask;
4100 int first;
4101 static char *sepchars = "/+=-#@\"|'^&";
4102 int i;
4103
4104 /* May have to write "matchgroup=group" */
4105 if (last_matchgroup != spp->sp_syn_match_id)
4106 {
4107 last_matchgroup = spp->sp_syn_match_id;
4108 msg_puts_attr((char_u *)"matchgroup", attr);
4109 msg_putchar('=');
4110 if (last_matchgroup == 0)
4111 msg_outtrans((char_u *)"NONE");
4112 else
4113 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4114 msg_putchar(' ');
4115 }
4116
4117 /* output the name of the pattern and an '=' or ' ' */
4118 msg_puts_attr((char_u *)s, attr);
4119 msg_putchar(c);
4120
4121 /* output the pattern, in between a char that is not in the pattern */
4122 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4123 if (sepchars[++i] == NUL)
4124 {
4125 i = 0; /* no good char found, just use the first one */
4126 break;
4127 }
4128 msg_putchar(sepchars[i]);
4129 msg_outtrans(spp->sp_pattern);
4130 msg_putchar(sepchars[i]);
4131
4132 /* output any pattern options */
4133 first = TRUE;
4134 for (i = 0; i < SPO_COUNT; ++i)
4135 {
4136 mask = (1 << i);
4137 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4138 {
4139 if (!first)
4140 msg_putchar(','); /* separate with commas */
4141 msg_puts((char_u *)spo_name_tab[i]);
4142 n = spp->sp_offsets[i];
4143 if (i != SPO_LC_OFF)
4144 {
4145 if (spp->sp_off_flags & mask)
4146 msg_putchar('s');
4147 else
4148 msg_putchar('e');
4149 if (n > 0)
4150 msg_putchar('+');
4151 }
4152 if (n || i == SPO_LC_OFF)
4153 msg_outnum(n);
4154 first = FALSE;
4155 }
4156 }
4157 msg_putchar(' ');
4158}
4159
4160/*
4161 * List or clear the keywords for one syntax group.
4162 * Return TRUE if the header has been printed.
4163 */
4164 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004165syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168 int did_header; /* header has already been printed */
4169 int attr;
4170{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004172 hashitem_T *hi;
4173 keyentry_T *kp;
4174 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004175 int prev_contained = 0;
4176 short *prev_next_list = NULL;
4177 short *prev_cont_in_list = NULL;
4178 int prev_skipnl = 0;
4179 int prev_skipwhite = 0;
4180 int prev_skipempty = 0;
4181
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 /*
4183 * Unfortunately, this list of keywords is not sorted on alphabet but on
4184 * hash value...
4185 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004186 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004187 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004189 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004191 --todo;
4192 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004194 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004196 if (prev_contained != (kp->flags & HL_CONTAINED)
4197 || prev_skipnl != (kp->flags & HL_SKIPNL)
4198 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4199 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4200 || prev_cont_in_list != kp->k_syn.cont_in_list
4201 || prev_next_list != kp->next_list)
4202 outlen = 9999;
4203 else
4204 outlen = (int)STRLEN(kp->keyword);
4205 /* output "contained" and "nextgroup" on each line */
4206 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004208 prev_contained = 0;
4209 prev_next_list = NULL;
4210 prev_cont_in_list = NULL;
4211 prev_skipnl = 0;
4212 prev_skipwhite = 0;
4213 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004214 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004215 did_header = TRUE;
4216 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004218 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004220 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004222 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004224 put_id_list((char_u *)"containedin",
4225 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004227 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004229 if (kp->next_list != prev_next_list)
4230 {
4231 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4232 msg_putchar(' ');
4233 prev_next_list = kp->next_list;
4234 if (kp->flags & HL_SKIPNL)
4235 {
4236 msg_puts_attr((char_u *)"skipnl", attr);
4237 msg_putchar(' ');
4238 prev_skipnl = (kp->flags & HL_SKIPNL);
4239 }
4240 if (kp->flags & HL_SKIPWHITE)
4241 {
4242 msg_puts_attr((char_u *)"skipwhite", attr);
4243 msg_putchar(' ');
4244 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4245 }
4246 if (kp->flags & HL_SKIPEMPTY)
4247 {
4248 msg_puts_attr((char_u *)"skipempty", attr);
4249 msg_putchar(' ');
4250 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4251 }
4252 }
4253 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 }
4256 }
4257 }
4258
4259 return did_header;
4260}
4261
4262 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004265 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004267 hashitem_T *hi;
4268 keyentry_T *kp;
4269 keyentry_T *kp_prev;
4270 keyentry_T *kp_next;
4271 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004274 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 --todo;
4280 kp_prev = NULL;
4281 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004283 if (kp->k_syn.id == id)
4284 {
4285 kp_next = kp->ke_next;
4286 if (kp_prev == NULL)
4287 {
4288 if (kp_next == NULL)
4289 hash_remove(ht, hi);
4290 else
4291 hi->hi_key = KE2HIKEY(kp_next);
4292 }
4293 else
4294 kp_prev->ke_next = kp_next;
4295 vim_free(kp->next_list);
4296 vim_free(kp->k_syn.cont_in_list);
4297 vim_free(kp);
4298 kp = kp_next;
4299 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004301 {
4302 kp_prev = kp;
4303 kp = kp->ke_next;
4304 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004305 }
4306 }
4307 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004308 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309}
4310
4311/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004312 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 */
4314 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004315clear_keywtab(ht)
4316 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004317{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004318 hashitem_T *hi;
4319 int todo;
4320 keyentry_T *kp;
4321 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004322
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004323 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004324 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004325 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004326 if (!HASHITEM_EMPTY(hi))
4327 {
4328 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004329 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004331 kp_next = kp->ke_next;
4332 vim_free(kp->next_list);
4333 vim_free(kp->k_syn.cont_in_list);
4334 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004335 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 hash_clear(ht);
4339 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340}
4341
4342/*
4343 * Add a keyword to the list of keywords.
4344 */
4345 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004346add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004347 char_u *name; /* name of keyword */
4348 int id; /* group ID for this keyword */
4349 int flags; /* flags for this keyword */
4350 short *cont_in_list; /* containedin for this keyword */
4351 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004352 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004354 keyentry_T *kp;
4355 hashtab_T *ht;
4356 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004357 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004358 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004359 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360
Bram Moolenaar860cae12010-06-05 23:22:07 +02004361 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004362 name_ic = str_foldcase(name, (int)STRLEN(name),
4363 name_folded, MAXKEYWLEN + 1);
4364 else
4365 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004366 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4367 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370 kp->k_syn.id = id;
4371 kp->k_syn.inc_tag = current_syn_inc_tag;
4372 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004373 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004375 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004376 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004377 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378
Bram Moolenaar860cae12010-06-05 23:22:07 +02004379 if (curwin->w_s->b_syn_ic)
4380 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004382 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004383
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 hash = hash_hash(kp->keyword);
4385 hi = hash_lookup(ht, kp->keyword, hash);
4386 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 /* new keyword, add to hashtable */
4389 kp->ke_next = NULL;
4390 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004392 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 /* keyword already exists, prepend to list */
4395 kp->ke_next = HI2KE(hi);
4396 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398}
4399
4400/*
4401 * Get the start and end of the group name argument.
4402 * Return a pointer to the first argument.
4403 * Return NULL if the end of the command was found instead of further args.
4404 */
4405 static char_u *
4406get_group_name(arg, name_end)
4407 char_u *arg; /* start of the argument */
4408 char_u **name_end; /* pointer to end of the name */
4409{
4410 char_u *rest;
4411
4412 *name_end = skiptowhite(arg);
4413 rest = skipwhite(*name_end);
4414
4415 /*
4416 * Check if there are enough arguments. The first argument may be a
4417 * pattern, where '|' is allowed, so only check for NUL.
4418 */
4419 if (ends_excmd(*arg) || *rest == NUL)
4420 return NULL;
4421 return rest;
4422}
4423
4424/*
4425 * Check for syntax command option arguments.
4426 * This can be called at any place in the list of arguments, and just picks
4427 * out the arguments that are known. Can be called several times in a row to
4428 * collect all options in between other arguments.
4429 * Return a pointer to the next argument (which isn't an option).
4430 * Return NULL for any error;
4431 */
4432 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004433get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004434 char_u *arg; /* next argument to be checked */
4435 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004436 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438 char_u *gname_start, *gname;
4439 int syn_id;
4440 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004441 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 int i;
4443 int fidx;
4444 static struct flag
4445 {
4446 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004447 int argtype;
4448 int flags;
4449 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4450 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4451 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4452 {"eExXtTeEnNdD", 0, HL_EXTEND},
4453 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4454 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4455 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4456 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4457 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4458 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4459 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4460 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4461 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004462 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4463 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4464 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004465 {"cCoOnNtTaAiInNsS", 1, 0},
4466 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4467 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004469 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470
4471 if (arg == NULL) /* already detected error */
4472 return NULL;
4473
Bram Moolenaar860cae12010-06-05 23:22:07 +02004474#ifdef FEAT_CONCEAL
4475 if (curwin->w_s->b_syn_conceal)
4476 opt->flags |= HL_CONCEAL;
4477#endif
4478
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 for (;;)
4480 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004481 /*
4482 * This is used very often when a large number of keywords is defined.
4483 * Need to skip quickly when no option name is found.
4484 * Also avoid tolower(), it's slow.
4485 */
4486 if (strchr(first_letters, *arg) == NULL)
4487 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488
4489 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4490 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004491 p = flagtab[fidx].name;
4492 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4493 if (arg[len] != p[i] && arg[len] != p[i + 1])
4494 break;
4495 if (p[i] == NUL && (vim_iswhite(arg[len])
4496 || (flagtab[fidx].argtype > 0
4497 ? arg[len] == '='
4498 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004500 if (opt->keyword
4501 && (flagtab[fidx].flags == HL_DISPLAY
4502 || flagtab[fidx].flags == HL_FOLD
4503 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 /* treat "display", "fold" and "extend" as a keyword */
4505 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 break;
4507 }
4508 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004509 if (fidx < 0) /* no match found */
4510 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004512 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004514 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515 {
4516 EMSG(_("E395: contains argument not accepted here"));
4517 return NULL;
4518 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520 return NULL;
4521 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004522 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 return NULL;
4526 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004527 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004529 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 return NULL;
4531 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004532 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4533 {
4534#ifdef FEAT_MBYTE
4535 /* cchar=? */
4536 if (has_mbyte)
4537 {
4538# ifdef FEAT_CONCEAL
4539 *conceal_char = mb_ptr2char(arg + 6);
4540# endif
4541 arg += mb_ptr2len(arg + 6) - 1;
4542 }
4543 else
4544#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004545 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004546#ifdef FEAT_CONCEAL
4547 *conceal_char = arg[6];
4548#else
4549 ;
4550#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004551 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004552#ifdef FEAT_CONCEAL
4553 if (!vim_isprintc_strict(*conceal_char))
4554 {
4555 EMSG(_("E844: invalid cchar value"));
4556 return NULL;
4557 }
4558#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004559 arg = skipwhite(arg + 7);
4560 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004562 {
4563 opt->flags |= flagtab[fidx].flags;
4564 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004565
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004566 if (flagtab[fidx].flags == HL_SYNC_HERE
4567 || flagtab[fidx].flags == HL_SYNC_THERE)
4568 {
4569 if (opt->sync_idx == NULL)
4570 {
4571 EMSG(_("E393: group[t]here not accepted here"));
4572 return NULL;
4573 }
4574 gname_start = arg;
4575 arg = skiptowhite(arg);
4576 if (gname_start == arg)
4577 return NULL;
4578 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4579 if (gname == NULL)
4580 return NULL;
4581 if (STRCMP(gname, "NONE") == 0)
4582 *opt->sync_idx = NONE_IDX;
4583 else
4584 {
4585 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004586 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4587 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4588 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004589 {
4590 *opt->sync_idx = i;
4591 break;
4592 }
4593 if (i < 0)
4594 {
4595 EMSG2(_("E394: Didn't find region item for %s"), gname);
4596 vim_free(gname);
4597 return NULL;
4598 }
4599 }
4600
4601 vim_free(gname);
4602 arg = skipwhite(arg);
4603 }
4604#ifdef FEAT_FOLDING
4605 else if (flagtab[fidx].flags == HL_FOLD
4606 && foldmethodIsSyntax(curwin))
4607 /* Need to update folds later. */
4608 foldUpdateAll(curwin);
4609#endif
4610 }
4611 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612
4613 return arg;
4614}
4615
4616/*
4617 * Adjustments to syntax item when declared in a ":syn include"'d file.
4618 * Set the contained flag, and if the item is not already contained, add it
4619 * to the specified top-level group, if any.
4620 */
4621 static void
4622syn_incl_toplevel(id, flagsp)
4623 int id;
4624 int *flagsp;
4625{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004626 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004627 return;
4628 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004629 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004630 {
4631 /* We have to alloc this, because syn_combine_list() will free it. */
4632 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004633 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634
4635 if (grp_list != NULL)
4636 {
4637 grp_list[0] = id;
4638 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004639 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640 CLUSTER_ADD);
4641 }
4642 }
4643}
4644
4645/*
4646 * Handle ":syntax include [@{group-name}] filename" command.
4647 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 static void
4649syn_cmd_include(eap, syncing)
4650 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004651 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652{
4653 char_u *arg = eap->arg;
4654 int sgl_id = 1;
4655 char_u *group_name_end;
4656 char_u *rest;
4657 char_u *errormsg = NULL;
4658 int prev_toplvl_grp;
4659 int prev_syn_inc_tag;
4660 int source = FALSE;
4661
4662 eap->nextcmd = find_nextcmd(arg);
4663 if (eap->skip)
4664 return;
4665
4666 if (arg[0] == '@')
4667 {
4668 ++arg;
4669 rest = get_group_name(arg, &group_name_end);
4670 if (rest == NULL)
4671 {
4672 EMSG((char_u *)_("E397: Filename required"));
4673 return;
4674 }
4675 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004676 if (sgl_id == 0)
4677 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004678 /* separate_nextcmd() and expand_filename() depend on this */
4679 eap->arg = rest;
4680 }
4681
4682 /*
4683 * Everything that's left, up to the next command, should be the
4684 * filename to include.
4685 */
4686 eap->argt |= (XFILE | NOSPC);
4687 separate_nextcmd(eap);
4688 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4689 {
4690 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4691 * file. Need to expand the file name first. In other cases
4692 * ":runtime!" is used. */
4693 source = TRUE;
4694 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4695 {
4696 if (errormsg != NULL)
4697 EMSG(errormsg);
4698 return;
4699 }
4700 }
4701
4702 /*
4703 * Save and restore the existing top-level grouplist id and ":syn
4704 * include" tag around the actual inclusion.
4705 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004706 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4707 {
4708 EMSG((char_u *)_("E847: Too many syntax includes"));
4709 return;
4710 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004711 prev_syn_inc_tag = current_syn_inc_tag;
4712 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004713 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4714 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004715 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4716 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004717 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004718 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004719 current_syn_inc_tag = prev_syn_inc_tag;
4720}
4721
4722/*
4723 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4724 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725 static void
4726syn_cmd_keyword(eap, syncing)
4727 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004728 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729{
4730 char_u *arg = eap->arg;
4731 char_u *group_name_end;
4732 int syn_id;
4733 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004734 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004736 char_u *kw;
4737 syn_opt_arg_T syn_opt_arg;
4738 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004739 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740
4741 rest = get_group_name(arg, &group_name_end);
4742
4743 if (rest != NULL)
4744 {
4745 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004746 if (syn_id != 0)
4747 /* allocate a buffer, for removing backslashes in the keyword */
4748 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004749 if (keyword_copy != NULL)
4750 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004751 syn_opt_arg.flags = 0;
4752 syn_opt_arg.keyword = TRUE;
4753 syn_opt_arg.sync_idx = NULL;
4754 syn_opt_arg.has_cont_list = FALSE;
4755 syn_opt_arg.cont_in_list = NULL;
4756 syn_opt_arg.next_list = NULL;
4757
Bram Moolenaar071d4272004-06-13 20:20:40 +00004758 /*
4759 * The options given apply to ALL keywords, so all options must be
4760 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004761 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004763 cnt = 0;
4764 p = keyword_copy;
4765 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004767 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004768 if (rest == NULL || ends_excmd(*rest))
4769 break;
4770 /* Copy the keyword, removing backslashes, and add a NUL. */
4771 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004773 if (*rest == '\\' && rest[1] != NUL)
4774 ++rest;
4775 *p++ = *rest++;
4776 }
4777 *p++ = NUL;
4778 ++cnt;
4779 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004781 if (!eap->skip)
4782 {
4783 /* Adjust flags for use of ":syn include". */
4784 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4785
4786 /*
4787 * 2: Add an entry for each keyword.
4788 */
4789 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4790 {
4791 for (p = vim_strchr(kw, '['); ; )
4792 {
4793 if (p != NULL)
4794 *p = NUL;
4795 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004796 syn_opt_arg.cont_in_list,
4797 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004798 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004799 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004800 if (p[1] == NUL)
4801 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004802 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004803 kw = p + 2; /* skip over the NUL */
4804 break;
4805 }
4806 if (p[1] == ']')
4807 {
4808 kw = p + 1; /* skip over the "]" */
4809 break;
4810 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004811#ifdef FEAT_MBYTE
4812 if (has_mbyte)
4813 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004814 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004815
4816 mch_memmove(p, p + 1, l);
4817 p += l;
4818 }
4819 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004821 {
4822 p[0] = p[1];
4823 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004824 }
4825 }
4826 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004828
Bram Moolenaar071d4272004-06-13 20:20:40 +00004829 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004830 vim_free(syn_opt_arg.cont_in_list);
4831 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832 }
4833 }
4834
4835 if (rest != NULL)
4836 eap->nextcmd = check_nextcmd(rest);
4837 else
4838 EMSG2(_(e_invarg2), arg);
4839
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004840 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004841 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004842}
4843
4844/*
4845 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4846 *
4847 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4848 */
4849 static void
4850syn_cmd_match(eap, syncing)
4851 exarg_T *eap;
4852 int syncing; /* TRUE for ":syntax sync match .. " */
4853{
4854 char_u *arg = eap->arg;
4855 char_u *group_name_end;
4856 char_u *rest;
4857 synpat_T item; /* the item found in the line */
4858 int syn_id;
4859 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004860 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004861 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004862 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004863
4864 /* Isolate the group name, check for validity */
4865 rest = get_group_name(arg, &group_name_end);
4866
4867 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004868 syn_opt_arg.flags = 0;
4869 syn_opt_arg.keyword = FALSE;
4870 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4871 syn_opt_arg.has_cont_list = TRUE;
4872 syn_opt_arg.cont_list = NULL;
4873 syn_opt_arg.cont_in_list = NULL;
4874 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004875 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004876
4877 /* get the pattern. */
4878 init_syn_patterns();
4879 vim_memset(&item, 0, sizeof(item));
4880 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004881 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4882 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883
4884 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004885 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886
4887 if (rest != NULL) /* all arguments are valid */
4888 {
4889 /*
4890 * Check for trailing command and illegal trailing arguments.
4891 */
4892 eap->nextcmd = check_nextcmd(rest);
4893 if (!ends_excmd(*rest) || eap->skip)
4894 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004895 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004896 && (syn_id = syn_check_group(arg,
4897 (int)(group_name_end - arg))) != 0)
4898 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004899 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 /*
4901 * Store the pattern in the syn_items list
4902 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004903 idx = curwin->w_s->b_syn_patterns.ga_len;
4904 SYN_ITEMS(curwin->w_s)[idx] = item;
4905 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4906 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4907 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4908 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4909 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4910 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4911 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4912 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004913 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004914#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004915 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004916#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004917 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004918 curwin->w_s->b_syn_containedin = TRUE;
4919 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4920 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004921
4922 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004923 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004924 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004925#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004926 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004927 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928#endif
4929
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004930 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004931 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932 return; /* don't free the progs and patterns now */
4933 }
4934 }
4935
4936 /*
4937 * Something failed, free the allocated memory.
4938 */
4939 vim_free(item.sp_prog);
4940 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004941 vim_free(syn_opt_arg.cont_list);
4942 vim_free(syn_opt_arg.cont_in_list);
4943 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004944
4945 if (rest == NULL)
4946 EMSG2(_(e_invarg2), arg);
4947}
4948
4949/*
4950 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4951 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4952 */
4953 static void
4954syn_cmd_region(eap, syncing)
4955 exarg_T *eap;
4956 int syncing; /* TRUE for ":syntax sync region .." */
4957{
4958 char_u *arg = eap->arg;
4959 char_u *group_name_end;
4960 char_u *rest; /* next arg, NULL on error */
4961 char_u *key_end;
4962 char_u *key = NULL;
4963 char_u *p;
4964 int item;
4965#define ITEM_START 0
4966#define ITEM_SKIP 1
4967#define ITEM_END 2
4968#define ITEM_MATCHGROUP 3
4969 struct pat_ptr
4970 {
4971 synpat_T *pp_synp; /* pointer to syn_pattern */
4972 int pp_matchgroup_id; /* matchgroup ID */
4973 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4974 } *(pat_ptrs[3]);
4975 /* patterns found in the line */
4976 struct pat_ptr *ppp;
4977 struct pat_ptr *ppp_next;
4978 int pat_count = 0; /* nr of syn_patterns found */
4979 int syn_id;
4980 int matchgroup_id = 0;
4981 int not_enough = FALSE; /* not enough arguments */
4982 int illegal = FALSE; /* illegal arguments */
4983 int success = FALSE;
4984 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004985 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004986 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987
4988 /* Isolate the group name, check for validity */
4989 rest = get_group_name(arg, &group_name_end);
4990
4991 pat_ptrs[0] = NULL;
4992 pat_ptrs[1] = NULL;
4993 pat_ptrs[2] = NULL;
4994
4995 init_syn_patterns();
4996
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004997 syn_opt_arg.flags = 0;
4998 syn_opt_arg.keyword = FALSE;
4999 syn_opt_arg.sync_idx = NULL;
5000 syn_opt_arg.has_cont_list = TRUE;
5001 syn_opt_arg.cont_list = NULL;
5002 syn_opt_arg.cont_in_list = NULL;
5003 syn_opt_arg.next_list = NULL;
5004
Bram Moolenaar071d4272004-06-13 20:20:40 +00005005 /*
5006 * get the options, patterns and matchgroup.
5007 */
5008 while (rest != NULL && !ends_excmd(*rest))
5009 {
5010 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005011 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005012 if (rest == NULL || ends_excmd(*rest))
5013 break;
5014
5015 /* must be a pattern or matchgroup then */
5016 key_end = rest;
5017 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5018 ++key_end;
5019 vim_free(key);
5020 key = vim_strnsave_up(rest, (int)(key_end - rest));
5021 if (key == NULL) /* out of memory */
5022 {
5023 rest = NULL;
5024 break;
5025 }
5026 if (STRCMP(key, "MATCHGROUP") == 0)
5027 item = ITEM_MATCHGROUP;
5028 else if (STRCMP(key, "START") == 0)
5029 item = ITEM_START;
5030 else if (STRCMP(key, "END") == 0)
5031 item = ITEM_END;
5032 else if (STRCMP(key, "SKIP") == 0)
5033 {
5034 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5035 {
5036 illegal = TRUE;
5037 break;
5038 }
5039 item = ITEM_SKIP;
5040 }
5041 else
5042 break;
5043 rest = skipwhite(key_end);
5044 if (*rest != '=')
5045 {
5046 rest = NULL;
5047 EMSG2(_("E398: Missing '=': %s"), arg);
5048 break;
5049 }
5050 rest = skipwhite(rest + 1);
5051 if (*rest == NUL)
5052 {
5053 not_enough = TRUE;
5054 break;
5055 }
5056
5057 if (item == ITEM_MATCHGROUP)
5058 {
5059 p = skiptowhite(rest);
5060 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5061 matchgroup_id = 0;
5062 else
5063 {
5064 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5065 if (matchgroup_id == 0)
5066 {
5067 illegal = TRUE;
5068 break;
5069 }
5070 }
5071 rest = skipwhite(p);
5072 }
5073 else
5074 {
5075 /*
5076 * Allocate room for a syn_pattern, and link it in the list of
5077 * syn_patterns for this item, at the start (because the list is
5078 * used from end to start).
5079 */
5080 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5081 if (ppp == NULL)
5082 {
5083 rest = NULL;
5084 break;
5085 }
5086 ppp->pp_next = pat_ptrs[item];
5087 pat_ptrs[item] = ppp;
5088 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5089 if (ppp->pp_synp == NULL)
5090 {
5091 rest = NULL;
5092 break;
5093 }
5094
5095 /*
5096 * Get the syntax pattern and the following offset(s).
5097 */
5098 /* Enable the appropriate \z specials. */
5099 if (item == ITEM_START)
5100 reg_do_extmatch = REX_SET;
5101 else if (item == ITEM_SKIP || item == ITEM_END)
5102 reg_do_extmatch = REX_USE;
5103 rest = get_syn_pattern(rest, ppp->pp_synp);
5104 reg_do_extmatch = 0;
5105 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005106 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005107 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5108 ppp->pp_matchgroup_id = matchgroup_id;
5109 ++pat_count;
5110 }
5111 }
5112 vim_free(key);
5113 if (illegal || not_enough)
5114 rest = NULL;
5115
5116 /*
5117 * Must have a "start" and "end" pattern.
5118 */
5119 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5120 pat_ptrs[ITEM_END] == NULL))
5121 {
5122 not_enough = TRUE;
5123 rest = NULL;
5124 }
5125
5126 if (rest != NULL)
5127 {
5128 /*
5129 * Check for trailing garbage or command.
5130 * If OK, add the item.
5131 */
5132 eap->nextcmd = check_nextcmd(rest);
5133 if (!ends_excmd(*rest) || eap->skip)
5134 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005135 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005136 && (syn_id = syn_check_group(arg,
5137 (int)(group_name_end - arg))) != 0)
5138 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005139 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005140 /*
5141 * Store the start/skip/end in the syn_items list
5142 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005143 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005144 for (item = ITEM_START; item <= ITEM_END; ++item)
5145 {
5146 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5147 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005148 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5149 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5150 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005151 (item == ITEM_START) ? SPTYPE_START :
5152 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005153 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5154 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005155 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5156 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005157 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005158 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005159#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005160 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005161#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005162 if (item == ITEM_START)
5163 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005164 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005165 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005166 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005167 syn_opt_arg.cont_in_list;
5168 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005169 curwin->w_s->b_syn_containedin = TRUE;
5170 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005171 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005173 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005174 ++idx;
5175#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005176 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005177 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178#endif
5179 }
5180 }
5181
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005182 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005183 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184 success = TRUE; /* don't free the progs and patterns now */
5185 }
5186 }
5187
5188 /*
5189 * Free the allocated memory.
5190 */
5191 for (item = ITEM_START; item <= ITEM_END; ++item)
5192 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5193 {
5194 if (!success)
5195 {
5196 vim_free(ppp->pp_synp->sp_prog);
5197 vim_free(ppp->pp_synp->sp_pattern);
5198 }
5199 vim_free(ppp->pp_synp);
5200 ppp_next = ppp->pp_next;
5201 vim_free(ppp);
5202 }
5203
5204 if (!success)
5205 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005206 vim_free(syn_opt_arg.cont_list);
5207 vim_free(syn_opt_arg.cont_in_list);
5208 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005209 if (not_enough)
5210 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5211 else if (illegal || rest == NULL)
5212 EMSG2(_(e_invarg2), arg);
5213 }
5214}
5215
5216/*
5217 * A simple syntax group ID comparison function suitable for use in qsort()
5218 */
5219 static int
5220#ifdef __BORLANDC__
5221_RTLENTRYF
5222#endif
5223syn_compare_stub(v1, v2)
5224 const void *v1;
5225 const void *v2;
5226{
5227 const short *s1 = v1;
5228 const short *s2 = v2;
5229
5230 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5231}
5232
5233/*
5234 * Combines lists of syntax clusters.
5235 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5236 */
5237 static void
5238syn_combine_list(clstr1, clstr2, list_op)
5239 short **clstr1;
5240 short **clstr2;
5241 int list_op;
5242{
5243 int count1 = 0;
5244 int count2 = 0;
5245 short *g1;
5246 short *g2;
5247 short *clstr = NULL;
5248 int count;
5249 int round;
5250
5251 /*
5252 * Handle degenerate cases.
5253 */
5254 if (*clstr2 == NULL)
5255 return;
5256 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5257 {
5258 if (list_op == CLUSTER_REPLACE)
5259 vim_free(*clstr1);
5260 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5261 *clstr1 = *clstr2;
5262 else
5263 vim_free(*clstr2);
5264 return;
5265 }
5266
5267 for (g1 = *clstr1; *g1; g1++)
5268 ++count1;
5269 for (g2 = *clstr2; *g2; g2++)
5270 ++count2;
5271
5272 /*
5273 * For speed purposes, sort both lists.
5274 */
5275 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5276 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5277
5278 /*
5279 * We proceed in two passes; in round 1, we count the elements to place
5280 * in the new list, and in round 2, we allocate and populate the new
5281 * list. For speed, we use a mergesort-like method, adding the smaller
5282 * of the current elements in each list to the new list.
5283 */
5284 for (round = 1; round <= 2; round++)
5285 {
5286 g1 = *clstr1;
5287 g2 = *clstr2;
5288 count = 0;
5289
5290 /*
5291 * First, loop through the lists until one of them is empty.
5292 */
5293 while (*g1 && *g2)
5294 {
5295 /*
5296 * We always want to add from the first list.
5297 */
5298 if (*g1 < *g2)
5299 {
5300 if (round == 2)
5301 clstr[count] = *g1;
5302 count++;
5303 g1++;
5304 continue;
5305 }
5306 /*
5307 * We only want to add from the second list if we're adding the
5308 * lists.
5309 */
5310 if (list_op == CLUSTER_ADD)
5311 {
5312 if (round == 2)
5313 clstr[count] = *g2;
5314 count++;
5315 }
5316 if (*g1 == *g2)
5317 g1++;
5318 g2++;
5319 }
5320
5321 /*
5322 * Now add the leftovers from whichever list didn't get finished
5323 * first. As before, we only want to add from the second list if
5324 * we're adding the lists.
5325 */
5326 for (; *g1; g1++, count++)
5327 if (round == 2)
5328 clstr[count] = *g1;
5329 if (list_op == CLUSTER_ADD)
5330 for (; *g2; g2++, count++)
5331 if (round == 2)
5332 clstr[count] = *g2;
5333
5334 if (round == 1)
5335 {
5336 /*
5337 * If the group ended up empty, we don't need to allocate any
5338 * space for it.
5339 */
5340 if (count == 0)
5341 {
5342 clstr = NULL;
5343 break;
5344 }
5345 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5346 if (clstr == NULL)
5347 break;
5348 clstr[count] = 0;
5349 }
5350 }
5351
5352 /*
5353 * Finally, put the new list in place.
5354 */
5355 vim_free(*clstr1);
5356 vim_free(*clstr2);
5357 *clstr1 = clstr;
5358}
5359
5360/*
5361 * Lookup a syntax cluster name and return it's ID.
5362 * If it is not found, 0 is returned.
5363 */
5364 static int
5365syn_scl_name2id(name)
5366 char_u *name;
5367{
5368 int i;
5369 char_u *name_u;
5370
5371 /* Avoid using stricmp() too much, it's slow on some systems */
5372 name_u = vim_strsave_up(name);
5373 if (name_u == NULL)
5374 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005375 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5376 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5377 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005378 break;
5379 vim_free(name_u);
5380 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5381}
5382
5383/*
5384 * Like syn_scl_name2id(), but take a pointer + length argument.
5385 */
5386 static int
5387syn_scl_namen2id(linep, len)
5388 char_u *linep;
5389 int len;
5390{
5391 char_u *name;
5392 int id = 0;
5393
5394 name = vim_strnsave(linep, len);
5395 if (name != NULL)
5396 {
5397 id = syn_scl_name2id(name);
5398 vim_free(name);
5399 }
5400 return id;
5401}
5402
5403/*
5404 * Find syntax cluster name in the table and return it's ID.
5405 * The argument is a pointer to the name and the length of the name.
5406 * If it doesn't exist yet, a new entry is created.
5407 * Return 0 for failure.
5408 */
5409 static int
5410syn_check_cluster(pp, len)
5411 char_u *pp;
5412 int len;
5413{
5414 int id;
5415 char_u *name;
5416
5417 name = vim_strnsave(pp, len);
5418 if (name == NULL)
5419 return 0;
5420
5421 id = syn_scl_name2id(name);
5422 if (id == 0) /* doesn't exist yet */
5423 id = syn_add_cluster(name);
5424 else
5425 vim_free(name);
5426 return id;
5427}
5428
5429/*
5430 * Add new syntax cluster and return it's ID.
5431 * "name" must be an allocated string, it will be consumed.
5432 * Return 0 for failure.
5433 */
5434 static int
5435syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005436 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005437{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005438 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005439
5440 /*
5441 * First call for this growarray: init growing array.
5442 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005443 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005444 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005445 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5446 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005447 }
5448
Bram Moolenaar42431a72011-04-01 14:44:59 +02005449 len = curwin->w_s->b_syn_clusters.ga_len;
5450 if (len >= MAX_CLUSTER_ID)
5451 {
5452 EMSG((char_u *)_("E848: Too many syntax clusters"));
5453 vim_free(name);
5454 return 0;
5455 }
5456
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457 /*
5458 * Make room for at least one other cluster entry.
5459 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005460 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005461 {
5462 vim_free(name);
5463 return 0;
5464 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005465
Bram Moolenaar860cae12010-06-05 23:22:07 +02005466 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5467 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5468 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5469 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5470 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005471
Bram Moolenaar217ad922005-03-20 22:37:15 +00005472 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005473 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005474 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005475 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005476
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477 return len + SYNID_CLUSTER;
5478}
5479
5480/*
5481 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5482 * [add={groupname},..] [remove={groupname},..]".
5483 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005484 static void
5485syn_cmd_cluster(eap, syncing)
5486 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005487 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005488{
5489 char_u *arg = eap->arg;
5490 char_u *group_name_end;
5491 char_u *rest;
5492 int scl_id;
5493 short *clstr_list;
5494 int got_clstr = FALSE;
5495 int opt_len;
5496 int list_op;
5497
5498 eap->nextcmd = find_nextcmd(arg);
5499 if (eap->skip)
5500 return;
5501
5502 rest = get_group_name(arg, &group_name_end);
5503
5504 if (rest != NULL)
5505 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005506 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5507 if (scl_id == 0)
5508 return;
5509 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005510
5511 for (;;)
5512 {
5513 if (STRNICMP(rest, "add", 3) == 0
5514 && (vim_iswhite(rest[3]) || rest[3] == '='))
5515 {
5516 opt_len = 3;
5517 list_op = CLUSTER_ADD;
5518 }
5519 else if (STRNICMP(rest, "remove", 6) == 0
5520 && (vim_iswhite(rest[6]) || rest[6] == '='))
5521 {
5522 opt_len = 6;
5523 list_op = CLUSTER_SUBTRACT;
5524 }
5525 else if (STRNICMP(rest, "contains", 8) == 0
5526 && (vim_iswhite(rest[8]) || rest[8] == '='))
5527 {
5528 opt_len = 8;
5529 list_op = CLUSTER_REPLACE;
5530 }
5531 else
5532 break;
5533
5534 clstr_list = NULL;
5535 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5536 {
5537 EMSG2(_(e_invarg2), rest);
5538 break;
5539 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005540 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005541 &clstr_list, list_op);
5542 got_clstr = TRUE;
5543 }
5544
5545 if (got_clstr)
5546 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005547 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005548 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549 }
5550 }
5551
5552 if (!got_clstr)
5553 EMSG(_("E400: No cluster specified"));
5554 if (rest == NULL || !ends_excmd(*rest))
5555 EMSG2(_(e_invarg2), arg);
5556}
5557
5558/*
5559 * On first call for current buffer: Init growing array.
5560 */
5561 static void
5562init_syn_patterns()
5563{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005564 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5565 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005566}
5567
5568/*
5569 * Get one pattern for a ":syntax match" or ":syntax region" command.
5570 * Stores the pattern and program in a synpat_T.
5571 * Returns a pointer to the next argument, or NULL in case of an error.
5572 */
5573 static char_u *
5574get_syn_pattern(arg, ci)
5575 char_u *arg;
5576 synpat_T *ci;
5577{
5578 char_u *end;
5579 int *p;
5580 int idx;
5581 char_u *cpo_save;
5582
5583 /* need at least three chars */
5584 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5585 return NULL;
5586
5587 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5588 if (*end != *arg) /* end delimiter not found */
5589 {
5590 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5591 return NULL;
5592 }
5593 /* store the pattern and compiled regexp program */
5594 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5595 return NULL;
5596
5597 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5598 cpo_save = p_cpo;
5599 p_cpo = (char_u *)"";
5600 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5601 p_cpo = cpo_save;
5602
5603 if (ci->sp_prog == NULL)
5604 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005605 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005606
5607 /*
5608 * Check for a match, highlight or region offset.
5609 */
5610 ++end;
5611 do
5612 {
5613 for (idx = SPO_COUNT; --idx >= 0; )
5614 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5615 break;
5616 if (idx >= 0)
5617 {
5618 p = &(ci->sp_offsets[idx]);
5619 if (idx != SPO_LC_OFF)
5620 switch (end[3])
5621 {
5622 case 's': break;
5623 case 'b': break;
5624 case 'e': idx += SPO_COUNT; break;
5625 default: idx = -1; break;
5626 }
5627 if (idx >= 0)
5628 {
5629 ci->sp_off_flags |= (1 << idx);
5630 if (idx == SPO_LC_OFF) /* lc=99 */
5631 {
5632 end += 3;
5633 *p = getdigits(&end);
5634
5635 /* "lc=" offset automatically sets "ms=" offset */
5636 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5637 {
5638 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5639 ci->sp_offsets[SPO_MS_OFF] = *p;
5640 }
5641 }
5642 else /* yy=x+99 */
5643 {
5644 end += 4;
5645 if (*end == '+')
5646 {
5647 ++end;
5648 *p = getdigits(&end); /* positive offset */
5649 }
5650 else if (*end == '-')
5651 {
5652 ++end;
5653 *p = -getdigits(&end); /* negative offset */
5654 }
5655 }
5656 if (*end != ',')
5657 break;
5658 ++end;
5659 }
5660 }
5661 } while (idx >= 0);
5662
5663 if (!ends_excmd(*end) && !vim_iswhite(*end))
5664 {
5665 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5666 return NULL;
5667 }
5668 return skipwhite(end);
5669}
5670
5671/*
5672 * Handle ":syntax sync .." command.
5673 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005674 static void
5675syn_cmd_sync(eap, syncing)
5676 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005677 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678{
5679 char_u *arg_start = eap->arg;
5680 char_u *arg_end;
5681 char_u *key = NULL;
5682 char_u *next_arg;
5683 int illegal = FALSE;
5684 int finished = FALSE;
5685 long n;
5686 char_u *cpo_save;
5687
5688 if (ends_excmd(*arg_start))
5689 {
5690 syn_cmd_list(eap, TRUE);
5691 return;
5692 }
5693
5694 while (!ends_excmd(*arg_start))
5695 {
5696 arg_end = skiptowhite(arg_start);
5697 next_arg = skipwhite(arg_end);
5698 vim_free(key);
5699 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5700 if (STRCMP(key, "CCOMMENT") == 0)
5701 {
5702 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005703 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 if (!ends_excmd(*next_arg))
5705 {
5706 arg_end = skiptowhite(next_arg);
5707 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005708 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005709 (int)(arg_end - next_arg));
5710 next_arg = skipwhite(arg_end);
5711 }
5712 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005713 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005714 }
5715 else if ( STRNCMP(key, "LINES", 5) == 0
5716 || STRNCMP(key, "MINLINES", 8) == 0
5717 || STRNCMP(key, "MAXLINES", 8) == 0
5718 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5719 {
5720 if (key[4] == 'S')
5721 arg_end = key + 6;
5722 else if (key[0] == 'L')
5723 arg_end = key + 11;
5724 else
5725 arg_end = key + 9;
5726 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5727 {
5728 illegal = TRUE;
5729 break;
5730 }
5731 n = getdigits(&arg_end);
5732 if (!eap->skip)
5733 {
5734 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005735 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005737 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005738 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005739 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005740 }
5741 }
5742 else if (STRCMP(key, "FROMSTART") == 0)
5743 {
5744 if (!eap->skip)
5745 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005746 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5747 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005748 }
5749 }
5750 else if (STRCMP(key, "LINECONT") == 0)
5751 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005752 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005753 {
5754 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5755 finished = TRUE;
5756 break;
5757 }
5758 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5759 if (*arg_end != *next_arg) /* end delimiter not found */
5760 {
5761 illegal = TRUE;
5762 break;
5763 }
5764
5765 if (!eap->skip)
5766 {
5767 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005768 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005769 (int)(arg_end - next_arg - 1))) == NULL)
5770 {
5771 finished = TRUE;
5772 break;
5773 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005774 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005775
5776 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5777 cpo_save = p_cpo;
5778 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005779 curwin->w_s->b_syn_linecont_prog =
5780 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005781 p_cpo = cpo_save;
5782
Bram Moolenaar860cae12010-06-05 23:22:07 +02005783 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005784 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005785 vim_free(curwin->w_s->b_syn_linecont_pat);
5786 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005787 finished = TRUE;
5788 break;
5789 }
5790 }
5791 next_arg = skipwhite(arg_end + 1);
5792 }
5793 else
5794 {
5795 eap->arg = next_arg;
5796 if (STRCMP(key, "MATCH") == 0)
5797 syn_cmd_match(eap, TRUE);
5798 else if (STRCMP(key, "REGION") == 0)
5799 syn_cmd_region(eap, TRUE);
5800 else if (STRCMP(key, "CLEAR") == 0)
5801 syn_cmd_clear(eap, TRUE);
5802 else
5803 illegal = TRUE;
5804 finished = TRUE;
5805 break;
5806 }
5807 arg_start = next_arg;
5808 }
5809 vim_free(key);
5810 if (illegal)
5811 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5812 else if (!finished)
5813 {
5814 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005815 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005816 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005817 }
5818}
5819
5820/*
5821 * Convert a line of highlight group names into a list of group ID numbers.
5822 * "arg" should point to the "contains" or "nextgroup" keyword.
5823 * "arg" is advanced to after the last group name.
5824 * Careful: the argument is modified (NULs added).
5825 * returns FAIL for some error, OK for success.
5826 */
5827 static int
5828get_id_list(arg, keylen, list)
5829 char_u **arg;
5830 int keylen; /* length of keyword */
5831 short **list; /* where to store the resulting list, if not
5832 NULL, the list is silently skipped! */
5833{
5834 char_u *p = NULL;
5835 char_u *end;
5836 int round;
5837 int count;
5838 int total_count = 0;
5839 short *retval = NULL;
5840 char_u *name;
5841 regmatch_T regmatch;
5842 int id;
5843 int i;
5844 int failed = FALSE;
5845
5846 /*
5847 * We parse the list twice:
5848 * round == 1: count the number of items, allocate the array.
5849 * round == 2: fill the array with the items.
5850 * In round 1 new groups may be added, causing the number of items to
5851 * grow when a regexp is used. In that case round 1 is done once again.
5852 */
5853 for (round = 1; round <= 2; ++round)
5854 {
5855 /*
5856 * skip "contains"
5857 */
5858 p = skipwhite(*arg + keylen);
5859 if (*p != '=')
5860 {
5861 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5862 break;
5863 }
5864 p = skipwhite(p + 1);
5865 if (ends_excmd(*p))
5866 {
5867 EMSG2(_("E406: Empty argument: %s"), *arg);
5868 break;
5869 }
5870
5871 /*
5872 * parse the arguments after "contains"
5873 */
5874 count = 0;
5875 while (!ends_excmd(*p))
5876 {
5877 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5878 ;
5879 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5880 if (name == NULL)
5881 {
5882 failed = TRUE;
5883 break;
5884 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005885 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005886 if ( STRCMP(name + 1, "ALLBUT") == 0
5887 || STRCMP(name + 1, "ALL") == 0
5888 || STRCMP(name + 1, "TOP") == 0
5889 || STRCMP(name + 1, "CONTAINED") == 0)
5890 {
5891 if (TOUPPER_ASC(**arg) != 'C')
5892 {
5893 EMSG2(_("E407: %s not allowed here"), name + 1);
5894 failed = TRUE;
5895 vim_free(name);
5896 break;
5897 }
5898 if (count != 0)
5899 {
5900 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5901 failed = TRUE;
5902 vim_free(name);
5903 break;
5904 }
5905 if (name[1] == 'A')
5906 id = SYNID_ALLBUT;
5907 else if (name[1] == 'T')
5908 id = SYNID_TOP;
5909 else
5910 id = SYNID_CONTAINED;
5911 id += current_syn_inc_tag;
5912 }
5913 else if (name[1] == '@')
5914 {
5915 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5916 }
5917 else
5918 {
5919 /*
5920 * Handle full group name.
5921 */
5922 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5923 id = syn_check_group(name + 1, (int)(end - p));
5924 else
5925 {
5926 /*
5927 * Handle match of regexp with group names.
5928 */
5929 *name = '^';
5930 STRCAT(name, "$");
5931 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5932 if (regmatch.regprog == NULL)
5933 {
5934 failed = TRUE;
5935 vim_free(name);
5936 break;
5937 }
5938
5939 regmatch.rm_ic = TRUE;
5940 id = 0;
5941 for (i = highlight_ga.ga_len; --i >= 0; )
5942 {
5943 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5944 (colnr_T)0))
5945 {
5946 if (round == 2)
5947 {
5948 /* Got more items than expected; can happen
5949 * when adding items that match:
5950 * "contains=a.*b,axb".
5951 * Go back to first round */
5952 if (count >= total_count)
5953 {
5954 vim_free(retval);
5955 round = 1;
5956 }
5957 else
5958 retval[count] = i + 1;
5959 }
5960 ++count;
5961 id = -1; /* remember that we found one */
5962 }
5963 }
5964 vim_free(regmatch.regprog);
5965 }
5966 }
5967 vim_free(name);
5968 if (id == 0)
5969 {
5970 EMSG2(_("E409: Unknown group name: %s"), p);
5971 failed = TRUE;
5972 break;
5973 }
5974 if (id > 0)
5975 {
5976 if (round == 2)
5977 {
5978 /* Got more items than expected, go back to first round */
5979 if (count >= total_count)
5980 {
5981 vim_free(retval);
5982 round = 1;
5983 }
5984 else
5985 retval[count] = id;
5986 }
5987 ++count;
5988 }
5989 p = skipwhite(end);
5990 if (*p != ',')
5991 break;
5992 p = skipwhite(p + 1); /* skip comma in between arguments */
5993 }
5994 if (failed)
5995 break;
5996 if (round == 1)
5997 {
5998 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5999 if (retval == NULL)
6000 break;
6001 retval[count] = 0; /* zero means end of the list */
6002 total_count = count;
6003 }
6004 }
6005
6006 *arg = p;
6007 if (failed || retval == NULL)
6008 {
6009 vim_free(retval);
6010 return FAIL;
6011 }
6012
6013 if (*list == NULL)
6014 *list = retval;
6015 else
6016 vim_free(retval); /* list already found, don't overwrite it */
6017
6018 return OK;
6019}
6020
6021/*
6022 * Make a copy of an ID list.
6023 */
6024 static short *
6025copy_id_list(list)
6026 short *list;
6027{
6028 int len;
6029 int count;
6030 short *retval;
6031
6032 if (list == NULL)
6033 return NULL;
6034
6035 for (count = 0; list[count]; ++count)
6036 ;
6037 len = (count + 1) * sizeof(short);
6038 retval = (short *)alloc((unsigned)len);
6039 if (retval != NULL)
6040 mch_memmove(retval, list, (size_t)len);
6041
6042 return retval;
6043}
6044
6045/*
6046 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6047 * "cur_si" can be NULL if not checking the "containedin" list.
6048 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6049 * the current item.
6050 * This function is called very often, keep it fast!!
6051 */
6052 static int
6053in_id_list(cur_si, list, ssp, contained)
6054 stateitem_T *cur_si; /* current item or NULL */
6055 short *list; /* id list */
6056 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6057 int contained; /* group id is contained */
6058{
6059 int retval;
6060 short *scl_list;
6061 short item;
6062 short id = ssp->id;
6063 static int depth = 0;
6064 int r;
6065
6066 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006067 if (cur_si != NULL && ssp->cont_in_list != NULL
6068 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006069 {
6070 /* Ignore transparent items without a contains argument. Double check
6071 * that we don't go back past the first one. */
6072 while ((cur_si->si_flags & HL_TRANS_CONT)
6073 && cur_si > (stateitem_T *)(current_state.ga_data))
6074 --cur_si;
6075 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6076 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006077 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6078 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006079 return TRUE;
6080 }
6081
6082 if (list == NULL)
6083 return FALSE;
6084
6085 /*
6086 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6087 * inside anything. Only allow not-contained groups.
6088 */
6089 if (list == ID_LIST_ALL)
6090 return !contained;
6091
6092 /*
6093 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6094 * contains list. We also require that "id" is at the same ":syn include"
6095 * level as the list.
6096 */
6097 item = *list;
6098 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6099 {
6100 if (item < SYNID_TOP)
6101 {
6102 /* ALL or ALLBUT: accept all groups in the same file */
6103 if (item - SYNID_ALLBUT != ssp->inc_tag)
6104 return FALSE;
6105 }
6106 else if (item < SYNID_CONTAINED)
6107 {
6108 /* TOP: accept all not-contained groups in the same file */
6109 if (item - SYNID_TOP != ssp->inc_tag || contained)
6110 return FALSE;
6111 }
6112 else
6113 {
6114 /* CONTAINED: accept all contained groups in the same file */
6115 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6116 return FALSE;
6117 }
6118 item = *++list;
6119 retval = FALSE;
6120 }
6121 else
6122 retval = TRUE;
6123
6124 /*
6125 * Return "retval" if id is in the contains list.
6126 */
6127 while (item != 0)
6128 {
6129 if (item == id)
6130 return retval;
6131 if (item >= SYNID_CLUSTER)
6132 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006133 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134 /* restrict recursiveness to 30 to avoid an endless loop for a
6135 * cluster that includes itself (indirectly) */
6136 if (scl_list != NULL && depth < 30)
6137 {
6138 ++depth;
6139 r = in_id_list(NULL, scl_list, ssp, contained);
6140 --depth;
6141 if (r)
6142 return retval;
6143 }
6144 }
6145 item = *++list;
6146 }
6147 return !retval;
6148}
6149
6150struct subcommand
6151{
6152 char *name; /* subcommand name */
6153 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6154};
6155
6156static struct subcommand subcommands[] =
6157{
6158 {"case", syn_cmd_case},
6159 {"clear", syn_cmd_clear},
6160 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006161 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006162 {"enable", syn_cmd_enable},
6163 {"include", syn_cmd_include},
6164 {"keyword", syn_cmd_keyword},
6165 {"list", syn_cmd_list},
6166 {"manual", syn_cmd_manual},
6167 {"match", syn_cmd_match},
6168 {"on", syn_cmd_on},
6169 {"off", syn_cmd_off},
6170 {"region", syn_cmd_region},
6171 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006172 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006173 {"sync", syn_cmd_sync},
6174 {"", syn_cmd_list},
6175 {NULL, NULL}
6176};
6177
6178/*
6179 * ":syntax".
6180 * This searches the subcommands[] table for the subcommand name, and calls a
6181 * syntax_subcommand() function to do the rest.
6182 */
6183 void
6184ex_syntax(eap)
6185 exarg_T *eap;
6186{
6187 char_u *arg = eap->arg;
6188 char_u *subcmd_end;
6189 char_u *subcmd_name;
6190 int i;
6191
6192 syn_cmdlinep = eap->cmdlinep;
6193
6194 /* isolate subcommand name */
6195 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6196 ;
6197 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6198 if (subcmd_name != NULL)
6199 {
6200 if (eap->skip) /* skip error messages for all subcommands */
6201 ++emsg_skip;
6202 for (i = 0; ; ++i)
6203 {
6204 if (subcommands[i].name == NULL)
6205 {
6206 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6207 break;
6208 }
6209 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6210 {
6211 eap->arg = skipwhite(subcmd_end);
6212 (subcommands[i].func)(eap, FALSE);
6213 break;
6214 }
6215 }
6216 vim_free(subcmd_name);
6217 if (eap->skip)
6218 --emsg_skip;
6219 }
6220}
6221
Bram Moolenaar860cae12010-06-05 23:22:07 +02006222 void
6223ex_ownsyntax(eap)
6224 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006226 char_u *old_value;
6227 char_u *new_value;
6228
Bram Moolenaar860cae12010-06-05 23:22:07 +02006229 if (curwin->w_s == &curwin->w_buffer->b_s)
6230 {
6231 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6232 memset(curwin->w_s, 0, sizeof(synblock_T));
6233#ifdef FEAT_SPELL
6234 curwin->w_p_spell = FALSE; /* No spell checking */
6235 clear_string_option(&curwin->w_s->b_p_spc);
6236 clear_string_option(&curwin->w_s->b_p_spf);
6237 vim_free(curwin->w_s->b_cap_prog);
6238 curwin->w_s->b_cap_prog = NULL;
6239 clear_string_option(&curwin->w_s->b_p_spl);
6240#endif
6241 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006242
6243 /* save value of b:current_syntax */
6244 old_value = get_var_value((char_u *)"b:current_syntax");
6245 if (old_value != NULL)
6246 old_value = vim_strsave(old_value);
6247
6248 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6249 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006250 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006251
6252 /* move value of b:current_syntax to w:current_syntax */
6253 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006254 if (new_value != NULL)
6255 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006256
6257 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006258 if (old_value == NULL)
6259 do_unlet((char_u *)"b:current_syntax", TRUE);
6260 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006261 {
6262 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6263 vim_free(old_value);
6264 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006265}
6266
6267 int
6268syntax_present(win)
6269 win_T *win;
6270{
6271 return (win->w_s->b_syn_patterns.ga_len != 0
6272 || win->w_s->b_syn_clusters.ga_len != 0
6273 || win->w_s->b_keywtab.ht_used > 0
6274 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006275}
6276
6277#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6278
6279static enum
6280{
6281 EXP_SUBCMD, /* expand ":syn" sub-commands */
6282 EXP_CASE /* expand ":syn case" arguments */
6283} expand_what;
6284
Bram Moolenaar4f688582007-07-24 12:34:30 +00006285/*
6286 * Reset include_link, include_default, include_none to 0.
6287 * Called when we are done expanding.
6288 */
6289 void
6290reset_expand_highlight()
6291{
6292 include_link = include_default = include_none = 0;
6293}
6294
6295/*
6296 * Handle command line completion for :match and :echohl command: Add "None"
6297 * as highlight group.
6298 */
6299 void
6300set_context_in_echohl_cmd(xp, arg)
6301 expand_T *xp;
6302 char_u *arg;
6303{
6304 xp->xp_context = EXPAND_HIGHLIGHT;
6305 xp->xp_pattern = arg;
6306 include_none = 1;
6307}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006308
6309/*
6310 * Handle command line completion for :syntax command.
6311 */
6312 void
6313set_context_in_syntax_cmd(xp, arg)
6314 expand_T *xp;
6315 char_u *arg;
6316{
6317 char_u *p;
6318
6319 /* Default: expand subcommands */
6320 xp->xp_context = EXPAND_SYNTAX;
6321 expand_what = EXP_SUBCMD;
6322 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006323 include_link = 0;
6324 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006325
6326 /* (part of) subcommand already typed */
6327 if (*arg != NUL)
6328 {
6329 p = skiptowhite(arg);
6330 if (*p != NUL) /* past first word */
6331 {
6332 xp->xp_pattern = skipwhite(p);
6333 if (*skiptowhite(xp->xp_pattern) != NUL)
6334 xp->xp_context = EXPAND_NOTHING;
6335 else if (STRNICMP(arg, "case", p - arg) == 0)
6336 expand_what = EXP_CASE;
6337 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6338 || STRNICMP(arg, "region", p - arg) == 0
6339 || STRNICMP(arg, "match", p - arg) == 0
6340 || STRNICMP(arg, "list", p - arg) == 0)
6341 xp->xp_context = EXPAND_HIGHLIGHT;
6342 else
6343 xp->xp_context = EXPAND_NOTHING;
6344 }
6345 }
6346}
6347
6348static char *(case_args[]) = {"match", "ignore", NULL};
6349
6350/*
6351 * Function given to ExpandGeneric() to obtain the list syntax names for
6352 * expansion.
6353 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006354 char_u *
6355get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006356 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006357 int idx;
6358{
6359 if (expand_what == EXP_SUBCMD)
6360 return (char_u *)subcommands[idx].name;
6361 return (char_u *)case_args[idx];
6362}
6363
6364#endif /* FEAT_CMDL_COMPL */
6365
Bram Moolenaar071d4272004-06-13 20:20:40 +00006366/*
6367 * Function called for expression evaluation: get syntax ID at file position.
6368 */
6369 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006370syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006371 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006372 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006373 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006374 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006375 int *spellp; /* return: can do spell checking */
6376 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006377{
6378 /* When the position is not after the current position and in the same
6379 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006380 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006381 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006382 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006383 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006385 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386
6387 return (trans ? current_trans_id : current_id);
6388}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389
Bram Moolenaar860cae12010-06-05 23:22:07 +02006390#if defined(FEAT_CONCEAL) || defined(PROTO)
6391/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006392 * Get extra information about the syntax item. Must be called right after
6393 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006394 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006395 * Returns the current flags.
6396 */
6397 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006398get_syntax_info(seqnrp)
6399 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006400{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006401 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006402 return current_flags;
6403}
6404
6405/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006406 * Return conceal substitution character
6407 */
6408 int
6409syn_get_sub_char()
6410{
6411 return current_sub_char;
6412}
6413#endif
6414
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006415#if defined(FEAT_EVAL) || defined(PROTO)
6416/*
6417 * Return the syntax ID at position "i" in the current stack.
6418 * The caller must have called syn_get_id() before to fill the stack.
6419 * Returns -1 when "i" is out of range.
6420 */
6421 int
6422syn_get_stack_item(i)
6423 int i;
6424{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006425 if (i >= current_state.ga_len)
6426 {
6427 /* Need to invalidate the state, because we didn't properly finish it
6428 * for the last character, "keep_state" was TRUE. */
6429 invalidate_current_state();
6430 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006431 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006432 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006433 return CUR_STATE(i).si_id;
6434}
6435#endif
6436
Bram Moolenaar071d4272004-06-13 20:20:40 +00006437#if defined(FEAT_FOLDING) || defined(PROTO)
6438/*
6439 * Function called to get folding level for line "lnum" in window "wp".
6440 */
6441 int
6442syn_get_foldlevel(wp, lnum)
6443 win_T *wp;
6444 long lnum;
6445{
6446 int level = 0;
6447 int i;
6448
6449 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006450 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006451 {
6452 syntax_start(wp, lnum);
6453
6454 for (i = 0; i < current_state.ga_len; ++i)
6455 if (CUR_STATE(i).si_flags & HL_FOLD)
6456 ++level;
6457 }
6458 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006459 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006460 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006461 if (level < 0)
6462 level = 0;
6463 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006464 return level;
6465}
6466#endif
6467
6468#endif /* FEAT_SYN_HL */
6469
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470/**************************************
6471 * Highlighting stuff *
6472 **************************************/
6473
6474/*
6475 * The default highlight groups. These are compiled-in for fast startup and
6476 * they still work when the runtime files can't be found.
6477 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006478 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6479 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006481#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006482# define CENT(a, b) b
6483#else
6484# define CENT(a, b) a
6485#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486static char *(highlight_init_both[]) =
6487 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006488 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6489 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6490 CENT("IncSearch term=reverse cterm=reverse",
6491 "IncSearch term=reverse cterm=reverse gui=reverse"),
6492 CENT("ModeMsg term=bold cterm=bold",
6493 "ModeMsg term=bold cterm=bold gui=bold"),
6494 CENT("NonText term=bold ctermfg=Blue",
6495 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6496 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6497 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6498 CENT("StatusLineNC term=reverse cterm=reverse",
6499 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006500#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006501 CENT("VertSplit term=reverse cterm=reverse",
6502 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006503#endif
6504#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006505 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6506 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006507#endif
6508#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006509 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6510 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006511#endif
6512#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006513 CENT("PmenuThumb cterm=reverse",
6514 "PmenuThumb cterm=reverse gui=reverse"),
6515 CENT("PmenuSbar ctermbg=Grey",
6516 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006517#endif
6518#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006519 CENT("TabLineSel term=bold cterm=bold",
6520 "TabLineSel term=bold cterm=bold gui=bold"),
6521 CENT("TabLineFill term=reverse cterm=reverse",
6522 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006523#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006524#ifdef FEAT_GUI
6525 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006526 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006527#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528 NULL
6529 };
6530
6531static char *(highlight_init_light[]) =
6532 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006533 CENT("Directory term=bold ctermfg=DarkBlue",
6534 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6535 CENT("LineNr term=underline ctermfg=Brown",
6536 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6537 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6538 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6539 CENT("Question term=standout ctermfg=DarkGreen",
6540 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6541 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6542 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006543#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006544 CENT("SpellBad term=reverse ctermbg=LightRed",
6545 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6546 CENT("SpellCap term=reverse ctermbg=LightBlue",
6547 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6548 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6549 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6550 CENT("SpellLocal term=underline ctermbg=Cyan",
6551 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006552#endif
6553#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006554 CENT("Pmenu ctermbg=LightMagenta",
6555 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6556 CENT("PmenuSel ctermbg=LightGrey",
6557 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006558#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006559 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6560 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6561 CENT("Title term=bold ctermfg=DarkMagenta",
6562 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6563 CENT("WarningMsg term=standout ctermfg=DarkRed",
6564 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006565#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006566 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6567 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006568#endif
6569#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006570 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6571 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6572 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6573 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006574#endif
6575#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006576 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6577 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006578#endif
6579#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006580 CENT("Visual term=reverse",
6581 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006582#endif
6583#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006584 CENT("DiffAdd term=bold ctermbg=LightBlue",
6585 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6586 CENT("DiffChange term=bold ctermbg=LightMagenta",
6587 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6588 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6589 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006590#endif
6591#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006592 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6593 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006594#endif
6595#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006596 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006597 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006598 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006599 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006600 CENT("ColorColumn term=reverse ctermbg=LightRed",
6601 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006602#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006603#ifdef FEAT_CONCEAL
6604 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6605 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6606#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006607#ifdef FEAT_AUTOCMD
6608 CENT("MatchParen term=reverse ctermbg=Cyan",
6609 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6610#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006611#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006612 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006613#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006614 NULL
6615 };
6616
6617static char *(highlight_init_dark[]) =
6618 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006619 CENT("Directory term=bold ctermfg=LightCyan",
6620 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6621 CENT("LineNr term=underline ctermfg=Yellow",
6622 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6623 CENT("MoreMsg term=bold ctermfg=LightGreen",
6624 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6625 CENT("Question term=standout ctermfg=LightGreen",
6626 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6627 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6628 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6629 CENT("SpecialKey term=bold ctermfg=LightBlue",
6630 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006631#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006632 CENT("SpellBad term=reverse ctermbg=Red",
6633 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6634 CENT("SpellCap term=reverse ctermbg=Blue",
6635 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6636 CENT("SpellRare term=reverse ctermbg=Magenta",
6637 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6638 CENT("SpellLocal term=underline ctermbg=Cyan",
6639 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006640#endif
6641#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006642 CENT("Pmenu ctermbg=Magenta",
6643 "Pmenu ctermbg=Magenta guibg=Magenta"),
6644 CENT("PmenuSel ctermbg=DarkGrey",
6645 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006646#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006647 CENT("Title term=bold ctermfg=LightMagenta",
6648 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6649 CENT("WarningMsg term=standout ctermfg=LightRed",
6650 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006651#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006652 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6653 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006654#endif
6655#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006656 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6657 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6658 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6659 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006660#endif
6661#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006662 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6663 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006664#endif
6665#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006666 CENT("Visual term=reverse",
6667 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006668#endif
6669#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006670 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6671 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6672 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6673 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6674 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6675 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006676#endif
6677#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006678 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6679 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006680#endif
6681#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006682 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006683 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006684 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006685 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006686 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6687 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006688#endif
6689#ifdef FEAT_AUTOCMD
6690 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6691 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006692#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006693#ifdef FEAT_CONCEAL
6694 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6695 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6696#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006697#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006698 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006699#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006700 NULL
6701 };
6702
6703 void
6704init_highlight(both, reset)
6705 int both; /* include groups where 'bg' doesn't matter */
6706 int reset; /* clear group first */
6707{
6708 int i;
6709 char **pp;
6710 static int had_both = FALSE;
6711#ifdef FEAT_EVAL
6712 char_u *p;
6713
6714 /*
6715 * Try finding the color scheme file. Used when a color file was loaded
6716 * and 'background' or 't_Co' is changed.
6717 */
6718 p = get_var_value((char_u *)"g:colors_name");
6719 if (p != NULL && load_colors(p) == OK)
6720 return;
6721#endif
6722
6723 /*
6724 * Didn't use a color file, use the compiled-in colors.
6725 */
6726 if (both)
6727 {
6728 had_both = TRUE;
6729 pp = highlight_init_both;
6730 for (i = 0; pp[i] != NULL; ++i)
6731 do_highlight((char_u *)pp[i], reset, TRUE);
6732 }
6733 else if (!had_both)
6734 /* Don't do anything before the call with both == TRUE from main().
6735 * Not everything has been setup then, and that call will overrule
6736 * everything anyway. */
6737 return;
6738
6739 if (*p_bg == 'l')
6740 pp = highlight_init_light;
6741 else
6742 pp = highlight_init_dark;
6743 for (i = 0; pp[i] != NULL; ++i)
6744 do_highlight((char_u *)pp[i], reset, TRUE);
6745
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006746 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006747 * depend on the number of colors available.
6748 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006749 * to avoid Statement highlighted text disappears.
6750 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006751 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006752 do_highlight((char_u *)(*p_bg == 'l'
6753 ? "Visual cterm=NONE ctermbg=LightGrey"
6754 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006755 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006756 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006757 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6758 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006759 if (*p_bg == 'l')
6760 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6761 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006762
Bram Moolenaar071d4272004-06-13 20:20:40 +00006763#ifdef FEAT_SYN_HL
6764 /*
6765 * If syntax highlighting is enabled load the highlighting for it.
6766 */
6767 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006768 {
6769 static int recursive = 0;
6770
6771 if (recursive >= 5)
6772 EMSG(_("E679: recursive loop loading syncolor.vim"));
6773 else
6774 {
6775 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006776 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006777 --recursive;
6778 }
6779 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006780#endif
6781}
6782
6783/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006784 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006785 * Return OK for success, FAIL for failure.
6786 */
6787 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006788load_colors(name)
6789 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006790{
6791 char_u *buf;
6792 int retval = FAIL;
6793 static int recursive = FALSE;
6794
6795 /* When being called recursively, this is probably because setting
6796 * 'background' caused the highlighting to be reloaded. This means it is
6797 * working, thus we should return OK. */
6798 if (recursive)
6799 return OK;
6800
6801 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006802 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006803 if (buf != NULL)
6804 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006805 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006806 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006807 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006808#ifdef FEAT_AUTOCMD
6809 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6810#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006811 }
6812 recursive = FALSE;
6813
6814 return retval;
6815}
6816
6817/*
6818 * Handle the ":highlight .." command.
6819 * When using ":hi clear" this is called recursively for each group with
6820 * "forceit" and "init" both TRUE.
6821 */
6822 void
6823do_highlight(line, forceit, init)
6824 char_u *line;
6825 int forceit;
6826 int init; /* TRUE when called for initializing */
6827{
6828 char_u *name_end;
6829 char_u *p;
6830 char_u *linep;
6831 char_u *key_start;
6832 char_u *arg_start;
6833 char_u *key = NULL, *arg = NULL;
6834 long i;
6835 int off;
6836 int len;
6837 int attr;
6838 int id;
6839 int idx;
6840 int dodefault = FALSE;
6841 int doclear = FALSE;
6842 int dolink = FALSE;
6843 int error = FALSE;
6844 int color;
6845 int is_normal_group = FALSE; /* "Normal" group */
6846#ifdef FEAT_GUI_X11
6847 int is_menu_group = FALSE; /* "Menu" group */
6848 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6849 int is_tooltip_group = FALSE; /* "Tooltip" group */
6850 int do_colors = FALSE; /* need to update colors? */
6851#else
6852# define is_menu_group 0
6853# define is_tooltip_group 0
6854#endif
6855
6856 /*
6857 * If no argument, list current highlighting.
6858 */
6859 if (ends_excmd(*line))
6860 {
6861 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6862 /* TODO: only call when the group has attributes set */
6863 highlight_list_one((int)i);
6864 return;
6865 }
6866
6867 /*
6868 * Isolate the name.
6869 */
6870 name_end = skiptowhite(line);
6871 linep = skipwhite(name_end);
6872
6873 /*
6874 * Check for "default" argument.
6875 */
6876 if (STRNCMP(line, "default", name_end - line) == 0)
6877 {
6878 dodefault = TRUE;
6879 line = linep;
6880 name_end = skiptowhite(line);
6881 linep = skipwhite(name_end);
6882 }
6883
6884 /*
6885 * Check for "clear" or "link" argument.
6886 */
6887 if (STRNCMP(line, "clear", name_end - line) == 0)
6888 doclear = TRUE;
6889 if (STRNCMP(line, "link", name_end - line) == 0)
6890 dolink = TRUE;
6891
6892 /*
6893 * ":highlight {group-name}": list highlighting for one group.
6894 */
6895 if (!doclear && !dolink && ends_excmd(*linep))
6896 {
6897 id = syn_namen2id(line, (int)(name_end - line));
6898 if (id == 0)
6899 EMSG2(_("E411: highlight group not found: %s"), line);
6900 else
6901 highlight_list_one(id);
6902 return;
6903 }
6904
6905 /*
6906 * Handle ":highlight link {from} {to}" command.
6907 */
6908 if (dolink)
6909 {
6910 char_u *from_start = linep;
6911 char_u *from_end;
6912 char_u *to_start;
6913 char_u *to_end;
6914 int from_id;
6915 int to_id;
6916
6917 from_end = skiptowhite(from_start);
6918 to_start = skipwhite(from_end);
6919 to_end = skiptowhite(to_start);
6920
6921 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6922 {
6923 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6924 from_start);
6925 return;
6926 }
6927
6928 if (!ends_excmd(*skipwhite(to_end)))
6929 {
6930 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6931 return;
6932 }
6933
6934 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6935 if (STRNCMP(to_start, "NONE", 4) == 0)
6936 to_id = 0;
6937 else
6938 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6939
6940 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6941 {
6942 /*
6943 * Don't allow a link when there already is some highlighting
6944 * for the group, unless '!' is used
6945 */
6946 if (to_id > 0 && !forceit && !init
6947 && hl_has_settings(from_id - 1, dodefault))
6948 {
6949 if (sourcing_name == NULL && !dodefault)
6950 EMSG(_("E414: group has settings, highlight link ignored"));
6951 }
6952 else
6953 {
6954 if (!init)
6955 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6956 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006957#ifdef FEAT_EVAL
6958 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6959#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006960 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006961 }
6962 }
6963
6964 /* Only call highlight_changed() once, after sourcing a syntax file */
6965 need_highlight_changed = TRUE;
6966
6967 return;
6968 }
6969
6970 if (doclear)
6971 {
6972 /*
6973 * ":highlight clear [group]" command.
6974 */
6975 line = linep;
6976 if (ends_excmd(*line))
6977 {
6978#ifdef FEAT_GUI
6979 /* First, we do not destroy the old values, but allocate the new
6980 * ones and update the display. THEN we destroy the old values.
6981 * If we destroy the old values first, then the old values
6982 * (such as GuiFont's or GuiFontset's) will still be displayed but
6983 * invalid because they were free'd.
6984 */
6985 if (gui.in_use)
6986 {
6987# ifdef FEAT_BEVAL_TIP
6988 gui_init_tooltip_font();
6989# endif
6990# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6991 gui_init_menu_font();
6992# endif
6993 }
6994# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6995 gui_mch_def_colors();
6996# endif
6997# ifdef FEAT_GUI_X11
6998# ifdef FEAT_MENU
6999
7000 /* This only needs to be done when there is no Menu highlight
7001 * group defined by default, which IS currently the case.
7002 */
7003 gui_mch_new_menu_colors();
7004# endif
7005 if (gui.in_use)
7006 {
7007 gui_new_scrollbar_colors();
7008# ifdef FEAT_BEVAL
7009 gui_mch_new_tooltip_colors();
7010# endif
7011# ifdef FEAT_MENU
7012 gui_mch_new_menu_font();
7013# endif
7014 }
7015# endif
7016
7017 /* Ok, we're done allocating the new default graphics items.
7018 * The screen should already be refreshed at this point.
7019 * It is now Ok to clear out the old data.
7020 */
7021#endif
7022#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007023 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007024#endif
7025 restore_cterm_colors();
7026
7027 /*
7028 * Clear all default highlight groups and load the defaults.
7029 */
7030 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7031 highlight_clear(idx);
7032 init_highlight(TRUE, TRUE);
7033#ifdef FEAT_GUI
7034 if (gui.in_use)
7035 highlight_gui_started();
7036#endif
7037 highlight_changed();
7038 redraw_later_clear();
7039 return;
7040 }
7041 name_end = skiptowhite(line);
7042 linep = skipwhite(name_end);
7043 }
7044
7045 /*
7046 * Find the group name in the table. If it does not exist yet, add it.
7047 */
7048 id = syn_check_group(line, (int)(name_end - line));
7049 if (id == 0) /* failed (out of memory) */
7050 return;
7051 idx = id - 1; /* index is ID minus one */
7052
7053 /* Return if "default" was used and the group already has settings. */
7054 if (dodefault && hl_has_settings(idx, TRUE))
7055 return;
7056
7057 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7058 is_normal_group = TRUE;
7059#ifdef FEAT_GUI_X11
7060 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7061 is_menu_group = TRUE;
7062 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7063 is_scrollbar_group = TRUE;
7064 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7065 is_tooltip_group = TRUE;
7066#endif
7067
7068 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7069 if (doclear || (forceit && init))
7070 {
7071 highlight_clear(idx);
7072 if (!doclear)
7073 HL_TABLE()[idx].sg_set = 0;
7074 }
7075
7076 if (!doclear)
7077 while (!ends_excmd(*linep))
7078 {
7079 key_start = linep;
7080 if (*linep == '=')
7081 {
7082 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7083 error = TRUE;
7084 break;
7085 }
7086
7087 /*
7088 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7089 * "guibg").
7090 */
7091 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7092 ++linep;
7093 vim_free(key);
7094 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7095 if (key == NULL)
7096 {
7097 error = TRUE;
7098 break;
7099 }
7100 linep = skipwhite(linep);
7101
7102 if (STRCMP(key, "NONE") == 0)
7103 {
7104 if (!init || HL_TABLE()[idx].sg_set == 0)
7105 {
7106 if (!init)
7107 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7108 highlight_clear(idx);
7109 }
7110 continue;
7111 }
7112
7113 /*
7114 * Check for the equal sign.
7115 */
7116 if (*linep != '=')
7117 {
7118 EMSG2(_("E416: missing equal sign: %s"), key_start);
7119 error = TRUE;
7120 break;
7121 }
7122 ++linep;
7123
7124 /*
7125 * Isolate the argument.
7126 */
7127 linep = skipwhite(linep);
7128 if (*linep == '\'') /* guifg='color name' */
7129 {
7130 arg_start = ++linep;
7131 linep = vim_strchr(linep, '\'');
7132 if (linep == NULL)
7133 {
7134 EMSG2(_(e_invarg2), key_start);
7135 error = TRUE;
7136 break;
7137 }
7138 }
7139 else
7140 {
7141 arg_start = linep;
7142 linep = skiptowhite(linep);
7143 }
7144 if (linep == arg_start)
7145 {
7146 EMSG2(_("E417: missing argument: %s"), key_start);
7147 error = TRUE;
7148 break;
7149 }
7150 vim_free(arg);
7151 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7152 if (arg == NULL)
7153 {
7154 error = TRUE;
7155 break;
7156 }
7157 if (*linep == '\'')
7158 ++linep;
7159
7160 /*
7161 * Store the argument.
7162 */
7163 if ( STRCMP(key, "TERM") == 0
7164 || STRCMP(key, "CTERM") == 0
7165 || STRCMP(key, "GUI") == 0)
7166 {
7167 attr = 0;
7168 off = 0;
7169 while (arg[off] != NUL)
7170 {
7171 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7172 {
7173 len = (int)STRLEN(hl_name_table[i]);
7174 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7175 {
7176 attr |= hl_attr_table[i];
7177 off += len;
7178 break;
7179 }
7180 }
7181 if (i < 0)
7182 {
7183 EMSG2(_("E418: Illegal value: %s"), arg);
7184 error = TRUE;
7185 break;
7186 }
7187 if (arg[off] == ',') /* another one follows */
7188 ++off;
7189 }
7190 if (error)
7191 break;
7192 if (*key == 'T')
7193 {
7194 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7195 {
7196 if (!init)
7197 HL_TABLE()[idx].sg_set |= SG_TERM;
7198 HL_TABLE()[idx].sg_term = attr;
7199 }
7200 }
7201 else if (*key == 'C')
7202 {
7203 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7204 {
7205 if (!init)
7206 HL_TABLE()[idx].sg_set |= SG_CTERM;
7207 HL_TABLE()[idx].sg_cterm = attr;
7208 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7209 }
7210 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007211#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007212 else
7213 {
7214 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7215 {
7216 if (!init)
7217 HL_TABLE()[idx].sg_set |= SG_GUI;
7218 HL_TABLE()[idx].sg_gui = attr;
7219 }
7220 }
7221#endif
7222 }
7223 else if (STRCMP(key, "FONT") == 0)
7224 {
7225 /* in non-GUI fonts are simply ignored */
7226#ifdef FEAT_GUI
7227 if (!gui.shell_created)
7228 {
7229 /* GUI not started yet, always accept the name. */
7230 vim_free(HL_TABLE()[idx].sg_font_name);
7231 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7232 }
7233 else
7234 {
7235 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7236# ifdef FEAT_XFONTSET
7237 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7238# endif
7239 /* First, save the current font/fontset.
7240 * Then try to allocate the font/fontset.
7241 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7242 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7243 */
7244
7245 HL_TABLE()[idx].sg_font = NOFONT;
7246# ifdef FEAT_XFONTSET
7247 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7248# endif
7249 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7250 is_tooltip_group);
7251
7252# ifdef FEAT_XFONTSET
7253 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7254 {
7255 /* New fontset was accepted. Free the old one, if there was
7256 * one.
7257 */
7258 gui_mch_free_fontset(temp_sg_fontset);
7259 vim_free(HL_TABLE()[idx].sg_font_name);
7260 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7261 }
7262 else
7263 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7264# endif
7265 if (HL_TABLE()[idx].sg_font != NOFONT)
7266 {
7267 /* New font was accepted. Free the old one, if there was
7268 * one.
7269 */
7270 gui_mch_free_font(temp_sg_font);
7271 vim_free(HL_TABLE()[idx].sg_font_name);
7272 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7273 }
7274 else
7275 HL_TABLE()[idx].sg_font = temp_sg_font;
7276 }
7277#endif
7278 }
7279 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7280 {
7281 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7282 {
7283 if (!init)
7284 HL_TABLE()[idx].sg_set |= SG_CTERM;
7285
7286 /* When setting the foreground color, and previously the "bold"
7287 * flag was set for a light color, reset it now */
7288 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7289 {
7290 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7291 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7292 }
7293
7294 if (VIM_ISDIGIT(*arg))
7295 color = atoi((char *)arg);
7296 else if (STRICMP(arg, "fg") == 0)
7297 {
7298 if (cterm_normal_fg_color)
7299 color = cterm_normal_fg_color - 1;
7300 else
7301 {
7302 EMSG(_("E419: FG color unknown"));
7303 error = TRUE;
7304 break;
7305 }
7306 }
7307 else if (STRICMP(arg, "bg") == 0)
7308 {
7309 if (cterm_normal_bg_color > 0)
7310 color = cterm_normal_bg_color - 1;
7311 else
7312 {
7313 EMSG(_("E420: BG color unknown"));
7314 error = TRUE;
7315 break;
7316 }
7317 }
7318 else
7319 {
7320 static char *(color_names[28]) = {
7321 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7322 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7323 "Gray", "Grey",
7324 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7325 "Blue", "LightBlue", "Green", "LightGreen",
7326 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7327 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7328 static int color_numbers_16[28] = {0, 1, 2, 3,
7329 4, 5, 6, 6,
7330 7, 7,
7331 7, 7, 8, 8,
7332 9, 9, 10, 10,
7333 11, 11, 12, 12, 13,
7334 13, 14, 14, 15, -1};
7335 /* for xterm with 88 colors... */
7336 static int color_numbers_88[28] = {0, 4, 2, 6,
7337 1, 5, 32, 72,
7338 84, 84,
7339 7, 7, 82, 82,
7340 12, 43, 10, 61,
7341 14, 63, 9, 74, 13,
7342 75, 11, 78, 15, -1};
7343 /* for xterm with 256 colors... */
7344 static int color_numbers_256[28] = {0, 4, 2, 6,
7345 1, 5, 130, 130,
7346 248, 248,
7347 7, 7, 242, 242,
7348 12, 81, 10, 121,
7349 14, 159, 9, 224, 13,
7350 225, 11, 229, 15, -1};
7351 /* for terminals with less than 16 colors... */
7352 static int color_numbers_8[28] = {0, 4, 2, 6,
7353 1, 5, 3, 3,
7354 7, 7,
7355 7, 7, 0+8, 0+8,
7356 4+8, 4+8, 2+8, 2+8,
7357 6+8, 6+8, 1+8, 1+8, 5+8,
7358 5+8, 3+8, 3+8, 7+8, -1};
7359#if defined(__QNXNTO__)
7360 static int *color_numbers_8_qansi = color_numbers_8;
7361 /* On qnx, the 8 & 16 color arrays are the same */
7362 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7363 color_numbers_8_qansi = color_numbers_16;
7364#endif
7365
7366 /* reduce calls to STRICMP a bit, it can be slow */
7367 off = TOUPPER_ASC(*arg);
7368 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7369 if (off == color_names[i][0]
7370 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7371 break;
7372 if (i < 0)
7373 {
7374 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7375 error = TRUE;
7376 break;
7377 }
7378
7379 /* Use the _16 table to check if its a valid color name. */
7380 color = color_numbers_16[i];
7381 if (color >= 0)
7382 {
7383 if (t_colors == 8)
7384 {
7385 /* t_Co is 8: use the 8 colors table */
7386#if defined(__QNXNTO__)
7387 color = color_numbers_8_qansi[i];
7388#else
7389 color = color_numbers_8[i];
7390#endif
7391 if (key[5] == 'F')
7392 {
7393 /* set/reset bold attribute to get light foreground
7394 * colors (on some terminals, e.g. "linux") */
7395 if (color & 8)
7396 {
7397 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7398 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7399 }
7400 else
7401 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7402 }
7403 color &= 7; /* truncate to 8 colors */
7404 }
7405 else if (t_colors == 16 || t_colors == 88
7406 || t_colors == 256)
7407 {
7408 /*
7409 * Guess: if the termcap entry ends in 'm', it is
7410 * probably an xterm-like terminal. Use the changed
7411 * order for colors.
7412 */
7413 if (*T_CAF != NUL)
7414 p = T_CAF;
7415 else
7416 p = T_CSF;
7417 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7418 switch (t_colors)
7419 {
7420 case 16:
7421 color = color_numbers_8[i];
7422 break;
7423 case 88:
7424 color = color_numbers_88[i];
7425 break;
7426 case 256:
7427 color = color_numbers_256[i];
7428 break;
7429 }
7430 }
7431 }
7432 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007433 /* Add one to the argument, to avoid zero. Zero is used for
7434 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007435 if (key[5] == 'F')
7436 {
7437 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7438 if (is_normal_group)
7439 {
7440 cterm_normal_fg_color = color + 1;
7441 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7442#ifdef FEAT_GUI
7443 /* Don't do this if the GUI is used. */
7444 if (!gui.in_use && !gui.starting)
7445#endif
7446 {
7447 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007448 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007449 term_fg_color(color);
7450 }
7451 }
7452 }
7453 else
7454 {
7455 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7456 if (is_normal_group)
7457 {
7458 cterm_normal_bg_color = color + 1;
7459#ifdef FEAT_GUI
7460 /* Don't mess with 'background' if the GUI is used. */
7461 if (!gui.in_use && !gui.starting)
7462#endif
7463 {
7464 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007465 if (color >= 0)
7466 {
7467 if (termcap_active)
7468 term_bg_color(color);
7469 if (t_colors < 16)
7470 i = (color == 0 || color == 4);
7471 else
7472 i = (color < 7 || color == 8);
7473 /* Set the 'background' option if the value is
7474 * wrong. */
7475 if (i != (*p_bg == 'd'))
7476 set_option_value((char_u *)"bg", 0L,
7477 i ? (char_u *)"dark"
7478 : (char_u *)"light", 0);
7479 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007480 }
7481 }
7482 }
7483 }
7484 }
7485 else if (STRCMP(key, "GUIFG") == 0)
7486 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007487#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007488 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007489 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007490 if (!init)
7491 HL_TABLE()[idx].sg_set |= SG_GUI;
7492
Bram Moolenaar61623362010-07-14 22:04:22 +02007493# ifdef FEAT_GUI
7494 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007495 i = color_name2handle(arg);
7496 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7497 {
7498 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007499# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007500 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7501 if (STRCMP(arg, "NONE"))
7502 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7503 else
7504 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007505# ifdef FEAT_GUI
7506# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007507 if (is_menu_group)
7508 gui.menu_fg_pixel = i;
7509 if (is_scrollbar_group)
7510 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007511# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007512 if (is_tooltip_group)
7513 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007514# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007515 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007516# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007517 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007518# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007520#endif
7521 }
7522 else if (STRCMP(key, "GUIBG") == 0)
7523 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007524#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007525 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007526 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007527 if (!init)
7528 HL_TABLE()[idx].sg_set |= SG_GUI;
7529
Bram Moolenaar61623362010-07-14 22:04:22 +02007530# ifdef FEAT_GUI
7531 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007532 i = color_name2handle(arg);
7533 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7534 {
7535 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007536# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007537 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7538 if (STRCMP(arg, "NONE") != 0)
7539 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7540 else
7541 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007542# ifdef FEAT_GUI
7543# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007544 if (is_menu_group)
7545 gui.menu_bg_pixel = i;
7546 if (is_scrollbar_group)
7547 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007548# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007549 if (is_tooltip_group)
7550 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007551# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007552 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007553# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007554 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007555# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007556 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007557#endif
7558 }
7559 else if (STRCMP(key, "GUISP") == 0)
7560 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007561#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007562 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7563 {
7564 if (!init)
7565 HL_TABLE()[idx].sg_set |= SG_GUI;
7566
Bram Moolenaar61623362010-07-14 22:04:22 +02007567# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007568 i = color_name2handle(arg);
7569 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7570 {
7571 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007572# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007573 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7574 if (STRCMP(arg, "NONE") != 0)
7575 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7576 else
7577 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007578# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007579 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007580# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007581 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007582#endif
7583 }
7584 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7585 {
7586 char_u buf[100];
7587 char_u *tname;
7588
7589 if (!init)
7590 HL_TABLE()[idx].sg_set |= SG_TERM;
7591
7592 /*
7593 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007594 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007595 */
7596 if (STRNCMP(arg, "t_", 2) == 0)
7597 {
7598 off = 0;
7599 buf[0] = 0;
7600 while (arg[off] != NUL)
7601 {
7602 /* Isolate one termcap name */
7603 for (len = 0; arg[off + len] &&
7604 arg[off + len] != ','; ++len)
7605 ;
7606 tname = vim_strnsave(arg + off, len);
7607 if (tname == NULL) /* out of memory */
7608 {
7609 error = TRUE;
7610 break;
7611 }
7612 /* lookup the escape sequence for the item */
7613 p = get_term_code(tname);
7614 vim_free(tname);
7615 if (p == NULL) /* ignore non-existing things */
7616 p = (char_u *)"";
7617
7618 /* Append it to the already found stuff */
7619 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7620 {
7621 EMSG2(_("E422: terminal code too long: %s"), arg);
7622 error = TRUE;
7623 break;
7624 }
7625 STRCAT(buf, p);
7626
7627 /* Advance to the next item */
7628 off += len;
7629 if (arg[off] == ',') /* another one follows */
7630 ++off;
7631 }
7632 }
7633 else
7634 {
7635 /*
7636 * Copy characters from arg[] to buf[], translating <> codes.
7637 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007638 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007639 {
7640 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007641 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007642 off += len;
7643 else /* copy as normal char */
7644 buf[off++] = *p++;
7645 }
7646 buf[off] = NUL;
7647 }
7648 if (error)
7649 break;
7650
7651 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7652 p = NULL;
7653 else
7654 p = vim_strsave(buf);
7655 if (key[2] == 'A')
7656 {
7657 vim_free(HL_TABLE()[idx].sg_start);
7658 HL_TABLE()[idx].sg_start = p;
7659 }
7660 else
7661 {
7662 vim_free(HL_TABLE()[idx].sg_stop);
7663 HL_TABLE()[idx].sg_stop = p;
7664 }
7665 }
7666 else
7667 {
7668 EMSG2(_("E423: Illegal argument: %s"), key_start);
7669 error = TRUE;
7670 break;
7671 }
7672
7673 /*
7674 * When highlighting has been given for a group, don't link it.
7675 */
7676 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7677 HL_TABLE()[idx].sg_link = 0;
7678
7679 /*
7680 * Continue with next argument.
7681 */
7682 linep = skipwhite(linep);
7683 }
7684
7685 /*
7686 * If there is an error, and it's a new entry, remove it from the table.
7687 */
7688 if (error && idx == highlight_ga.ga_len)
7689 syn_unadd_group();
7690 else
7691 {
7692 if (is_normal_group)
7693 {
7694 HL_TABLE()[idx].sg_term_attr = 0;
7695 HL_TABLE()[idx].sg_cterm_attr = 0;
7696#ifdef FEAT_GUI
7697 HL_TABLE()[idx].sg_gui_attr = 0;
7698 /*
7699 * Need to update all groups, because they might be using "bg"
7700 * and/or "fg", which have been changed now.
7701 */
7702 if (gui.in_use)
7703 highlight_gui_started();
7704#endif
7705 }
7706#ifdef FEAT_GUI_X11
7707# ifdef FEAT_MENU
7708 else if (is_menu_group)
7709 {
7710 if (gui.in_use && do_colors)
7711 gui_mch_new_menu_colors();
7712 }
7713# endif
7714 else if (is_scrollbar_group)
7715 {
7716 if (gui.in_use && do_colors)
7717 gui_new_scrollbar_colors();
7718 }
7719# ifdef FEAT_BEVAL
7720 else if (is_tooltip_group)
7721 {
7722 if (gui.in_use && do_colors)
7723 gui_mch_new_tooltip_colors();
7724 }
7725# endif
7726#endif
7727 else
7728 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007729#ifdef FEAT_EVAL
7730 HL_TABLE()[idx].sg_scriptID = current_SID;
7731#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007732 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007733 }
7734 vim_free(key);
7735 vim_free(arg);
7736
7737 /* Only call highlight_changed() once, after sourcing a syntax file */
7738 need_highlight_changed = TRUE;
7739}
7740
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007741#if defined(EXITFREE) || defined(PROTO)
7742 void
7743free_highlight()
7744{
7745 int i;
7746
7747 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007748 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007749 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007750 vim_free(HL_TABLE()[i].sg_name);
7751 vim_free(HL_TABLE()[i].sg_name_u);
7752 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007753 ga_clear(&highlight_ga);
7754}
7755#endif
7756
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757/*
7758 * Reset the cterm colors to what they were before Vim was started, if
7759 * possible. Otherwise reset them to zero.
7760 */
7761 void
7762restore_cterm_colors()
7763{
7764#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7765 /* Since t_me has been set, this probably means that the user
7766 * wants to use this as default colors. Need to reset default
7767 * background/foreground colors. */
7768 mch_set_normal_colors();
7769#else
7770 cterm_normal_fg_color = 0;
7771 cterm_normal_fg_bold = 0;
7772 cterm_normal_bg_color = 0;
7773#endif
7774}
7775
7776/*
7777 * Return TRUE if highlight group "idx" has any settings.
7778 * When "check_link" is TRUE also check for an existing link.
7779 */
7780 static int
7781hl_has_settings(idx, check_link)
7782 int idx;
7783 int check_link;
7784{
7785 return ( HL_TABLE()[idx].sg_term_attr != 0
7786 || HL_TABLE()[idx].sg_cterm_attr != 0
7787#ifdef FEAT_GUI
7788 || HL_TABLE()[idx].sg_gui_attr != 0
7789#endif
7790 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7791}
7792
7793/*
7794 * Clear highlighting for one group.
7795 */
7796 static void
7797highlight_clear(idx)
7798 int idx;
7799{
7800 HL_TABLE()[idx].sg_term = 0;
7801 vim_free(HL_TABLE()[idx].sg_start);
7802 HL_TABLE()[idx].sg_start = NULL;
7803 vim_free(HL_TABLE()[idx].sg_stop);
7804 HL_TABLE()[idx].sg_stop = NULL;
7805 HL_TABLE()[idx].sg_term_attr = 0;
7806 HL_TABLE()[idx].sg_cterm = 0;
7807 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7808 HL_TABLE()[idx].sg_cterm_fg = 0;
7809 HL_TABLE()[idx].sg_cterm_bg = 0;
7810 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007811#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007812 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7814 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7816 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007817 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7818 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007819#endif
7820#ifdef FEAT_GUI
7821 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7822 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7823 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007824 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7825 HL_TABLE()[idx].sg_font = NOFONT;
7826# ifdef FEAT_XFONTSET
7827 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7828 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7829# endif
7830 vim_free(HL_TABLE()[idx].sg_font_name);
7831 HL_TABLE()[idx].sg_font_name = NULL;
7832 HL_TABLE()[idx].sg_gui_attr = 0;
7833#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007834#ifdef FEAT_EVAL
7835 /* Clear the script ID only when there is no link, since that is not
7836 * cleared. */
7837 if (HL_TABLE()[idx].sg_link == 0)
7838 HL_TABLE()[idx].sg_scriptID = 0;
7839#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007840}
7841
7842#if defined(FEAT_GUI) || defined(PROTO)
7843/*
7844 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007845 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007846 * "Tooltip" colors.
7847 */
7848 void
7849set_normal_colors()
7850{
7851 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007852 &gui.norm_pixel, &gui.back_pixel,
7853 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 {
7855 gui_mch_new_colors();
7856 must_redraw = CLEAR;
7857 }
7858#ifdef FEAT_GUI_X11
7859 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007860 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7861 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007862 {
7863# ifdef FEAT_MENU
7864 gui_mch_new_menu_colors();
7865# endif
7866 must_redraw = CLEAR;
7867 }
7868# ifdef FEAT_BEVAL
7869 if (set_group_colors((char_u *)"Tooltip",
7870 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7871 FALSE, FALSE, TRUE))
7872 {
7873# ifdef FEAT_TOOLBAR
7874 gui_mch_new_tooltip_colors();
7875# endif
7876 must_redraw = CLEAR;
7877 }
7878#endif
7879 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007880 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7881 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007882 {
7883 gui_new_scrollbar_colors();
7884 must_redraw = CLEAR;
7885 }
7886#endif
7887}
7888
7889/*
7890 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7891 */
7892 static int
7893set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7894 char_u *name;
7895 guicolor_T *fgp;
7896 guicolor_T *bgp;
7897 int do_menu;
7898 int use_norm;
7899 int do_tooltip;
7900{
7901 int idx;
7902
7903 idx = syn_name2id(name) - 1;
7904 if (idx >= 0)
7905 {
7906 gui_do_one_color(idx, do_menu, do_tooltip);
7907
7908 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7909 *fgp = HL_TABLE()[idx].sg_gui_fg;
7910 else if (use_norm)
7911 *fgp = gui.def_norm_pixel;
7912 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7913 *bgp = HL_TABLE()[idx].sg_gui_bg;
7914 else if (use_norm)
7915 *bgp = gui.def_back_pixel;
7916 return TRUE;
7917 }
7918 return FALSE;
7919}
7920
7921/*
7922 * Get the font of the "Normal" group.
7923 * Returns "" when it's not found or not set.
7924 */
7925 char_u *
7926hl_get_font_name()
7927{
7928 int id;
7929 char_u *s;
7930
7931 id = syn_name2id((char_u *)"Normal");
7932 if (id > 0)
7933 {
7934 s = HL_TABLE()[id - 1].sg_font_name;
7935 if (s != NULL)
7936 return s;
7937 }
7938 return (char_u *)"";
7939}
7940
7941/*
7942 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7943 * actually chosen to be used.
7944 */
7945 void
7946hl_set_font_name(font_name)
7947 char_u *font_name;
7948{
7949 int id;
7950
7951 id = syn_name2id((char_u *)"Normal");
7952 if (id > 0)
7953 {
7954 vim_free(HL_TABLE()[id - 1].sg_font_name);
7955 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7956 }
7957}
7958
7959/*
7960 * Set background color for "Normal" group. Called by gui_set_bg_color()
7961 * when the color is known.
7962 */
7963 void
7964hl_set_bg_color_name(name)
7965 char_u *name; /* must have been allocated */
7966{
7967 int id;
7968
7969 if (name != NULL)
7970 {
7971 id = syn_name2id((char_u *)"Normal");
7972 if (id > 0)
7973 {
7974 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7975 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7976 }
7977 }
7978}
7979
7980/*
7981 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7982 * when the color is known.
7983 */
7984 void
7985hl_set_fg_color_name(name)
7986 char_u *name; /* must have been allocated */
7987{
7988 int id;
7989
7990 if (name != NULL)
7991 {
7992 id = syn_name2id((char_u *)"Normal");
7993 if (id > 0)
7994 {
7995 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7996 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7997 }
7998 }
7999}
8000
8001/*
8002 * Return the handle for a color name.
8003 * Returns INVALCOLOR when failed.
8004 */
8005 static guicolor_T
8006color_name2handle(name)
8007 char_u *name;
8008{
8009 if (STRCMP(name, "NONE") == 0)
8010 return INVALCOLOR;
8011
8012 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8013 return gui.norm_pixel;
8014 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8015 return gui.back_pixel;
8016
8017 return gui_get_color(name);
8018}
8019
8020/*
8021 * Return the handle for a font name.
8022 * Returns NOFONT when failed.
8023 */
8024 static GuiFont
8025font_name2handle(name)
8026 char_u *name;
8027{
8028 if (STRCMP(name, "NONE") == 0)
8029 return NOFONT;
8030
8031 return gui_mch_get_font(name, TRUE);
8032}
8033
8034# ifdef FEAT_XFONTSET
8035/*
8036 * Return the handle for a fontset name.
8037 * Returns NOFONTSET when failed.
8038 */
8039 static GuiFontset
8040fontset_name2handle(name, fixed_width)
8041 char_u *name;
8042 int fixed_width;
8043{
8044 if (STRCMP(name, "NONE") == 0)
8045 return NOFONTSET;
8046
8047 return gui_mch_get_fontset(name, TRUE, fixed_width);
8048}
8049# endif
8050
8051/*
8052 * Get the font or fontset for one highlight group.
8053 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008054 static void
8055hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8056 int idx;
8057 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008058 int do_normal; /* set normal font */
8059 int do_menu UNUSED; /* set menu font */
8060 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008061{
8062# ifdef FEAT_XFONTSET
8063 /* If 'guifontset' is not empty, first try using the name as a
8064 * fontset. If that doesn't work, use it as a font name. */
8065 if (*p_guifontset != NUL
8066# ifdef FONTSET_ALWAYS
8067 || do_menu
8068# endif
8069# ifdef FEAT_BEVAL_TIP
8070 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8071 || do_tooltip
8072# endif
8073 )
8074 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8075# ifdef FONTSET_ALWAYS
8076 || do_menu
8077# endif
8078# ifdef FEAT_BEVAL_TIP
8079 || do_tooltip
8080# endif
8081 );
8082 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8083 {
8084 /* If it worked and it's the Normal group, use it as the
8085 * normal fontset. Same for the Menu group. */
8086 if (do_normal)
8087 gui_init_font(arg, TRUE);
8088# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8089 if (do_menu)
8090 {
8091# ifdef FONTSET_ALWAYS
8092 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8093# else
8094 /* YIKES! This is a bug waiting to crash the program */
8095 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8096# endif
8097 gui_mch_new_menu_font();
8098 }
8099# ifdef FEAT_BEVAL
8100 if (do_tooltip)
8101 {
8102 /* The Athena widget set cannot currently handle switching between
8103 * displaying a single font and a fontset.
8104 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008105 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008106 * XFontStruct is used.
8107 */
8108 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8109 gui_mch_new_tooltip_font();
8110 }
8111# endif
8112# endif
8113 }
8114 else
8115# endif
8116 {
8117 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8118 /* If it worked and it's the Normal group, use it as the
8119 * normal font. Same for the Menu group. */
8120 if (HL_TABLE()[idx].sg_font != NOFONT)
8121 {
8122 if (do_normal)
8123 gui_init_font(arg, FALSE);
8124#ifndef FONTSET_ALWAYS
8125# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8126 if (do_menu)
8127 {
8128 gui.menu_font = HL_TABLE()[idx].sg_font;
8129 gui_mch_new_menu_font();
8130 }
8131# endif
8132#endif
8133 }
8134 }
8135}
8136
8137#endif /* FEAT_GUI */
8138
8139/*
8140 * Table with the specifications for an attribute number.
8141 * Note that this table is used by ALL buffers. This is required because the
8142 * GUI can redraw at any time for any buffer.
8143 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008144static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008145
8146#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8147
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008148static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149
8150#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8151
8152#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008153static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154
8155#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8156#endif
8157
8158/*
8159 * Return the attr number for a set of colors and font.
8160 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8161 * if the combination is new.
8162 * Return 0 for error (no more room).
8163 */
8164 static int
8165get_attr_entry(table, aep)
8166 garray_T *table;
8167 attrentry_T *aep;
8168{
8169 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008170 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008171 static int recursive = FALSE;
8172
8173 /*
8174 * Init the table, in case it wasn't done yet.
8175 */
8176 table->ga_itemsize = sizeof(attrentry_T);
8177 table->ga_growsize = 7;
8178
8179 /*
8180 * Try to find an entry with the same specifications.
8181 */
8182 for (i = 0; i < table->ga_len; ++i)
8183 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008184 taep = &(((attrentry_T *)table->ga_data)[i]);
8185 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008186 && (
8187#ifdef FEAT_GUI
8188 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008189 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8190 && aep->ae_u.gui.bg_color
8191 == taep->ae_u.gui.bg_color
8192 && aep->ae_u.gui.sp_color
8193 == taep->ae_u.gui.sp_color
8194 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008195# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008196 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008197# endif
8198 ))
8199 ||
8200#endif
8201 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008202 && (aep->ae_u.term.start == NULL)
8203 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008204 && (aep->ae_u.term.start == NULL
8205 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008206 taep->ae_u.term.start) == 0)
8207 && (aep->ae_u.term.stop == NULL)
8208 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008209 && (aep->ae_u.term.stop == NULL
8210 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008211 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008212 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008213 && aep->ae_u.cterm.fg_color
8214 == taep->ae_u.cterm.fg_color
8215 && aep->ae_u.cterm.bg_color
8216 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008217 ))
8218
8219 return i + ATTR_OFF;
8220 }
8221
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008222 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223 {
8224 /*
8225 * Running out of attribute entries! remove all attributes, and
8226 * compute new ones for all groups.
8227 * When called recursively, we are really out of numbers.
8228 */
8229 if (recursive)
8230 {
8231 EMSG(_("E424: Too many different highlighting attributes in use"));
8232 return 0;
8233 }
8234 recursive = TRUE;
8235
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008236 clear_hl_tables();
8237
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238 must_redraw = CLEAR;
8239
8240 for (i = 0; i < highlight_ga.ga_len; ++i)
8241 set_hl_attr(i);
8242
8243 recursive = FALSE;
8244 }
8245
8246 /*
8247 * This is a new combination of colors and font, add an entry.
8248 */
8249 if (ga_grow(table, 1) == FAIL)
8250 return 0;
8251
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008252 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8253 vim_memset(taep, 0, sizeof(attrentry_T));
8254 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008255#ifdef FEAT_GUI
8256 if (table == &gui_attr_table)
8257 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008258 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8259 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8260 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8261 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008262# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008263 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008264# endif
8265 }
8266#endif
8267 if (table == &term_attr_table)
8268 {
8269 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008270 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008271 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008272 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008273 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008274 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008275 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008276 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008277 }
8278 else if (table == &cterm_attr_table)
8279 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008280 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8281 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008282 }
8283 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008284 return (table->ga_len - 1 + ATTR_OFF);
8285}
8286
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008287/*
8288 * Clear all highlight tables.
8289 */
8290 void
8291clear_hl_tables()
8292{
8293 int i;
8294 attrentry_T *taep;
8295
8296#ifdef FEAT_GUI
8297 ga_clear(&gui_attr_table);
8298#endif
8299 for (i = 0; i < term_attr_table.ga_len; ++i)
8300 {
8301 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8302 vim_free(taep->ae_u.term.start);
8303 vim_free(taep->ae_u.term.stop);
8304 }
8305 ga_clear(&term_attr_table);
8306 ga_clear(&cterm_attr_table);
8307}
8308
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008309#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008310/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008311 * Combine special attributes (e.g., for spelling) with other attributes
8312 * (e.g., for syntax highlighting).
8313 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008314 * This creates a new group when required.
8315 * Since we expect there to be few spelling mistakes we don't cache the
8316 * result.
8317 * Return the resulting attributes.
8318 */
8319 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008320hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008321 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008322 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008323{
8324 attrentry_T *char_aep = NULL;
8325 attrentry_T *spell_aep;
8326 attrentry_T new_en;
8327
8328 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008329 return prim_attr;
8330 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8331 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008332#ifdef FEAT_GUI
8333 if (gui.in_use)
8334 {
8335 if (char_attr > HL_ALL)
8336 char_aep = syn_gui_attr2entry(char_attr);
8337 if (char_aep != NULL)
8338 new_en = *char_aep;
8339 else
8340 {
8341 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008342 new_en.ae_u.gui.fg_color = INVALCOLOR;
8343 new_en.ae_u.gui.bg_color = INVALCOLOR;
8344 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008345 if (char_attr <= HL_ALL)
8346 new_en.ae_attr = char_attr;
8347 }
8348
Bram Moolenaar30abd282005-06-22 22:35:10 +00008349 if (prim_attr <= HL_ALL)
8350 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008351 else
8352 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008353 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008354 if (spell_aep != NULL)
8355 {
8356 new_en.ae_attr |= spell_aep->ae_attr;
8357 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8358 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8359 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8360 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8361 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8362 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8363 if (spell_aep->ae_u.gui.font != NOFONT)
8364 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8365# ifdef FEAT_XFONTSET
8366 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8367 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8368# endif
8369 }
8370 }
8371 return get_attr_entry(&gui_attr_table, &new_en);
8372 }
8373#endif
8374
8375 if (t_colors > 1)
8376 {
8377 if (char_attr > HL_ALL)
8378 char_aep = syn_cterm_attr2entry(char_attr);
8379 if (char_aep != NULL)
8380 new_en = *char_aep;
8381 else
8382 {
8383 vim_memset(&new_en, 0, sizeof(new_en));
8384 if (char_attr <= HL_ALL)
8385 new_en.ae_attr = char_attr;
8386 }
8387
Bram Moolenaar30abd282005-06-22 22:35:10 +00008388 if (prim_attr <= HL_ALL)
8389 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008390 else
8391 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008392 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008393 if (spell_aep != NULL)
8394 {
8395 new_en.ae_attr |= spell_aep->ae_attr;
8396 if (spell_aep->ae_u.cterm.fg_color > 0)
8397 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8398 if (spell_aep->ae_u.cterm.bg_color > 0)
8399 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8400 }
8401 }
8402 return get_attr_entry(&cterm_attr_table, &new_en);
8403 }
8404
8405 if (char_attr > HL_ALL)
8406 char_aep = syn_term_attr2entry(char_attr);
8407 if (char_aep != NULL)
8408 new_en = *char_aep;
8409 else
8410 {
8411 vim_memset(&new_en, 0, sizeof(new_en));
8412 if (char_attr <= HL_ALL)
8413 new_en.ae_attr = char_attr;
8414 }
8415
Bram Moolenaar30abd282005-06-22 22:35:10 +00008416 if (prim_attr <= HL_ALL)
8417 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008418 else
8419 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008420 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008421 if (spell_aep != NULL)
8422 {
8423 new_en.ae_attr |= spell_aep->ae_attr;
8424 if (spell_aep->ae_u.term.start != NULL)
8425 {
8426 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8427 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8428 }
8429 }
8430 }
8431 return get_attr_entry(&term_attr_table, &new_en);
8432}
8433#endif
8434
Bram Moolenaar071d4272004-06-13 20:20:40 +00008435#ifdef FEAT_GUI
8436
8437 attrentry_T *
8438syn_gui_attr2entry(attr)
8439 int attr;
8440{
8441 attr -= ATTR_OFF;
8442 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8443 return NULL;
8444 return &(GUI_ATTR_ENTRY(attr));
8445}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008446#endif /* FEAT_GUI */
8447
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008448/*
8449 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8450 * Only to be used when "attr" > HL_ALL.
8451 */
8452 int
8453syn_attr2attr(attr)
8454 int attr;
8455{
8456 attrentry_T *aep;
8457
8458#ifdef FEAT_GUI
8459 if (gui.in_use)
8460 aep = syn_gui_attr2entry(attr);
8461 else
8462#endif
8463 if (t_colors > 1)
8464 aep = syn_cterm_attr2entry(attr);
8465 else
8466 aep = syn_term_attr2entry(attr);
8467
8468 if (aep == NULL) /* highlighting not set */
8469 return 0;
8470 return aep->ae_attr;
8471}
8472
8473
Bram Moolenaar071d4272004-06-13 20:20:40 +00008474 attrentry_T *
8475syn_term_attr2entry(attr)
8476 int attr;
8477{
8478 attr -= ATTR_OFF;
8479 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8480 return NULL;
8481 return &(TERM_ATTR_ENTRY(attr));
8482}
8483
8484 attrentry_T *
8485syn_cterm_attr2entry(attr)
8486 int attr;
8487{
8488 attr -= ATTR_OFF;
8489 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8490 return NULL;
8491 return &(CTERM_ATTR_ENTRY(attr));
8492}
8493
8494#define LIST_ATTR 1
8495#define LIST_STRING 2
8496#define LIST_INT 3
8497
8498 static void
8499highlight_list_one(id)
8500 int id;
8501{
8502 struct hl_group *sgp;
8503 int didh = FALSE;
8504
8505 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8506
8507 didh = highlight_list_arg(id, didh, LIST_ATTR,
8508 sgp->sg_term, NULL, "term");
8509 didh = highlight_list_arg(id, didh, LIST_STRING,
8510 0, sgp->sg_start, "start");
8511 didh = highlight_list_arg(id, didh, LIST_STRING,
8512 0, sgp->sg_stop, "stop");
8513
8514 didh = highlight_list_arg(id, didh, LIST_ATTR,
8515 sgp->sg_cterm, NULL, "cterm");
8516 didh = highlight_list_arg(id, didh, LIST_INT,
8517 sgp->sg_cterm_fg, NULL, "ctermfg");
8518 didh = highlight_list_arg(id, didh, LIST_INT,
8519 sgp->sg_cterm_bg, NULL, "ctermbg");
8520
Bram Moolenaar61623362010-07-14 22:04:22 +02008521#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008522 didh = highlight_list_arg(id, didh, LIST_ATTR,
8523 sgp->sg_gui, NULL, "gui");
8524 didh = highlight_list_arg(id, didh, LIST_STRING,
8525 0, sgp->sg_gui_fg_name, "guifg");
8526 didh = highlight_list_arg(id, didh, LIST_STRING,
8527 0, sgp->sg_gui_bg_name, "guibg");
8528 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008529 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008530#endif
8531#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008532 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008533 0, sgp->sg_font_name, "font");
8534#endif
8535
Bram Moolenaar661b1822005-07-28 22:36:45 +00008536 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537 {
8538 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008539 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008540 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8541 msg_putchar(' ');
8542 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8543 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008544
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008545 if (!didh)
8546 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008547#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008548 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008549 last_set_msg(sgp->sg_scriptID);
8550#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008551}
8552
8553 static int
8554highlight_list_arg(id, didh, type, iarg, sarg, name)
8555 int id;
8556 int didh;
8557 int type;
8558 int iarg;
8559 char_u *sarg;
8560 char *name;
8561{
8562 char_u buf[100];
8563 char_u *ts;
8564 int i;
8565
Bram Moolenaar661b1822005-07-28 22:36:45 +00008566 if (got_int)
8567 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008568 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8569 {
8570 ts = buf;
8571 if (type == LIST_INT)
8572 sprintf((char *)buf, "%d", iarg - 1);
8573 else if (type == LIST_STRING)
8574 ts = sarg;
8575 else /* type == LIST_ATTR */
8576 {
8577 buf[0] = NUL;
8578 for (i = 0; hl_attr_table[i] != 0; ++i)
8579 {
8580 if (iarg & hl_attr_table[i])
8581 {
8582 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008583 vim_strcat(buf, (char_u *)",", 100);
8584 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008585 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8586 }
8587 }
8588 }
8589
8590 (void)syn_list_header(didh,
8591 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8592 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008593 if (!got_int)
8594 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008595 if (*name != NUL)
8596 {
8597 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8598 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8599 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008600 msg_outtrans(ts);
8601 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008602 }
8603 return didh;
8604}
8605
8606#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8607/*
8608 * Return "1" if highlight group "id" has attribute "flag".
8609 * Return NULL otherwise.
8610 */
8611 char_u *
8612highlight_has_attr(id, flag, modec)
8613 int id;
8614 int flag;
8615 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8616{
8617 int attr;
8618
8619 if (id <= 0 || id > highlight_ga.ga_len)
8620 return NULL;
8621
Bram Moolenaar61623362010-07-14 22:04:22 +02008622#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008623 if (modec == 'g')
8624 attr = HL_TABLE()[id - 1].sg_gui;
8625 else
8626#endif
8627 if (modec == 'c')
8628 attr = HL_TABLE()[id - 1].sg_cterm;
8629 else
8630 attr = HL_TABLE()[id - 1].sg_term;
8631
8632 if (attr & flag)
8633 return (char_u *)"1";
8634 return NULL;
8635}
8636#endif
8637
8638#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8639/*
8640 * Return color name of highlight group "id".
8641 */
8642 char_u *
8643highlight_color(id, what, modec)
8644 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008645 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008646 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8647{
8648 static char_u name[20];
8649 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008650 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008651 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008652 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008653
8654 if (id <= 0 || id > highlight_ga.ga_len)
8655 return NULL;
8656
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008657 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008658 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008659 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008660 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008661 font = TRUE;
8662 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008663 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008664 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8665 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008666 if (modec == 'g')
8667 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008668# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008669 /* return font name */
8670 if (font)
8671 return HL_TABLE()[id - 1].sg_font_name;
8672
Bram Moolenaar071d4272004-06-13 20:20:40 +00008673 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008674 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008675 {
8676 guicolor_T color;
8677 long_u rgb;
8678 static char_u buf[10];
8679
8680 if (fg)
8681 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008682 else if (sp)
8683 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008684 else
8685 color = HL_TABLE()[id - 1].sg_gui_bg;
8686 if (color == INVALCOLOR)
8687 return NULL;
8688 rgb = gui_mch_get_rgb(color);
8689 sprintf((char *)buf, "#%02x%02x%02x",
8690 (unsigned)(rgb >> 16),
8691 (unsigned)(rgb >> 8) & 255,
8692 (unsigned)rgb & 255);
8693 return buf;
8694 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008695#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008696 if (fg)
8697 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008698 if (sp)
8699 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008700 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8701 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008702 if (font || sp)
8703 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008704 if (modec == 'c')
8705 {
8706 if (fg)
8707 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8708 else
8709 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8710 sprintf((char *)name, "%d", n);
8711 return name;
8712 }
8713 /* term doesn't have color */
8714 return NULL;
8715}
8716#endif
8717
8718#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8719 || defined(PROTO)
8720/*
8721 * Return color name of highlight group "id" as RGB value.
8722 */
8723 long_u
8724highlight_gui_color_rgb(id, fg)
8725 int id;
8726 int fg; /* TRUE = fg, FALSE = bg */
8727{
8728 guicolor_T color;
8729
8730 if (id <= 0 || id > highlight_ga.ga_len)
8731 return 0L;
8732
8733 if (fg)
8734 color = HL_TABLE()[id - 1].sg_gui_fg;
8735 else
8736 color = HL_TABLE()[id - 1].sg_gui_bg;
8737
8738 if (color == INVALCOLOR)
8739 return 0L;
8740
8741 return gui_mch_get_rgb(color);
8742}
8743#endif
8744
8745/*
8746 * Output the syntax list header.
8747 * Return TRUE when started a new line.
8748 */
8749 static int
8750syn_list_header(did_header, outlen, id)
8751 int did_header; /* did header already */
8752 int outlen; /* length of string that comes */
8753 int id; /* highlight group id */
8754{
8755 int endcol = 19;
8756 int newline = TRUE;
8757
8758 if (!did_header)
8759 {
8760 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008761 if (got_int)
8762 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008763 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8764 endcol = 15;
8765 }
8766 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008767 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008768 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008769 if (got_int)
8770 return TRUE;
8771 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008772 else
8773 {
8774 if (msg_col >= endcol) /* wrap around is like starting a new line */
8775 newline = FALSE;
8776 }
8777
8778 if (msg_col >= endcol) /* output at least one space */
8779 endcol = msg_col + 1;
8780 if (Columns <= endcol) /* avoid hang for tiny window */
8781 endcol = Columns - 1;
8782
8783 msg_advance(endcol);
8784
8785 /* Show "xxx" with the attributes. */
8786 if (!did_header)
8787 {
8788 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8789 msg_putchar(' ');
8790 }
8791
8792 return newline;
8793}
8794
8795/*
8796 * Set the attribute numbers for a highlight group.
8797 * Called after one of the attributes has changed.
8798 */
8799 static void
8800set_hl_attr(idx)
8801 int idx; /* index in array */
8802{
8803 attrentry_T at_en;
8804 struct hl_group *sgp = HL_TABLE() + idx;
8805
8806 /* The "Normal" group doesn't need an attribute number */
8807 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8808 return;
8809
8810#ifdef FEAT_GUI
8811 /*
8812 * For the GUI mode: If there are other than "normal" highlighting
8813 * attributes, need to allocate an attr number.
8814 */
8815 if (sgp->sg_gui_fg == INVALCOLOR
8816 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008817 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008818 && sgp->sg_font == NOFONT
8819# ifdef FEAT_XFONTSET
8820 && sgp->sg_fontset == NOFONTSET
8821# endif
8822 )
8823 {
8824 sgp->sg_gui_attr = sgp->sg_gui;
8825 }
8826 else
8827 {
8828 at_en.ae_attr = sgp->sg_gui;
8829 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8830 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008831 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008832 at_en.ae_u.gui.font = sgp->sg_font;
8833# ifdef FEAT_XFONTSET
8834 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8835# endif
8836 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8837 }
8838#endif
8839 /*
8840 * For the term mode: If there are other than "normal" highlighting
8841 * attributes, need to allocate an attr number.
8842 */
8843 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8844 sgp->sg_term_attr = sgp->sg_term;
8845 else
8846 {
8847 at_en.ae_attr = sgp->sg_term;
8848 at_en.ae_u.term.start = sgp->sg_start;
8849 at_en.ae_u.term.stop = sgp->sg_stop;
8850 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8851 }
8852
8853 /*
8854 * For the color term mode: If there are other than "normal"
8855 * highlighting attributes, need to allocate an attr number.
8856 */
8857 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8858 sgp->sg_cterm_attr = sgp->sg_cterm;
8859 else
8860 {
8861 at_en.ae_attr = sgp->sg_cterm;
8862 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8863 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8864 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8865 }
8866}
8867
8868/*
8869 * Lookup a highlight group name and return it's ID.
8870 * If it is not found, 0 is returned.
8871 */
8872 int
8873syn_name2id(name)
8874 char_u *name;
8875{
8876 int i;
8877 char_u name_u[200];
8878
8879 /* Avoid using stricmp() too much, it's slow on some systems */
8880 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8881 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008882 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008883 vim_strup(name_u);
8884 for (i = highlight_ga.ga_len; --i >= 0; )
8885 if (HL_TABLE()[i].sg_name_u != NULL
8886 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8887 break;
8888 return i + 1;
8889}
8890
8891#if defined(FEAT_EVAL) || defined(PROTO)
8892/*
8893 * Return TRUE if highlight group "name" exists.
8894 */
8895 int
8896highlight_exists(name)
8897 char_u *name;
8898{
8899 return (syn_name2id(name) > 0);
8900}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008901
8902# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8903/*
8904 * Return the name of highlight group "id".
8905 * When not a valid ID return an empty string.
8906 */
8907 char_u *
8908syn_id2name(id)
8909 int id;
8910{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008911 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008912 return (char_u *)"";
8913 return HL_TABLE()[id - 1].sg_name;
8914}
8915# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008916#endif
8917
8918/*
8919 * Like syn_name2id(), but take a pointer + length argument.
8920 */
8921 int
8922syn_namen2id(linep, len)
8923 char_u *linep;
8924 int len;
8925{
8926 char_u *name;
8927 int id = 0;
8928
8929 name = vim_strnsave(linep, len);
8930 if (name != NULL)
8931 {
8932 id = syn_name2id(name);
8933 vim_free(name);
8934 }
8935 return id;
8936}
8937
8938/*
8939 * Find highlight group name in the table and return it's ID.
8940 * The argument is a pointer to the name and the length of the name.
8941 * If it doesn't exist yet, a new entry is created.
8942 * Return 0 for failure.
8943 */
8944 int
8945syn_check_group(pp, len)
8946 char_u *pp;
8947 int len;
8948{
8949 int id;
8950 char_u *name;
8951
8952 name = vim_strnsave(pp, len);
8953 if (name == NULL)
8954 return 0;
8955
8956 id = syn_name2id(name);
8957 if (id == 0) /* doesn't exist yet */
8958 id = syn_add_group(name);
8959 else
8960 vim_free(name);
8961 return id;
8962}
8963
8964/*
8965 * Add new highlight group and return it's ID.
8966 * "name" must be an allocated string, it will be consumed.
8967 * Return 0 for failure.
8968 */
8969 static int
8970syn_add_group(name)
8971 char_u *name;
8972{
8973 char_u *p;
8974
8975 /* Check that the name is ASCII letters, digits and underscore. */
8976 for (p = name; *p != NUL; ++p)
8977 {
8978 if (!vim_isprintc(*p))
8979 {
8980 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008981 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008982 return 0;
8983 }
8984 else if (!ASCII_ISALNUM(*p) && *p != '_')
8985 {
8986 /* This is an error, but since there previously was no check only
8987 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008988 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008989 MSG(_("W18: Invalid character in group name"));
8990 break;
8991 }
8992 }
8993
8994 /*
8995 * First call for this growarray: init growing array.
8996 */
8997 if (highlight_ga.ga_data == NULL)
8998 {
8999 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9000 highlight_ga.ga_growsize = 10;
9001 }
9002
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009003 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009004 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009005 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009006 vim_free(name);
9007 return 0;
9008 }
9009
Bram Moolenaar071d4272004-06-13 20:20:40 +00009010 /*
9011 * Make room for at least one other syntax_highlight entry.
9012 */
9013 if (ga_grow(&highlight_ga, 1) == FAIL)
9014 {
9015 vim_free(name);
9016 return 0;
9017 }
9018
9019 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9020 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9021 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9022#ifdef FEAT_GUI
9023 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9024 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009025 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009026#endif
9027 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009028
9029 return highlight_ga.ga_len; /* ID is index plus one */
9030}
9031
9032/*
9033 * When, just after calling syn_add_group(), an error is discovered, this
9034 * function deletes the new name.
9035 */
9036 static void
9037syn_unadd_group()
9038{
9039 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009040 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9041 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9042}
9043
9044/*
9045 * Translate a group ID to highlight attributes.
9046 */
9047 int
9048syn_id2attr(hl_id)
9049 int hl_id;
9050{
9051 int attr;
9052 struct hl_group *sgp;
9053
9054 hl_id = syn_get_final_id(hl_id);
9055 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9056
9057#ifdef FEAT_GUI
9058 /*
9059 * Only use GUI attr when the GUI is being used.
9060 */
9061 if (gui.in_use)
9062 attr = sgp->sg_gui_attr;
9063 else
9064#endif
9065 if (t_colors > 1)
9066 attr = sgp->sg_cterm_attr;
9067 else
9068 attr = sgp->sg_term_attr;
9069
9070 return attr;
9071}
9072
9073#ifdef FEAT_GUI
9074/*
9075 * Get the GUI colors and attributes for a group ID.
9076 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9077 */
9078 int
9079syn_id2colors(hl_id, fgp, bgp)
9080 int hl_id;
9081 guicolor_T *fgp;
9082 guicolor_T *bgp;
9083{
9084 struct hl_group *sgp;
9085
9086 hl_id = syn_get_final_id(hl_id);
9087 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9088
9089 *fgp = sgp->sg_gui_fg;
9090 *bgp = sgp->sg_gui_bg;
9091 return sgp->sg_gui;
9092}
9093#endif
9094
9095/*
9096 * Translate a group ID to the final group ID (following links).
9097 */
9098 int
9099syn_get_final_id(hl_id)
9100 int hl_id;
9101{
9102 int count;
9103 struct hl_group *sgp;
9104
9105 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9106 return 0; /* Can be called from eval!! */
9107
9108 /*
9109 * Follow links until there is no more.
9110 * Look out for loops! Break after 100 links.
9111 */
9112 for (count = 100; --count >= 0; )
9113 {
9114 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9115 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9116 break;
9117 hl_id = sgp->sg_link;
9118 }
9119
9120 return hl_id;
9121}
9122
9123#ifdef FEAT_GUI
9124/*
9125 * Call this function just after the GUI has started.
9126 * It finds the font and color handles for the highlighting groups.
9127 */
9128 void
9129highlight_gui_started()
9130{
9131 int idx;
9132
9133 /* First get the colors from the "Normal" and "Menu" group, if set */
9134 set_normal_colors();
9135
9136 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9137 gui_do_one_color(idx, FALSE, FALSE);
9138
9139 highlight_changed();
9140}
9141
9142 static void
9143gui_do_one_color(idx, do_menu, do_tooltip)
9144 int idx;
9145 int do_menu; /* TRUE: might set the menu font */
9146 int do_tooltip; /* TRUE: might set the tooltip font */
9147{
9148 int didit = FALSE;
9149
9150 if (HL_TABLE()[idx].sg_font_name != NULL)
9151 {
9152 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9153 do_tooltip);
9154 didit = TRUE;
9155 }
9156 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9157 {
9158 HL_TABLE()[idx].sg_gui_fg =
9159 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9160 didit = TRUE;
9161 }
9162 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9163 {
9164 HL_TABLE()[idx].sg_gui_bg =
9165 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9166 didit = TRUE;
9167 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009168 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9169 {
9170 HL_TABLE()[idx].sg_gui_sp =
9171 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9172 didit = TRUE;
9173 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009174 if (didit) /* need to get a new attr number */
9175 set_hl_attr(idx);
9176}
9177
9178#endif
9179
9180/*
9181 * Translate the 'highlight' option into attributes in highlight_attr[] and
9182 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9183 * corresponding highlights to use on top of HLF_SNC is computed.
9184 * Called only when the 'highlight' option has been changed and upon first
9185 * screen redraw after any :highlight command.
9186 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9187 */
9188 int
9189highlight_changed()
9190{
9191 int hlf;
9192 int i;
9193 char_u *p;
9194 int attr;
9195 char_u *end;
9196 int id;
9197#ifdef USER_HIGHLIGHT
9198 char_u userhl[10];
9199# ifdef FEAT_STL_OPT
9200 int id_SNC = -1;
9201 int id_S = -1;
9202 int hlcnt;
9203# endif
9204#endif
9205 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9206
9207 need_highlight_changed = FALSE;
9208
9209 /*
9210 * Clear all attributes.
9211 */
9212 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9213 highlight_attr[hlf] = 0;
9214
9215 /*
9216 * First set all attributes to their default value.
9217 * Then use the attributes from the 'highlight' option.
9218 */
9219 for (i = 0; i < 2; ++i)
9220 {
9221 if (i)
9222 p = p_hl;
9223 else
9224 p = get_highlight_default();
9225 if (p == NULL) /* just in case */
9226 continue;
9227
9228 while (*p)
9229 {
9230 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9231 if (hl_flags[hlf] == *p)
9232 break;
9233 ++p;
9234 if (hlf == (int)HLF_COUNT || *p == NUL)
9235 return FAIL;
9236
9237 /*
9238 * Allow several hl_flags to be combined, like "bu" for
9239 * bold-underlined.
9240 */
9241 attr = 0;
9242 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9243 {
9244 if (vim_iswhite(*p)) /* ignore white space */
9245 continue;
9246
9247 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9248 return FAIL;
9249
9250 switch (*p)
9251 {
9252 case 'b': attr |= HL_BOLD;
9253 break;
9254 case 'i': attr |= HL_ITALIC;
9255 break;
9256 case '-':
9257 case 'n': /* no highlighting */
9258 break;
9259 case 'r': attr |= HL_INVERSE;
9260 break;
9261 case 's': attr |= HL_STANDOUT;
9262 break;
9263 case 'u': attr |= HL_UNDERLINE;
9264 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009265 case 'c': attr |= HL_UNDERCURL;
9266 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009267 case ':': ++p; /* highlight group name */
9268 if (attr || *p == NUL) /* no combinations */
9269 return FAIL;
9270 end = vim_strchr(p, ',');
9271 if (end == NULL)
9272 end = p + STRLEN(p);
9273 id = syn_check_group(p, (int)(end - p));
9274 if (id == 0)
9275 return FAIL;
9276 attr = syn_id2attr(id);
9277 p = end - 1;
9278#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9279 if (hlf == (int)HLF_SNC)
9280 id_SNC = syn_get_final_id(id);
9281 else if (hlf == (int)HLF_S)
9282 id_S = syn_get_final_id(id);
9283#endif
9284 break;
9285 default: return FAIL;
9286 }
9287 }
9288 highlight_attr[hlf] = attr;
9289
9290 p = skip_to_option_part(p); /* skip comma and spaces */
9291 }
9292 }
9293
9294#ifdef USER_HIGHLIGHT
9295 /* Setup the user highlights
9296 *
9297 * Temporarily utilize 10 more hl entries. Have to be in there
9298 * simultaneously in case of table overflows in get_attr_entry()
9299 */
9300# ifdef FEAT_STL_OPT
9301 if (ga_grow(&highlight_ga, 10) == FAIL)
9302 return FAIL;
9303 hlcnt = highlight_ga.ga_len;
9304 if (id_S == 0)
9305 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009306 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009307 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9308 id_S = hlcnt + 10;
9309 }
9310# endif
9311 for (i = 0; i < 9; i++)
9312 {
9313 sprintf((char *)userhl, "User%d", i + 1);
9314 id = syn_name2id(userhl);
9315 if (id == 0)
9316 {
9317 highlight_user[i] = 0;
9318# ifdef FEAT_STL_OPT
9319 highlight_stlnc[i] = 0;
9320# endif
9321 }
9322 else
9323 {
9324# ifdef FEAT_STL_OPT
9325 struct hl_group *hlt = HL_TABLE();
9326# endif
9327
9328 highlight_user[i] = syn_id2attr(id);
9329# ifdef FEAT_STL_OPT
9330 if (id_SNC == 0)
9331 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009332 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009333 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9334 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009335# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009336 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9337# endif
9338 }
9339 else
9340 mch_memmove(&hlt[hlcnt + i],
9341 &hlt[id_SNC - 1],
9342 sizeof(struct hl_group));
9343 hlt[hlcnt + i].sg_link = 0;
9344
9345 /* Apply difference between UserX and HLF_S to HLF_SNC */
9346 hlt[hlcnt + i].sg_term ^=
9347 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9348 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9349 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9350 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9351 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9352 hlt[hlcnt + i].sg_cterm ^=
9353 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9354 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9355 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9356 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9357 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009358# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009359 hlt[hlcnt + i].sg_gui ^=
9360 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009361# endif
9362# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009363 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9364 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9365 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9366 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009367 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9368 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009369 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9370 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9371# ifdef FEAT_XFONTSET
9372 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9373 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9374# endif
9375# endif
9376 highlight_ga.ga_len = hlcnt + i + 1;
9377 set_hl_attr(hlcnt + i); /* At long last we can apply */
9378 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9379# endif
9380 }
9381 }
9382# ifdef FEAT_STL_OPT
9383 highlight_ga.ga_len = hlcnt;
9384# endif
9385
9386#endif /* USER_HIGHLIGHT */
9387
9388 return OK;
9389}
9390
Bram Moolenaar4f688582007-07-24 12:34:30 +00009391#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009392
9393static void highlight_list __ARGS((void));
9394static void highlight_list_two __ARGS((int cnt, int attr));
9395
9396/*
9397 * Handle command line completion for :highlight command.
9398 */
9399 void
9400set_context_in_highlight_cmd(xp, arg)
9401 expand_T *xp;
9402 char_u *arg;
9403{
9404 char_u *p;
9405
9406 /* Default: expand group names */
9407 xp->xp_context = EXPAND_HIGHLIGHT;
9408 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009409 include_link = 2;
9410 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009411
9412 /* (part of) subcommand already typed */
9413 if (*arg != NUL)
9414 {
9415 p = skiptowhite(arg);
9416 if (*p != NUL) /* past "default" or group name */
9417 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009418 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009419 if (STRNCMP("default", arg, p - arg) == 0)
9420 {
9421 arg = skipwhite(p);
9422 xp->xp_pattern = arg;
9423 p = skiptowhite(arg);
9424 }
9425 if (*p != NUL) /* past group name */
9426 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009427 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009428 if (arg[1] == 'i' && arg[0] == 'N')
9429 highlight_list();
9430 if (STRNCMP("link", arg, p - arg) == 0
9431 || STRNCMP("clear", arg, p - arg) == 0)
9432 {
9433 xp->xp_pattern = skipwhite(p);
9434 p = skiptowhite(xp->xp_pattern);
9435 if (*p != NUL) /* past first group name */
9436 {
9437 xp->xp_pattern = skipwhite(p);
9438 p = skiptowhite(xp->xp_pattern);
9439 }
9440 }
9441 if (*p != NUL) /* past group name(s) */
9442 xp->xp_context = EXPAND_NOTHING;
9443 }
9444 }
9445 }
9446}
9447
9448/*
9449 * List highlighting matches in a nice way.
9450 */
9451 static void
9452highlight_list()
9453{
9454 int i;
9455
9456 for (i = 10; --i >= 0; )
9457 highlight_list_two(i, hl_attr(HLF_D));
9458 for (i = 40; --i >= 0; )
9459 highlight_list_two(99, 0);
9460}
9461
9462 static void
9463highlight_list_two(cnt, attr)
9464 int cnt;
9465 int attr;
9466{
9467 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9468 msg_clr_eos();
9469 out_flush();
9470 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9471}
9472
9473#endif /* FEAT_CMDL_COMPL */
9474
9475#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9476 || defined(FEAT_SIGNS) || defined(PROTO)
9477/*
9478 * Function given to ExpandGeneric() to obtain the list of group names.
9479 * Also used for synIDattr() function.
9480 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009481 char_u *
9482get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009483 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009484 int idx;
9485{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009486#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009487 if (idx == highlight_ga.ga_len && include_none != 0)
9488 return (char_u *)"none";
9489 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009490 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009491 if (idx == highlight_ga.ga_len + include_none + include_default
9492 && include_link != 0)
9493 return (char_u *)"link";
9494 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9495 && include_link != 0)
9496 return (char_u *)"clear";
9497#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009498 if (idx < 0 || idx >= highlight_ga.ga_len)
9499 return NULL;
9500 return HL_TABLE()[idx].sg_name;
9501}
9502#endif
9503
Bram Moolenaar4f688582007-07-24 12:34:30 +00009504#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009505/*
9506 * Free all the highlight group fonts.
9507 * Used when quitting for systems which need it.
9508 */
9509 void
9510free_highlight_fonts()
9511{
9512 int idx;
9513
9514 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9515 {
9516 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9517 HL_TABLE()[idx].sg_font = NOFONT;
9518# ifdef FEAT_XFONTSET
9519 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9520 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9521# endif
9522 }
9523
9524 gui_mch_free_font(gui.norm_font);
9525# ifdef FEAT_XFONTSET
9526 gui_mch_free_fontset(gui.fontset);
9527# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009528# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009529 gui_mch_free_font(gui.bold_font);
9530 gui_mch_free_font(gui.ital_font);
9531 gui_mch_free_font(gui.boldital_font);
9532# endif
9533}
9534#endif
9535
9536/**************************************
9537 * End of Highlighting stuff *
9538 **************************************/