blob: 4adbaf087b92dd5ea894b9c6fe7c2e066f2b84a5 [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
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +0200108static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109#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 Moolenaard6761c32011-06-19 04:54:21 +0200562 if (current_lnum == 1)
563 /* First line is always valid, no matter "minlines". */
564 first_stored = 1;
565 else
566 /* Need to parse "minlines" lines before state can be considered
567 * valid to store. */
568 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 }
570 else
571 first_stored = current_lnum;
572
573 /*
574 * Advance from the sync point or saved state until the current line.
575 * Save some entries for syncing with later on.
576 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200577 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000578 dist = 999999;
579 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200580 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 while (current_lnum < lnum)
582 {
583 syn_start_line();
584 (void)syn_finish_line(FALSE);
585 ++current_lnum;
586
587 /* If we parsed at least "minlines" lines or started at a valid
588 * state, the current state is considered valid. */
589 if (current_lnum >= first_stored)
590 {
591 /* Check if the saved state entry is for the current line and is
592 * equal to the current state. If so, then validate all saved
593 * states that depended on a change before the parsed line. */
594 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000595 prev = syn_stack_find_entry(current_lnum - 1);
596 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200597 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000599 sp = prev;
600 while (sp != NULL && sp->sst_lnum < current_lnum)
601 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602 if (sp != NULL
603 && sp->sst_lnum == current_lnum
604 && syn_stack_equal(sp))
605 {
606 parsed_lnum = current_lnum;
607 prev = sp;
608 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
609 {
610 if (sp->sst_lnum <= lnum)
611 /* valid state before desired line, use this one */
612 prev = sp;
613 else if (sp->sst_change_lnum == 0)
614 /* past saved states depending on change, break here. */
615 break;
616 sp->sst_change_lnum = 0;
617 sp = sp->sst_next;
618 }
619 load_current_state(prev);
620 }
621 /* Store the state at this line when it's the first one, the line
622 * where we start parsing, or some distance from the previously
623 * saved state. But only when parsed at least 'minlines'. */
624 else if (prev == NULL
625 || current_lnum == lnum
626 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000627 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 }
629
630 /* This can take a long time: break when CTRL-C pressed. The current
631 * state will be wrong then. */
632 line_breakcheck();
633 if (got_int)
634 {
635 current_lnum = lnum;
636 break;
637 }
638 }
639
640 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641}
642
643/*
644 * We cannot simply discard growarrays full of state_items or buf_states; we
645 * have to manually release their extmatch pointers first.
646 */
647 static void
648clear_syn_state(p)
649 synstate_T *p;
650{
651 int i;
652 garray_T *gap;
653
654 if (p->sst_stacksize > SST_FIX_STATES)
655 {
656 gap = &(p->sst_union.sst_ga);
657 for (i = 0; i < gap->ga_len; i++)
658 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
659 ga_clear(gap);
660 }
661 else
662 {
663 for (i = 0; i < p->sst_stacksize; i++)
664 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
665 }
666}
667
668/*
669 * Cleanup the current_state stack.
670 */
671 static void
672clear_current_state()
673{
674 int i;
675 stateitem_T *sip;
676
677 sip = (stateitem_T *)(current_state.ga_data);
678 for (i = 0; i < current_state.ga_len; i++)
679 unref_extmatch(sip[i].si_extmatch);
680 ga_clear(&current_state);
681}
682
683/*
684 * Try to find a synchronisation point for line "lnum".
685 *
686 * This sets current_lnum and the current state. One of three methods is
687 * used:
688 * 1. Search backwards for the end of a C-comment.
689 * 2. Search backwards for given sync patterns.
690 * 3. Simply start on a given number of lines above "lnum".
691 */
692 static void
693syn_sync(wp, start_lnum, last_valid)
694 win_T *wp;
695 linenr_T start_lnum;
696 synstate_T *last_valid;
697{
698 buf_T *curbuf_save;
699 win_T *curwin_save;
700 pos_T cursor_save;
701 int idx;
702 linenr_T lnum;
703 linenr_T end_lnum;
704 linenr_T break_lnum;
705 int had_sync_point;
706 stateitem_T *cur_si;
707 synpat_T *spp;
708 char_u *line;
709 int found_flags = 0;
710 int found_match_idx = 0;
711 linenr_T found_current_lnum = 0;
712 int found_current_col= 0;
713 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000714 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715
716 /*
717 * Clear any current state that might be hanging around.
718 */
719 invalidate_current_state();
720
721 /*
722 * Start at least "minlines" back. Default starting point for parsing is
723 * there.
724 * Start further back, to avoid that scrolling backwards will result in
725 * resyncing for every line. Now it resyncs only one out of N lines,
726 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
727 * Watch out for overflow when minlines is MAXLNUM.
728 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200729 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 start_lnum = 1;
731 else
732 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200733 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200735 else if (syn_block->b_syn_sync_minlines < 10)
736 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200738 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
739 if (syn_block->b_syn_sync_maxlines != 0
740 && lnum > syn_block->b_syn_sync_maxlines)
741 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 if (lnum >= start_lnum)
743 start_lnum = 1;
744 else
745 start_lnum -= lnum;
746 }
747 current_lnum = start_lnum;
748
749 /*
750 * 1. Search backwards for the end of a C-style comment.
751 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200752 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000753 {
754 /* Need to make syn_buf the current buffer for a moment, to be able to
755 * use find_start_comment(). */
756 curwin_save = curwin;
757 curwin = wp;
758 curbuf_save = curbuf;
759 curbuf = syn_buf;
760
761 /*
762 * Skip lines that end in a backslash.
763 */
764 for ( ; start_lnum > 1; --start_lnum)
765 {
766 line = ml_get(start_lnum - 1);
767 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
768 break;
769 }
770 current_lnum = start_lnum;
771
772 /* set cursor to start of search */
773 cursor_save = wp->w_cursor;
774 wp->w_cursor.lnum = start_lnum;
775 wp->w_cursor.col = 0;
776
777 /*
778 * If the line is inside a comment, need to find the syntax item that
779 * defines the comment.
780 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
781 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200782 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000783 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200784 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
785 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
786 == syn_block->b_syn_sync_id
787 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788 {
789 validate_current_state();
790 if (push_current_state(idx) == OK)
791 update_si_attr(current_state.ga_len - 1);
792 break;
793 }
794 }
795
796 /* restore cursor and buffer */
797 wp->w_cursor = cursor_save;
798 curwin = curwin_save;
799 curbuf = curbuf_save;
800 }
801
802 /*
803 * 2. Search backwards for given sync patterns.
804 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200805 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200807 if (syn_block->b_syn_sync_maxlines != 0
808 && start_lnum > syn_block->b_syn_sync_maxlines)
809 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000810 else
811 break_lnum = 0;
812
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000813 found_m_endpos.lnum = 0;
814 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000815 end_lnum = start_lnum;
816 lnum = start_lnum;
817 while (--lnum > break_lnum)
818 {
819 /* This can take a long time: break when CTRL-C pressed. */
820 line_breakcheck();
821 if (got_int)
822 {
823 invalidate_current_state();
824 current_lnum = start_lnum;
825 break;
826 }
827
828 /* Check if we have run into a valid saved state stack now. */
829 if (last_valid != NULL && lnum == last_valid->sst_lnum)
830 {
831 load_current_state(last_valid);
832 break;
833 }
834
835 /*
836 * Check if the previous line has the line-continuation pattern.
837 */
838 if (lnum > 1 && syn_match_linecont(lnum - 1))
839 continue;
840
841 /*
842 * Start with nothing on the state stack
843 */
844 validate_current_state();
845
846 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
847 {
848 syn_start_line();
849 for (;;)
850 {
851 had_sync_point = syn_finish_line(TRUE);
852 /*
853 * When a sync point has been found, remember where, and
854 * continue to look for another one, further on in the line.
855 */
856 if (had_sync_point && current_state.ga_len)
857 {
858 cur_si = &CUR_STATE(current_state.ga_len - 1);
859 if (cur_si->si_m_endpos.lnum > start_lnum)
860 {
861 /* ignore match that goes to after where started */
862 current_lnum = end_lnum;
863 break;
864 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000865 if (cur_si->si_idx < 0)
866 {
867 /* Cannot happen? */
868 found_flags = 0;
869 found_match_idx = KEYWORD_IDX;
870 }
871 else
872 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200873 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000874 found_flags = spp->sp_flags;
875 found_match_idx = spp->sp_sync_idx;
876 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000877 found_current_lnum = current_lnum;
878 found_current_col = current_col;
879 found_m_endpos = cur_si->si_m_endpos;
880 /*
881 * Continue after the match (be aware of a zero-length
882 * match).
883 */
884 if (found_m_endpos.lnum > current_lnum)
885 {
886 current_lnum = found_m_endpos.lnum;
887 current_col = found_m_endpos.col;
888 if (current_lnum >= end_lnum)
889 break;
890 }
891 else if (found_m_endpos.col > current_col)
892 current_col = found_m_endpos.col;
893 else
894 ++current_col;
895
896 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000897 * an item that ends here, need to do that now. Be
898 * careful not to go past the NUL. */
899 prev_current_col = current_col;
900 if (syn_getcurline()[current_col] != NUL)
901 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000902 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000903 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 }
905 else
906 break;
907 }
908 }
909
910 /*
911 * If a sync point was encountered, break here.
912 */
913 if (found_flags)
914 {
915 /*
916 * Put the item that was specified by the sync point on the
917 * state stack. If there was no item specified, make the
918 * state stack empty.
919 */
920 clear_current_state();
921 if (found_match_idx >= 0
922 && push_current_state(found_match_idx) == OK)
923 update_si_attr(current_state.ga_len - 1);
924
925 /*
926 * When using "grouphere", continue from the sync point
927 * match, until the end of the line. Parsing starts at
928 * the next line.
929 * For "groupthere" the parsing starts at start_lnum.
930 */
931 if (found_flags & HL_SYNC_HERE)
932 {
933 if (current_state.ga_len)
934 {
935 cur_si = &CUR_STATE(current_state.ga_len - 1);
936 cur_si->si_h_startpos.lnum = found_current_lnum;
937 cur_si->si_h_startpos.col = found_current_col;
938 update_si_end(cur_si, (int)current_col, TRUE);
939 check_keepend();
940 }
941 current_col = found_m_endpos.col;
942 current_lnum = found_m_endpos.lnum;
943 (void)syn_finish_line(FALSE);
944 ++current_lnum;
945 }
946 else
947 current_lnum = start_lnum;
948
949 break;
950 }
951
952 end_lnum = lnum;
953 invalidate_current_state();
954 }
955
956 /* Ran into start of the file or exceeded maximum number of lines */
957 if (lnum <= break_lnum)
958 {
959 invalidate_current_state();
960 current_lnum = break_lnum + 1;
961 }
962 }
963
964 validate_current_state();
965}
966
967/*
968 * Return TRUE if the line-continuation pattern matches in line "lnum".
969 */
970 static int
971syn_match_linecont(lnum)
972 linenr_T lnum;
973{
974 regmmatch_T regmatch;
975
Bram Moolenaar860cae12010-06-05 23:22:07 +0200976 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200978 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
979 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980 return syn_regexec(&regmatch, lnum, (colnr_T)0);
981 }
982 return FALSE;
983}
984
985/*
986 * Prepare the current state for the start of a line.
987 */
988 static void
989syn_start_line()
990{
991 current_finished = FALSE;
992 current_col = 0;
993
994 /*
995 * Need to update the end of a start/skip/end that continues from the
996 * previous line and regions that have "keepend".
997 */
998 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +0200999 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001000 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001001 check_state_ends();
1002 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003
1004 next_match_idx = -1;
1005 ++current_line_id;
1006}
1007
1008/*
1009 * Check for items in the stack that need their end updated.
1010 * When "startofline" is TRUE the last item is always updated.
1011 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1012 */
1013 static void
1014syn_update_ends(startofline)
1015 int startofline;
1016{
1017 stateitem_T *cur_si;
1018 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001019 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020
1021 if (startofline)
1022 {
1023 /* Check for a match carried over from a previous line with a
1024 * contained region. The match ends as soon as the region ends. */
1025 for (i = 0; i < current_state.ga_len; ++i)
1026 {
1027 cur_si = &CUR_STATE(i);
1028 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001029 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001030 == SPTYPE_MATCH
1031 && cur_si->si_m_endpos.lnum < current_lnum)
1032 {
1033 cur_si->si_flags |= HL_MATCHCONT;
1034 cur_si->si_m_endpos.lnum = 0;
1035 cur_si->si_m_endpos.col = 0;
1036 cur_si->si_h_endpos = cur_si->si_m_endpos;
1037 cur_si->si_ends = TRUE;
1038 }
1039 }
1040 }
1041
1042 /*
1043 * Need to update the end of a start/skip/end that continues from the
1044 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001045 * influence contained items. If we've just removed "extend"
1046 * (startofline == 0) then we should update ends of normal regions
1047 * contained inside "keepend" because "extend" could have extended
1048 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 * Then check for items ending in column 0.
1050 */
1051 i = current_state.ga_len - 1;
1052 if (keepend_level >= 0)
1053 for ( ; i > keepend_level; --i)
1054 if (CUR_STATE(i).si_flags & HL_EXTEND)
1055 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001056
1057 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058 for ( ; i < current_state.ga_len; ++i)
1059 {
1060 cur_si = &CUR_STATE(i);
1061 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001062 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063 || (i == current_state.ga_len - 1 && startofline))
1064 {
1065 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1066 cur_si->si_h_startpos.lnum = current_lnum;
1067
1068 if (!(cur_si->si_flags & HL_MATCHCONT))
1069 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001070
1071 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1072 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073 }
1074 }
1075 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076}
1077
1078/****************************************
1079 * Handling of the state stack cache.
1080 */
1081
1082/*
1083 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1084 *
1085 * To speed up syntax highlighting, the state stack for the start of some
1086 * lines is cached. These entries can be used to start parsing at that point.
1087 *
1088 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1089 * valid entries. b_sst_first points to the first one, then follow sst_next.
1090 * The entries are sorted on line number. The first entry is often for line 2
1091 * (line 1 always starts with an empty stack).
1092 * There is also a list for free entries. This construction is used to avoid
1093 * having to allocate and free memory blocks too often.
1094 *
1095 * When making changes to the buffer, this is logged in b_mod_*. When calling
1096 * update_screen() to update the display, it will call
1097 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1098 * entries. The entries which are inside the changed area are removed,
1099 * because they must be recomputed. Entries below the changed have their line
1100 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1101 * set to indicate that a check must be made if the changed lines would change
1102 * the cached entry.
1103 *
1104 * When later displaying lines, an entry is stored for each line. Displayed
1105 * lines are likely to be displayed again, in which case the state at the
1106 * start of the line is needed.
1107 * For not displayed lines, an entry is stored for every so many lines. These
1108 * entries will be used e.g., when scrolling backwards. The distance between
1109 * entries depends on the number of lines in the buffer. For small buffers
1110 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1111 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1112 */
1113
Bram Moolenaar860cae12010-06-05 23:22:07 +02001114 static void
1115syn_stack_free_block(block)
1116 synblock_T *block;
1117{
1118 synstate_T *p;
1119
1120 if (block->b_sst_array != NULL)
1121 {
1122 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1123 clear_syn_state(p);
1124 vim_free(block->b_sst_array);
1125 block->b_sst_array = NULL;
1126 block->b_sst_len = 0;
1127 }
1128}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129/*
1130 * Free b_sst_array[] for buffer "buf".
1131 * Used when syntax items changed to force resyncing everywhere.
1132 */
1133 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001134syn_stack_free_all(block)
1135 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001136{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 win_T *wp;
1138
Bram Moolenaar860cae12010-06-05 23:22:07 +02001139 syn_stack_free_block(block);
1140
1141
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142#ifdef FEAT_FOLDING
1143 /* When using "syntax" fold method, must update all folds. */
1144 FOR_ALL_WINDOWS(wp)
1145 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001146 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147 foldUpdateAll(wp);
1148 }
1149#endif
1150}
1151
1152/*
1153 * Allocate the syntax state stack for syn_buf when needed.
1154 * If the number of entries in b_sst_array[] is much too big or a bit too
1155 * small, reallocate it.
1156 * Also used to allocate b_sst_array[] for the first time.
1157 */
1158 static void
1159syn_stack_alloc()
1160{
1161 long len;
1162 synstate_T *to, *from;
1163 synstate_T *sstp;
1164
1165 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1166 if (len < SST_MIN_ENTRIES)
1167 len = SST_MIN_ENTRIES;
1168 else if (len > SST_MAX_ENTRIES)
1169 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001170 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 {
1172 /* Allocate 50% too much, to avoid reallocating too often. */
1173 len = syn_buf->b_ml.ml_line_count;
1174 len = (len + len / 2) / SST_DIST + Rows * 2;
1175 if (len < SST_MIN_ENTRIES)
1176 len = SST_MIN_ENTRIES;
1177 else if (len > SST_MAX_ENTRIES)
1178 len = SST_MAX_ENTRIES;
1179
Bram Moolenaar860cae12010-06-05 23:22:07 +02001180 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 {
1182 /* When shrinking the array, cleanup the existing stack.
1183 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001184 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001185 && syn_stack_cleanup())
1186 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001187 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1188 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 }
1190
1191 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1192 if (sstp == NULL) /* out of memory! */
1193 return;
1194
1195 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001196 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001197 {
1198 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001199 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001200 from = from->sst_next)
1201 {
1202 ++to;
1203 *to = *from;
1204 to->sst_next = to + 1;
1205 }
1206 }
1207 if (to != sstp - 1)
1208 {
1209 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001210 syn_block->b_sst_first = sstp;
1211 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 }
1213 else
1214 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001215 syn_block->b_sst_first = NULL;
1216 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001217 }
1218
1219 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001220 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 while (++to < sstp + len)
1222 to->sst_next = to + 1;
1223 (sstp + len - 1)->sst_next = NULL;
1224
Bram Moolenaar860cae12010-06-05 23:22:07 +02001225 vim_free(syn_block->b_sst_array);
1226 syn_block->b_sst_array = sstp;
1227 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001228 }
1229}
1230
1231/*
1232 * Check for changes in a buffer to affect stored syntax states. Uses the
1233 * b_mod_* fields.
1234 * Called from update_screen(), before screen is being updated, once for each
1235 * displayed buffer.
1236 */
1237 void
1238syn_stack_apply_changes(buf)
1239 buf_T *buf;
1240{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 win_T *wp;
1242
1243 syn_stack_apply_changes_block(&buf->b_s, buf);
1244
1245 FOR_ALL_WINDOWS(wp)
1246 {
1247 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1248 syn_stack_apply_changes_block(wp->w_s, buf);
1249 }
1250}
1251
1252 static void
1253syn_stack_apply_changes_block(block, buf)
1254 synblock_T *block;
1255 buf_T *buf;
1256{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 synstate_T *p, *prev, *np;
1258 linenr_T n;
1259
Bram Moolenaar860cae12010-06-05 23:22:07 +02001260 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 return;
1262
1263 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001264 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001266 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 {
1268 n = p->sst_lnum + buf->b_mod_xlines;
1269 if (n <= buf->b_mod_bot)
1270 {
1271 /* this state is inside the changed area, remove it */
1272 np = p->sst_next;
1273 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001274 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275 else
1276 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001277 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 p = np;
1279 continue;
1280 }
1281 /* This state is below the changed area. Remember the line
1282 * that needs to be parsed before this entry can be made valid
1283 * again. */
1284 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1285 {
1286 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1287 p->sst_change_lnum += buf->b_mod_xlines;
1288 else
1289 p->sst_change_lnum = buf->b_mod_top;
1290 }
1291 if (p->sst_change_lnum == 0
1292 || p->sst_change_lnum < buf->b_mod_bot)
1293 p->sst_change_lnum = buf->b_mod_bot;
1294
1295 p->sst_lnum = n;
1296 }
1297 prev = p;
1298 p = p->sst_next;
1299 }
1300}
1301
1302/*
1303 * Reduce the number of entries in the state stack for syn_buf.
1304 * Returns TRUE if at least one entry was freed.
1305 */
1306 static int
1307syn_stack_cleanup()
1308{
1309 synstate_T *p, *prev;
1310 disptick_T tick;
1311 int above;
1312 int dist;
1313 int retval = FALSE;
1314
Bram Moolenaar860cae12010-06-05 23:22:07 +02001315 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316 return retval;
1317
1318 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001319 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001320 dist = 999999;
1321 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001322 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323
1324 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001325 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326 * be removed. Set "above" when the "tick" for the oldest entry is above
1327 * "b_sst_lasttick" (the display tick wraps around).
1328 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001329 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001331 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1333 {
1334 if (prev->sst_lnum + dist > p->sst_lnum)
1335 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001336 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001337 {
1338 if (!above || p->sst_tick < tick)
1339 tick = p->sst_tick;
1340 above = TRUE;
1341 }
1342 else if (!above && p->sst_tick < tick)
1343 tick = p->sst_tick;
1344 }
1345 }
1346
1347 /*
1348 * Go through the list to make the entries for the oldest tick at an
1349 * interval of several lines.
1350 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001351 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1353 {
1354 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1355 {
1356 /* Move this entry from used list to free list */
1357 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001358 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001359 p = prev;
1360 retval = TRUE;
1361 }
1362 }
1363 return retval;
1364}
1365
1366/*
1367 * Free the allocated memory for a syn_state item.
1368 * Move the entry into the free list.
1369 */
1370 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001371syn_stack_free_entry(block, p)
1372 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 synstate_T *p;
1374{
1375 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001376 p->sst_next = block->b_sst_firstfree;
1377 block->b_sst_firstfree = p;
1378 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379}
1380
1381/*
1382 * Find an entry in the list of state stacks at or before "lnum".
1383 * Returns NULL when there is no entry or the first entry is after "lnum".
1384 */
1385 static synstate_T *
1386syn_stack_find_entry(lnum)
1387 linenr_T lnum;
1388{
1389 synstate_T *p, *prev;
1390
1391 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001392 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001393 {
1394 if (p->sst_lnum == lnum)
1395 return p;
1396 if (p->sst_lnum > lnum)
1397 break;
1398 }
1399 return prev;
1400}
1401
1402/*
1403 * Try saving the current state in b_sst_array[].
1404 * The current state must be valid for the start of the current_lnum line!
1405 */
1406 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001407store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001408{
1409 int i;
1410 synstate_T *p;
1411 bufstate_T *bp;
1412 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001413 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414
1415 /*
1416 * If the current state contains a start or end pattern that continues
1417 * from the previous line, we can't use it. Don't store it then.
1418 */
1419 for (i = current_state.ga_len - 1; i >= 0; --i)
1420 {
1421 cur_si = &CUR_STATE(i);
1422 if (cur_si->si_h_startpos.lnum >= current_lnum
1423 || cur_si->si_m_endpos.lnum >= current_lnum
1424 || cur_si->si_h_endpos.lnum >= current_lnum
1425 || (cur_si->si_end_idx
1426 && cur_si->si_eoe_pos.lnum >= current_lnum))
1427 break;
1428 }
1429 if (i >= 0)
1430 {
1431 if (sp != NULL)
1432 {
1433 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001434 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001436 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 else
1438 {
1439 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001440 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 if (p->sst_next == sp)
1442 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001443 if (p != NULL) /* just in case */
1444 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001446 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001447 sp = NULL;
1448 }
1449 }
1450 else if (sp == NULL || sp->sst_lnum != current_lnum)
1451 {
1452 /*
1453 * Add a new entry
1454 */
1455 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001456 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001457 {
1458 (void)syn_stack_cleanup();
1459 /* "sp" may have been moved to the freelist now */
1460 sp = syn_stack_find_entry(current_lnum);
1461 }
1462 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001463 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 sp = NULL;
1465 else
1466 {
1467 /* Take the first item from the free list and put it in the used
1468 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001469 p = syn_block->b_sst_firstfree;
1470 syn_block->b_sst_firstfree = p->sst_next;
1471 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472 if (sp == NULL)
1473 {
1474 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001475 p->sst_next = syn_block->b_sst_first;
1476 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001477 }
1478 else
1479 {
1480 /* insert in list after *sp */
1481 p->sst_next = sp->sst_next;
1482 sp->sst_next = p;
1483 }
1484 sp = p;
1485 sp->sst_stacksize = 0;
1486 sp->sst_lnum = current_lnum;
1487 }
1488 }
1489 if (sp != NULL)
1490 {
1491 /* When overwriting an existing state stack, clear it first */
1492 clear_syn_state(sp);
1493 sp->sst_stacksize = current_state.ga_len;
1494 if (current_state.ga_len > SST_FIX_STATES)
1495 {
1496 /* Need to clear it, might be something remaining from when the
1497 * length was less than SST_FIX_STATES. */
1498 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1499 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1500 sp->sst_stacksize = 0;
1501 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001502 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1504 }
1505 else
1506 bp = sp->sst_union.sst_stack;
1507 for (i = 0; i < sp->sst_stacksize; ++i)
1508 {
1509 bp[i].bs_idx = CUR_STATE(i).si_idx;
1510 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001511#ifdef FEAT_CONCEAL
1512 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1513 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1514#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001515 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1516 }
1517 sp->sst_next_flags = current_next_flags;
1518 sp->sst_next_list = current_next_list;
1519 sp->sst_tick = display_tick;
1520 sp->sst_change_lnum = 0;
1521 }
1522 current_state_stored = TRUE;
1523 return sp;
1524}
1525
1526/*
1527 * Copy a state stack from "from" in b_sst_array[] to current_state;
1528 */
1529 static void
1530load_current_state(from)
1531 synstate_T *from;
1532{
1533 int i;
1534 bufstate_T *bp;
1535
1536 clear_current_state();
1537 validate_current_state();
1538 keepend_level = -1;
1539 if (from->sst_stacksize
1540 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1541 {
1542 if (from->sst_stacksize > SST_FIX_STATES)
1543 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1544 else
1545 bp = from->sst_union.sst_stack;
1546 for (i = 0; i < from->sst_stacksize; ++i)
1547 {
1548 CUR_STATE(i).si_idx = bp[i].bs_idx;
1549 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001550#ifdef FEAT_CONCEAL
1551 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1552 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1553#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1555 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1556 keepend_level = i;
1557 CUR_STATE(i).si_ends = FALSE;
1558 CUR_STATE(i).si_m_lnum = 0;
1559 if (CUR_STATE(i).si_idx >= 0)
1560 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001561 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 else
1563 CUR_STATE(i).si_next_list = NULL;
1564 update_si_attr(i);
1565 }
1566 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567 }
1568 current_next_list = from->sst_next_list;
1569 current_next_flags = from->sst_next_flags;
1570 current_lnum = from->sst_lnum;
1571}
1572
1573/*
1574 * Compare saved state stack "*sp" with the current state.
1575 * Return TRUE when they are equal.
1576 */
1577 static int
1578syn_stack_equal(sp)
1579 synstate_T *sp;
1580{
1581 int i, j;
1582 bufstate_T *bp;
1583 reg_extmatch_T *six, *bsx;
1584
1585 /* First a quick check if the stacks have the same size end nextlist. */
1586 if (sp->sst_stacksize == current_state.ga_len
1587 && sp->sst_next_list == current_next_list)
1588 {
1589 /* Need to compare all states on both stacks. */
1590 if (sp->sst_stacksize > SST_FIX_STATES)
1591 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1592 else
1593 bp = sp->sst_union.sst_stack;
1594
1595 for (i = current_state.ga_len; --i >= 0; )
1596 {
1597 /* If the item has another index the state is different. */
1598 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1599 break;
1600 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1601 {
1602 /* When the extmatch pointers are different, the strings in
1603 * them can still be the same. Check if the extmatch
1604 * references are equal. */
1605 bsx = bp[i].bs_extmatch;
1606 six = CUR_STATE(i).si_extmatch;
1607 /* If one of the extmatch pointers is NULL the states are
1608 * different. */
1609 if (bsx == NULL || six == NULL)
1610 break;
1611 for (j = 0; j < NSUBEXP; ++j)
1612 {
1613 /* Check each referenced match string. They must all be
1614 * equal. */
1615 if (bsx->matches[j] != six->matches[j])
1616 {
1617 /* If the pointer is different it can still be the
1618 * same text. Compare the strings, ignore case when
1619 * the start item has the sp_ic flag set. */
1620 if (bsx->matches[j] == NULL
1621 || six->matches[j] == NULL)
1622 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001623 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 ? MB_STRICMP(bsx->matches[j],
1625 six->matches[j]) != 0
1626 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1627 break;
1628 }
1629 }
1630 if (j != NSUBEXP)
1631 break;
1632 }
1633 }
1634 if (i < 0)
1635 return TRUE;
1636 }
1637 return FALSE;
1638}
1639
1640/*
1641 * We stop parsing syntax above line "lnum". If the stored state at or below
1642 * this line depended on a change before it, it now depends on the line below
1643 * the last parsed line.
1644 * The window looks like this:
1645 * line which changed
1646 * displayed line
1647 * displayed line
1648 * lnum -> line below window
1649 */
1650 void
1651syntax_end_parsing(lnum)
1652 linenr_T lnum;
1653{
1654 synstate_T *sp;
1655
1656 sp = syn_stack_find_entry(lnum);
1657 if (sp != NULL && sp->sst_lnum < lnum)
1658 sp = sp->sst_next;
1659
1660 if (sp != NULL && sp->sst_change_lnum != 0)
1661 sp->sst_change_lnum = lnum;
1662}
1663
1664/*
1665 * End of handling of the state stack.
1666 ****************************************/
1667
1668 static void
1669invalidate_current_state()
1670{
1671 clear_current_state();
1672 current_state.ga_itemsize = 0; /* mark current_state invalid */
1673 current_next_list = NULL;
1674 keepend_level = -1;
1675}
1676
1677 static void
1678validate_current_state()
1679{
1680 current_state.ga_itemsize = sizeof(stateitem_T);
1681 current_state.ga_growsize = 3;
1682}
1683
1684/*
1685 * Return TRUE if the syntax at start of lnum changed since last time.
1686 * This will only be called just after get_syntax_attr() for the previous
1687 * line, to check if the next line needs to be redrawn too.
1688 */
1689 int
1690syntax_check_changed(lnum)
1691 linenr_T lnum;
1692{
1693 int retval = TRUE;
1694 synstate_T *sp;
1695
Bram Moolenaar071d4272004-06-13 20:20:40 +00001696 /*
1697 * Check the state stack when:
1698 * - lnum is just below the previously syntaxed line.
1699 * - lnum is not before the lines with saved states.
1700 * - lnum is not past the lines with saved states.
1701 * - lnum is at or before the last changed line.
1702 */
1703 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1704 {
1705 sp = syn_stack_find_entry(lnum);
1706 if (sp != NULL && sp->sst_lnum == lnum)
1707 {
1708 /*
1709 * finish the previous line (needed when not all of the line was
1710 * drawn)
1711 */
1712 (void)syn_finish_line(FALSE);
1713
1714 /*
1715 * Compare the current state with the previously saved state of
1716 * the line.
1717 */
1718 if (syn_stack_equal(sp))
1719 retval = FALSE;
1720
1721 /*
1722 * Store the current state in b_sst_array[] for later use.
1723 */
1724 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001725 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001726 }
1727 }
1728
Bram Moolenaar071d4272004-06-13 20:20:40 +00001729 return retval;
1730}
1731
1732/*
1733 * Finish the current line.
1734 * This doesn't return any attributes, it only gets the state at the end of
1735 * the line. It can start anywhere in the line, as long as the current state
1736 * is valid.
1737 */
1738 static int
1739syn_finish_line(syncing)
1740 int syncing; /* called for syncing */
1741{
1742 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001743 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744
1745 if (!current_finished)
1746 {
1747 while (!current_finished)
1748 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001749 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 /*
1751 * When syncing, and found some item, need to check the item.
1752 */
1753 if (syncing && current_state.ga_len)
1754 {
1755 /*
1756 * Check for match with sync item.
1757 */
1758 cur_si = &CUR_STATE(current_state.ga_len - 1);
1759 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001760 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001761 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1762 return TRUE;
1763
1764 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001765 * that ends here, need to do that now. Be careful not to go
1766 * past the NUL. */
1767 prev_current_col = current_col;
1768 if (syn_getcurline()[current_col] != NUL)
1769 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001771 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001772 }
1773 ++current_col;
1774 }
1775 }
1776 return FALSE;
1777}
1778
1779/*
1780 * Return highlight attributes for next character.
1781 * Must first call syntax_start() once for the line.
1782 * "col" is normally 0 for the first use in a line, and increments by one each
1783 * time. It's allowed to skip characters and to stop before the end of the
1784 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001785 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1786 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001787 */
1788 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001789get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001790 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001791 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001792 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793{
1794 int attr = 0;
1795
Bram Moolenaar349955a2007-08-14 21:07:36 +00001796 if (can_spell != NULL)
1797 /* Default: Only do spelling when there is no @Spell cluster or when
1798 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001799 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1800 ? (syn_block->b_spell_cluster_id == 0)
1801 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001802
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001804 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001805 return 0;
1806
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001807 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001808 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001809 {
1810 clear_current_state();
1811#ifdef FEAT_EVAL
1812 current_id = 0;
1813 current_trans_id = 0;
1814#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001815#ifdef FEAT_CONCEAL
1816 current_flags = 0;
1817#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001818 return 0;
1819 }
1820
Bram Moolenaar071d4272004-06-13 20:20:40 +00001821 /* Make sure current_state is valid */
1822 if (INVALID_STATE(&current_state))
1823 validate_current_state();
1824
1825 /*
1826 * Skip from the current column to "col", get the attributes for "col".
1827 */
1828 while (current_col <= col)
1829 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001830 attr = syn_current_attr(FALSE, TRUE, can_spell,
1831 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832 ++current_col;
1833 }
1834
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835 return attr;
1836}
1837
1838/*
1839 * Get syntax attributes for current_lnum, current_col.
1840 */
1841 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001842syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001843 int syncing; /* When 1: called for syncing */
1844 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001845 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001846 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001847{
1848 int syn_id;
1849 lpos_T endpos; /* was: char_u *endp; */
1850 lpos_T hl_startpos; /* was: int hl_startcol; */
1851 lpos_T hl_endpos;
1852 lpos_T eos_pos; /* end-of-start match (start region) */
1853 lpos_T eoe_pos; /* end-of-end pattern */
1854 int end_idx; /* group ID for end pattern */
1855 int idx;
1856 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001857 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001858 int startcol;
1859 int endcol;
1860 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001861 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001862 short *next_list;
1863 int found_match; /* found usable match */
1864 static int try_next_column = FALSE; /* must try in next col */
1865 int do_keywords;
1866 regmmatch_T regmatch;
1867 lpos_T pos;
1868 int lc_col;
1869 reg_extmatch_T *cur_extmatch = NULL;
1870 char_u *line; /* current line. NOTE: becomes invalid after
1871 looking for a pattern match! */
1872
1873 /* variables for zero-width matches that have a "nextgroup" argument */
1874 int keep_next_list;
1875 int zero_width_next_list = FALSE;
1876 garray_T zero_width_next_ga;
1877
1878 /*
1879 * No character, no attributes! Past end of line?
1880 * Do try matching with an empty line (could be the start of a region).
1881 */
1882 line = syn_getcurline();
1883 if (line[current_col] == NUL && current_col != 0)
1884 {
1885 /*
1886 * If we found a match after the last column, use it.
1887 */
1888 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1889 && next_match_col != MAXCOL)
1890 (void)push_next_match(NULL);
1891
1892 current_finished = TRUE;
1893 current_state_stored = FALSE;
1894 return 0;
1895 }
1896
1897 /* if the current or next character is NUL, we will finish the line now */
1898 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1899 {
1900 current_finished = TRUE;
1901 current_state_stored = FALSE;
1902 }
1903
1904 /*
1905 * When in the previous column there was a match but it could not be used
1906 * (empty match or already matched in this column) need to try again in
1907 * the next column.
1908 */
1909 if (try_next_column)
1910 {
1911 next_match_idx = -1;
1912 try_next_column = FALSE;
1913 }
1914
1915 /* Only check for keywords when not syncing and there are some. */
1916 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001917 && (syn_block->b_keywtab.ht_used > 0
1918 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001919
1920 /* Init the list of zero-width matches with a nextlist. This is used to
1921 * avoid matching the same item in the same position twice. */
1922 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1923
1924 /*
1925 * Repeat matching keywords and patterns, to find contained items at the
1926 * same column. This stops when there are no extra matches at the current
1927 * column.
1928 */
1929 do
1930 {
1931 found_match = FALSE;
1932 keep_next_list = FALSE;
1933 syn_id = 0;
1934
1935 /*
1936 * 1. Check for a current state.
1937 * Only when there is no current state, or if the current state may
1938 * contain other things, we need to check for keywords and patterns.
1939 * Always need to check for contained items if some item has the
1940 * "containedin" argument (takes extra time!).
1941 */
1942 if (current_state.ga_len)
1943 cur_si = &CUR_STATE(current_state.ga_len - 1);
1944 else
1945 cur_si = NULL;
1946
Bram Moolenaar860cae12010-06-05 23:22:07 +02001947 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001948 || cur_si->si_cont_list != NULL)
1949 {
1950 /*
1951 * 2. Check for keywords, if on a keyword char after a non-keyword
1952 * char. Don't do this when syncing.
1953 */
1954 if (do_keywords)
1955 {
1956 line = syn_getcurline();
1957 if (vim_iswordc_buf(line + current_col, syn_buf)
1958 && (current_col == 0
1959 || !vim_iswordc_buf(line + current_col - 1
1960#ifdef FEAT_MBYTE
1961 - (has_mbyte
1962 ? (*mb_head_off)(line, line + current_col - 1)
1963 : 0)
1964#endif
1965 , syn_buf)))
1966 {
1967 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001968 &endcol, &flags, &next_list, cur_si,
1969 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001970 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971 {
1972 if (push_current_state(KEYWORD_IDX) == OK)
1973 {
1974 cur_si = &CUR_STATE(current_state.ga_len - 1);
1975 cur_si->si_m_startcol = current_col;
1976 cur_si->si_h_startpos.lnum = current_lnum;
1977 cur_si->si_h_startpos.col = 0; /* starts right away */
1978 cur_si->si_m_endpos.lnum = current_lnum;
1979 cur_si->si_m_endpos.col = endcol;
1980 cur_si->si_h_endpos.lnum = current_lnum;
1981 cur_si->si_h_endpos.col = endcol;
1982 cur_si->si_ends = TRUE;
1983 cur_si->si_end_idx = 0;
1984 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001985#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001986 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001987 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001988 if (current_state.ga_len > 1)
1989 cur_si->si_flags |=
1990 CUR_STATE(current_state.ga_len - 2).si_flags
1991 & HL_CONCEAL;
1992#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001993 cur_si->si_id = syn_id;
1994 cur_si->si_trans_id = syn_id;
1995 if (flags & HL_TRANSP)
1996 {
1997 if (current_state.ga_len < 2)
1998 {
1999 cur_si->si_attr = 0;
2000 cur_si->si_trans_id = 0;
2001 }
2002 else
2003 {
2004 cur_si->si_attr = CUR_STATE(
2005 current_state.ga_len - 2).si_attr;
2006 cur_si->si_trans_id = CUR_STATE(
2007 current_state.ga_len - 2).si_trans_id;
2008 }
2009 }
2010 else
2011 cur_si->si_attr = syn_id2attr(syn_id);
2012 cur_si->si_cont_list = NULL;
2013 cur_si->si_next_list = next_list;
2014 check_keepend();
2015 }
2016 else
2017 vim_free(next_list);
2018 }
2019 }
2020 }
2021
2022 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002023 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002024 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002025 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002026 {
2027 /*
2028 * If we didn't check for a match yet, or we are past it, check
2029 * for any match with a pattern.
2030 */
2031 if (next_match_idx < 0 || next_match_col < (int)current_col)
2032 {
2033 /*
2034 * Check all relevant patterns for a match at this
2035 * position. This is complicated, because matching with a
2036 * pattern takes quite a bit of time, thus we want to
2037 * avoid doing it when it's not needed.
2038 */
2039 next_match_idx = 0; /* no match in this line yet */
2040 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002041 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002043 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044 if ( spp->sp_syncing == syncing
2045 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2046 && (spp->sp_type == SPTYPE_MATCH
2047 || spp->sp_type == SPTYPE_START)
2048 && (current_next_list != NULL
2049 ? in_id_list(NULL, current_next_list,
2050 &spp->sp_syn, 0)
2051 : (cur_si == NULL
2052 ? !(spp->sp_flags & HL_CONTAINED)
2053 : in_id_list(cur_si,
2054 cur_si->si_cont_list, &spp->sp_syn,
2055 spp->sp_flags & HL_CONTAINED))))
2056 {
2057 /* If we already tried matching in this line, and
2058 * there isn't a match before next_match_col, skip
2059 * this item. */
2060 if (spp->sp_line_id == current_line_id
2061 && spp->sp_startcol >= next_match_col)
2062 continue;
2063 spp->sp_line_id = current_line_id;
2064
2065 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2066 if (lc_col < 0)
2067 lc_col = 0;
2068
2069 regmatch.rmm_ic = spp->sp_ic;
2070 regmatch.regprog = spp->sp_prog;
2071 if (!syn_regexec(&regmatch, current_lnum,
2072 (colnr_T)lc_col))
2073 {
2074 /* no match in this line, try another one */
2075 spp->sp_startcol = MAXCOL;
2076 continue;
2077 }
2078
2079 /*
2080 * Compute the first column of the match.
2081 */
2082 syn_add_start_off(&pos, &regmatch,
2083 spp, SPO_MS_OFF, -1);
2084 if (pos.lnum > current_lnum)
2085 {
2086 /* must have used end of match in a next line,
2087 * we can't handle that */
2088 spp->sp_startcol = MAXCOL;
2089 continue;
2090 }
2091 startcol = pos.col;
2092
2093 /* remember the next column where this pattern
2094 * matches in the current line */
2095 spp->sp_startcol = startcol;
2096
2097 /*
2098 * If a previously found match starts at a lower
2099 * column number, don't use this one.
2100 */
2101 if (startcol >= next_match_col)
2102 continue;
2103
2104 /*
2105 * If we matched this pattern at this position
2106 * before, skip it. Must retry in the next
2107 * column, because it may match from there.
2108 */
2109 if (did_match_already(idx, &zero_width_next_ga))
2110 {
2111 try_next_column = TRUE;
2112 continue;
2113 }
2114
2115 endpos.lnum = regmatch.endpos[0].lnum;
2116 endpos.col = regmatch.endpos[0].col;
2117
2118 /* Compute the highlight start. */
2119 syn_add_start_off(&hl_startpos, &regmatch,
2120 spp, SPO_HS_OFF, -1);
2121
2122 /* Compute the region start. */
2123 /* Default is to use the end of the match. */
2124 syn_add_end_off(&eos_pos, &regmatch,
2125 spp, SPO_RS_OFF, 0);
2126
2127 /*
2128 * Grab the external submatches before they get
2129 * overwritten. Reference count doesn't change.
2130 */
2131 unref_extmatch(cur_extmatch);
2132 cur_extmatch = re_extmatch_out;
2133 re_extmatch_out = NULL;
2134
2135 flags = 0;
2136 eoe_pos.lnum = 0; /* avoid warning */
2137 eoe_pos.col = 0;
2138 end_idx = 0;
2139 hl_endpos.lnum = 0;
2140
2141 /*
2142 * For a "oneline" the end must be found in the
2143 * same line too. Search for it after the end of
2144 * the match with the start pattern. Set the
2145 * resulting end positions at the same time.
2146 */
2147 if (spp->sp_type == SPTYPE_START
2148 && (spp->sp_flags & HL_ONELINE))
2149 {
2150 lpos_T startpos;
2151
2152 startpos = endpos;
2153 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2154 &flags, &eoe_pos, &end_idx, cur_extmatch);
2155 if (endpos.lnum == 0)
2156 continue; /* not found */
2157 }
2158
2159 /*
2160 * For a "match" the size must be > 0 after the
2161 * end offset needs has been added. Except when
2162 * syncing.
2163 */
2164 else if (spp->sp_type == SPTYPE_MATCH)
2165 {
2166 syn_add_end_off(&hl_endpos, &regmatch, spp,
2167 SPO_HE_OFF, 0);
2168 syn_add_end_off(&endpos, &regmatch, spp,
2169 SPO_ME_OFF, 0);
2170 if (endpos.lnum == current_lnum
2171 && (int)endpos.col + syncing < startcol)
2172 {
2173 /*
2174 * If an empty string is matched, may need
2175 * to try matching again at next column.
2176 */
2177 if (regmatch.startpos[0].col
2178 == regmatch.endpos[0].col)
2179 try_next_column = TRUE;
2180 continue;
2181 }
2182 }
2183
2184 /*
2185 * keep the best match so far in next_match_*
2186 */
2187 /* Highlighting must start after startpos and end
2188 * before endpos. */
2189 if (hl_startpos.lnum == current_lnum
2190 && (int)hl_startpos.col < startcol)
2191 hl_startpos.col = startcol;
2192 limit_pos_zero(&hl_endpos, &endpos);
2193
2194 next_match_idx = idx;
2195 next_match_col = startcol;
2196 next_match_m_endpos = endpos;
2197 next_match_h_endpos = hl_endpos;
2198 next_match_h_startpos = hl_startpos;
2199 next_match_flags = flags;
2200 next_match_eos_pos = eos_pos;
2201 next_match_eoe_pos = eoe_pos;
2202 next_match_end_idx = end_idx;
2203 unref_extmatch(next_match_extmatch);
2204 next_match_extmatch = cur_extmatch;
2205 cur_extmatch = NULL;
2206 }
2207 }
2208 }
2209
2210 /*
2211 * If we found a match at the current column, use it.
2212 */
2213 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2214 {
2215 synpat_T *lspp;
2216
2217 /* When a zero-width item matched which has a nextgroup,
2218 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002219 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220 if (next_match_m_endpos.lnum == current_lnum
2221 && next_match_m_endpos.col == current_col
2222 && lspp->sp_next_list != NULL)
2223 {
2224 current_next_list = lspp->sp_next_list;
2225 current_next_flags = lspp->sp_flags;
2226 keep_next_list = TRUE;
2227 zero_width_next_list = TRUE;
2228
2229 /* Add the index to a list, so that we can check
2230 * later that we don't match it again (and cause an
2231 * endless loop). */
2232 if (ga_grow(&zero_width_next_ga, 1) == OK)
2233 {
2234 ((int *)(zero_width_next_ga.ga_data))
2235 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 }
2237 next_match_idx = -1;
2238 }
2239 else
2240 cur_si = push_next_match(cur_si);
2241 found_match = TRUE;
2242 }
2243 }
2244 }
2245
2246 /*
2247 * Handle searching for nextgroup match.
2248 */
2249 if (current_next_list != NULL && !keep_next_list)
2250 {
2251 /*
2252 * If a nextgroup was not found, continue looking for one if:
2253 * - this is an empty line and the "skipempty" option was given
2254 * - we are on white space and the "skipwhite" option was given
2255 */
2256 if (!found_match)
2257 {
2258 line = syn_getcurline();
2259 if (((current_next_flags & HL_SKIPWHITE)
2260 && vim_iswhite(line[current_col]))
2261 || ((current_next_flags & HL_SKIPEMPTY)
2262 && *line == NUL))
2263 break;
2264 }
2265
2266 /*
2267 * If a nextgroup was found: Use it, and continue looking for
2268 * contained matches.
2269 * If a nextgroup was not found: Continue looking for a normal
2270 * match.
2271 * When did set current_next_list for a zero-width item and no
2272 * match was found don't loop (would get stuck).
2273 */
2274 current_next_list = NULL;
2275 next_match_idx = -1;
2276 if (!zero_width_next_list)
2277 found_match = TRUE;
2278 }
2279
2280 } while (found_match);
2281
2282 /*
2283 * Use attributes from the current state, if within its highlighting.
2284 * If not, use attributes from the current-but-one state, etc.
2285 */
2286 current_attr = 0;
2287#ifdef FEAT_EVAL
2288 current_id = 0;
2289 current_trans_id = 0;
2290#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002291#ifdef FEAT_CONCEAL
2292 current_flags = 0;
2293#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002294 if (cur_si != NULL)
2295 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002296#ifndef FEAT_EVAL
2297 int current_trans_id = 0;
2298#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002299 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2300 {
2301 sip = &CUR_STATE(idx);
2302 if ((current_lnum > sip->si_h_startpos.lnum
2303 || (current_lnum == sip->si_h_startpos.lnum
2304 && current_col >= sip->si_h_startpos.col))
2305 && (sip->si_h_endpos.lnum == 0
2306 || current_lnum < sip->si_h_endpos.lnum
2307 || (current_lnum == sip->si_h_endpos.lnum
2308 && current_col < sip->si_h_endpos.col)))
2309 {
2310 current_attr = sip->si_attr;
2311#ifdef FEAT_EVAL
2312 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002313#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002314 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002315#ifdef FEAT_CONCEAL
2316 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002317 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002318 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002319#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002320 break;
2321 }
2322 }
2323
Bram Moolenaar217ad922005-03-20 22:37:15 +00002324 if (can_spell != NULL)
2325 {
2326 struct sp_syn sps;
2327
2328 /*
2329 * set "can_spell" to TRUE if spell checking is supposed to be
2330 * done in the current item.
2331 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002332 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002333 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002334 /* There is no @Spell cluster: Do spelling for items without
2335 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002336 if (syn_block->b_nospell_cluster_id == 0
2337 || current_trans_id == 0)
2338 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002339 else
2340 {
2341 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002342 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002343 sps.cont_in_list = NULL;
2344 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2345 }
2346 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002347 else
2348 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002349 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002350 * the @Spell cluster. But not when @NoSpell is also there.
2351 * At the toplevel only spell check when ":syn spell toplevel"
2352 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002353 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002354 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002355 else
2356 {
2357 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002358 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002359 sps.cont_in_list = NULL;
2360 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2361
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002363 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002364 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002365 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2366 *can_spell = FALSE;
2367 }
2368 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002369 }
2370 }
2371
2372
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 /*
2374 * Check for end of current state (and the states before it) at the
2375 * next column. Don't do this for syncing, because we would miss a
2376 * single character match.
2377 * First check if the current state ends at the current column. It
2378 * may be for an empty match and a containing item might end in the
2379 * current column.
2380 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002381 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002382 {
2383 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002384 if (current_state.ga_len > 0
2385 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002386 {
2387 ++current_col;
2388 check_state_ends();
2389 --current_col;
2390 }
2391 }
2392 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002393 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002394 /* Default: Only do spelling when there is no @Spell cluster or when
2395 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002396 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2397 ? (syn_block->b_spell_cluster_id == 0)
2398 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002400 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002401 if (current_next_list != NULL
2402 && syn_getcurline()[current_col + 1] == NUL
2403 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2404 current_next_list = NULL;
2405
2406 if (zero_width_next_ga.ga_len > 0)
2407 ga_clear(&zero_width_next_ga);
2408
2409 /* No longer need external matches. But keep next_match_extmatch. */
2410 unref_extmatch(re_extmatch_out);
2411 re_extmatch_out = NULL;
2412 unref_extmatch(cur_extmatch);
2413
2414 return current_attr;
2415}
2416
2417
2418/*
2419 * Check if we already matched pattern "idx" at the current column.
2420 */
2421 static int
2422did_match_already(idx, gap)
2423 int idx;
2424 garray_T *gap;
2425{
2426 int i;
2427
2428 for (i = current_state.ga_len; --i >= 0; )
2429 if (CUR_STATE(i).si_m_startcol == (int)current_col
2430 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2431 && CUR_STATE(i).si_idx == idx)
2432 return TRUE;
2433
2434 /* Zero-width matches with a nextgroup argument are not put on the syntax
2435 * stack, and can only be matched once anyway. */
2436 for (i = gap->ga_len; --i >= 0; )
2437 if (((int *)(gap->ga_data))[i] == idx)
2438 return TRUE;
2439
2440 return FALSE;
2441}
2442
2443/*
2444 * Push the next match onto the stack.
2445 */
2446 static stateitem_T *
2447push_next_match(cur_si)
2448 stateitem_T *cur_si;
2449{
2450 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002451#ifdef FEAT_CONCEAL
2452 int save_flags;
2453#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454
Bram Moolenaar860cae12010-06-05 23:22:07 +02002455 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456
2457 /*
2458 * Push the item in current_state stack;
2459 */
2460 if (push_current_state(next_match_idx) == OK)
2461 {
2462 /*
2463 * If it's a start-skip-end type that crosses lines, figure out how
2464 * much it continues in this line. Otherwise just fill in the length.
2465 */
2466 cur_si = &CUR_STATE(current_state.ga_len - 1);
2467 cur_si->si_h_startpos = next_match_h_startpos;
2468 cur_si->si_m_startcol = current_col;
2469 cur_si->si_m_lnum = current_lnum;
2470 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002471#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002472 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002473 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002474 if (current_state.ga_len > 1)
2475 cur_si->si_flags |=
2476 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2477#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002478 cur_si->si_next_list = spp->sp_next_list;
2479 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2480 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2481 {
2482 /* Try to find the end pattern in the current line */
2483 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2484 check_keepend();
2485 }
2486 else
2487 {
2488 cur_si->si_m_endpos = next_match_m_endpos;
2489 cur_si->si_h_endpos = next_match_h_endpos;
2490 cur_si->si_ends = TRUE;
2491 cur_si->si_flags |= next_match_flags;
2492 cur_si->si_eoe_pos = next_match_eoe_pos;
2493 cur_si->si_end_idx = next_match_end_idx;
2494 }
2495 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2496 keepend_level = current_state.ga_len - 1;
2497 check_keepend();
2498 update_si_attr(current_state.ga_len - 1);
2499
Bram Moolenaar860cae12010-06-05 23:22:07 +02002500#ifdef FEAT_CONCEAL
2501 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2502#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 /*
2504 * If the start pattern has another highlight group, push another item
2505 * on the stack for the start pattern.
2506 */
2507 if ( spp->sp_type == SPTYPE_START
2508 && spp->sp_syn_match_id != 0
2509 && push_current_state(next_match_idx) == OK)
2510 {
2511 cur_si = &CUR_STATE(current_state.ga_len - 1);
2512 cur_si->si_h_startpos = next_match_h_startpos;
2513 cur_si->si_m_startcol = current_col;
2514 cur_si->si_m_lnum = current_lnum;
2515 cur_si->si_m_endpos = next_match_eos_pos;
2516 cur_si->si_h_endpos = next_match_eos_pos;
2517 cur_si->si_ends = TRUE;
2518 cur_si->si_end_idx = 0;
2519 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002520#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002521 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002522 cur_si->si_flags |= save_flags;
2523 if (cur_si->si_flags & HL_CONCEALENDS)
2524 cur_si->si_flags |= HL_CONCEAL;
2525#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 cur_si->si_next_list = NULL;
2527 check_keepend();
2528 update_si_attr(current_state.ga_len - 1);
2529 }
2530 }
2531
2532 next_match_idx = -1; /* try other match next time */
2533
2534 return cur_si;
2535}
2536
2537/*
2538 * Check for end of current state (and the states before it).
2539 */
2540 static void
2541check_state_ends()
2542{
2543 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002544 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002545
2546 cur_si = &CUR_STATE(current_state.ga_len - 1);
2547 for (;;)
2548 {
2549 if (cur_si->si_ends
2550 && (cur_si->si_m_endpos.lnum < current_lnum
2551 || (cur_si->si_m_endpos.lnum == current_lnum
2552 && cur_si->si_m_endpos.col <= current_col)))
2553 {
2554 /*
2555 * If there is an end pattern group ID, highlight the end pattern
2556 * now. No need to pop the current item from the stack.
2557 * Only do this if the end pattern continues beyond the current
2558 * position.
2559 */
2560 if (cur_si->si_end_idx
2561 && (cur_si->si_eoe_pos.lnum > current_lnum
2562 || (cur_si->si_eoe_pos.lnum == current_lnum
2563 && cur_si->si_eoe_pos.col > current_col)))
2564 {
2565 cur_si->si_idx = cur_si->si_end_idx;
2566 cur_si->si_end_idx = 0;
2567 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2568 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2569 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002570#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002571 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002572 if (cur_si->si_flags & HL_CONCEALENDS)
2573 cur_si->si_flags |= HL_CONCEAL;
2574#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002576
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002577 /* nextgroup= should not match in the end pattern */
2578 current_next_list = NULL;
2579
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002580 /* what matches next may be different now, clear it */
2581 next_match_idx = 0;
2582 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002583 break;
2584 }
2585 else
2586 {
2587 /* handle next_list, unless at end of line and no "skipnl" or
2588 * "skipempty" */
2589 current_next_list = cur_si->si_next_list;
2590 current_next_flags = cur_si->si_flags;
2591 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2592 && syn_getcurline()[current_col] == NUL)
2593 current_next_list = NULL;
2594
2595 /* When the ended item has "extend", another item with
2596 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002597 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598
2599 pop_current_state();
2600
2601 if (current_state.ga_len == 0)
2602 break;
2603
Bram Moolenaar81993f42008-01-11 20:27:45 +00002604 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605 {
2606 syn_update_ends(FALSE);
2607 if (current_state.ga_len == 0)
2608 break;
2609 }
2610
2611 cur_si = &CUR_STATE(current_state.ga_len - 1);
2612
2613 /*
2614 * Only for a region the search for the end continues after
2615 * the end of the contained item. If the contained match
2616 * included the end-of-line, break here, the region continues.
2617 * Don't do this when:
2618 * - "keepend" is used for the contained item
2619 * - not at the end of the line (could be end="x$"me=e-1).
2620 * - "excludenl" is used (HL_HAS_EOL won't be set)
2621 */
2622 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002623 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 == SPTYPE_START
2625 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2626 {
2627 update_si_end(cur_si, (int)current_col, TRUE);
2628 check_keepend();
2629 if ((current_next_flags & HL_HAS_EOL)
2630 && keepend_level < 0
2631 && syn_getcurline()[current_col] == NUL)
2632 break;
2633 }
2634 }
2635 }
2636 else
2637 break;
2638 }
2639}
2640
2641/*
2642 * Update an entry in the current_state stack for a match or region. This
2643 * fills in si_attr, si_next_list and si_cont_list.
2644 */
2645 static void
2646update_si_attr(idx)
2647 int idx;
2648{
2649 stateitem_T *sip = &CUR_STATE(idx);
2650 synpat_T *spp;
2651
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002652 /* This should not happen... */
2653 if (sip->si_idx < 0)
2654 return;
2655
Bram Moolenaar860cae12010-06-05 23:22:07 +02002656 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002657 if (sip->si_flags & HL_MATCH)
2658 sip->si_id = spp->sp_syn_match_id;
2659 else
2660 sip->si_id = spp->sp_syn.id;
2661 sip->si_attr = syn_id2attr(sip->si_id);
2662 sip->si_trans_id = sip->si_id;
2663 if (sip->si_flags & HL_MATCH)
2664 sip->si_cont_list = NULL;
2665 else
2666 sip->si_cont_list = spp->sp_cont_list;
2667
2668 /*
2669 * For transparent items, take attr from outer item.
2670 * Also take cont_list, if there is none.
2671 * Don't do this for the matchgroup of a start or end pattern.
2672 */
2673 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2674 {
2675 if (idx == 0)
2676 {
2677 sip->si_attr = 0;
2678 sip->si_trans_id = 0;
2679 if (sip->si_cont_list == NULL)
2680 sip->si_cont_list = ID_LIST_ALL;
2681 }
2682 else
2683 {
2684 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2685 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002686 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2687 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688 if (sip->si_cont_list == NULL)
2689 {
2690 sip->si_flags |= HL_TRANS_CONT;
2691 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2692 }
2693 }
2694 }
2695}
2696
2697/*
2698 * Check the current stack for patterns with "keepend" flag.
2699 * Propagate the match-end to contained items, until a "skipend" item is found.
2700 */
2701 static void
2702check_keepend()
2703{
2704 int i;
2705 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002706 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002707 stateitem_T *sip;
2708
2709 /*
2710 * This check can consume a lot of time; only do it from the level where
2711 * there really is a keepend.
2712 */
2713 if (keepend_level < 0)
2714 return;
2715
2716 /*
2717 * Find the last index of an "extend" item. "keepend" items before that
2718 * won't do anything. If there is no "extend" item "i" will be
2719 * "keepend_level" and all "keepend" items will work normally.
2720 */
2721 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2722 if (CUR_STATE(i).si_flags & HL_EXTEND)
2723 break;
2724
2725 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002726 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002727 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002728 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 for ( ; i < current_state.ga_len; ++i)
2730 {
2731 sip = &CUR_STATE(i);
2732 if (maxpos.lnum != 0)
2733 {
2734 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002735 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002736 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2737 sip->si_ends = TRUE;
2738 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002739 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2740 {
2741 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002742 || maxpos.lnum > sip->si_m_endpos.lnum
2743 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002744 && maxpos.col > sip->si_m_endpos.col))
2745 maxpos = sip->si_m_endpos;
2746 if (maxpos_h.lnum == 0
2747 || maxpos_h.lnum > sip->si_h_endpos.lnum
2748 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2749 && maxpos_h.col > sip->si_h_endpos.col))
2750 maxpos_h = sip->si_h_endpos;
2751 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 }
2753}
2754
2755/*
2756 * Update an entry in the current_state stack for a start-skip-end pattern.
2757 * This finds the end of the current item, if it's in the current line.
2758 *
2759 * Return the flags for the matched END.
2760 */
2761 static void
2762update_si_end(sip, startcol, force)
2763 stateitem_T *sip;
2764 int startcol; /* where to start searching for the end */
2765 int force; /* when TRUE overrule a previous end */
2766{
2767 lpos_T startpos;
2768 lpos_T endpos;
2769 lpos_T hl_endpos;
2770 lpos_T end_endpos;
2771 int end_idx;
2772
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002773 /* return quickly for a keyword */
2774 if (sip->si_idx < 0)
2775 return;
2776
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 /* Don't update when it's already done. Can be a match of an end pattern
2778 * that started in a previous line. Watch out: can also be a "keepend"
2779 * from a containing item. */
2780 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2781 return;
2782
2783 /*
2784 * We need to find the end of the region. It may continue in the next
2785 * line.
2786 */
2787 end_idx = 0;
2788 startpos.lnum = current_lnum;
2789 startpos.col = startcol;
2790 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2791 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2792
2793 if (endpos.lnum == 0)
2794 {
2795 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002796 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002797 {
2798 /* a "oneline" never continues in the next line */
2799 sip->si_ends = TRUE;
2800 sip->si_m_endpos.lnum = current_lnum;
2801 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2802 }
2803 else
2804 {
2805 /* continues in the next line */
2806 sip->si_ends = FALSE;
2807 sip->si_m_endpos.lnum = 0;
2808 }
2809 sip->si_h_endpos = sip->si_m_endpos;
2810 }
2811 else
2812 {
2813 /* match within this line */
2814 sip->si_m_endpos = endpos;
2815 sip->si_h_endpos = hl_endpos;
2816 sip->si_eoe_pos = end_endpos;
2817 sip->si_ends = TRUE;
2818 sip->si_end_idx = end_idx;
2819 }
2820}
2821
2822/*
2823 * Add a new state to the current state stack.
2824 * It is cleared and the index set to "idx".
2825 * Return FAIL if it's not possible (out of memory).
2826 */
2827 static int
2828push_current_state(idx)
2829 int idx;
2830{
2831 if (ga_grow(&current_state, 1) == FAIL)
2832 return FAIL;
2833 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2834 CUR_STATE(current_state.ga_len).si_idx = idx;
2835 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002836 return OK;
2837}
2838
2839/*
2840 * Remove a state from the current_state stack.
2841 */
2842 static void
2843pop_current_state()
2844{
2845 if (current_state.ga_len)
2846 {
2847 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2848 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002849 }
2850 /* after the end of a pattern, try matching a keyword or pattern */
2851 next_match_idx = -1;
2852
2853 /* if first state with "keepend" is popped, reset keepend_level */
2854 if (keepend_level >= current_state.ga_len)
2855 keepend_level = -1;
2856}
2857
2858/*
2859 * Find the end of a start/skip/end syntax region after "startpos".
2860 * Only checks one line.
2861 * Also handles a match item that continued from a previous line.
2862 * If not found, the syntax item continues in the next line. m_endpos->lnum
2863 * will be 0.
2864 * If found, the end of the region and the end of the highlighting is
2865 * computed.
2866 */
2867 static void
2868find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2869 end_idx, start_ext)
2870 int idx; /* index of the pattern */
2871 lpos_T *startpos; /* where to start looking for an END match */
2872 lpos_T *m_endpos; /* return: end of match */
2873 lpos_T *hl_endpos; /* return: end of highlighting */
2874 long *flagsp; /* return: flags of matching END */
2875 lpos_T *end_endpos; /* return: end of end pattern match */
2876 int *end_idx; /* return: group ID for end pat. match, or 0 */
2877 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2878{
2879 colnr_T matchcol;
2880 synpat_T *spp, *spp_skip;
2881 int start_idx;
2882 int best_idx;
2883 regmmatch_T regmatch;
2884 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2885 lpos_T pos;
2886 char_u *line;
2887 int had_match = FALSE;
2888
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002889 /* just in case we are invoked for a keyword */
2890 if (idx < 0)
2891 return;
2892
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 /*
2894 * Check for being called with a START pattern.
2895 * Can happen with a match that continues to the next line, because it
2896 * contained a region.
2897 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002898 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899 if (spp->sp_type != SPTYPE_START)
2900 {
2901 *hl_endpos = *startpos;
2902 return;
2903 }
2904
2905 /*
2906 * Find the SKIP or first END pattern after the last START pattern.
2907 */
2908 for (;;)
2909 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002910 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002911 if (spp->sp_type != SPTYPE_START)
2912 break;
2913 ++idx;
2914 }
2915
2916 /*
2917 * Lookup the SKIP pattern (if present)
2918 */
2919 if (spp->sp_type == SPTYPE_SKIP)
2920 {
2921 spp_skip = spp;
2922 ++idx;
2923 }
2924 else
2925 spp_skip = NULL;
2926
2927 /* Setup external matches for syn_regexec(). */
2928 unref_extmatch(re_extmatch_in);
2929 re_extmatch_in = ref_extmatch(start_ext);
2930
2931 matchcol = startpos->col; /* start looking for a match at sstart */
2932 start_idx = idx; /* remember the first END pattern. */
2933 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2934 for (;;)
2935 {
2936 /*
2937 * Find end pattern that matches first after "matchcol".
2938 */
2939 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002940 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 {
2942 int lc_col = matchcol;
2943
Bram Moolenaar860cae12010-06-05 23:22:07 +02002944 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002945 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2946 break;
2947 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2948 if (lc_col < 0)
2949 lc_col = 0;
2950
2951 regmatch.rmm_ic = spp->sp_ic;
2952 regmatch.regprog = spp->sp_prog;
2953 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2954 {
2955 if (best_idx == -1 || regmatch.startpos[0].col
2956 < best_regmatch.startpos[0].col)
2957 {
2958 best_idx = idx;
2959 best_regmatch.startpos[0] = regmatch.startpos[0];
2960 best_regmatch.endpos[0] = regmatch.endpos[0];
2961 }
2962 }
2963 }
2964
2965 /*
2966 * If all end patterns have been tried, and there is no match, the
2967 * item continues until end-of-line.
2968 */
2969 if (best_idx == -1)
2970 break;
2971
2972 /*
2973 * If the skip pattern matches before the end pattern,
2974 * continue searching after the skip pattern.
2975 */
2976 if (spp_skip != NULL)
2977 {
2978 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2979
2980 if (lc_col < 0)
2981 lc_col = 0;
2982 regmatch.rmm_ic = spp_skip->sp_ic;
2983 regmatch.regprog = spp_skip->sp_prog;
2984 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2985 && regmatch.startpos[0].col
2986 <= best_regmatch.startpos[0].col)
2987 {
2988 /* Add offset to skip pattern match */
2989 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2990
2991 /* If the skip pattern goes on to the next line, there is no
2992 * match with an end pattern in this line. */
2993 if (pos.lnum > startpos->lnum)
2994 break;
2995
2996 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2997
2998 /* take care of an empty match or negative offset */
2999 if (pos.col <= matchcol)
3000 ++matchcol;
3001 else if (pos.col <= regmatch.endpos[0].col)
3002 matchcol = pos.col;
3003 else
3004 /* Be careful not to jump over the NUL at the end-of-line */
3005 for (matchcol = regmatch.endpos[0].col;
3006 line[matchcol] != NUL && matchcol < pos.col;
3007 ++matchcol)
3008 ;
3009
3010 /* if the skip pattern includes end-of-line, break here */
3011 if (line[matchcol] == NUL)
3012 break;
3013
3014 continue; /* start with first end pattern again */
3015 }
3016 }
3017
3018 /*
3019 * Match from start pattern to end pattern.
3020 * Correct for match and highlight offset of end pattern.
3021 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003022 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3024 /* can't end before the start */
3025 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3026 m_endpos->col = startpos->col;
3027
3028 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3029 /* can't end before the start */
3030 if (end_endpos->lnum == startpos->lnum
3031 && end_endpos->col < startpos->col)
3032 end_endpos->col = startpos->col;
3033 /* can't end after the match */
3034 limit_pos(end_endpos, m_endpos);
3035
3036 /*
3037 * If the end group is highlighted differently, adjust the pointers.
3038 */
3039 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3040 {
3041 *end_idx = best_idx;
3042 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3043 {
3044 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3045 hl_endpos->col = best_regmatch.endpos[0].col;
3046 }
3047 else
3048 {
3049 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3050 hl_endpos->col = best_regmatch.startpos[0].col;
3051 }
3052 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3053
3054 /* can't end before the start */
3055 if (hl_endpos->lnum == startpos->lnum
3056 && hl_endpos->col < startpos->col)
3057 hl_endpos->col = startpos->col;
3058 limit_pos(hl_endpos, m_endpos);
3059
3060 /* now the match ends where the highlighting ends, it is turned
3061 * into the matchgroup for the end */
3062 *m_endpos = *hl_endpos;
3063 }
3064 else
3065 {
3066 *end_idx = 0;
3067 *hl_endpos = *end_endpos;
3068 }
3069
3070 *flagsp = spp->sp_flags;
3071
3072 had_match = TRUE;
3073 break;
3074 }
3075
3076 /* no match for an END pattern in this line */
3077 if (!had_match)
3078 m_endpos->lnum = 0;
3079
3080 /* Remove external matches. */
3081 unref_extmatch(re_extmatch_in);
3082 re_extmatch_in = NULL;
3083}
3084
3085/*
3086 * Limit "pos" not to be after "limit".
3087 */
3088 static void
3089limit_pos(pos, limit)
3090 lpos_T *pos;
3091 lpos_T *limit;
3092{
3093 if (pos->lnum > limit->lnum)
3094 *pos = *limit;
3095 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3096 pos->col = limit->col;
3097}
3098
3099/*
3100 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3101 */
3102 static void
3103limit_pos_zero(pos, limit)
3104 lpos_T *pos;
3105 lpos_T *limit;
3106{
3107 if (pos->lnum == 0)
3108 *pos = *limit;
3109 else
3110 limit_pos(pos, limit);
3111}
3112
3113/*
3114 * Add offset to matched text for end of match or highlight.
3115 */
3116 static void
3117syn_add_end_off(result, regmatch, spp, idx, extra)
3118 lpos_T *result; /* returned position */
3119 regmmatch_T *regmatch; /* start/end of match */
3120 synpat_T *spp; /* matched pattern */
3121 int idx; /* index of offset */
3122 int extra; /* extra chars for offset to start */
3123{
3124 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003125 int off;
3126 char_u *base;
3127 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003128
3129 if (spp->sp_off_flags & (1 << idx))
3130 {
3131 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003132 col = regmatch->startpos[0].col;
3133 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003134 }
3135 else
3136 {
3137 result->lnum = regmatch->endpos[0].lnum;
3138 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003139 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003140 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003141 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3142 * is a matchgroup. Watch out for match with last NL in the buffer. */
3143 if (result->lnum > syn_buf->b_ml.ml_line_count)
3144 col = 0;
3145 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003146 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003147 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3148 p = base + col;
3149 if (off > 0)
3150 {
3151 while (off-- > 0 && *p != NUL)
3152 mb_ptr_adv(p);
3153 }
3154 else if (off < 0)
3155 {
3156 while (off++ < 0 && base < p)
3157 mb_ptr_back(base, p);
3158 }
3159 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003160 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003161 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003162}
3163
3164/*
3165 * Add offset to matched text for start of match or highlight.
3166 * Avoid resulting column to become negative.
3167 */
3168 static void
3169syn_add_start_off(result, regmatch, spp, idx, extra)
3170 lpos_T *result; /* returned position */
3171 regmmatch_T *regmatch; /* start/end of match */
3172 synpat_T *spp;
3173 int idx;
3174 int extra; /* extra chars for offset to end */
3175{
3176 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003177 int off;
3178 char_u *base;
3179 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003180
3181 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3182 {
3183 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003184 col = regmatch->endpos[0].col;
3185 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186 }
3187 else
3188 {
3189 result->lnum = regmatch->startpos[0].lnum;
3190 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003191 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003192 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003193 if (result->lnum > syn_buf->b_ml.ml_line_count)
3194 {
3195 /* a "\n" at the end of the pattern may take us below the last line */
3196 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003197 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003198 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003199 if (off != 0)
3200 {
3201 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3202 p = base + col;
3203 if (off > 0)
3204 {
3205 while (off-- && *p != NUL)
3206 mb_ptr_adv(p);
3207 }
3208 else if (off < 0)
3209 {
3210 while (off++ && base < p)
3211 mb_ptr_back(base, p);
3212 }
3213 col = (int)(p - base);
3214 }
3215 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003216}
3217
3218/*
3219 * Get current line in syntax buffer.
3220 */
3221 static char_u *
3222syn_getcurline()
3223{
3224 return ml_get_buf(syn_buf, current_lnum, FALSE);
3225}
3226
3227/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003228 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 * Returns TRUE when there is a match.
3230 */
3231 static int
3232syn_regexec(rmp, lnum, col)
3233 regmmatch_T *rmp;
3234 linenr_T lnum;
3235 colnr_T col;
3236{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003237 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003238 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239 {
3240 rmp->startpos[0].lnum += lnum;
3241 rmp->endpos[0].lnum += lnum;
3242 return TRUE;
3243 }
3244 return FALSE;
3245}
3246
3247/*
3248 * Check one position in a line for a matching keyword.
3249 * The caller must check if a keyword can start at startcol.
3250 * Return it's ID if found, 0 otherwise.
3251 */
3252 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003253check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 char_u *line;
3255 int startcol; /* position in line to check for keyword */
3256 int *endcolp; /* return: character after found keyword */
3257 long *flagsp; /* return: flags of matching keyword */
3258 short **next_listp; /* return: next_list of matching keyword */
3259 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003260 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003262 keyentry_T *kp;
3263 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003264 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003265 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003267 hashtab_T *ht;
3268 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269
3270 /* Find first character after the keyword. First character was already
3271 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003272 kwp = line + startcol;
3273 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274 do
3275 {
3276#ifdef FEAT_MBYTE
3277 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003278 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003279 else
3280#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003281 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003282 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003283 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003284
Bram Moolenaardad6b692005-01-25 22:14:34 +00003285 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286 return 0;
3287
3288 /*
3289 * Must make a copy of the keyword, so we can add a NUL and make it
3290 * lowercase.
3291 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003292 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293
3294 /*
3295 * Try twice:
3296 * 1. matching case
3297 * 2. ignoring case
3298 */
3299 for (round = 1; round <= 2; ++round)
3300 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003301 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003302 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003304 if (round == 2) /* ignore case */
3305 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003306
3307 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003308 * Find keywords that match. There can be several with different
3309 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310 * When current_next_list is non-zero accept only that group, otherwise:
3311 * Accept a not-contained keyword at toplevel.
3312 * Accept a keyword at other levels only if it is in the contains list.
3313 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003314 hi = hash_find(ht, keyword);
3315 if (!HASHITEM_EMPTY(hi))
3316 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003317 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003318 if (current_next_list != 0
3319 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3320 : (cur_si == NULL
3321 ? !(kp->flags & HL_CONTAINED)
3322 : in_id_list(cur_si, cur_si->si_cont_list,
3323 &kp->k_syn, kp->flags & HL_CONTAINED)))
3324 {
3325 *endcolp = startcol + kwlen;
3326 *flagsp = kp->flags;
3327 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003328#ifdef FEAT_CONCEAL
3329 *ccharp = kp->k_char;
3330#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003331 return kp->k_syn.id;
3332 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333 }
3334 }
3335 return 0;
3336}
3337
3338/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003339 * Handle ":syntax conceal" command.
3340 */
3341 static void
3342syn_cmd_conceal(eap, syncing)
3343 exarg_T *eap UNUSED;
3344 int syncing UNUSED;
3345{
3346#ifdef FEAT_CONCEAL
3347 char_u *arg = eap->arg;
3348 char_u *next;
3349
3350 eap->nextcmd = find_nextcmd(arg);
3351 if (eap->skip)
3352 return;
3353
3354 next = skiptowhite(arg);
3355 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3356 curwin->w_s->b_syn_conceal = TRUE;
3357 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3358 curwin->w_s->b_syn_conceal = FALSE;
3359 else
3360 EMSG2(_("E390: Illegal argument: %s"), arg);
3361#endif
3362}
3363
3364/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365 * Handle ":syntax case" command.
3366 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 static void
3368syn_cmd_case(eap, syncing)
3369 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003370 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371{
3372 char_u *arg = eap->arg;
3373 char_u *next;
3374
3375 eap->nextcmd = find_nextcmd(arg);
3376 if (eap->skip)
3377 return;
3378
3379 next = skiptowhite(arg);
3380 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003381 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003383 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003384 else
3385 EMSG2(_("E390: Illegal argument: %s"), arg);
3386}
3387
3388/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003389 * Handle ":syntax spell" command.
3390 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003391 static void
3392syn_cmd_spell(eap, syncing)
3393 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003394 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003395{
3396 char_u *arg = eap->arg;
3397 char_u *next;
3398
3399 eap->nextcmd = find_nextcmd(arg);
3400 if (eap->skip)
3401 return;
3402
3403 next = skiptowhite(arg);
3404 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003405 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003406 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003407 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003408 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003409 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003410 else
3411 EMSG2(_("E390: Illegal argument: %s"), arg);
3412}
3413
3414/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415 * Clear all syntax info for one buffer.
3416 */
3417 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003418syntax_clear(block)
3419 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420{
3421 int i;
3422
Bram Moolenaar860cae12010-06-05 23:22:07 +02003423 block->b_syn_error = FALSE; /* clear previous error */
3424 block->b_syn_ic = FALSE; /* Use case, by default */
3425 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3426 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003427
3428 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003429 clear_keywtab(&block->b_keywtab);
3430 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003431
3432 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003433 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3434 syn_clear_pattern(block, i);
3435 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436
3437 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003438 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3439 syn_clear_cluster(block, i);
3440 ga_clear(&block->b_syn_clusters);
3441 block->b_spell_cluster_id = 0;
3442 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443
Bram Moolenaar860cae12010-06-05 23:22:07 +02003444 block->b_syn_sync_flags = 0;
3445 block->b_syn_sync_minlines = 0;
3446 block->b_syn_sync_maxlines = 0;
3447 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448
Bram Moolenaar860cae12010-06-05 23:22:07 +02003449 vim_free(block->b_syn_linecont_prog);
3450 block->b_syn_linecont_prog = NULL;
3451 vim_free(block->b_syn_linecont_pat);
3452 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003454 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455#endif
3456
3457 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003458 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003459 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003460
3461 /* Reset the counter for ":syn include" */
3462 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003463}
3464
3465/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003466 * Get rid of ownsyntax for window "wp".
3467 */
3468 void
3469reset_synblock(wp)
3470 win_T *wp;
3471{
3472 if (wp->w_s != &wp->w_buffer->b_s)
3473 {
3474 syntax_clear(wp->w_s);
3475 vim_free(wp->w_s);
3476 wp->w_s = &wp->w_buffer->b_s;
3477 }
3478}
3479
3480/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003481 * Clear syncing info for one buffer.
3482 */
3483 static void
3484syntax_sync_clear()
3485{
3486 int i;
3487
3488 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003489 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3490 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3491 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 curwin->w_s->b_syn_sync_flags = 0;
3494 curwin->w_s->b_syn_sync_minlines = 0;
3495 curwin->w_s->b_syn_sync_maxlines = 0;
3496 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497
Bram Moolenaar860cae12010-06-05 23:22:07 +02003498 vim_free(curwin->w_s->b_syn_linecont_prog);
3499 curwin->w_s->b_syn_linecont_prog = NULL;
3500 vim_free(curwin->w_s->b_syn_linecont_pat);
3501 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504}
3505
3506/*
3507 * Remove one pattern from the buffer's pattern list.
3508 */
3509 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003510syn_remove_pattern(block, idx)
3511 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 int idx;
3513{
3514 synpat_T *spp;
3515
Bram Moolenaar860cae12010-06-05 23:22:07 +02003516 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517#ifdef FEAT_FOLDING
3518 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003519 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003520#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003521 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003523 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3524 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525}
3526
3527/*
3528 * Clear and free one syntax pattern. When clearing all, must be called from
3529 * last to first!
3530 */
3531 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003532syn_clear_pattern(block, i)
3533 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534 int i;
3535{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003536 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3537 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003538 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003539 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003541 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3542 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3543 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003544 }
3545}
3546
3547/*
3548 * Clear and free one syntax cluster.
3549 */
3550 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003551syn_clear_cluster(block, i)
3552 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003553 int i;
3554{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003555 vim_free(SYN_CLSTR(block)[i].scl_name);
3556 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3557 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003558}
3559
3560/*
3561 * Handle ":syntax clear" command.
3562 */
3563 static void
3564syn_cmd_clear(eap, syncing)
3565 exarg_T *eap;
3566 int syncing;
3567{
3568 char_u *arg = eap->arg;
3569 char_u *arg_end;
3570 int id;
3571
3572 eap->nextcmd = find_nextcmd(arg);
3573 if (eap->skip)
3574 return;
3575
3576 /*
3577 * We have to disable this within ":syn include @group filename",
3578 * because otherwise @group would get deleted.
3579 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3580 * clear".
3581 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003582 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583 return;
3584
3585 if (ends_excmd(*arg))
3586 {
3587 /*
3588 * No argument: Clear all syntax items.
3589 */
3590 if (syncing)
3591 syntax_sync_clear();
3592 else
3593 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003594 syntax_clear(curwin->w_s);
3595 if (curwin->w_s == &curwin->w_buffer->b_s)
3596 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003597 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003598 }
3599 }
3600 else
3601 {
3602 /*
3603 * Clear the group IDs that are in the argument.
3604 */
3605 while (!ends_excmd(*arg))
3606 {
3607 arg_end = skiptowhite(arg);
3608 if (*arg == '@')
3609 {
3610 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3611 if (id == 0)
3612 {
3613 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3614 break;
3615 }
3616 else
3617 {
3618 /*
3619 * We can't physically delete a cluster without changing
3620 * the IDs of other clusters, so we do the next best thing
3621 * and make it empty.
3622 */
3623 short scl_id = id - SYNID_CLUSTER;
3624
Bram Moolenaar860cae12010-06-05 23:22:07 +02003625 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3626 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627 }
3628 }
3629 else
3630 {
3631 id = syn_namen2id(arg, (int)(arg_end - arg));
3632 if (id == 0)
3633 {
3634 EMSG2(_(e_nogroup), arg);
3635 break;
3636 }
3637 else
3638 syn_clear_one(id, syncing);
3639 }
3640 arg = skipwhite(arg_end);
3641 }
3642 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003643 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003644 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645}
3646
3647/*
3648 * Clear one syntax group for the current buffer.
3649 */
3650 static void
3651syn_clear_one(id, syncing)
3652 int id;
3653 int syncing;
3654{
3655 synpat_T *spp;
3656 int idx;
3657
3658 /* Clear keywords only when not ":syn sync clear group-name" */
3659 if (!syncing)
3660 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003661 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3662 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 }
3664
3665 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003666 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003667 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003668 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003669 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3670 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003671 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672 }
3673}
3674
3675/*
3676 * Handle ":syntax on" command.
3677 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003678 static void
3679syn_cmd_on(eap, syncing)
3680 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003681 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682{
3683 syn_cmd_onoff(eap, "syntax");
3684}
3685
3686/*
3687 * Handle ":syntax enable" command.
3688 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003689 static void
3690syn_cmd_enable(eap, syncing)
3691 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003692 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003693{
3694 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3695 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003696 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697}
3698
3699/*
3700 * Handle ":syntax reset" command.
3701 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003702 static void
3703syn_cmd_reset(eap, syncing)
3704 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003705 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706{
3707 eap->nextcmd = check_nextcmd(eap->arg);
3708 if (!eap->skip)
3709 {
3710 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3711 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003712 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713 }
3714}
3715
3716/*
3717 * Handle ":syntax manual" command.
3718 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 static void
3720syn_cmd_manual(eap, syncing)
3721 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003722 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723{
3724 syn_cmd_onoff(eap, "manual");
3725}
3726
3727/*
3728 * Handle ":syntax off" command.
3729 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 static void
3731syn_cmd_off(eap, syncing)
3732 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003733 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734{
3735 syn_cmd_onoff(eap, "nosyntax");
3736}
3737
3738 static void
3739syn_cmd_onoff(eap, name)
3740 exarg_T *eap;
3741 char *name;
3742{
3743 char_u buf[100];
3744
3745 eap->nextcmd = check_nextcmd(eap->arg);
3746 if (!eap->skip)
3747 {
3748 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003749 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003750 do_cmdline_cmd(buf);
3751 }
3752}
3753
3754/*
3755 * Handle ":syntax [list]" command: list current syntax words.
3756 */
3757 static void
3758syn_cmd_list(eap, syncing)
3759 exarg_T *eap;
3760 int syncing; /* when TRUE: list syncing items */
3761{
3762 char_u *arg = eap->arg;
3763 int id;
3764 char_u *arg_end;
3765
3766 eap->nextcmd = find_nextcmd(arg);
3767 if (eap->skip)
3768 return;
3769
Bram Moolenaar860cae12010-06-05 23:22:07 +02003770 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003771 {
3772 MSG(_("No Syntax items defined for this buffer"));
3773 return;
3774 }
3775
3776 if (syncing)
3777 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003778 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779 {
3780 MSG_PUTS(_("syncing on C-style comments"));
3781 syn_lines_msg();
3782 syn_match_msg();
3783 return;
3784 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003785 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003787 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788 MSG_PUTS(_("no syncing"));
3789 else
3790 {
3791 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003792 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793 MSG_PUTS(_(" lines before top line"));
3794 syn_match_msg();
3795 }
3796 return;
3797 }
3798 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003799 if (curwin->w_s->b_syn_sync_minlines > 0
3800 || curwin->w_s->b_syn_sync_maxlines > 0
3801 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003802 {
3803 MSG_PUTS(_("\nsyncing on items"));
3804 syn_lines_msg();
3805 syn_match_msg();
3806 }
3807 }
3808 else
3809 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3810 if (ends_excmd(*arg))
3811 {
3812 /*
3813 * No argument: List all group IDs and all syntax clusters.
3814 */
3815 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3816 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003817 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 syn_list_cluster(id);
3819 }
3820 else
3821 {
3822 /*
3823 * List the group IDs and syntax clusters that are in the argument.
3824 */
3825 while (!ends_excmd(*arg) && !got_int)
3826 {
3827 arg_end = skiptowhite(arg);
3828 if (*arg == '@')
3829 {
3830 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3831 if (id == 0)
3832 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3833 else
3834 syn_list_cluster(id - SYNID_CLUSTER);
3835 }
3836 else
3837 {
3838 id = syn_namen2id(arg, (int)(arg_end - arg));
3839 if (id == 0)
3840 EMSG2(_(e_nogroup), arg);
3841 else
3842 syn_list_one(id, syncing, TRUE);
3843 }
3844 arg = skipwhite(arg_end);
3845 }
3846 }
3847 eap->nextcmd = check_nextcmd(arg);
3848}
3849
3850 static void
3851syn_lines_msg()
3852{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 if (curwin->w_s->b_syn_sync_maxlines > 0
3854 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 {
3856 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003857 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 {
3859 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003860 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3861 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 MSG_PUTS(", ");
3863 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003864 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 {
3866 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003867 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868 }
3869 MSG_PUTS(_(" lines before top line"));
3870 }
3871}
3872
3873 static void
3874syn_match_msg()
3875{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003876 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877 {
3878 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003879 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003880 MSG_PUTS(_(" line breaks"));
3881 }
3882}
3883
3884static int last_matchgroup;
3885
3886struct name_list
3887{
3888 int flag;
3889 char *name;
3890};
3891
3892static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3893
3894/*
3895 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3896 */
3897 static void
3898syn_list_one(id, syncing, link_only)
3899 int id;
3900 int syncing; /* when TRUE: list syncing items */
3901 int link_only; /* when TRUE; list link-only too */
3902{
3903 int attr;
3904 int idx;
3905 int did_header = FALSE;
3906 synpat_T *spp;
3907 static struct name_list namelist1[] =
3908 {
3909 {HL_DISPLAY, "display"},
3910 {HL_CONTAINED, "contained"},
3911 {HL_ONELINE, "oneline"},
3912 {HL_KEEPEND, "keepend"},
3913 {HL_EXTEND, "extend"},
3914 {HL_EXCLUDENL, "excludenl"},
3915 {HL_TRANSP, "transparent"},
3916 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003917#ifdef FEAT_CONCEAL
3918 {HL_CONCEAL, "conceal"},
3919 {HL_CONCEALENDS, "concealends"},
3920#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 {0, NULL}
3922 };
3923 static struct name_list namelist2[] =
3924 {
3925 {HL_SKIPWHITE, "skipwhite"},
3926 {HL_SKIPNL, "skipnl"},
3927 {HL_SKIPEMPTY, "skipempty"},
3928 {0, NULL}
3929 };
3930
3931 attr = hl_attr(HLF_D); /* highlight like directories */
3932
3933 /* list the keywords for "id" */
3934 if (!syncing)
3935 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003936 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3937 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 did_header, attr);
3939 }
3940
3941 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003942 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003944 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3946 continue;
3947
3948 (void)syn_list_header(did_header, 999, id);
3949 did_header = TRUE;
3950 last_matchgroup = 0;
3951 if (spp->sp_type == SPTYPE_MATCH)
3952 {
3953 put_pattern("match", ' ', spp, attr);
3954 msg_putchar(' ');
3955 }
3956 else if (spp->sp_type == SPTYPE_START)
3957 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003958 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3959 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3960 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3961 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3962 while (idx < curwin->w_s->b_syn_patterns.ga_len
3963 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3964 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965 --idx;
3966 msg_putchar(' ');
3967 }
3968 syn_list_flags(namelist1, spp->sp_flags, attr);
3969
3970 if (spp->sp_cont_list != NULL)
3971 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3972
3973 if (spp->sp_syn.cont_in_list != NULL)
3974 put_id_list((char_u *)"containedin",
3975 spp->sp_syn.cont_in_list, attr);
3976
3977 if (spp->sp_next_list != NULL)
3978 {
3979 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3980 syn_list_flags(namelist2, spp->sp_flags, attr);
3981 }
3982 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3983 {
3984 if (spp->sp_flags & HL_SYNC_HERE)
3985 msg_puts_attr((char_u *)"grouphere", attr);
3986 else
3987 msg_puts_attr((char_u *)"groupthere", attr);
3988 msg_putchar(' ');
3989 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003990 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3992 else
3993 MSG_PUTS("NONE");
3994 msg_putchar(' ');
3995 }
3996 }
3997
3998 /* list the link, if there is one */
3999 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4000 {
4001 (void)syn_list_header(did_header, 999, id);
4002 msg_puts_attr((char_u *)"links to", attr);
4003 msg_putchar(' ');
4004 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4005 }
4006}
4007
4008 static void
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004009syn_list_flags(nlist, flags, attr)
4010 struct name_list *nlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004011 int flags;
4012 int attr;
4013{
4014 int i;
4015
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004016 for (i = 0; nlist[i].flag != 0; ++i)
4017 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004019 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004020 msg_putchar(' ');
4021 }
4022}
4023
4024/*
4025 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4026 */
4027 static void
4028syn_list_cluster(id)
4029 int id;
4030{
4031 int endcol = 15;
4032
4033 /* slight hack: roughly duplicate the guts of syn_list_header() */
4034 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004035 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004036
4037 if (msg_col >= endcol) /* output at least one space */
4038 endcol = msg_col + 1;
4039 if (Columns <= endcol) /* avoid hang for tiny window */
4040 endcol = Columns - 1;
4041
4042 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004043 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004044 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004045 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004046 hl_attr(HLF_D));
4047 }
4048 else
4049 {
4050 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4051 msg_puts((char_u *)"=NONE");
4052 }
4053}
4054
4055 static void
4056put_id_list(name, list, attr)
4057 char_u *name;
4058 short *list;
4059 int attr;
4060{
4061 short *p;
4062
4063 msg_puts_attr(name, attr);
4064 msg_putchar('=');
4065 for (p = list; *p; ++p)
4066 {
4067 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4068 {
4069 if (p[1])
4070 MSG_PUTS("ALLBUT");
4071 else
4072 MSG_PUTS("ALL");
4073 }
4074 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4075 {
4076 MSG_PUTS("TOP");
4077 }
4078 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4079 {
4080 MSG_PUTS("CONTAINED");
4081 }
4082 else if (*p >= SYNID_CLUSTER)
4083 {
4084 short scl_id = *p - SYNID_CLUSTER;
4085
4086 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004087 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 }
4089 else
4090 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4091 if (p[1])
4092 msg_putchar(',');
4093 }
4094 msg_putchar(' ');
4095}
4096
4097 static void
4098put_pattern(s, c, spp, attr)
4099 char *s;
4100 int c;
4101 synpat_T *spp;
4102 int attr;
4103{
4104 long n;
4105 int mask;
4106 int first;
4107 static char *sepchars = "/+=-#@\"|'^&";
4108 int i;
4109
4110 /* May have to write "matchgroup=group" */
4111 if (last_matchgroup != spp->sp_syn_match_id)
4112 {
4113 last_matchgroup = spp->sp_syn_match_id;
4114 msg_puts_attr((char_u *)"matchgroup", attr);
4115 msg_putchar('=');
4116 if (last_matchgroup == 0)
4117 msg_outtrans((char_u *)"NONE");
4118 else
4119 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4120 msg_putchar(' ');
4121 }
4122
4123 /* output the name of the pattern and an '=' or ' ' */
4124 msg_puts_attr((char_u *)s, attr);
4125 msg_putchar(c);
4126
4127 /* output the pattern, in between a char that is not in the pattern */
4128 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4129 if (sepchars[++i] == NUL)
4130 {
4131 i = 0; /* no good char found, just use the first one */
4132 break;
4133 }
4134 msg_putchar(sepchars[i]);
4135 msg_outtrans(spp->sp_pattern);
4136 msg_putchar(sepchars[i]);
4137
4138 /* output any pattern options */
4139 first = TRUE;
4140 for (i = 0; i < SPO_COUNT; ++i)
4141 {
4142 mask = (1 << i);
4143 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4144 {
4145 if (!first)
4146 msg_putchar(','); /* separate with commas */
4147 msg_puts((char_u *)spo_name_tab[i]);
4148 n = spp->sp_offsets[i];
4149 if (i != SPO_LC_OFF)
4150 {
4151 if (spp->sp_off_flags & mask)
4152 msg_putchar('s');
4153 else
4154 msg_putchar('e');
4155 if (n > 0)
4156 msg_putchar('+');
4157 }
4158 if (n || i == SPO_LC_OFF)
4159 msg_outnum(n);
4160 first = FALSE;
4161 }
4162 }
4163 msg_putchar(' ');
4164}
4165
4166/*
4167 * List or clear the keywords for one syntax group.
4168 * Return TRUE if the header has been printed.
4169 */
4170 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004171syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004173 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 int did_header; /* header has already been printed */
4175 int attr;
4176{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004178 hashitem_T *hi;
4179 keyentry_T *kp;
4180 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 int prev_contained = 0;
4182 short *prev_next_list = NULL;
4183 short *prev_cont_in_list = NULL;
4184 int prev_skipnl = 0;
4185 int prev_skipwhite = 0;
4186 int prev_skipempty = 0;
4187
Bram Moolenaar071d4272004-06-13 20:20:40 +00004188 /*
4189 * Unfortunately, this list of keywords is not sorted on alphabet but on
4190 * hash value...
4191 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004192 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004193 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004195 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004196 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004197 --todo;
4198 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004200 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004202 if (prev_contained != (kp->flags & HL_CONTAINED)
4203 || prev_skipnl != (kp->flags & HL_SKIPNL)
4204 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4205 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4206 || prev_cont_in_list != kp->k_syn.cont_in_list
4207 || prev_next_list != kp->next_list)
4208 outlen = 9999;
4209 else
4210 outlen = (int)STRLEN(kp->keyword);
4211 /* output "contained" and "nextgroup" on each line */
4212 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004214 prev_contained = 0;
4215 prev_next_list = NULL;
4216 prev_cont_in_list = NULL;
4217 prev_skipnl = 0;
4218 prev_skipwhite = 0;
4219 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004221 did_header = TRUE;
4222 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004224 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004226 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004228 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004229 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004230 put_id_list((char_u *)"containedin",
4231 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004232 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004233 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004234 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004235 if (kp->next_list != prev_next_list)
4236 {
4237 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4238 msg_putchar(' ');
4239 prev_next_list = kp->next_list;
4240 if (kp->flags & HL_SKIPNL)
4241 {
4242 msg_puts_attr((char_u *)"skipnl", attr);
4243 msg_putchar(' ');
4244 prev_skipnl = (kp->flags & HL_SKIPNL);
4245 }
4246 if (kp->flags & HL_SKIPWHITE)
4247 {
4248 msg_puts_attr((char_u *)"skipwhite", attr);
4249 msg_putchar(' ');
4250 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4251 }
4252 if (kp->flags & HL_SKIPEMPTY)
4253 {
4254 msg_puts_attr((char_u *)"skipempty", attr);
4255 msg_putchar(' ');
4256 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4257 }
4258 }
4259 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004260 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004261 }
4262 }
4263 }
4264
4265 return did_header;
4266}
4267
4268 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004269syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004270 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004271 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 hashitem_T *hi;
4274 keyentry_T *kp;
4275 keyentry_T *kp_prev;
4276 keyentry_T *kp_next;
4277 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004280 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004281 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004283 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004284 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004285 --todo;
4286 kp_prev = NULL;
4287 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004288 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004289 if (kp->k_syn.id == id)
4290 {
4291 kp_next = kp->ke_next;
4292 if (kp_prev == NULL)
4293 {
4294 if (kp_next == NULL)
4295 hash_remove(ht, hi);
4296 else
4297 hi->hi_key = KE2HIKEY(kp_next);
4298 }
4299 else
4300 kp_prev->ke_next = kp_next;
4301 vim_free(kp->next_list);
4302 vim_free(kp->k_syn.cont_in_list);
4303 vim_free(kp);
4304 kp = kp_next;
4305 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004306 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 {
4308 kp_prev = kp;
4309 kp = kp->ke_next;
4310 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311 }
4312 }
4313 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004314 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004315}
4316
4317/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004318 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319 */
4320 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004321clear_keywtab(ht)
4322 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004324 hashitem_T *hi;
4325 int todo;
4326 keyentry_T *kp;
4327 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004329 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 if (!HASHITEM_EMPTY(hi))
4333 {
4334 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 kp_next = kp->ke_next;
4338 vim_free(kp->next_list);
4339 vim_free(kp->k_syn.cont_in_list);
4340 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004341 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004342 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004344 hash_clear(ht);
4345 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346}
4347
4348/*
4349 * Add a keyword to the list of keywords.
4350 */
4351 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004352add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 char_u *name; /* name of keyword */
4354 int id; /* group ID for this keyword */
4355 int flags; /* flags for this keyword */
4356 short *cont_in_list; /* containedin for this keyword */
4357 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004358 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004360 keyentry_T *kp;
4361 hashtab_T *ht;
4362 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004363 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004364 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004365 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366
Bram Moolenaar860cae12010-06-05 23:22:07 +02004367 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004368 name_ic = str_foldcase(name, (int)STRLEN(name),
4369 name_folded, MAXKEYWLEN + 1);
4370 else
4371 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004372 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4373 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004375 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004376 kp->k_syn.id = id;
4377 kp->k_syn.inc_tag = current_syn_inc_tag;
4378 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004379 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004380 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004382 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004384
Bram Moolenaar860cae12010-06-05 23:22:07 +02004385 if (curwin->w_s->b_syn_ic)
4386 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004387 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004388 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389
Bram Moolenaardad6b692005-01-25 22:14:34 +00004390 hash = hash_hash(kp->keyword);
4391 hi = hash_lookup(ht, kp->keyword, hash);
4392 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 /* new keyword, add to hashtable */
4395 kp->ke_next = NULL;
4396 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004398 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004399 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004400 /* keyword already exists, prepend to list */
4401 kp->ke_next = HI2KE(hi);
4402 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004404}
4405
4406/*
4407 * Get the start and end of the group name argument.
4408 * Return a pointer to the first argument.
4409 * Return NULL if the end of the command was found instead of further args.
4410 */
4411 static char_u *
4412get_group_name(arg, name_end)
4413 char_u *arg; /* start of the argument */
4414 char_u **name_end; /* pointer to end of the name */
4415{
4416 char_u *rest;
4417
4418 *name_end = skiptowhite(arg);
4419 rest = skipwhite(*name_end);
4420
4421 /*
4422 * Check if there are enough arguments. The first argument may be a
4423 * pattern, where '|' is allowed, so only check for NUL.
4424 */
4425 if (ends_excmd(*arg) || *rest == NUL)
4426 return NULL;
4427 return rest;
4428}
4429
4430/*
4431 * Check for syntax command option arguments.
4432 * This can be called at any place in the list of arguments, and just picks
4433 * out the arguments that are known. Can be called several times in a row to
4434 * collect all options in between other arguments.
4435 * Return a pointer to the next argument (which isn't an option).
4436 * Return NULL for any error;
4437 */
4438 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004439get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004440 char_u *arg; /* next argument to be checked */
4441 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004442 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004443{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444 char_u *gname_start, *gname;
4445 int syn_id;
4446 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004447 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 int i;
4449 int fidx;
4450 static struct flag
4451 {
4452 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004453 int argtype;
4454 int flags;
4455 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4456 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4457 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4458 {"eExXtTeEnNdD", 0, HL_EXTEND},
4459 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4460 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4461 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4462 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4463 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4464 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4465 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4466 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4467 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004468 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4469 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4470 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004471 {"cCoOnNtTaAiInNsS", 1, 0},
4472 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4473 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004475 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476
4477 if (arg == NULL) /* already detected error */
4478 return NULL;
4479
Bram Moolenaar860cae12010-06-05 23:22:07 +02004480#ifdef FEAT_CONCEAL
4481 if (curwin->w_s->b_syn_conceal)
4482 opt->flags |= HL_CONCEAL;
4483#endif
4484
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 for (;;)
4486 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004487 /*
4488 * This is used very often when a large number of keywords is defined.
4489 * Need to skip quickly when no option name is found.
4490 * Also avoid tolower(), it's slow.
4491 */
4492 if (strchr(first_letters, *arg) == NULL)
4493 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004494
4495 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4496 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004497 p = flagtab[fidx].name;
4498 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4499 if (arg[len] != p[i] && arg[len] != p[i + 1])
4500 break;
4501 if (p[i] == NUL && (vim_iswhite(arg[len])
4502 || (flagtab[fidx].argtype > 0
4503 ? arg[len] == '='
4504 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004506 if (opt->keyword
4507 && (flagtab[fidx].flags == HL_DISPLAY
4508 || flagtab[fidx].flags == HL_FOLD
4509 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 /* treat "display", "fold" and "extend" as a keyword */
4511 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512 break;
4513 }
4514 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 if (fidx < 0) /* no match found */
4516 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004517
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004518 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004520 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521 {
4522 EMSG(_("E395: contains argument not accepted here"));
4523 return NULL;
4524 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004525 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 return NULL;
4527 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004530 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004531 return NULL;
4532 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004533 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004535 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004536 return NULL;
4537 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004538 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4539 {
4540#ifdef FEAT_MBYTE
4541 /* cchar=? */
4542 if (has_mbyte)
4543 {
4544# ifdef FEAT_CONCEAL
4545 *conceal_char = mb_ptr2char(arg + 6);
4546# endif
4547 arg += mb_ptr2len(arg + 6) - 1;
4548 }
4549 else
4550#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004551 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004552#ifdef FEAT_CONCEAL
4553 *conceal_char = arg[6];
4554#else
4555 ;
4556#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004557 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004558#ifdef FEAT_CONCEAL
4559 if (!vim_isprintc_strict(*conceal_char))
4560 {
4561 EMSG(_("E844: invalid cchar value"));
4562 return NULL;
4563 }
4564#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004565 arg = skipwhite(arg + 7);
4566 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004568 {
4569 opt->flags |= flagtab[fidx].flags;
4570 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004571
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004572 if (flagtab[fidx].flags == HL_SYNC_HERE
4573 || flagtab[fidx].flags == HL_SYNC_THERE)
4574 {
4575 if (opt->sync_idx == NULL)
4576 {
4577 EMSG(_("E393: group[t]here not accepted here"));
4578 return NULL;
4579 }
4580 gname_start = arg;
4581 arg = skiptowhite(arg);
4582 if (gname_start == arg)
4583 return NULL;
4584 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4585 if (gname == NULL)
4586 return NULL;
4587 if (STRCMP(gname, "NONE") == 0)
4588 *opt->sync_idx = NONE_IDX;
4589 else
4590 {
4591 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004592 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4593 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4594 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004595 {
4596 *opt->sync_idx = i;
4597 break;
4598 }
4599 if (i < 0)
4600 {
4601 EMSG2(_("E394: Didn't find region item for %s"), gname);
4602 vim_free(gname);
4603 return NULL;
4604 }
4605 }
4606
4607 vim_free(gname);
4608 arg = skipwhite(arg);
4609 }
4610#ifdef FEAT_FOLDING
4611 else if (flagtab[fidx].flags == HL_FOLD
4612 && foldmethodIsSyntax(curwin))
4613 /* Need to update folds later. */
4614 foldUpdateAll(curwin);
4615#endif
4616 }
4617 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004618
4619 return arg;
4620}
4621
4622/*
4623 * Adjustments to syntax item when declared in a ":syn include"'d file.
4624 * Set the contained flag, and if the item is not already contained, add it
4625 * to the specified top-level group, if any.
4626 */
4627 static void
4628syn_incl_toplevel(id, flagsp)
4629 int id;
4630 int *flagsp;
4631{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004632 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633 return;
4634 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004635 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004636 {
4637 /* We have to alloc this, because syn_combine_list() will free it. */
4638 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004639 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004640
4641 if (grp_list != NULL)
4642 {
4643 grp_list[0] = id;
4644 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004645 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646 CLUSTER_ADD);
4647 }
4648 }
4649}
4650
4651/*
4652 * Handle ":syntax include [@{group-name}] filename" command.
4653 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004654 static void
4655syn_cmd_include(eap, syncing)
4656 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004657 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658{
4659 char_u *arg = eap->arg;
4660 int sgl_id = 1;
4661 char_u *group_name_end;
4662 char_u *rest;
4663 char_u *errormsg = NULL;
4664 int prev_toplvl_grp;
4665 int prev_syn_inc_tag;
4666 int source = FALSE;
4667
4668 eap->nextcmd = find_nextcmd(arg);
4669 if (eap->skip)
4670 return;
4671
4672 if (arg[0] == '@')
4673 {
4674 ++arg;
4675 rest = get_group_name(arg, &group_name_end);
4676 if (rest == NULL)
4677 {
4678 EMSG((char_u *)_("E397: Filename required"));
4679 return;
4680 }
4681 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004682 if (sgl_id == 0)
4683 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004684 /* separate_nextcmd() and expand_filename() depend on this */
4685 eap->arg = rest;
4686 }
4687
4688 /*
4689 * Everything that's left, up to the next command, should be the
4690 * filename to include.
4691 */
4692 eap->argt |= (XFILE | NOSPC);
4693 separate_nextcmd(eap);
4694 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4695 {
4696 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4697 * file. Need to expand the file name first. In other cases
4698 * ":runtime!" is used. */
4699 source = TRUE;
4700 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4701 {
4702 if (errormsg != NULL)
4703 EMSG(errormsg);
4704 return;
4705 }
4706 }
4707
4708 /*
4709 * Save and restore the existing top-level grouplist id and ":syn
4710 * include" tag around the actual inclusion.
4711 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004712 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4713 {
4714 EMSG((char_u *)_("E847: Too many syntax includes"));
4715 return;
4716 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004717 prev_syn_inc_tag = current_syn_inc_tag;
4718 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004719 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4720 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004721 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4722 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004723 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004724 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725 current_syn_inc_tag = prev_syn_inc_tag;
4726}
4727
4728/*
4729 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4730 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 static void
4732syn_cmd_keyword(eap, syncing)
4733 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004734 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735{
4736 char_u *arg = eap->arg;
4737 char_u *group_name_end;
4738 int syn_id;
4739 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004740 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004741 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004742 char_u *kw;
4743 syn_opt_arg_T syn_opt_arg;
4744 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004745 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746
4747 rest = get_group_name(arg, &group_name_end);
4748
4749 if (rest != NULL)
4750 {
4751 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004752 if (syn_id != 0)
4753 /* allocate a buffer, for removing backslashes in the keyword */
4754 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755 if (keyword_copy != NULL)
4756 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004757 syn_opt_arg.flags = 0;
4758 syn_opt_arg.keyword = TRUE;
4759 syn_opt_arg.sync_idx = NULL;
4760 syn_opt_arg.has_cont_list = FALSE;
4761 syn_opt_arg.cont_in_list = NULL;
4762 syn_opt_arg.next_list = NULL;
4763
Bram Moolenaar071d4272004-06-13 20:20:40 +00004764 /*
4765 * The options given apply to ALL keywords, so all options must be
4766 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004767 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004769 cnt = 0;
4770 p = keyword_copy;
4771 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004773 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004774 if (rest == NULL || ends_excmd(*rest))
4775 break;
4776 /* Copy the keyword, removing backslashes, and add a NUL. */
4777 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004778 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004779 if (*rest == '\\' && rest[1] != NUL)
4780 ++rest;
4781 *p++ = *rest++;
4782 }
4783 *p++ = NUL;
4784 ++cnt;
4785 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004786
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004787 if (!eap->skip)
4788 {
4789 /* Adjust flags for use of ":syn include". */
4790 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4791
4792 /*
4793 * 2: Add an entry for each keyword.
4794 */
4795 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4796 {
4797 for (p = vim_strchr(kw, '['); ; )
4798 {
4799 if (p != NULL)
4800 *p = NUL;
4801 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004802 syn_opt_arg.cont_in_list,
4803 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004804 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004805 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004806 if (p[1] == NUL)
4807 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004808 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004809 kw = p + 2; /* skip over the NUL */
4810 break;
4811 }
4812 if (p[1] == ']')
4813 {
4814 kw = p + 1; /* skip over the "]" */
4815 break;
4816 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004817#ifdef FEAT_MBYTE
4818 if (has_mbyte)
4819 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004820 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004821
4822 mch_memmove(p, p + 1, l);
4823 p += l;
4824 }
4825 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004826#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004827 {
4828 p[0] = p[1];
4829 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004830 }
4831 }
4832 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004834
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004836 vim_free(syn_opt_arg.cont_in_list);
4837 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838 }
4839 }
4840
4841 if (rest != NULL)
4842 eap->nextcmd = check_nextcmd(rest);
4843 else
4844 EMSG2(_(e_invarg2), arg);
4845
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004846 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004847 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004848}
4849
4850/*
4851 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4852 *
4853 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4854 */
4855 static void
4856syn_cmd_match(eap, syncing)
4857 exarg_T *eap;
4858 int syncing; /* TRUE for ":syntax sync match .. " */
4859{
4860 char_u *arg = eap->arg;
4861 char_u *group_name_end;
4862 char_u *rest;
4863 synpat_T item; /* the item found in the line */
4864 int syn_id;
4865 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004866 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004868 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004869
4870 /* Isolate the group name, check for validity */
4871 rest = get_group_name(arg, &group_name_end);
4872
4873 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004874 syn_opt_arg.flags = 0;
4875 syn_opt_arg.keyword = FALSE;
4876 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4877 syn_opt_arg.has_cont_list = TRUE;
4878 syn_opt_arg.cont_list = NULL;
4879 syn_opt_arg.cont_in_list = NULL;
4880 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004881 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882
4883 /* get the pattern. */
4884 init_syn_patterns();
4885 vim_memset(&item, 0, sizeof(item));
4886 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004887 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4888 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889
4890 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004891 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004892
4893 if (rest != NULL) /* all arguments are valid */
4894 {
4895 /*
4896 * Check for trailing command and illegal trailing arguments.
4897 */
4898 eap->nextcmd = check_nextcmd(rest);
4899 if (!ends_excmd(*rest) || eap->skip)
4900 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004901 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902 && (syn_id = syn_check_group(arg,
4903 (int)(group_name_end - arg))) != 0)
4904 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004905 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004906 /*
4907 * Store the pattern in the syn_items list
4908 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004909 idx = curwin->w_s->b_syn_patterns.ga_len;
4910 SYN_ITEMS(curwin->w_s)[idx] = item;
4911 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4912 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4913 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4914 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4915 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4916 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4917 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4918 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004919 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004920#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004921 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004922#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004923 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004924 curwin->w_s->b_syn_containedin = TRUE;
4925 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4926 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927
4928 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004929 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004930 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004932 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004933 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934#endif
4935
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004936 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004937 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938 return; /* don't free the progs and patterns now */
4939 }
4940 }
4941
4942 /*
4943 * Something failed, free the allocated memory.
4944 */
4945 vim_free(item.sp_prog);
4946 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004947 vim_free(syn_opt_arg.cont_list);
4948 vim_free(syn_opt_arg.cont_in_list);
4949 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004950
4951 if (rest == NULL)
4952 EMSG2(_(e_invarg2), arg);
4953}
4954
4955/*
4956 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4957 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4958 */
4959 static void
4960syn_cmd_region(eap, syncing)
4961 exarg_T *eap;
4962 int syncing; /* TRUE for ":syntax sync region .." */
4963{
4964 char_u *arg = eap->arg;
4965 char_u *group_name_end;
4966 char_u *rest; /* next arg, NULL on error */
4967 char_u *key_end;
4968 char_u *key = NULL;
4969 char_u *p;
4970 int item;
4971#define ITEM_START 0
4972#define ITEM_SKIP 1
4973#define ITEM_END 2
4974#define ITEM_MATCHGROUP 3
4975 struct pat_ptr
4976 {
4977 synpat_T *pp_synp; /* pointer to syn_pattern */
4978 int pp_matchgroup_id; /* matchgroup ID */
4979 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4980 } *(pat_ptrs[3]);
4981 /* patterns found in the line */
4982 struct pat_ptr *ppp;
4983 struct pat_ptr *ppp_next;
4984 int pat_count = 0; /* nr of syn_patterns found */
4985 int syn_id;
4986 int matchgroup_id = 0;
4987 int not_enough = FALSE; /* not enough arguments */
4988 int illegal = FALSE; /* illegal arguments */
4989 int success = FALSE;
4990 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004991 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004992 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004993
4994 /* Isolate the group name, check for validity */
4995 rest = get_group_name(arg, &group_name_end);
4996
4997 pat_ptrs[0] = NULL;
4998 pat_ptrs[1] = NULL;
4999 pat_ptrs[2] = NULL;
5000
5001 init_syn_patterns();
5002
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005003 syn_opt_arg.flags = 0;
5004 syn_opt_arg.keyword = FALSE;
5005 syn_opt_arg.sync_idx = NULL;
5006 syn_opt_arg.has_cont_list = TRUE;
5007 syn_opt_arg.cont_list = NULL;
5008 syn_opt_arg.cont_in_list = NULL;
5009 syn_opt_arg.next_list = NULL;
5010
Bram Moolenaar071d4272004-06-13 20:20:40 +00005011 /*
5012 * get the options, patterns and matchgroup.
5013 */
5014 while (rest != NULL && !ends_excmd(*rest))
5015 {
5016 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005017 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005018 if (rest == NULL || ends_excmd(*rest))
5019 break;
5020
5021 /* must be a pattern or matchgroup then */
5022 key_end = rest;
5023 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5024 ++key_end;
5025 vim_free(key);
5026 key = vim_strnsave_up(rest, (int)(key_end - rest));
5027 if (key == NULL) /* out of memory */
5028 {
5029 rest = NULL;
5030 break;
5031 }
5032 if (STRCMP(key, "MATCHGROUP") == 0)
5033 item = ITEM_MATCHGROUP;
5034 else if (STRCMP(key, "START") == 0)
5035 item = ITEM_START;
5036 else if (STRCMP(key, "END") == 0)
5037 item = ITEM_END;
5038 else if (STRCMP(key, "SKIP") == 0)
5039 {
5040 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5041 {
5042 illegal = TRUE;
5043 break;
5044 }
5045 item = ITEM_SKIP;
5046 }
5047 else
5048 break;
5049 rest = skipwhite(key_end);
5050 if (*rest != '=')
5051 {
5052 rest = NULL;
5053 EMSG2(_("E398: Missing '=': %s"), arg);
5054 break;
5055 }
5056 rest = skipwhite(rest + 1);
5057 if (*rest == NUL)
5058 {
5059 not_enough = TRUE;
5060 break;
5061 }
5062
5063 if (item == ITEM_MATCHGROUP)
5064 {
5065 p = skiptowhite(rest);
5066 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5067 matchgroup_id = 0;
5068 else
5069 {
5070 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5071 if (matchgroup_id == 0)
5072 {
5073 illegal = TRUE;
5074 break;
5075 }
5076 }
5077 rest = skipwhite(p);
5078 }
5079 else
5080 {
5081 /*
5082 * Allocate room for a syn_pattern, and link it in the list of
5083 * syn_patterns for this item, at the start (because the list is
5084 * used from end to start).
5085 */
5086 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5087 if (ppp == NULL)
5088 {
5089 rest = NULL;
5090 break;
5091 }
5092 ppp->pp_next = pat_ptrs[item];
5093 pat_ptrs[item] = ppp;
5094 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5095 if (ppp->pp_synp == NULL)
5096 {
5097 rest = NULL;
5098 break;
5099 }
5100
5101 /*
5102 * Get the syntax pattern and the following offset(s).
5103 */
5104 /* Enable the appropriate \z specials. */
5105 if (item == ITEM_START)
5106 reg_do_extmatch = REX_SET;
5107 else if (item == ITEM_SKIP || item == ITEM_END)
5108 reg_do_extmatch = REX_USE;
5109 rest = get_syn_pattern(rest, ppp->pp_synp);
5110 reg_do_extmatch = 0;
5111 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005112 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5114 ppp->pp_matchgroup_id = matchgroup_id;
5115 ++pat_count;
5116 }
5117 }
5118 vim_free(key);
5119 if (illegal || not_enough)
5120 rest = NULL;
5121
5122 /*
5123 * Must have a "start" and "end" pattern.
5124 */
5125 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5126 pat_ptrs[ITEM_END] == NULL))
5127 {
5128 not_enough = TRUE;
5129 rest = NULL;
5130 }
5131
5132 if (rest != NULL)
5133 {
5134 /*
5135 * Check for trailing garbage or command.
5136 * If OK, add the item.
5137 */
5138 eap->nextcmd = check_nextcmd(rest);
5139 if (!ends_excmd(*rest) || eap->skip)
5140 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005141 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142 && (syn_id = syn_check_group(arg,
5143 (int)(group_name_end - arg))) != 0)
5144 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005145 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146 /*
5147 * Store the start/skip/end in the syn_items list
5148 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005149 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150 for (item = ITEM_START; item <= ITEM_END; ++item)
5151 {
5152 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5153 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005154 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5155 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5156 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 (item == ITEM_START) ? SPTYPE_START :
5158 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005159 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5160 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005161 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5162 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005163 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005164 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005165#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005166 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005167#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005168 if (item == ITEM_START)
5169 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005170 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005171 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005172 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005173 syn_opt_arg.cont_in_list;
5174 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005175 curwin->w_s->b_syn_containedin = TRUE;
5176 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005177 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005179 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005180 ++idx;
5181#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005182 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005183 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005184#endif
5185 }
5186 }
5187
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005188 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005189 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005190 success = TRUE; /* don't free the progs and patterns now */
5191 }
5192 }
5193
5194 /*
5195 * Free the allocated memory.
5196 */
5197 for (item = ITEM_START; item <= ITEM_END; ++item)
5198 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5199 {
5200 if (!success)
5201 {
5202 vim_free(ppp->pp_synp->sp_prog);
5203 vim_free(ppp->pp_synp->sp_pattern);
5204 }
5205 vim_free(ppp->pp_synp);
5206 ppp_next = ppp->pp_next;
5207 vim_free(ppp);
5208 }
5209
5210 if (!success)
5211 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005212 vim_free(syn_opt_arg.cont_list);
5213 vim_free(syn_opt_arg.cont_in_list);
5214 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005215 if (not_enough)
5216 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5217 else if (illegal || rest == NULL)
5218 EMSG2(_(e_invarg2), arg);
5219 }
5220}
5221
5222/*
5223 * A simple syntax group ID comparison function suitable for use in qsort()
5224 */
5225 static int
5226#ifdef __BORLANDC__
5227_RTLENTRYF
5228#endif
5229syn_compare_stub(v1, v2)
5230 const void *v1;
5231 const void *v2;
5232{
5233 const short *s1 = v1;
5234 const short *s2 = v2;
5235
5236 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5237}
5238
5239/*
5240 * Combines lists of syntax clusters.
5241 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5242 */
5243 static void
5244syn_combine_list(clstr1, clstr2, list_op)
5245 short **clstr1;
5246 short **clstr2;
5247 int list_op;
5248{
5249 int count1 = 0;
5250 int count2 = 0;
5251 short *g1;
5252 short *g2;
5253 short *clstr = NULL;
5254 int count;
5255 int round;
5256
5257 /*
5258 * Handle degenerate cases.
5259 */
5260 if (*clstr2 == NULL)
5261 return;
5262 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5263 {
5264 if (list_op == CLUSTER_REPLACE)
5265 vim_free(*clstr1);
5266 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5267 *clstr1 = *clstr2;
5268 else
5269 vim_free(*clstr2);
5270 return;
5271 }
5272
5273 for (g1 = *clstr1; *g1; g1++)
5274 ++count1;
5275 for (g2 = *clstr2; *g2; g2++)
5276 ++count2;
5277
5278 /*
5279 * For speed purposes, sort both lists.
5280 */
5281 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5282 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5283
5284 /*
5285 * We proceed in two passes; in round 1, we count the elements to place
5286 * in the new list, and in round 2, we allocate and populate the new
5287 * list. For speed, we use a mergesort-like method, adding the smaller
5288 * of the current elements in each list to the new list.
5289 */
5290 for (round = 1; round <= 2; round++)
5291 {
5292 g1 = *clstr1;
5293 g2 = *clstr2;
5294 count = 0;
5295
5296 /*
5297 * First, loop through the lists until one of them is empty.
5298 */
5299 while (*g1 && *g2)
5300 {
5301 /*
5302 * We always want to add from the first list.
5303 */
5304 if (*g1 < *g2)
5305 {
5306 if (round == 2)
5307 clstr[count] = *g1;
5308 count++;
5309 g1++;
5310 continue;
5311 }
5312 /*
5313 * We only want to add from the second list if we're adding the
5314 * lists.
5315 */
5316 if (list_op == CLUSTER_ADD)
5317 {
5318 if (round == 2)
5319 clstr[count] = *g2;
5320 count++;
5321 }
5322 if (*g1 == *g2)
5323 g1++;
5324 g2++;
5325 }
5326
5327 /*
5328 * Now add the leftovers from whichever list didn't get finished
5329 * first. As before, we only want to add from the second list if
5330 * we're adding the lists.
5331 */
5332 for (; *g1; g1++, count++)
5333 if (round == 2)
5334 clstr[count] = *g1;
5335 if (list_op == CLUSTER_ADD)
5336 for (; *g2; g2++, count++)
5337 if (round == 2)
5338 clstr[count] = *g2;
5339
5340 if (round == 1)
5341 {
5342 /*
5343 * If the group ended up empty, we don't need to allocate any
5344 * space for it.
5345 */
5346 if (count == 0)
5347 {
5348 clstr = NULL;
5349 break;
5350 }
5351 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5352 if (clstr == NULL)
5353 break;
5354 clstr[count] = 0;
5355 }
5356 }
5357
5358 /*
5359 * Finally, put the new list in place.
5360 */
5361 vim_free(*clstr1);
5362 vim_free(*clstr2);
5363 *clstr1 = clstr;
5364}
5365
5366/*
5367 * Lookup a syntax cluster name and return it's ID.
5368 * If it is not found, 0 is returned.
5369 */
5370 static int
5371syn_scl_name2id(name)
5372 char_u *name;
5373{
5374 int i;
5375 char_u *name_u;
5376
5377 /* Avoid using stricmp() too much, it's slow on some systems */
5378 name_u = vim_strsave_up(name);
5379 if (name_u == NULL)
5380 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005381 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5382 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5383 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005384 break;
5385 vim_free(name_u);
5386 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5387}
5388
5389/*
5390 * Like syn_scl_name2id(), but take a pointer + length argument.
5391 */
5392 static int
5393syn_scl_namen2id(linep, len)
5394 char_u *linep;
5395 int len;
5396{
5397 char_u *name;
5398 int id = 0;
5399
5400 name = vim_strnsave(linep, len);
5401 if (name != NULL)
5402 {
5403 id = syn_scl_name2id(name);
5404 vim_free(name);
5405 }
5406 return id;
5407}
5408
5409/*
5410 * Find syntax cluster name in the table and return it's ID.
5411 * The argument is a pointer to the name and the length of the name.
5412 * If it doesn't exist yet, a new entry is created.
5413 * Return 0 for failure.
5414 */
5415 static int
5416syn_check_cluster(pp, len)
5417 char_u *pp;
5418 int len;
5419{
5420 int id;
5421 char_u *name;
5422
5423 name = vim_strnsave(pp, len);
5424 if (name == NULL)
5425 return 0;
5426
5427 id = syn_scl_name2id(name);
5428 if (id == 0) /* doesn't exist yet */
5429 id = syn_add_cluster(name);
5430 else
5431 vim_free(name);
5432 return id;
5433}
5434
5435/*
5436 * Add new syntax cluster and return it's ID.
5437 * "name" must be an allocated string, it will be consumed.
5438 * Return 0 for failure.
5439 */
5440 static int
5441syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005442 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005444 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005445
5446 /*
5447 * First call for this growarray: init growing array.
5448 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005449 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005451 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5452 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005453 }
5454
Bram Moolenaar42431a72011-04-01 14:44:59 +02005455 len = curwin->w_s->b_syn_clusters.ga_len;
5456 if (len >= MAX_CLUSTER_ID)
5457 {
5458 EMSG((char_u *)_("E848: Too many syntax clusters"));
5459 vim_free(name);
5460 return 0;
5461 }
5462
Bram Moolenaar071d4272004-06-13 20:20:40 +00005463 /*
5464 * Make room for at least one other cluster entry.
5465 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005466 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005467 {
5468 vim_free(name);
5469 return 0;
5470 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005471
Bram Moolenaar860cae12010-06-05 23:22:07 +02005472 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5473 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5474 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5475 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5476 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005477
Bram Moolenaar217ad922005-03-20 22:37:15 +00005478 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005479 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005480 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005481 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005482
Bram Moolenaar071d4272004-06-13 20:20:40 +00005483 return len + SYNID_CLUSTER;
5484}
5485
5486/*
5487 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5488 * [add={groupname},..] [remove={groupname},..]".
5489 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005490 static void
5491syn_cmd_cluster(eap, syncing)
5492 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005493 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494{
5495 char_u *arg = eap->arg;
5496 char_u *group_name_end;
5497 char_u *rest;
5498 int scl_id;
5499 short *clstr_list;
5500 int got_clstr = FALSE;
5501 int opt_len;
5502 int list_op;
5503
5504 eap->nextcmd = find_nextcmd(arg);
5505 if (eap->skip)
5506 return;
5507
5508 rest = get_group_name(arg, &group_name_end);
5509
5510 if (rest != NULL)
5511 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005512 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5513 if (scl_id == 0)
5514 return;
5515 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005516
5517 for (;;)
5518 {
5519 if (STRNICMP(rest, "add", 3) == 0
5520 && (vim_iswhite(rest[3]) || rest[3] == '='))
5521 {
5522 opt_len = 3;
5523 list_op = CLUSTER_ADD;
5524 }
5525 else if (STRNICMP(rest, "remove", 6) == 0
5526 && (vim_iswhite(rest[6]) || rest[6] == '='))
5527 {
5528 opt_len = 6;
5529 list_op = CLUSTER_SUBTRACT;
5530 }
5531 else if (STRNICMP(rest, "contains", 8) == 0
5532 && (vim_iswhite(rest[8]) || rest[8] == '='))
5533 {
5534 opt_len = 8;
5535 list_op = CLUSTER_REPLACE;
5536 }
5537 else
5538 break;
5539
5540 clstr_list = NULL;
5541 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5542 {
5543 EMSG2(_(e_invarg2), rest);
5544 break;
5545 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005546 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005547 &clstr_list, list_op);
5548 got_clstr = TRUE;
5549 }
5550
5551 if (got_clstr)
5552 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005553 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005554 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 }
5556 }
5557
5558 if (!got_clstr)
5559 EMSG(_("E400: No cluster specified"));
5560 if (rest == NULL || !ends_excmd(*rest))
5561 EMSG2(_(e_invarg2), arg);
5562}
5563
5564/*
5565 * On first call for current buffer: Init growing array.
5566 */
5567 static void
5568init_syn_patterns()
5569{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005570 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5571 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005572}
5573
5574/*
5575 * Get one pattern for a ":syntax match" or ":syntax region" command.
5576 * Stores the pattern and program in a synpat_T.
5577 * Returns a pointer to the next argument, or NULL in case of an error.
5578 */
5579 static char_u *
5580get_syn_pattern(arg, ci)
5581 char_u *arg;
5582 synpat_T *ci;
5583{
5584 char_u *end;
5585 int *p;
5586 int idx;
5587 char_u *cpo_save;
5588
5589 /* need at least three chars */
5590 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5591 return NULL;
5592
5593 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5594 if (*end != *arg) /* end delimiter not found */
5595 {
5596 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5597 return NULL;
5598 }
5599 /* store the pattern and compiled regexp program */
5600 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5601 return NULL;
5602
5603 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5604 cpo_save = p_cpo;
5605 p_cpo = (char_u *)"";
5606 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5607 p_cpo = cpo_save;
5608
5609 if (ci->sp_prog == NULL)
5610 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005611 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005612
5613 /*
5614 * Check for a match, highlight or region offset.
5615 */
5616 ++end;
5617 do
5618 {
5619 for (idx = SPO_COUNT; --idx >= 0; )
5620 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5621 break;
5622 if (idx >= 0)
5623 {
5624 p = &(ci->sp_offsets[idx]);
5625 if (idx != SPO_LC_OFF)
5626 switch (end[3])
5627 {
5628 case 's': break;
5629 case 'b': break;
5630 case 'e': idx += SPO_COUNT; break;
5631 default: idx = -1; break;
5632 }
5633 if (idx >= 0)
5634 {
5635 ci->sp_off_flags |= (1 << idx);
5636 if (idx == SPO_LC_OFF) /* lc=99 */
5637 {
5638 end += 3;
5639 *p = getdigits(&end);
5640
5641 /* "lc=" offset automatically sets "ms=" offset */
5642 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5643 {
5644 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5645 ci->sp_offsets[SPO_MS_OFF] = *p;
5646 }
5647 }
5648 else /* yy=x+99 */
5649 {
5650 end += 4;
5651 if (*end == '+')
5652 {
5653 ++end;
5654 *p = getdigits(&end); /* positive offset */
5655 }
5656 else if (*end == '-')
5657 {
5658 ++end;
5659 *p = -getdigits(&end); /* negative offset */
5660 }
5661 }
5662 if (*end != ',')
5663 break;
5664 ++end;
5665 }
5666 }
5667 } while (idx >= 0);
5668
5669 if (!ends_excmd(*end) && !vim_iswhite(*end))
5670 {
5671 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5672 return NULL;
5673 }
5674 return skipwhite(end);
5675}
5676
5677/*
5678 * Handle ":syntax sync .." command.
5679 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005680 static void
5681syn_cmd_sync(eap, syncing)
5682 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005683 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684{
5685 char_u *arg_start = eap->arg;
5686 char_u *arg_end;
5687 char_u *key = NULL;
5688 char_u *next_arg;
5689 int illegal = FALSE;
5690 int finished = FALSE;
5691 long n;
5692 char_u *cpo_save;
5693
5694 if (ends_excmd(*arg_start))
5695 {
5696 syn_cmd_list(eap, TRUE);
5697 return;
5698 }
5699
5700 while (!ends_excmd(*arg_start))
5701 {
5702 arg_end = skiptowhite(arg_start);
5703 next_arg = skipwhite(arg_end);
5704 vim_free(key);
5705 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5706 if (STRCMP(key, "CCOMMENT") == 0)
5707 {
5708 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005709 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005710 if (!ends_excmd(*next_arg))
5711 {
5712 arg_end = skiptowhite(next_arg);
5713 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005714 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005715 (int)(arg_end - next_arg));
5716 next_arg = skipwhite(arg_end);
5717 }
5718 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005719 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005720 }
5721 else if ( STRNCMP(key, "LINES", 5) == 0
5722 || STRNCMP(key, "MINLINES", 8) == 0
5723 || STRNCMP(key, "MAXLINES", 8) == 0
5724 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5725 {
5726 if (key[4] == 'S')
5727 arg_end = key + 6;
5728 else if (key[0] == 'L')
5729 arg_end = key + 11;
5730 else
5731 arg_end = key + 9;
5732 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5733 {
5734 illegal = TRUE;
5735 break;
5736 }
5737 n = getdigits(&arg_end);
5738 if (!eap->skip)
5739 {
5740 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005741 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005742 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005743 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005744 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005745 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005746 }
5747 }
5748 else if (STRCMP(key, "FROMSTART") == 0)
5749 {
5750 if (!eap->skip)
5751 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005752 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5753 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005754 }
5755 }
5756 else if (STRCMP(key, "LINECONT") == 0)
5757 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005758 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005759 {
5760 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5761 finished = TRUE;
5762 break;
5763 }
5764 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5765 if (*arg_end != *next_arg) /* end delimiter not found */
5766 {
5767 illegal = TRUE;
5768 break;
5769 }
5770
5771 if (!eap->skip)
5772 {
5773 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005774 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005775 (int)(arg_end - next_arg - 1))) == NULL)
5776 {
5777 finished = TRUE;
5778 break;
5779 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005780 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005781
5782 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5783 cpo_save = p_cpo;
5784 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005785 curwin->w_s->b_syn_linecont_prog =
5786 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005787 p_cpo = cpo_save;
5788
Bram Moolenaar860cae12010-06-05 23:22:07 +02005789 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005790 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005791 vim_free(curwin->w_s->b_syn_linecont_pat);
5792 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 finished = TRUE;
5794 break;
5795 }
5796 }
5797 next_arg = skipwhite(arg_end + 1);
5798 }
5799 else
5800 {
5801 eap->arg = next_arg;
5802 if (STRCMP(key, "MATCH") == 0)
5803 syn_cmd_match(eap, TRUE);
5804 else if (STRCMP(key, "REGION") == 0)
5805 syn_cmd_region(eap, TRUE);
5806 else if (STRCMP(key, "CLEAR") == 0)
5807 syn_cmd_clear(eap, TRUE);
5808 else
5809 illegal = TRUE;
5810 finished = TRUE;
5811 break;
5812 }
5813 arg_start = next_arg;
5814 }
5815 vim_free(key);
5816 if (illegal)
5817 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5818 else if (!finished)
5819 {
5820 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005821 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005822 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005823 }
5824}
5825
5826/*
5827 * Convert a line of highlight group names into a list of group ID numbers.
5828 * "arg" should point to the "contains" or "nextgroup" keyword.
5829 * "arg" is advanced to after the last group name.
5830 * Careful: the argument is modified (NULs added).
5831 * returns FAIL for some error, OK for success.
5832 */
5833 static int
5834get_id_list(arg, keylen, list)
5835 char_u **arg;
5836 int keylen; /* length of keyword */
5837 short **list; /* where to store the resulting list, if not
5838 NULL, the list is silently skipped! */
5839{
5840 char_u *p = NULL;
5841 char_u *end;
5842 int round;
5843 int count;
5844 int total_count = 0;
5845 short *retval = NULL;
5846 char_u *name;
5847 regmatch_T regmatch;
5848 int id;
5849 int i;
5850 int failed = FALSE;
5851
5852 /*
5853 * We parse the list twice:
5854 * round == 1: count the number of items, allocate the array.
5855 * round == 2: fill the array with the items.
5856 * In round 1 new groups may be added, causing the number of items to
5857 * grow when a regexp is used. In that case round 1 is done once again.
5858 */
5859 for (round = 1; round <= 2; ++round)
5860 {
5861 /*
5862 * skip "contains"
5863 */
5864 p = skipwhite(*arg + keylen);
5865 if (*p != '=')
5866 {
5867 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5868 break;
5869 }
5870 p = skipwhite(p + 1);
5871 if (ends_excmd(*p))
5872 {
5873 EMSG2(_("E406: Empty argument: %s"), *arg);
5874 break;
5875 }
5876
5877 /*
5878 * parse the arguments after "contains"
5879 */
5880 count = 0;
5881 while (!ends_excmd(*p))
5882 {
5883 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5884 ;
5885 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5886 if (name == NULL)
5887 {
5888 failed = TRUE;
5889 break;
5890 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005891 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005892 if ( STRCMP(name + 1, "ALLBUT") == 0
5893 || STRCMP(name + 1, "ALL") == 0
5894 || STRCMP(name + 1, "TOP") == 0
5895 || STRCMP(name + 1, "CONTAINED") == 0)
5896 {
5897 if (TOUPPER_ASC(**arg) != 'C')
5898 {
5899 EMSG2(_("E407: %s not allowed here"), name + 1);
5900 failed = TRUE;
5901 vim_free(name);
5902 break;
5903 }
5904 if (count != 0)
5905 {
5906 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5907 failed = TRUE;
5908 vim_free(name);
5909 break;
5910 }
5911 if (name[1] == 'A')
5912 id = SYNID_ALLBUT;
5913 else if (name[1] == 'T')
5914 id = SYNID_TOP;
5915 else
5916 id = SYNID_CONTAINED;
5917 id += current_syn_inc_tag;
5918 }
5919 else if (name[1] == '@')
5920 {
5921 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5922 }
5923 else
5924 {
5925 /*
5926 * Handle full group name.
5927 */
5928 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5929 id = syn_check_group(name + 1, (int)(end - p));
5930 else
5931 {
5932 /*
5933 * Handle match of regexp with group names.
5934 */
5935 *name = '^';
5936 STRCAT(name, "$");
5937 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5938 if (regmatch.regprog == NULL)
5939 {
5940 failed = TRUE;
5941 vim_free(name);
5942 break;
5943 }
5944
5945 regmatch.rm_ic = TRUE;
5946 id = 0;
5947 for (i = highlight_ga.ga_len; --i >= 0; )
5948 {
5949 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5950 (colnr_T)0))
5951 {
5952 if (round == 2)
5953 {
5954 /* Got more items than expected; can happen
5955 * when adding items that match:
5956 * "contains=a.*b,axb".
5957 * Go back to first round */
5958 if (count >= total_count)
5959 {
5960 vim_free(retval);
5961 round = 1;
5962 }
5963 else
5964 retval[count] = i + 1;
5965 }
5966 ++count;
5967 id = -1; /* remember that we found one */
5968 }
5969 }
5970 vim_free(regmatch.regprog);
5971 }
5972 }
5973 vim_free(name);
5974 if (id == 0)
5975 {
5976 EMSG2(_("E409: Unknown group name: %s"), p);
5977 failed = TRUE;
5978 break;
5979 }
5980 if (id > 0)
5981 {
5982 if (round == 2)
5983 {
5984 /* Got more items than expected, go back to first round */
5985 if (count >= total_count)
5986 {
5987 vim_free(retval);
5988 round = 1;
5989 }
5990 else
5991 retval[count] = id;
5992 }
5993 ++count;
5994 }
5995 p = skipwhite(end);
5996 if (*p != ',')
5997 break;
5998 p = skipwhite(p + 1); /* skip comma in between arguments */
5999 }
6000 if (failed)
6001 break;
6002 if (round == 1)
6003 {
6004 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6005 if (retval == NULL)
6006 break;
6007 retval[count] = 0; /* zero means end of the list */
6008 total_count = count;
6009 }
6010 }
6011
6012 *arg = p;
6013 if (failed || retval == NULL)
6014 {
6015 vim_free(retval);
6016 return FAIL;
6017 }
6018
6019 if (*list == NULL)
6020 *list = retval;
6021 else
6022 vim_free(retval); /* list already found, don't overwrite it */
6023
6024 return OK;
6025}
6026
6027/*
6028 * Make a copy of an ID list.
6029 */
6030 static short *
6031copy_id_list(list)
6032 short *list;
6033{
6034 int len;
6035 int count;
6036 short *retval;
6037
6038 if (list == NULL)
6039 return NULL;
6040
6041 for (count = 0; list[count]; ++count)
6042 ;
6043 len = (count + 1) * sizeof(short);
6044 retval = (short *)alloc((unsigned)len);
6045 if (retval != NULL)
6046 mch_memmove(retval, list, (size_t)len);
6047
6048 return retval;
6049}
6050
6051/*
6052 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6053 * "cur_si" can be NULL if not checking the "containedin" list.
6054 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6055 * the current item.
6056 * This function is called very often, keep it fast!!
6057 */
6058 static int
6059in_id_list(cur_si, list, ssp, contained)
6060 stateitem_T *cur_si; /* current item or NULL */
6061 short *list; /* id list */
6062 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6063 int contained; /* group id is contained */
6064{
6065 int retval;
6066 short *scl_list;
6067 short item;
6068 short id = ssp->id;
6069 static int depth = 0;
6070 int r;
6071
6072 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006073 if (cur_si != NULL && ssp->cont_in_list != NULL
6074 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006075 {
6076 /* Ignore transparent items without a contains argument. Double check
6077 * that we don't go back past the first one. */
6078 while ((cur_si->si_flags & HL_TRANS_CONT)
6079 && cur_si > (stateitem_T *)(current_state.ga_data))
6080 --cur_si;
6081 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6082 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006083 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6084 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006085 return TRUE;
6086 }
6087
6088 if (list == NULL)
6089 return FALSE;
6090
6091 /*
6092 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6093 * inside anything. Only allow not-contained groups.
6094 */
6095 if (list == ID_LIST_ALL)
6096 return !contained;
6097
6098 /*
6099 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6100 * contains list. We also require that "id" is at the same ":syn include"
6101 * level as the list.
6102 */
6103 item = *list;
6104 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6105 {
6106 if (item < SYNID_TOP)
6107 {
6108 /* ALL or ALLBUT: accept all groups in the same file */
6109 if (item - SYNID_ALLBUT != ssp->inc_tag)
6110 return FALSE;
6111 }
6112 else if (item < SYNID_CONTAINED)
6113 {
6114 /* TOP: accept all not-contained groups in the same file */
6115 if (item - SYNID_TOP != ssp->inc_tag || contained)
6116 return FALSE;
6117 }
6118 else
6119 {
6120 /* CONTAINED: accept all contained groups in the same file */
6121 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6122 return FALSE;
6123 }
6124 item = *++list;
6125 retval = FALSE;
6126 }
6127 else
6128 retval = TRUE;
6129
6130 /*
6131 * Return "retval" if id is in the contains list.
6132 */
6133 while (item != 0)
6134 {
6135 if (item == id)
6136 return retval;
6137 if (item >= SYNID_CLUSTER)
6138 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006139 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006140 /* restrict recursiveness to 30 to avoid an endless loop for a
6141 * cluster that includes itself (indirectly) */
6142 if (scl_list != NULL && depth < 30)
6143 {
6144 ++depth;
6145 r = in_id_list(NULL, scl_list, ssp, contained);
6146 --depth;
6147 if (r)
6148 return retval;
6149 }
6150 }
6151 item = *++list;
6152 }
6153 return !retval;
6154}
6155
6156struct subcommand
6157{
6158 char *name; /* subcommand name */
6159 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6160};
6161
6162static struct subcommand subcommands[] =
6163{
6164 {"case", syn_cmd_case},
6165 {"clear", syn_cmd_clear},
6166 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006167 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006168 {"enable", syn_cmd_enable},
6169 {"include", syn_cmd_include},
6170 {"keyword", syn_cmd_keyword},
6171 {"list", syn_cmd_list},
6172 {"manual", syn_cmd_manual},
6173 {"match", syn_cmd_match},
6174 {"on", syn_cmd_on},
6175 {"off", syn_cmd_off},
6176 {"region", syn_cmd_region},
6177 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006178 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006179 {"sync", syn_cmd_sync},
6180 {"", syn_cmd_list},
6181 {NULL, NULL}
6182};
6183
6184/*
6185 * ":syntax".
6186 * This searches the subcommands[] table for the subcommand name, and calls a
6187 * syntax_subcommand() function to do the rest.
6188 */
6189 void
6190ex_syntax(eap)
6191 exarg_T *eap;
6192{
6193 char_u *arg = eap->arg;
6194 char_u *subcmd_end;
6195 char_u *subcmd_name;
6196 int i;
6197
6198 syn_cmdlinep = eap->cmdlinep;
6199
6200 /* isolate subcommand name */
6201 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6202 ;
6203 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6204 if (subcmd_name != NULL)
6205 {
6206 if (eap->skip) /* skip error messages for all subcommands */
6207 ++emsg_skip;
6208 for (i = 0; ; ++i)
6209 {
6210 if (subcommands[i].name == NULL)
6211 {
6212 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6213 break;
6214 }
6215 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6216 {
6217 eap->arg = skipwhite(subcmd_end);
6218 (subcommands[i].func)(eap, FALSE);
6219 break;
6220 }
6221 }
6222 vim_free(subcmd_name);
6223 if (eap->skip)
6224 --emsg_skip;
6225 }
6226}
6227
Bram Moolenaar860cae12010-06-05 23:22:07 +02006228 void
6229ex_ownsyntax(eap)
6230 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006231{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006232 char_u *old_value;
6233 char_u *new_value;
6234
Bram Moolenaar860cae12010-06-05 23:22:07 +02006235 if (curwin->w_s == &curwin->w_buffer->b_s)
6236 {
6237 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6238 memset(curwin->w_s, 0, sizeof(synblock_T));
6239#ifdef FEAT_SPELL
6240 curwin->w_p_spell = FALSE; /* No spell checking */
6241 clear_string_option(&curwin->w_s->b_p_spc);
6242 clear_string_option(&curwin->w_s->b_p_spf);
6243 vim_free(curwin->w_s->b_cap_prog);
6244 curwin->w_s->b_cap_prog = NULL;
6245 clear_string_option(&curwin->w_s->b_p_spl);
6246#endif
6247 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006248
6249 /* save value of b:current_syntax */
6250 old_value = get_var_value((char_u *)"b:current_syntax");
6251 if (old_value != NULL)
6252 old_value = vim_strsave(old_value);
6253
6254 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6255 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006256 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006257
6258 /* move value of b:current_syntax to w:current_syntax */
6259 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006260 if (new_value != NULL)
6261 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006262
6263 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006264 if (old_value == NULL)
6265 do_unlet((char_u *)"b:current_syntax", TRUE);
6266 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006267 {
6268 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6269 vim_free(old_value);
6270 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006271}
6272
6273 int
6274syntax_present(win)
6275 win_T *win;
6276{
6277 return (win->w_s->b_syn_patterns.ga_len != 0
6278 || win->w_s->b_syn_clusters.ga_len != 0
6279 || win->w_s->b_keywtab.ht_used > 0
6280 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006281}
6282
6283#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6284
6285static enum
6286{
6287 EXP_SUBCMD, /* expand ":syn" sub-commands */
6288 EXP_CASE /* expand ":syn case" arguments */
6289} expand_what;
6290
Bram Moolenaar4f688582007-07-24 12:34:30 +00006291/*
6292 * Reset include_link, include_default, include_none to 0.
6293 * Called when we are done expanding.
6294 */
6295 void
6296reset_expand_highlight()
6297{
6298 include_link = include_default = include_none = 0;
6299}
6300
6301/*
6302 * Handle command line completion for :match and :echohl command: Add "None"
6303 * as highlight group.
6304 */
6305 void
6306set_context_in_echohl_cmd(xp, arg)
6307 expand_T *xp;
6308 char_u *arg;
6309{
6310 xp->xp_context = EXPAND_HIGHLIGHT;
6311 xp->xp_pattern = arg;
6312 include_none = 1;
6313}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006314
6315/*
6316 * Handle command line completion for :syntax command.
6317 */
6318 void
6319set_context_in_syntax_cmd(xp, arg)
6320 expand_T *xp;
6321 char_u *arg;
6322{
6323 char_u *p;
6324
6325 /* Default: expand subcommands */
6326 xp->xp_context = EXPAND_SYNTAX;
6327 expand_what = EXP_SUBCMD;
6328 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006329 include_link = 0;
6330 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006331
6332 /* (part of) subcommand already typed */
6333 if (*arg != NUL)
6334 {
6335 p = skiptowhite(arg);
6336 if (*p != NUL) /* past first word */
6337 {
6338 xp->xp_pattern = skipwhite(p);
6339 if (*skiptowhite(xp->xp_pattern) != NUL)
6340 xp->xp_context = EXPAND_NOTHING;
6341 else if (STRNICMP(arg, "case", p - arg) == 0)
6342 expand_what = EXP_CASE;
6343 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6344 || STRNICMP(arg, "region", p - arg) == 0
6345 || STRNICMP(arg, "match", p - arg) == 0
6346 || STRNICMP(arg, "list", p - arg) == 0)
6347 xp->xp_context = EXPAND_HIGHLIGHT;
6348 else
6349 xp->xp_context = EXPAND_NOTHING;
6350 }
6351 }
6352}
6353
6354static char *(case_args[]) = {"match", "ignore", NULL};
6355
6356/*
6357 * Function given to ExpandGeneric() to obtain the list syntax names for
6358 * expansion.
6359 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006360 char_u *
6361get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006362 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006363 int idx;
6364{
6365 if (expand_what == EXP_SUBCMD)
6366 return (char_u *)subcommands[idx].name;
6367 return (char_u *)case_args[idx];
6368}
6369
6370#endif /* FEAT_CMDL_COMPL */
6371
Bram Moolenaar071d4272004-06-13 20:20:40 +00006372/*
6373 * Function called for expression evaluation: get syntax ID at file position.
6374 */
6375 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006376syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006377 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006378 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006379 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006380 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006381 int *spellp; /* return: can do spell checking */
6382 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006383{
6384 /* When the position is not after the current position and in the same
6385 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006386 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006387 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006388 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006389 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006390
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006391 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392
6393 return (trans ? current_trans_id : current_id);
6394}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006395
Bram Moolenaar860cae12010-06-05 23:22:07 +02006396#if defined(FEAT_CONCEAL) || defined(PROTO)
6397/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006398 * Get extra information about the syntax item. Must be called right after
6399 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006400 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006401 * Returns the current flags.
6402 */
6403 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006404get_syntax_info(seqnrp)
6405 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006406{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006407 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006408 return current_flags;
6409}
6410
6411/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006412 * Return conceal substitution character
6413 */
6414 int
6415syn_get_sub_char()
6416{
6417 return current_sub_char;
6418}
6419#endif
6420
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006421#if defined(FEAT_EVAL) || defined(PROTO)
6422/*
6423 * Return the syntax ID at position "i" in the current stack.
6424 * The caller must have called syn_get_id() before to fill the stack.
6425 * Returns -1 when "i" is out of range.
6426 */
6427 int
6428syn_get_stack_item(i)
6429 int i;
6430{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006431 if (i >= current_state.ga_len)
6432 {
6433 /* Need to invalidate the state, because we didn't properly finish it
6434 * for the last character, "keep_state" was TRUE. */
6435 invalidate_current_state();
6436 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006437 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006438 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006439 return CUR_STATE(i).si_id;
6440}
6441#endif
6442
Bram Moolenaar071d4272004-06-13 20:20:40 +00006443#if defined(FEAT_FOLDING) || defined(PROTO)
6444/*
6445 * Function called to get folding level for line "lnum" in window "wp".
6446 */
6447 int
6448syn_get_foldlevel(wp, lnum)
6449 win_T *wp;
6450 long lnum;
6451{
6452 int level = 0;
6453 int i;
6454
6455 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006456 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006457 {
6458 syntax_start(wp, lnum);
6459
6460 for (i = 0; i < current_state.ga_len; ++i)
6461 if (CUR_STATE(i).si_flags & HL_FOLD)
6462 ++level;
6463 }
6464 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006465 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006467 if (level < 0)
6468 level = 0;
6469 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470 return level;
6471}
6472#endif
6473
6474#endif /* FEAT_SYN_HL */
6475
Bram Moolenaar071d4272004-06-13 20:20:40 +00006476/**************************************
6477 * Highlighting stuff *
6478 **************************************/
6479
6480/*
6481 * The default highlight groups. These are compiled-in for fast startup and
6482 * they still work when the runtime files can't be found.
6483 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006484 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6485 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006486 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006487#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006488# define CENT(a, b) b
6489#else
6490# define CENT(a, b) a
6491#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006492static char *(highlight_init_both[]) =
6493 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006494 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6495 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6496 CENT("IncSearch term=reverse cterm=reverse",
6497 "IncSearch term=reverse cterm=reverse gui=reverse"),
6498 CENT("ModeMsg term=bold cterm=bold",
6499 "ModeMsg term=bold cterm=bold gui=bold"),
6500 CENT("NonText term=bold ctermfg=Blue",
6501 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6502 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6503 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6504 CENT("StatusLineNC term=reverse cterm=reverse",
6505 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006506#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006507 CENT("VertSplit term=reverse cterm=reverse",
6508 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006509#endif
6510#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006511 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6512 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006513#endif
6514#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006515 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6516 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006517#endif
6518#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006519 CENT("PmenuSbar ctermbg=Grey",
6520 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006521#endif
6522#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006523 CENT("TabLineSel term=bold cterm=bold",
6524 "TabLineSel term=bold cterm=bold gui=bold"),
6525 CENT("TabLineFill term=reverse cterm=reverse",
6526 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006527#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528#ifdef FEAT_GUI
6529 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006530 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006531#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006532 NULL
6533 };
6534
6535static char *(highlight_init_light[]) =
6536 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006537 CENT("Directory term=bold ctermfg=DarkBlue",
6538 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6539 CENT("LineNr term=underline ctermfg=Brown",
6540 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006541 CENT("CursorLineNr term=bold ctermfg=Brown",
6542 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006543 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6544 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6545 CENT("Question term=standout ctermfg=DarkGreen",
6546 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6547 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6548 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006549#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006550 CENT("SpellBad term=reverse ctermbg=LightRed",
6551 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6552 CENT("SpellCap term=reverse ctermbg=LightBlue",
6553 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6554 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6555 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6556 CENT("SpellLocal term=underline ctermbg=Cyan",
6557 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006558#endif
6559#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006560 CENT("PmenuThumb ctermbg=Black",
6561 "PmenuThumb ctermbg=Black guibg=Black"),
6562 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6563 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6564 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6565 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006566#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006567 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6568 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6569 CENT("Title term=bold ctermfg=DarkMagenta",
6570 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6571 CENT("WarningMsg term=standout ctermfg=DarkRed",
6572 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006573#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006574 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6575 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006576#endif
6577#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006578 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6579 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6580 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6581 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006582#endif
6583#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006584 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6585 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006586#endif
6587#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006588 CENT("Visual term=reverse",
6589 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006590#endif
6591#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006592 CENT("DiffAdd term=bold ctermbg=LightBlue",
6593 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6594 CENT("DiffChange term=bold ctermbg=LightMagenta",
6595 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6596 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6597 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006598#endif
6599#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006600 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6601 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006602#endif
6603#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006604 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006605 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006606 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006607 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006608 CENT("ColorColumn term=reverse ctermbg=LightRed",
6609 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006610#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006611#ifdef FEAT_CONCEAL
6612 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6613 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6614#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006615#ifdef FEAT_AUTOCMD
6616 CENT("MatchParen term=reverse ctermbg=Cyan",
6617 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6618#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006619#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006620 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006621#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006622 NULL
6623 };
6624
6625static char *(highlight_init_dark[]) =
6626 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006627 CENT("Directory term=bold ctermfg=LightCyan",
6628 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6629 CENT("LineNr term=underline ctermfg=Yellow",
6630 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006631 CENT("CursorLineNr term=bold ctermfg=Yellow",
6632 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006633 CENT("MoreMsg term=bold ctermfg=LightGreen",
6634 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6635 CENT("Question term=standout ctermfg=LightGreen",
6636 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6637 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6638 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6639 CENT("SpecialKey term=bold ctermfg=LightBlue",
6640 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006641#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006642 CENT("SpellBad term=reverse ctermbg=Red",
6643 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6644 CENT("SpellCap term=reverse ctermbg=Blue",
6645 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6646 CENT("SpellRare term=reverse ctermbg=Magenta",
6647 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6648 CENT("SpellLocal term=underline ctermbg=Cyan",
6649 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006650#endif
6651#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006652 CENT("PmenuThumb ctermbg=White",
6653 "PmenuThumb ctermbg=White guibg=White"),
6654 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6655 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006656 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6657 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006658#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006659 CENT("Title term=bold ctermfg=LightMagenta",
6660 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6661 CENT("WarningMsg term=standout ctermfg=LightRed",
6662 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006663#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006664 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6665 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006666#endif
6667#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006668 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6669 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6670 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6671 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006672#endif
6673#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006674 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6675 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006676#endif
6677#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006678 CENT("Visual term=reverse",
6679 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006680#endif
6681#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006682 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6683 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6684 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6685 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6686 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6687 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006688#endif
6689#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006690 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6691 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006692#endif
6693#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006694 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006695 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006696 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006697 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006698 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6699 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006700#endif
6701#ifdef FEAT_AUTOCMD
6702 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6703 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006704#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006705#ifdef FEAT_CONCEAL
6706 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6707 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6708#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006709#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006710 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006711#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006712 NULL
6713 };
6714
6715 void
6716init_highlight(both, reset)
6717 int both; /* include groups where 'bg' doesn't matter */
6718 int reset; /* clear group first */
6719{
6720 int i;
6721 char **pp;
6722 static int had_both = FALSE;
6723#ifdef FEAT_EVAL
6724 char_u *p;
6725
6726 /*
6727 * Try finding the color scheme file. Used when a color file was loaded
6728 * and 'background' or 't_Co' is changed.
6729 */
6730 p = get_var_value((char_u *)"g:colors_name");
6731 if (p != NULL && load_colors(p) == OK)
6732 return;
6733#endif
6734
6735 /*
6736 * Didn't use a color file, use the compiled-in colors.
6737 */
6738 if (both)
6739 {
6740 had_both = TRUE;
6741 pp = highlight_init_both;
6742 for (i = 0; pp[i] != NULL; ++i)
6743 do_highlight((char_u *)pp[i], reset, TRUE);
6744 }
6745 else if (!had_both)
6746 /* Don't do anything before the call with both == TRUE from main().
6747 * Not everything has been setup then, and that call will overrule
6748 * everything anyway. */
6749 return;
6750
6751 if (*p_bg == 'l')
6752 pp = highlight_init_light;
6753 else
6754 pp = highlight_init_dark;
6755 for (i = 0; pp[i] != NULL; ++i)
6756 do_highlight((char_u *)pp[i], reset, TRUE);
6757
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006758 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006759 * depend on the number of colors available.
6760 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006761 * to avoid Statement highlighted text disappears.
6762 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006763 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006764 do_highlight((char_u *)(*p_bg == 'l'
6765 ? "Visual cterm=NONE ctermbg=LightGrey"
6766 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006767 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006768 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006769 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6770 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006771 if (*p_bg == 'l')
6772 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6773 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006774
Bram Moolenaar071d4272004-06-13 20:20:40 +00006775#ifdef FEAT_SYN_HL
6776 /*
6777 * If syntax highlighting is enabled load the highlighting for it.
6778 */
6779 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006780 {
6781 static int recursive = 0;
6782
6783 if (recursive >= 5)
6784 EMSG(_("E679: recursive loop loading syncolor.vim"));
6785 else
6786 {
6787 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006788 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006789 --recursive;
6790 }
6791 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006792#endif
6793}
6794
6795/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006796 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006797 * Return OK for success, FAIL for failure.
6798 */
6799 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006800load_colors(name)
6801 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006802{
6803 char_u *buf;
6804 int retval = FAIL;
6805 static int recursive = FALSE;
6806
6807 /* When being called recursively, this is probably because setting
6808 * 'background' caused the highlighting to be reloaded. This means it is
6809 * working, thus we should return OK. */
6810 if (recursive)
6811 return OK;
6812
6813 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006814 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006815 if (buf != NULL)
6816 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006817 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006818 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006819 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006820#ifdef FEAT_AUTOCMD
6821 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6822#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006823 }
6824 recursive = FALSE;
6825
6826 return retval;
6827}
6828
6829/*
6830 * Handle the ":highlight .." command.
6831 * When using ":hi clear" this is called recursively for each group with
6832 * "forceit" and "init" both TRUE.
6833 */
6834 void
6835do_highlight(line, forceit, init)
6836 char_u *line;
6837 int forceit;
6838 int init; /* TRUE when called for initializing */
6839{
6840 char_u *name_end;
6841 char_u *p;
6842 char_u *linep;
6843 char_u *key_start;
6844 char_u *arg_start;
6845 char_u *key = NULL, *arg = NULL;
6846 long i;
6847 int off;
6848 int len;
6849 int attr;
6850 int id;
6851 int idx;
6852 int dodefault = FALSE;
6853 int doclear = FALSE;
6854 int dolink = FALSE;
6855 int error = FALSE;
6856 int color;
6857 int is_normal_group = FALSE; /* "Normal" group */
6858#ifdef FEAT_GUI_X11
6859 int is_menu_group = FALSE; /* "Menu" group */
6860 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6861 int is_tooltip_group = FALSE; /* "Tooltip" group */
6862 int do_colors = FALSE; /* need to update colors? */
6863#else
6864# define is_menu_group 0
6865# define is_tooltip_group 0
6866#endif
6867
6868 /*
6869 * If no argument, list current highlighting.
6870 */
6871 if (ends_excmd(*line))
6872 {
6873 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6874 /* TODO: only call when the group has attributes set */
6875 highlight_list_one((int)i);
6876 return;
6877 }
6878
6879 /*
6880 * Isolate the name.
6881 */
6882 name_end = skiptowhite(line);
6883 linep = skipwhite(name_end);
6884
6885 /*
6886 * Check for "default" argument.
6887 */
6888 if (STRNCMP(line, "default", name_end - line) == 0)
6889 {
6890 dodefault = TRUE;
6891 line = linep;
6892 name_end = skiptowhite(line);
6893 linep = skipwhite(name_end);
6894 }
6895
6896 /*
6897 * Check for "clear" or "link" argument.
6898 */
6899 if (STRNCMP(line, "clear", name_end - line) == 0)
6900 doclear = TRUE;
6901 if (STRNCMP(line, "link", name_end - line) == 0)
6902 dolink = TRUE;
6903
6904 /*
6905 * ":highlight {group-name}": list highlighting for one group.
6906 */
6907 if (!doclear && !dolink && ends_excmd(*linep))
6908 {
6909 id = syn_namen2id(line, (int)(name_end - line));
6910 if (id == 0)
6911 EMSG2(_("E411: highlight group not found: %s"), line);
6912 else
6913 highlight_list_one(id);
6914 return;
6915 }
6916
6917 /*
6918 * Handle ":highlight link {from} {to}" command.
6919 */
6920 if (dolink)
6921 {
6922 char_u *from_start = linep;
6923 char_u *from_end;
6924 char_u *to_start;
6925 char_u *to_end;
6926 int from_id;
6927 int to_id;
6928
6929 from_end = skiptowhite(from_start);
6930 to_start = skipwhite(from_end);
6931 to_end = skiptowhite(to_start);
6932
6933 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6934 {
6935 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6936 from_start);
6937 return;
6938 }
6939
6940 if (!ends_excmd(*skipwhite(to_end)))
6941 {
6942 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6943 return;
6944 }
6945
6946 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6947 if (STRNCMP(to_start, "NONE", 4) == 0)
6948 to_id = 0;
6949 else
6950 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6951
6952 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6953 {
6954 /*
6955 * Don't allow a link when there already is some highlighting
6956 * for the group, unless '!' is used
6957 */
6958 if (to_id > 0 && !forceit && !init
6959 && hl_has_settings(from_id - 1, dodefault))
6960 {
6961 if (sourcing_name == NULL && !dodefault)
6962 EMSG(_("E414: group has settings, highlight link ignored"));
6963 }
6964 else
6965 {
6966 if (!init)
6967 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6968 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006969#ifdef FEAT_EVAL
6970 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6971#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006972 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006973 }
6974 }
6975
6976 /* Only call highlight_changed() once, after sourcing a syntax file */
6977 need_highlight_changed = TRUE;
6978
6979 return;
6980 }
6981
6982 if (doclear)
6983 {
6984 /*
6985 * ":highlight clear [group]" command.
6986 */
6987 line = linep;
6988 if (ends_excmd(*line))
6989 {
6990#ifdef FEAT_GUI
6991 /* First, we do not destroy the old values, but allocate the new
6992 * ones and update the display. THEN we destroy the old values.
6993 * If we destroy the old values first, then the old values
6994 * (such as GuiFont's or GuiFontset's) will still be displayed but
6995 * invalid because they were free'd.
6996 */
6997 if (gui.in_use)
6998 {
6999# ifdef FEAT_BEVAL_TIP
7000 gui_init_tooltip_font();
7001# endif
7002# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7003 gui_init_menu_font();
7004# endif
7005 }
7006# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7007 gui_mch_def_colors();
7008# endif
7009# ifdef FEAT_GUI_X11
7010# ifdef FEAT_MENU
7011
7012 /* This only needs to be done when there is no Menu highlight
7013 * group defined by default, which IS currently the case.
7014 */
7015 gui_mch_new_menu_colors();
7016# endif
7017 if (gui.in_use)
7018 {
7019 gui_new_scrollbar_colors();
7020# ifdef FEAT_BEVAL
7021 gui_mch_new_tooltip_colors();
7022# endif
7023# ifdef FEAT_MENU
7024 gui_mch_new_menu_font();
7025# endif
7026 }
7027# endif
7028
7029 /* Ok, we're done allocating the new default graphics items.
7030 * The screen should already be refreshed at this point.
7031 * It is now Ok to clear out the old data.
7032 */
7033#endif
7034#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007035 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007036#endif
7037 restore_cterm_colors();
7038
7039 /*
7040 * Clear all default highlight groups and load the defaults.
7041 */
7042 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7043 highlight_clear(idx);
7044 init_highlight(TRUE, TRUE);
7045#ifdef FEAT_GUI
7046 if (gui.in_use)
7047 highlight_gui_started();
7048#endif
7049 highlight_changed();
7050 redraw_later_clear();
7051 return;
7052 }
7053 name_end = skiptowhite(line);
7054 linep = skipwhite(name_end);
7055 }
7056
7057 /*
7058 * Find the group name in the table. If it does not exist yet, add it.
7059 */
7060 id = syn_check_group(line, (int)(name_end - line));
7061 if (id == 0) /* failed (out of memory) */
7062 return;
7063 idx = id - 1; /* index is ID minus one */
7064
7065 /* Return if "default" was used and the group already has settings. */
7066 if (dodefault && hl_has_settings(idx, TRUE))
7067 return;
7068
7069 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7070 is_normal_group = TRUE;
7071#ifdef FEAT_GUI_X11
7072 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7073 is_menu_group = TRUE;
7074 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7075 is_scrollbar_group = TRUE;
7076 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7077 is_tooltip_group = TRUE;
7078#endif
7079
7080 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7081 if (doclear || (forceit && init))
7082 {
7083 highlight_clear(idx);
7084 if (!doclear)
7085 HL_TABLE()[idx].sg_set = 0;
7086 }
7087
7088 if (!doclear)
7089 while (!ends_excmd(*linep))
7090 {
7091 key_start = linep;
7092 if (*linep == '=')
7093 {
7094 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7095 error = TRUE;
7096 break;
7097 }
7098
7099 /*
7100 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7101 * "guibg").
7102 */
7103 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7104 ++linep;
7105 vim_free(key);
7106 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7107 if (key == NULL)
7108 {
7109 error = TRUE;
7110 break;
7111 }
7112 linep = skipwhite(linep);
7113
7114 if (STRCMP(key, "NONE") == 0)
7115 {
7116 if (!init || HL_TABLE()[idx].sg_set == 0)
7117 {
7118 if (!init)
7119 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7120 highlight_clear(idx);
7121 }
7122 continue;
7123 }
7124
7125 /*
7126 * Check for the equal sign.
7127 */
7128 if (*linep != '=')
7129 {
7130 EMSG2(_("E416: missing equal sign: %s"), key_start);
7131 error = TRUE;
7132 break;
7133 }
7134 ++linep;
7135
7136 /*
7137 * Isolate the argument.
7138 */
7139 linep = skipwhite(linep);
7140 if (*linep == '\'') /* guifg='color name' */
7141 {
7142 arg_start = ++linep;
7143 linep = vim_strchr(linep, '\'');
7144 if (linep == NULL)
7145 {
7146 EMSG2(_(e_invarg2), key_start);
7147 error = TRUE;
7148 break;
7149 }
7150 }
7151 else
7152 {
7153 arg_start = linep;
7154 linep = skiptowhite(linep);
7155 }
7156 if (linep == arg_start)
7157 {
7158 EMSG2(_("E417: missing argument: %s"), key_start);
7159 error = TRUE;
7160 break;
7161 }
7162 vim_free(arg);
7163 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7164 if (arg == NULL)
7165 {
7166 error = TRUE;
7167 break;
7168 }
7169 if (*linep == '\'')
7170 ++linep;
7171
7172 /*
7173 * Store the argument.
7174 */
7175 if ( STRCMP(key, "TERM") == 0
7176 || STRCMP(key, "CTERM") == 0
7177 || STRCMP(key, "GUI") == 0)
7178 {
7179 attr = 0;
7180 off = 0;
7181 while (arg[off] != NUL)
7182 {
7183 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7184 {
7185 len = (int)STRLEN(hl_name_table[i]);
7186 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7187 {
7188 attr |= hl_attr_table[i];
7189 off += len;
7190 break;
7191 }
7192 }
7193 if (i < 0)
7194 {
7195 EMSG2(_("E418: Illegal value: %s"), arg);
7196 error = TRUE;
7197 break;
7198 }
7199 if (arg[off] == ',') /* another one follows */
7200 ++off;
7201 }
7202 if (error)
7203 break;
7204 if (*key == 'T')
7205 {
7206 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7207 {
7208 if (!init)
7209 HL_TABLE()[idx].sg_set |= SG_TERM;
7210 HL_TABLE()[idx].sg_term = attr;
7211 }
7212 }
7213 else if (*key == 'C')
7214 {
7215 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7216 {
7217 if (!init)
7218 HL_TABLE()[idx].sg_set |= SG_CTERM;
7219 HL_TABLE()[idx].sg_cterm = attr;
7220 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7221 }
7222 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007223#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007224 else
7225 {
7226 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7227 {
7228 if (!init)
7229 HL_TABLE()[idx].sg_set |= SG_GUI;
7230 HL_TABLE()[idx].sg_gui = attr;
7231 }
7232 }
7233#endif
7234 }
7235 else if (STRCMP(key, "FONT") == 0)
7236 {
7237 /* in non-GUI fonts are simply ignored */
7238#ifdef FEAT_GUI
7239 if (!gui.shell_created)
7240 {
7241 /* GUI not started yet, always accept the name. */
7242 vim_free(HL_TABLE()[idx].sg_font_name);
7243 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7244 }
7245 else
7246 {
7247 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7248# ifdef FEAT_XFONTSET
7249 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7250# endif
7251 /* First, save the current font/fontset.
7252 * Then try to allocate the font/fontset.
7253 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7254 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7255 */
7256
7257 HL_TABLE()[idx].sg_font = NOFONT;
7258# ifdef FEAT_XFONTSET
7259 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7260# endif
7261 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007262 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007263
7264# ifdef FEAT_XFONTSET
7265 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7266 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007267 /* New fontset was accepted. Free the old one, if there
7268 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007269 gui_mch_free_fontset(temp_sg_fontset);
7270 vim_free(HL_TABLE()[idx].sg_font_name);
7271 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7272 }
7273 else
7274 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7275# endif
7276 if (HL_TABLE()[idx].sg_font != NOFONT)
7277 {
7278 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007279 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007280 gui_mch_free_font(temp_sg_font);
7281 vim_free(HL_TABLE()[idx].sg_font_name);
7282 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7283 }
7284 else
7285 HL_TABLE()[idx].sg_font = temp_sg_font;
7286 }
7287#endif
7288 }
7289 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7290 {
7291 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7292 {
7293 if (!init)
7294 HL_TABLE()[idx].sg_set |= SG_CTERM;
7295
7296 /* When setting the foreground color, and previously the "bold"
7297 * flag was set for a light color, reset it now */
7298 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7299 {
7300 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7301 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7302 }
7303
7304 if (VIM_ISDIGIT(*arg))
7305 color = atoi((char *)arg);
7306 else if (STRICMP(arg, "fg") == 0)
7307 {
7308 if (cterm_normal_fg_color)
7309 color = cterm_normal_fg_color - 1;
7310 else
7311 {
7312 EMSG(_("E419: FG color unknown"));
7313 error = TRUE;
7314 break;
7315 }
7316 }
7317 else if (STRICMP(arg, "bg") == 0)
7318 {
7319 if (cterm_normal_bg_color > 0)
7320 color = cterm_normal_bg_color - 1;
7321 else
7322 {
7323 EMSG(_("E420: BG color unknown"));
7324 error = TRUE;
7325 break;
7326 }
7327 }
7328 else
7329 {
7330 static char *(color_names[28]) = {
7331 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7332 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7333 "Gray", "Grey",
7334 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7335 "Blue", "LightBlue", "Green", "LightGreen",
7336 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7337 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7338 static int color_numbers_16[28] = {0, 1, 2, 3,
7339 4, 5, 6, 6,
7340 7, 7,
7341 7, 7, 8, 8,
7342 9, 9, 10, 10,
7343 11, 11, 12, 12, 13,
7344 13, 14, 14, 15, -1};
7345 /* for xterm with 88 colors... */
7346 static int color_numbers_88[28] = {0, 4, 2, 6,
7347 1, 5, 32, 72,
7348 84, 84,
7349 7, 7, 82, 82,
7350 12, 43, 10, 61,
7351 14, 63, 9, 74, 13,
7352 75, 11, 78, 15, -1};
7353 /* for xterm with 256 colors... */
7354 static int color_numbers_256[28] = {0, 4, 2, 6,
7355 1, 5, 130, 130,
7356 248, 248,
7357 7, 7, 242, 242,
7358 12, 81, 10, 121,
7359 14, 159, 9, 224, 13,
7360 225, 11, 229, 15, -1};
7361 /* for terminals with less than 16 colors... */
7362 static int color_numbers_8[28] = {0, 4, 2, 6,
7363 1, 5, 3, 3,
7364 7, 7,
7365 7, 7, 0+8, 0+8,
7366 4+8, 4+8, 2+8, 2+8,
7367 6+8, 6+8, 1+8, 1+8, 5+8,
7368 5+8, 3+8, 3+8, 7+8, -1};
7369#if defined(__QNXNTO__)
7370 static int *color_numbers_8_qansi = color_numbers_8;
7371 /* On qnx, the 8 & 16 color arrays are the same */
7372 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7373 color_numbers_8_qansi = color_numbers_16;
7374#endif
7375
7376 /* reduce calls to STRICMP a bit, it can be slow */
7377 off = TOUPPER_ASC(*arg);
7378 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7379 if (off == color_names[i][0]
7380 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7381 break;
7382 if (i < 0)
7383 {
7384 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7385 error = TRUE;
7386 break;
7387 }
7388
7389 /* Use the _16 table to check if its a valid color name. */
7390 color = color_numbers_16[i];
7391 if (color >= 0)
7392 {
7393 if (t_colors == 8)
7394 {
7395 /* t_Co is 8: use the 8 colors table */
7396#if defined(__QNXNTO__)
7397 color = color_numbers_8_qansi[i];
7398#else
7399 color = color_numbers_8[i];
7400#endif
7401 if (key[5] == 'F')
7402 {
7403 /* set/reset bold attribute to get light foreground
7404 * colors (on some terminals, e.g. "linux") */
7405 if (color & 8)
7406 {
7407 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7408 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7409 }
7410 else
7411 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7412 }
7413 color &= 7; /* truncate to 8 colors */
7414 }
7415 else if (t_colors == 16 || t_colors == 88
7416 || t_colors == 256)
7417 {
7418 /*
7419 * Guess: if the termcap entry ends in 'm', it is
7420 * probably an xterm-like terminal. Use the changed
7421 * order for colors.
7422 */
7423 if (*T_CAF != NUL)
7424 p = T_CAF;
7425 else
7426 p = T_CSF;
7427 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7428 switch (t_colors)
7429 {
7430 case 16:
7431 color = color_numbers_8[i];
7432 break;
7433 case 88:
7434 color = color_numbers_88[i];
7435 break;
7436 case 256:
7437 color = color_numbers_256[i];
7438 break;
7439 }
7440 }
7441 }
7442 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007443 /* Add one to the argument, to avoid zero. Zero is used for
7444 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007445 if (key[5] == 'F')
7446 {
7447 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7448 if (is_normal_group)
7449 {
7450 cterm_normal_fg_color = color + 1;
7451 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7452#ifdef FEAT_GUI
7453 /* Don't do this if the GUI is used. */
7454 if (!gui.in_use && !gui.starting)
7455#endif
7456 {
7457 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007458 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007459 term_fg_color(color);
7460 }
7461 }
7462 }
7463 else
7464 {
7465 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7466 if (is_normal_group)
7467 {
7468 cterm_normal_bg_color = color + 1;
7469#ifdef FEAT_GUI
7470 /* Don't mess with 'background' if the GUI is used. */
7471 if (!gui.in_use && !gui.starting)
7472#endif
7473 {
7474 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007475 if (color >= 0)
7476 {
7477 if (termcap_active)
7478 term_bg_color(color);
7479 if (t_colors < 16)
7480 i = (color == 0 || color == 4);
7481 else
7482 i = (color < 7 || color == 8);
7483 /* Set the 'background' option if the value is
7484 * wrong. */
7485 if (i != (*p_bg == 'd'))
7486 set_option_value((char_u *)"bg", 0L,
7487 i ? (char_u *)"dark"
7488 : (char_u *)"light", 0);
7489 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007490 }
7491 }
7492 }
7493 }
7494 }
7495 else if (STRCMP(key, "GUIFG") == 0)
7496 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007497#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007498 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007499 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007500 if (!init)
7501 HL_TABLE()[idx].sg_set |= SG_GUI;
7502
Bram Moolenaar61623362010-07-14 22:04:22 +02007503# ifdef FEAT_GUI
7504 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007505 i = color_name2handle(arg);
7506 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7507 {
7508 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007509# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007510 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7511 if (STRCMP(arg, "NONE"))
7512 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7513 else
7514 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007515# ifdef FEAT_GUI
7516# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007517 if (is_menu_group)
7518 gui.menu_fg_pixel = i;
7519 if (is_scrollbar_group)
7520 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007521# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007522 if (is_tooltip_group)
7523 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007524# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007525 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007526# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007527 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007528# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007529 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007530#endif
7531 }
7532 else if (STRCMP(key, "GUIBG") == 0)
7533 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007534#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007535 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007536 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007537 if (!init)
7538 HL_TABLE()[idx].sg_set |= SG_GUI;
7539
Bram Moolenaar61623362010-07-14 22:04:22 +02007540# ifdef FEAT_GUI
7541 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007542 i = color_name2handle(arg);
7543 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7544 {
7545 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007546# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007547 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7548 if (STRCMP(arg, "NONE") != 0)
7549 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7550 else
7551 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007552# ifdef FEAT_GUI
7553# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007554 if (is_menu_group)
7555 gui.menu_bg_pixel = i;
7556 if (is_scrollbar_group)
7557 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007558# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007559 if (is_tooltip_group)
7560 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007561# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007562 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007563# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007564 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007565# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007566 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007567#endif
7568 }
7569 else if (STRCMP(key, "GUISP") == 0)
7570 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007571#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007572 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7573 {
7574 if (!init)
7575 HL_TABLE()[idx].sg_set |= SG_GUI;
7576
Bram Moolenaar61623362010-07-14 22:04:22 +02007577# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007578 i = color_name2handle(arg);
7579 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7580 {
7581 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007582# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007583 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7584 if (STRCMP(arg, "NONE") != 0)
7585 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7586 else
7587 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007588# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007589 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007590# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007591 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007592#endif
7593 }
7594 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7595 {
7596 char_u buf[100];
7597 char_u *tname;
7598
7599 if (!init)
7600 HL_TABLE()[idx].sg_set |= SG_TERM;
7601
7602 /*
7603 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007604 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007605 */
7606 if (STRNCMP(arg, "t_", 2) == 0)
7607 {
7608 off = 0;
7609 buf[0] = 0;
7610 while (arg[off] != NUL)
7611 {
7612 /* Isolate one termcap name */
7613 for (len = 0; arg[off + len] &&
7614 arg[off + len] != ','; ++len)
7615 ;
7616 tname = vim_strnsave(arg + off, len);
7617 if (tname == NULL) /* out of memory */
7618 {
7619 error = TRUE;
7620 break;
7621 }
7622 /* lookup the escape sequence for the item */
7623 p = get_term_code(tname);
7624 vim_free(tname);
7625 if (p == NULL) /* ignore non-existing things */
7626 p = (char_u *)"";
7627
7628 /* Append it to the already found stuff */
7629 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7630 {
7631 EMSG2(_("E422: terminal code too long: %s"), arg);
7632 error = TRUE;
7633 break;
7634 }
7635 STRCAT(buf, p);
7636
7637 /* Advance to the next item */
7638 off += len;
7639 if (arg[off] == ',') /* another one follows */
7640 ++off;
7641 }
7642 }
7643 else
7644 {
7645 /*
7646 * Copy characters from arg[] to buf[], translating <> codes.
7647 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007648 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007649 {
7650 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007651 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007652 off += len;
7653 else /* copy as normal char */
7654 buf[off++] = *p++;
7655 }
7656 buf[off] = NUL;
7657 }
7658 if (error)
7659 break;
7660
7661 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7662 p = NULL;
7663 else
7664 p = vim_strsave(buf);
7665 if (key[2] == 'A')
7666 {
7667 vim_free(HL_TABLE()[idx].sg_start);
7668 HL_TABLE()[idx].sg_start = p;
7669 }
7670 else
7671 {
7672 vim_free(HL_TABLE()[idx].sg_stop);
7673 HL_TABLE()[idx].sg_stop = p;
7674 }
7675 }
7676 else
7677 {
7678 EMSG2(_("E423: Illegal argument: %s"), key_start);
7679 error = TRUE;
7680 break;
7681 }
7682
7683 /*
7684 * When highlighting has been given for a group, don't link it.
7685 */
7686 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7687 HL_TABLE()[idx].sg_link = 0;
7688
7689 /*
7690 * Continue with next argument.
7691 */
7692 linep = skipwhite(linep);
7693 }
7694
7695 /*
7696 * If there is an error, and it's a new entry, remove it from the table.
7697 */
7698 if (error && idx == highlight_ga.ga_len)
7699 syn_unadd_group();
7700 else
7701 {
7702 if (is_normal_group)
7703 {
7704 HL_TABLE()[idx].sg_term_attr = 0;
7705 HL_TABLE()[idx].sg_cterm_attr = 0;
7706#ifdef FEAT_GUI
7707 HL_TABLE()[idx].sg_gui_attr = 0;
7708 /*
7709 * Need to update all groups, because they might be using "bg"
7710 * and/or "fg", which have been changed now.
7711 */
7712 if (gui.in_use)
7713 highlight_gui_started();
7714#endif
7715 }
7716#ifdef FEAT_GUI_X11
7717# ifdef FEAT_MENU
7718 else if (is_menu_group)
7719 {
7720 if (gui.in_use && do_colors)
7721 gui_mch_new_menu_colors();
7722 }
7723# endif
7724 else if (is_scrollbar_group)
7725 {
7726 if (gui.in_use && do_colors)
7727 gui_new_scrollbar_colors();
7728 }
7729# ifdef FEAT_BEVAL
7730 else if (is_tooltip_group)
7731 {
7732 if (gui.in_use && do_colors)
7733 gui_mch_new_tooltip_colors();
7734 }
7735# endif
7736#endif
7737 else
7738 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007739#ifdef FEAT_EVAL
7740 HL_TABLE()[idx].sg_scriptID = current_SID;
7741#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007742 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007743 }
7744 vim_free(key);
7745 vim_free(arg);
7746
7747 /* Only call highlight_changed() once, after sourcing a syntax file */
7748 need_highlight_changed = TRUE;
7749}
7750
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007751#if defined(EXITFREE) || defined(PROTO)
7752 void
7753free_highlight()
7754{
7755 int i;
7756
7757 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007758 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007759 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007760 vim_free(HL_TABLE()[i].sg_name);
7761 vim_free(HL_TABLE()[i].sg_name_u);
7762 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007763 ga_clear(&highlight_ga);
7764}
7765#endif
7766
Bram Moolenaar071d4272004-06-13 20:20:40 +00007767/*
7768 * Reset the cterm colors to what they were before Vim was started, if
7769 * possible. Otherwise reset them to zero.
7770 */
7771 void
7772restore_cterm_colors()
7773{
7774#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7775 /* Since t_me has been set, this probably means that the user
7776 * wants to use this as default colors. Need to reset default
7777 * background/foreground colors. */
7778 mch_set_normal_colors();
7779#else
7780 cterm_normal_fg_color = 0;
7781 cterm_normal_fg_bold = 0;
7782 cterm_normal_bg_color = 0;
7783#endif
7784}
7785
7786/*
7787 * Return TRUE if highlight group "idx" has any settings.
7788 * When "check_link" is TRUE also check for an existing link.
7789 */
7790 static int
7791hl_has_settings(idx, check_link)
7792 int idx;
7793 int check_link;
7794{
7795 return ( HL_TABLE()[idx].sg_term_attr != 0
7796 || HL_TABLE()[idx].sg_cterm_attr != 0
7797#ifdef FEAT_GUI
7798 || HL_TABLE()[idx].sg_gui_attr != 0
7799#endif
7800 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7801}
7802
7803/*
7804 * Clear highlighting for one group.
7805 */
7806 static void
7807highlight_clear(idx)
7808 int idx;
7809{
7810 HL_TABLE()[idx].sg_term = 0;
7811 vim_free(HL_TABLE()[idx].sg_start);
7812 HL_TABLE()[idx].sg_start = NULL;
7813 vim_free(HL_TABLE()[idx].sg_stop);
7814 HL_TABLE()[idx].sg_stop = NULL;
7815 HL_TABLE()[idx].sg_term_attr = 0;
7816 HL_TABLE()[idx].sg_cterm = 0;
7817 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7818 HL_TABLE()[idx].sg_cterm_fg = 0;
7819 HL_TABLE()[idx].sg_cterm_bg = 0;
7820 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007821#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007822 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7824 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007825 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7826 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007827 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7828 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007829#endif
7830#ifdef FEAT_GUI
7831 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7832 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7833 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007834 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7835 HL_TABLE()[idx].sg_font = NOFONT;
7836# ifdef FEAT_XFONTSET
7837 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7838 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7839# endif
7840 vim_free(HL_TABLE()[idx].sg_font_name);
7841 HL_TABLE()[idx].sg_font_name = NULL;
7842 HL_TABLE()[idx].sg_gui_attr = 0;
7843#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007844#ifdef FEAT_EVAL
7845 /* Clear the script ID only when there is no link, since that is not
7846 * cleared. */
7847 if (HL_TABLE()[idx].sg_link == 0)
7848 HL_TABLE()[idx].sg_scriptID = 0;
7849#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850}
7851
7852#if defined(FEAT_GUI) || defined(PROTO)
7853/*
7854 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007855 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007856 * "Tooltip" colors.
7857 */
7858 void
7859set_normal_colors()
7860{
7861 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007862 &gui.norm_pixel, &gui.back_pixel,
7863 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007864 {
7865 gui_mch_new_colors();
7866 must_redraw = CLEAR;
7867 }
7868#ifdef FEAT_GUI_X11
7869 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007870 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7871 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007872 {
7873# ifdef FEAT_MENU
7874 gui_mch_new_menu_colors();
7875# endif
7876 must_redraw = CLEAR;
7877 }
7878# ifdef FEAT_BEVAL
7879 if (set_group_colors((char_u *)"Tooltip",
7880 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7881 FALSE, FALSE, TRUE))
7882 {
7883# ifdef FEAT_TOOLBAR
7884 gui_mch_new_tooltip_colors();
7885# endif
7886 must_redraw = CLEAR;
7887 }
7888#endif
7889 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007890 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7891 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007892 {
7893 gui_new_scrollbar_colors();
7894 must_redraw = CLEAR;
7895 }
7896#endif
7897}
7898
7899/*
7900 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7901 */
7902 static int
7903set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7904 char_u *name;
7905 guicolor_T *fgp;
7906 guicolor_T *bgp;
7907 int do_menu;
7908 int use_norm;
7909 int do_tooltip;
7910{
7911 int idx;
7912
7913 idx = syn_name2id(name) - 1;
7914 if (idx >= 0)
7915 {
7916 gui_do_one_color(idx, do_menu, do_tooltip);
7917
7918 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7919 *fgp = HL_TABLE()[idx].sg_gui_fg;
7920 else if (use_norm)
7921 *fgp = gui.def_norm_pixel;
7922 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7923 *bgp = HL_TABLE()[idx].sg_gui_bg;
7924 else if (use_norm)
7925 *bgp = gui.def_back_pixel;
7926 return TRUE;
7927 }
7928 return FALSE;
7929}
7930
7931/*
7932 * Get the font of the "Normal" group.
7933 * Returns "" when it's not found or not set.
7934 */
7935 char_u *
7936hl_get_font_name()
7937{
7938 int id;
7939 char_u *s;
7940
7941 id = syn_name2id((char_u *)"Normal");
7942 if (id > 0)
7943 {
7944 s = HL_TABLE()[id - 1].sg_font_name;
7945 if (s != NULL)
7946 return s;
7947 }
7948 return (char_u *)"";
7949}
7950
7951/*
7952 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7953 * actually chosen to be used.
7954 */
7955 void
7956hl_set_font_name(font_name)
7957 char_u *font_name;
7958{
7959 int id;
7960
7961 id = syn_name2id((char_u *)"Normal");
7962 if (id > 0)
7963 {
7964 vim_free(HL_TABLE()[id - 1].sg_font_name);
7965 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7966 }
7967}
7968
7969/*
7970 * Set background color for "Normal" group. Called by gui_set_bg_color()
7971 * when the color is known.
7972 */
7973 void
7974hl_set_bg_color_name(name)
7975 char_u *name; /* must have been allocated */
7976{
7977 int id;
7978
7979 if (name != NULL)
7980 {
7981 id = syn_name2id((char_u *)"Normal");
7982 if (id > 0)
7983 {
7984 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7985 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7986 }
7987 }
7988}
7989
7990/*
7991 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7992 * when the color is known.
7993 */
7994 void
7995hl_set_fg_color_name(name)
7996 char_u *name; /* must have been allocated */
7997{
7998 int id;
7999
8000 if (name != NULL)
8001 {
8002 id = syn_name2id((char_u *)"Normal");
8003 if (id > 0)
8004 {
8005 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8006 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8007 }
8008 }
8009}
8010
8011/*
8012 * Return the handle for a color name.
8013 * Returns INVALCOLOR when failed.
8014 */
8015 static guicolor_T
8016color_name2handle(name)
8017 char_u *name;
8018{
8019 if (STRCMP(name, "NONE") == 0)
8020 return INVALCOLOR;
8021
8022 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8023 return gui.norm_pixel;
8024 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8025 return gui.back_pixel;
8026
8027 return gui_get_color(name);
8028}
8029
8030/*
8031 * Return the handle for a font name.
8032 * Returns NOFONT when failed.
8033 */
8034 static GuiFont
8035font_name2handle(name)
8036 char_u *name;
8037{
8038 if (STRCMP(name, "NONE") == 0)
8039 return NOFONT;
8040
8041 return gui_mch_get_font(name, TRUE);
8042}
8043
8044# ifdef FEAT_XFONTSET
8045/*
8046 * Return the handle for a fontset name.
8047 * Returns NOFONTSET when failed.
8048 */
8049 static GuiFontset
8050fontset_name2handle(name, fixed_width)
8051 char_u *name;
8052 int fixed_width;
8053{
8054 if (STRCMP(name, "NONE") == 0)
8055 return NOFONTSET;
8056
8057 return gui_mch_get_fontset(name, TRUE, fixed_width);
8058}
8059# endif
8060
8061/*
8062 * Get the font or fontset for one highlight group.
8063 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008064 static void
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008065hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008066 int idx;
8067 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008068 int do_normal; /* set normal font */
8069 int do_menu UNUSED; /* set menu font */
8070 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008071 int free_font; /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008072{
8073# ifdef FEAT_XFONTSET
8074 /* If 'guifontset' is not empty, first try using the name as a
8075 * fontset. If that doesn't work, use it as a font name. */
8076 if (*p_guifontset != NUL
8077# ifdef FONTSET_ALWAYS
8078 || do_menu
8079# endif
8080# ifdef FEAT_BEVAL_TIP
8081 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8082 || do_tooltip
8083# endif
8084 )
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008085 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008086 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008087 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8088# ifdef FONTSET_ALWAYS
8089 || do_menu
8090# endif
8091# ifdef FEAT_BEVAL_TIP
8092 || do_tooltip
8093# endif
8094 );
8095 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8096 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008097 /* If it worked and it's the Normal group, use it as the normal
8098 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008099 if (do_normal)
8100 gui_init_font(arg, TRUE);
8101# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8102 if (do_menu)
8103 {
8104# ifdef FONTSET_ALWAYS
8105 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8106# else
8107 /* YIKES! This is a bug waiting to crash the program */
8108 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8109# endif
8110 gui_mch_new_menu_font();
8111 }
8112# ifdef FEAT_BEVAL
8113 if (do_tooltip)
8114 {
8115 /* The Athena widget set cannot currently handle switching between
8116 * displaying a single font and a fontset.
8117 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008118 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008119 * XFontStruct is used.
8120 */
8121 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8122 gui_mch_new_tooltip_font();
8123 }
8124# endif
8125# endif
8126 }
8127 else
8128# endif
8129 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008130 if (free_font)
8131 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008132 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8133 /* If it worked and it's the Normal group, use it as the
8134 * normal font. Same for the Menu group. */
8135 if (HL_TABLE()[idx].sg_font != NOFONT)
8136 {
8137 if (do_normal)
8138 gui_init_font(arg, FALSE);
8139#ifndef FONTSET_ALWAYS
8140# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8141 if (do_menu)
8142 {
8143 gui.menu_font = HL_TABLE()[idx].sg_font;
8144 gui_mch_new_menu_font();
8145 }
8146# endif
8147#endif
8148 }
8149 }
8150}
8151
8152#endif /* FEAT_GUI */
8153
8154/*
8155 * Table with the specifications for an attribute number.
8156 * Note that this table is used by ALL buffers. This is required because the
8157 * GUI can redraw at any time for any buffer.
8158 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008159static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008160
8161#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8162
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008163static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008164
8165#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8166
8167#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008168static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008169
8170#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8171#endif
8172
8173/*
8174 * Return the attr number for a set of colors and font.
8175 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8176 * if the combination is new.
8177 * Return 0 for error (no more room).
8178 */
8179 static int
8180get_attr_entry(table, aep)
8181 garray_T *table;
8182 attrentry_T *aep;
8183{
8184 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008185 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008186 static int recursive = FALSE;
8187
8188 /*
8189 * Init the table, in case it wasn't done yet.
8190 */
8191 table->ga_itemsize = sizeof(attrentry_T);
8192 table->ga_growsize = 7;
8193
8194 /*
8195 * Try to find an entry with the same specifications.
8196 */
8197 for (i = 0; i < table->ga_len; ++i)
8198 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008199 taep = &(((attrentry_T *)table->ga_data)[i]);
8200 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008201 && (
8202#ifdef FEAT_GUI
8203 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008204 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8205 && aep->ae_u.gui.bg_color
8206 == taep->ae_u.gui.bg_color
8207 && aep->ae_u.gui.sp_color
8208 == taep->ae_u.gui.sp_color
8209 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008211 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008212# endif
8213 ))
8214 ||
8215#endif
8216 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008217 && (aep->ae_u.term.start == NULL)
8218 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008219 && (aep->ae_u.term.start == NULL
8220 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008221 taep->ae_u.term.start) == 0)
8222 && (aep->ae_u.term.stop == NULL)
8223 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008224 && (aep->ae_u.term.stop == NULL
8225 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008226 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008228 && aep->ae_u.cterm.fg_color
8229 == taep->ae_u.cterm.fg_color
8230 && aep->ae_u.cterm.bg_color
8231 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008232 ))
8233
8234 return i + ATTR_OFF;
8235 }
8236
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008237 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008238 {
8239 /*
8240 * Running out of attribute entries! remove all attributes, and
8241 * compute new ones for all groups.
8242 * When called recursively, we are really out of numbers.
8243 */
8244 if (recursive)
8245 {
8246 EMSG(_("E424: Too many different highlighting attributes in use"));
8247 return 0;
8248 }
8249 recursive = TRUE;
8250
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008251 clear_hl_tables();
8252
Bram Moolenaar071d4272004-06-13 20:20:40 +00008253 must_redraw = CLEAR;
8254
8255 for (i = 0; i < highlight_ga.ga_len; ++i)
8256 set_hl_attr(i);
8257
8258 recursive = FALSE;
8259 }
8260
8261 /*
8262 * This is a new combination of colors and font, add an entry.
8263 */
8264 if (ga_grow(table, 1) == FAIL)
8265 return 0;
8266
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008267 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8268 vim_memset(taep, 0, sizeof(attrentry_T));
8269 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008270#ifdef FEAT_GUI
8271 if (table == &gui_attr_table)
8272 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008273 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8274 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8275 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8276 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008277# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008278 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008279# endif
8280 }
8281#endif
8282 if (table == &term_attr_table)
8283 {
8284 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008285 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008286 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008287 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008288 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008289 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008290 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008291 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008292 }
8293 else if (table == &cterm_attr_table)
8294 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008295 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8296 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008297 }
8298 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008299 return (table->ga_len - 1 + ATTR_OFF);
8300}
8301
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008302/*
8303 * Clear all highlight tables.
8304 */
8305 void
8306clear_hl_tables()
8307{
8308 int i;
8309 attrentry_T *taep;
8310
8311#ifdef FEAT_GUI
8312 ga_clear(&gui_attr_table);
8313#endif
8314 for (i = 0; i < term_attr_table.ga_len; ++i)
8315 {
8316 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8317 vim_free(taep->ae_u.term.start);
8318 vim_free(taep->ae_u.term.stop);
8319 }
8320 ga_clear(&term_attr_table);
8321 ga_clear(&cterm_attr_table);
8322}
8323
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008324#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008325/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008326 * Combine special attributes (e.g., for spelling) with other attributes
8327 * (e.g., for syntax highlighting).
8328 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008329 * This creates a new group when required.
8330 * Since we expect there to be few spelling mistakes we don't cache the
8331 * result.
8332 * Return the resulting attributes.
8333 */
8334 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008335hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008336 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008337 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008338{
8339 attrentry_T *char_aep = NULL;
8340 attrentry_T *spell_aep;
8341 attrentry_T new_en;
8342
8343 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008344 return prim_attr;
8345 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8346 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008347#ifdef FEAT_GUI
8348 if (gui.in_use)
8349 {
8350 if (char_attr > HL_ALL)
8351 char_aep = syn_gui_attr2entry(char_attr);
8352 if (char_aep != NULL)
8353 new_en = *char_aep;
8354 else
8355 {
8356 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008357 new_en.ae_u.gui.fg_color = INVALCOLOR;
8358 new_en.ae_u.gui.bg_color = INVALCOLOR;
8359 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008360 if (char_attr <= HL_ALL)
8361 new_en.ae_attr = char_attr;
8362 }
8363
Bram Moolenaar30abd282005-06-22 22:35:10 +00008364 if (prim_attr <= HL_ALL)
8365 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008366 else
8367 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008368 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008369 if (spell_aep != NULL)
8370 {
8371 new_en.ae_attr |= spell_aep->ae_attr;
8372 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8373 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8374 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8375 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8376 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8377 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8378 if (spell_aep->ae_u.gui.font != NOFONT)
8379 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8380# ifdef FEAT_XFONTSET
8381 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8382 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8383# endif
8384 }
8385 }
8386 return get_attr_entry(&gui_attr_table, &new_en);
8387 }
8388#endif
8389
8390 if (t_colors > 1)
8391 {
8392 if (char_attr > HL_ALL)
8393 char_aep = syn_cterm_attr2entry(char_attr);
8394 if (char_aep != NULL)
8395 new_en = *char_aep;
8396 else
8397 {
8398 vim_memset(&new_en, 0, sizeof(new_en));
8399 if (char_attr <= HL_ALL)
8400 new_en.ae_attr = char_attr;
8401 }
8402
Bram Moolenaar30abd282005-06-22 22:35:10 +00008403 if (prim_attr <= HL_ALL)
8404 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008405 else
8406 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008407 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008408 if (spell_aep != NULL)
8409 {
8410 new_en.ae_attr |= spell_aep->ae_attr;
8411 if (spell_aep->ae_u.cterm.fg_color > 0)
8412 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8413 if (spell_aep->ae_u.cterm.bg_color > 0)
8414 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8415 }
8416 }
8417 return get_attr_entry(&cterm_attr_table, &new_en);
8418 }
8419
8420 if (char_attr > HL_ALL)
8421 char_aep = syn_term_attr2entry(char_attr);
8422 if (char_aep != NULL)
8423 new_en = *char_aep;
8424 else
8425 {
8426 vim_memset(&new_en, 0, sizeof(new_en));
8427 if (char_attr <= HL_ALL)
8428 new_en.ae_attr = char_attr;
8429 }
8430
Bram Moolenaar30abd282005-06-22 22:35:10 +00008431 if (prim_attr <= HL_ALL)
8432 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008433 else
8434 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008435 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008436 if (spell_aep != NULL)
8437 {
8438 new_en.ae_attr |= spell_aep->ae_attr;
8439 if (spell_aep->ae_u.term.start != NULL)
8440 {
8441 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8442 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8443 }
8444 }
8445 }
8446 return get_attr_entry(&term_attr_table, &new_en);
8447}
8448#endif
8449
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450#ifdef FEAT_GUI
8451
8452 attrentry_T *
8453syn_gui_attr2entry(attr)
8454 int attr;
8455{
8456 attr -= ATTR_OFF;
8457 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8458 return NULL;
8459 return &(GUI_ATTR_ENTRY(attr));
8460}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008461#endif /* FEAT_GUI */
8462
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008463/*
8464 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8465 * Only to be used when "attr" > HL_ALL.
8466 */
8467 int
8468syn_attr2attr(attr)
8469 int attr;
8470{
8471 attrentry_T *aep;
8472
8473#ifdef FEAT_GUI
8474 if (gui.in_use)
8475 aep = syn_gui_attr2entry(attr);
8476 else
8477#endif
8478 if (t_colors > 1)
8479 aep = syn_cterm_attr2entry(attr);
8480 else
8481 aep = syn_term_attr2entry(attr);
8482
8483 if (aep == NULL) /* highlighting not set */
8484 return 0;
8485 return aep->ae_attr;
8486}
8487
8488
Bram Moolenaar071d4272004-06-13 20:20:40 +00008489 attrentry_T *
8490syn_term_attr2entry(attr)
8491 int attr;
8492{
8493 attr -= ATTR_OFF;
8494 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8495 return NULL;
8496 return &(TERM_ATTR_ENTRY(attr));
8497}
8498
8499 attrentry_T *
8500syn_cterm_attr2entry(attr)
8501 int attr;
8502{
8503 attr -= ATTR_OFF;
8504 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8505 return NULL;
8506 return &(CTERM_ATTR_ENTRY(attr));
8507}
8508
8509#define LIST_ATTR 1
8510#define LIST_STRING 2
8511#define LIST_INT 3
8512
8513 static void
8514highlight_list_one(id)
8515 int id;
8516{
8517 struct hl_group *sgp;
8518 int didh = FALSE;
8519
8520 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8521
8522 didh = highlight_list_arg(id, didh, LIST_ATTR,
8523 sgp->sg_term, NULL, "term");
8524 didh = highlight_list_arg(id, didh, LIST_STRING,
8525 0, sgp->sg_start, "start");
8526 didh = highlight_list_arg(id, didh, LIST_STRING,
8527 0, sgp->sg_stop, "stop");
8528
8529 didh = highlight_list_arg(id, didh, LIST_ATTR,
8530 sgp->sg_cterm, NULL, "cterm");
8531 didh = highlight_list_arg(id, didh, LIST_INT,
8532 sgp->sg_cterm_fg, NULL, "ctermfg");
8533 didh = highlight_list_arg(id, didh, LIST_INT,
8534 sgp->sg_cterm_bg, NULL, "ctermbg");
8535
Bram Moolenaar61623362010-07-14 22:04:22 +02008536#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537 didh = highlight_list_arg(id, didh, LIST_ATTR,
8538 sgp->sg_gui, NULL, "gui");
8539 didh = highlight_list_arg(id, didh, LIST_STRING,
8540 0, sgp->sg_gui_fg_name, "guifg");
8541 didh = highlight_list_arg(id, didh, LIST_STRING,
8542 0, sgp->sg_gui_bg_name, "guibg");
8543 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008544 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008545#endif
8546#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008547 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008548 0, sgp->sg_font_name, "font");
8549#endif
8550
Bram Moolenaar661b1822005-07-28 22:36:45 +00008551 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008552 {
8553 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008554 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008555 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8556 msg_putchar(' ');
8557 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8558 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008559
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008560 if (!didh)
8561 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008562#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008563 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008564 last_set_msg(sgp->sg_scriptID);
8565#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008566}
8567
8568 static int
8569highlight_list_arg(id, didh, type, iarg, sarg, name)
8570 int id;
8571 int didh;
8572 int type;
8573 int iarg;
8574 char_u *sarg;
8575 char *name;
8576{
8577 char_u buf[100];
8578 char_u *ts;
8579 int i;
8580
Bram Moolenaar661b1822005-07-28 22:36:45 +00008581 if (got_int)
8582 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008583 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8584 {
8585 ts = buf;
8586 if (type == LIST_INT)
8587 sprintf((char *)buf, "%d", iarg - 1);
8588 else if (type == LIST_STRING)
8589 ts = sarg;
8590 else /* type == LIST_ATTR */
8591 {
8592 buf[0] = NUL;
8593 for (i = 0; hl_attr_table[i] != 0; ++i)
8594 {
8595 if (iarg & hl_attr_table[i])
8596 {
8597 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008598 vim_strcat(buf, (char_u *)",", 100);
8599 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008600 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8601 }
8602 }
8603 }
8604
8605 (void)syn_list_header(didh,
8606 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8607 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008608 if (!got_int)
8609 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008610 if (*name != NUL)
8611 {
8612 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8613 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8614 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008615 msg_outtrans(ts);
8616 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008617 }
8618 return didh;
8619}
8620
8621#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8622/*
8623 * Return "1" if highlight group "id" has attribute "flag".
8624 * Return NULL otherwise.
8625 */
8626 char_u *
8627highlight_has_attr(id, flag, modec)
8628 int id;
8629 int flag;
8630 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8631{
8632 int attr;
8633
8634 if (id <= 0 || id > highlight_ga.ga_len)
8635 return NULL;
8636
Bram Moolenaar61623362010-07-14 22:04:22 +02008637#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008638 if (modec == 'g')
8639 attr = HL_TABLE()[id - 1].sg_gui;
8640 else
8641#endif
8642 if (modec == 'c')
8643 attr = HL_TABLE()[id - 1].sg_cterm;
8644 else
8645 attr = HL_TABLE()[id - 1].sg_term;
8646
8647 if (attr & flag)
8648 return (char_u *)"1";
8649 return NULL;
8650}
8651#endif
8652
8653#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8654/*
8655 * Return color name of highlight group "id".
8656 */
8657 char_u *
8658highlight_color(id, what, modec)
8659 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008660 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008661 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8662{
8663 static char_u name[20];
8664 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008665 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008666 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008667 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008668
8669 if (id <= 0 || id > highlight_ga.ga_len)
8670 return NULL;
8671
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008672 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008673 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008674 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008675 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008676 font = TRUE;
8677 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008678 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008679 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8680 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008681 if (modec == 'g')
8682 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008683# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008684 /* return font name */
8685 if (font)
8686 return HL_TABLE()[id - 1].sg_font_name;
8687
Bram Moolenaar071d4272004-06-13 20:20:40 +00008688 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008689 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008690 {
8691 guicolor_T color;
8692 long_u rgb;
8693 static char_u buf[10];
8694
8695 if (fg)
8696 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008697 else if (sp)
8698 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008699 else
8700 color = HL_TABLE()[id - 1].sg_gui_bg;
8701 if (color == INVALCOLOR)
8702 return NULL;
8703 rgb = gui_mch_get_rgb(color);
8704 sprintf((char *)buf, "#%02x%02x%02x",
8705 (unsigned)(rgb >> 16),
8706 (unsigned)(rgb >> 8) & 255,
8707 (unsigned)rgb & 255);
8708 return buf;
8709 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008710#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008711 if (fg)
8712 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008713 if (sp)
8714 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008715 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8716 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008717 if (font || sp)
8718 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008719 if (modec == 'c')
8720 {
8721 if (fg)
8722 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8723 else
8724 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8725 sprintf((char *)name, "%d", n);
8726 return name;
8727 }
8728 /* term doesn't have color */
8729 return NULL;
8730}
8731#endif
8732
8733#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8734 || defined(PROTO)
8735/*
8736 * Return color name of highlight group "id" as RGB value.
8737 */
8738 long_u
8739highlight_gui_color_rgb(id, fg)
8740 int id;
8741 int fg; /* TRUE = fg, FALSE = bg */
8742{
8743 guicolor_T color;
8744
8745 if (id <= 0 || id > highlight_ga.ga_len)
8746 return 0L;
8747
8748 if (fg)
8749 color = HL_TABLE()[id - 1].sg_gui_fg;
8750 else
8751 color = HL_TABLE()[id - 1].sg_gui_bg;
8752
8753 if (color == INVALCOLOR)
8754 return 0L;
8755
8756 return gui_mch_get_rgb(color);
8757}
8758#endif
8759
8760/*
8761 * Output the syntax list header.
8762 * Return TRUE when started a new line.
8763 */
8764 static int
8765syn_list_header(did_header, outlen, id)
8766 int did_header; /* did header already */
8767 int outlen; /* length of string that comes */
8768 int id; /* highlight group id */
8769{
8770 int endcol = 19;
8771 int newline = TRUE;
8772
8773 if (!did_header)
8774 {
8775 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008776 if (got_int)
8777 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008778 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8779 endcol = 15;
8780 }
8781 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008782 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008783 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008784 if (got_int)
8785 return TRUE;
8786 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008787 else
8788 {
8789 if (msg_col >= endcol) /* wrap around is like starting a new line */
8790 newline = FALSE;
8791 }
8792
8793 if (msg_col >= endcol) /* output at least one space */
8794 endcol = msg_col + 1;
8795 if (Columns <= endcol) /* avoid hang for tiny window */
8796 endcol = Columns - 1;
8797
8798 msg_advance(endcol);
8799
8800 /* Show "xxx" with the attributes. */
8801 if (!did_header)
8802 {
8803 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8804 msg_putchar(' ');
8805 }
8806
8807 return newline;
8808}
8809
8810/*
8811 * Set the attribute numbers for a highlight group.
8812 * Called after one of the attributes has changed.
8813 */
8814 static void
8815set_hl_attr(idx)
8816 int idx; /* index in array */
8817{
8818 attrentry_T at_en;
8819 struct hl_group *sgp = HL_TABLE() + idx;
8820
8821 /* The "Normal" group doesn't need an attribute number */
8822 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8823 return;
8824
8825#ifdef FEAT_GUI
8826 /*
8827 * For the GUI mode: If there are other than "normal" highlighting
8828 * attributes, need to allocate an attr number.
8829 */
8830 if (sgp->sg_gui_fg == INVALCOLOR
8831 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008832 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008833 && sgp->sg_font == NOFONT
8834# ifdef FEAT_XFONTSET
8835 && sgp->sg_fontset == NOFONTSET
8836# endif
8837 )
8838 {
8839 sgp->sg_gui_attr = sgp->sg_gui;
8840 }
8841 else
8842 {
8843 at_en.ae_attr = sgp->sg_gui;
8844 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8845 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008846 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008847 at_en.ae_u.gui.font = sgp->sg_font;
8848# ifdef FEAT_XFONTSET
8849 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8850# endif
8851 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8852 }
8853#endif
8854 /*
8855 * For the term mode: If there are other than "normal" highlighting
8856 * attributes, need to allocate an attr number.
8857 */
8858 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8859 sgp->sg_term_attr = sgp->sg_term;
8860 else
8861 {
8862 at_en.ae_attr = sgp->sg_term;
8863 at_en.ae_u.term.start = sgp->sg_start;
8864 at_en.ae_u.term.stop = sgp->sg_stop;
8865 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8866 }
8867
8868 /*
8869 * For the color term mode: If there are other than "normal"
8870 * highlighting attributes, need to allocate an attr number.
8871 */
8872 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8873 sgp->sg_cterm_attr = sgp->sg_cterm;
8874 else
8875 {
8876 at_en.ae_attr = sgp->sg_cterm;
8877 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8878 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8879 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8880 }
8881}
8882
8883/*
8884 * Lookup a highlight group name and return it's ID.
8885 * If it is not found, 0 is returned.
8886 */
8887 int
8888syn_name2id(name)
8889 char_u *name;
8890{
8891 int i;
8892 char_u name_u[200];
8893
8894 /* Avoid using stricmp() too much, it's slow on some systems */
8895 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8896 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008897 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008898 vim_strup(name_u);
8899 for (i = highlight_ga.ga_len; --i >= 0; )
8900 if (HL_TABLE()[i].sg_name_u != NULL
8901 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8902 break;
8903 return i + 1;
8904}
8905
8906#if defined(FEAT_EVAL) || defined(PROTO)
8907/*
8908 * Return TRUE if highlight group "name" exists.
8909 */
8910 int
8911highlight_exists(name)
8912 char_u *name;
8913{
8914 return (syn_name2id(name) > 0);
8915}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008916
8917# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8918/*
8919 * Return the name of highlight group "id".
8920 * When not a valid ID return an empty string.
8921 */
8922 char_u *
8923syn_id2name(id)
8924 int id;
8925{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008926 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008927 return (char_u *)"";
8928 return HL_TABLE()[id - 1].sg_name;
8929}
8930# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008931#endif
8932
8933/*
8934 * Like syn_name2id(), but take a pointer + length argument.
8935 */
8936 int
8937syn_namen2id(linep, len)
8938 char_u *linep;
8939 int len;
8940{
8941 char_u *name;
8942 int id = 0;
8943
8944 name = vim_strnsave(linep, len);
8945 if (name != NULL)
8946 {
8947 id = syn_name2id(name);
8948 vim_free(name);
8949 }
8950 return id;
8951}
8952
8953/*
8954 * Find highlight group name in the table and return it's ID.
8955 * The argument is a pointer to the name and the length of the name.
8956 * If it doesn't exist yet, a new entry is created.
8957 * Return 0 for failure.
8958 */
8959 int
8960syn_check_group(pp, len)
8961 char_u *pp;
8962 int len;
8963{
8964 int id;
8965 char_u *name;
8966
8967 name = vim_strnsave(pp, len);
8968 if (name == NULL)
8969 return 0;
8970
8971 id = syn_name2id(name);
8972 if (id == 0) /* doesn't exist yet */
8973 id = syn_add_group(name);
8974 else
8975 vim_free(name);
8976 return id;
8977}
8978
8979/*
8980 * Add new highlight group and return it's ID.
8981 * "name" must be an allocated string, it will be consumed.
8982 * Return 0 for failure.
8983 */
8984 static int
8985syn_add_group(name)
8986 char_u *name;
8987{
8988 char_u *p;
8989
8990 /* Check that the name is ASCII letters, digits and underscore. */
8991 for (p = name; *p != NUL; ++p)
8992 {
8993 if (!vim_isprintc(*p))
8994 {
8995 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008996 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008997 return 0;
8998 }
8999 else if (!ASCII_ISALNUM(*p) && *p != '_')
9000 {
9001 /* This is an error, but since there previously was no check only
9002 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009003 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009004 MSG(_("W18: Invalid character in group name"));
9005 break;
9006 }
9007 }
9008
9009 /*
9010 * First call for this growarray: init growing array.
9011 */
9012 if (highlight_ga.ga_data == NULL)
9013 {
9014 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9015 highlight_ga.ga_growsize = 10;
9016 }
9017
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009018 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009019 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009020 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009021 vim_free(name);
9022 return 0;
9023 }
9024
Bram Moolenaar071d4272004-06-13 20:20:40 +00009025 /*
9026 * Make room for at least one other syntax_highlight entry.
9027 */
9028 if (ga_grow(&highlight_ga, 1) == FAIL)
9029 {
9030 vim_free(name);
9031 return 0;
9032 }
9033
9034 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9035 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9036 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9037#ifdef FEAT_GUI
9038 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9039 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009040 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009041#endif
9042 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009043
9044 return highlight_ga.ga_len; /* ID is index plus one */
9045}
9046
9047/*
9048 * When, just after calling syn_add_group(), an error is discovered, this
9049 * function deletes the new name.
9050 */
9051 static void
9052syn_unadd_group()
9053{
9054 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009055 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9056 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9057}
9058
9059/*
9060 * Translate a group ID to highlight attributes.
9061 */
9062 int
9063syn_id2attr(hl_id)
9064 int hl_id;
9065{
9066 int attr;
9067 struct hl_group *sgp;
9068
9069 hl_id = syn_get_final_id(hl_id);
9070 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9071
9072#ifdef FEAT_GUI
9073 /*
9074 * Only use GUI attr when the GUI is being used.
9075 */
9076 if (gui.in_use)
9077 attr = sgp->sg_gui_attr;
9078 else
9079#endif
9080 if (t_colors > 1)
9081 attr = sgp->sg_cterm_attr;
9082 else
9083 attr = sgp->sg_term_attr;
9084
9085 return attr;
9086}
9087
9088#ifdef FEAT_GUI
9089/*
9090 * Get the GUI colors and attributes for a group ID.
9091 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9092 */
9093 int
9094syn_id2colors(hl_id, fgp, bgp)
9095 int hl_id;
9096 guicolor_T *fgp;
9097 guicolor_T *bgp;
9098{
9099 struct hl_group *sgp;
9100
9101 hl_id = syn_get_final_id(hl_id);
9102 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9103
9104 *fgp = sgp->sg_gui_fg;
9105 *bgp = sgp->sg_gui_bg;
9106 return sgp->sg_gui;
9107}
9108#endif
9109
9110/*
9111 * Translate a group ID to the final group ID (following links).
9112 */
9113 int
9114syn_get_final_id(hl_id)
9115 int hl_id;
9116{
9117 int count;
9118 struct hl_group *sgp;
9119
9120 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9121 return 0; /* Can be called from eval!! */
9122
9123 /*
9124 * Follow links until there is no more.
9125 * Look out for loops! Break after 100 links.
9126 */
9127 for (count = 100; --count >= 0; )
9128 {
9129 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9130 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9131 break;
9132 hl_id = sgp->sg_link;
9133 }
9134
9135 return hl_id;
9136}
9137
9138#ifdef FEAT_GUI
9139/*
9140 * Call this function just after the GUI has started.
9141 * It finds the font and color handles for the highlighting groups.
9142 */
9143 void
9144highlight_gui_started()
9145{
9146 int idx;
9147
9148 /* First get the colors from the "Normal" and "Menu" group, if set */
9149 set_normal_colors();
9150
9151 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9152 gui_do_one_color(idx, FALSE, FALSE);
9153
9154 highlight_changed();
9155}
9156
9157 static void
9158gui_do_one_color(idx, do_menu, do_tooltip)
9159 int idx;
9160 int do_menu; /* TRUE: might set the menu font */
9161 int do_tooltip; /* TRUE: might set the tooltip font */
9162{
9163 int didit = FALSE;
9164
9165 if (HL_TABLE()[idx].sg_font_name != NULL)
9166 {
9167 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009168 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009169 didit = TRUE;
9170 }
9171 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9172 {
9173 HL_TABLE()[idx].sg_gui_fg =
9174 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9175 didit = TRUE;
9176 }
9177 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9178 {
9179 HL_TABLE()[idx].sg_gui_bg =
9180 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9181 didit = TRUE;
9182 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009183 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9184 {
9185 HL_TABLE()[idx].sg_gui_sp =
9186 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9187 didit = TRUE;
9188 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009189 if (didit) /* need to get a new attr number */
9190 set_hl_attr(idx);
9191}
9192
9193#endif
9194
9195/*
9196 * Translate the 'highlight' option into attributes in highlight_attr[] and
9197 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9198 * corresponding highlights to use on top of HLF_SNC is computed.
9199 * Called only when the 'highlight' option has been changed and upon first
9200 * screen redraw after any :highlight command.
9201 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9202 */
9203 int
9204highlight_changed()
9205{
9206 int hlf;
9207 int i;
9208 char_u *p;
9209 int attr;
9210 char_u *end;
9211 int id;
9212#ifdef USER_HIGHLIGHT
9213 char_u userhl[10];
9214# ifdef FEAT_STL_OPT
9215 int id_SNC = -1;
9216 int id_S = -1;
9217 int hlcnt;
9218# endif
9219#endif
9220 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9221
9222 need_highlight_changed = FALSE;
9223
9224 /*
9225 * Clear all attributes.
9226 */
9227 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9228 highlight_attr[hlf] = 0;
9229
9230 /*
9231 * First set all attributes to their default value.
9232 * Then use the attributes from the 'highlight' option.
9233 */
9234 for (i = 0; i < 2; ++i)
9235 {
9236 if (i)
9237 p = p_hl;
9238 else
9239 p = get_highlight_default();
9240 if (p == NULL) /* just in case */
9241 continue;
9242
9243 while (*p)
9244 {
9245 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9246 if (hl_flags[hlf] == *p)
9247 break;
9248 ++p;
9249 if (hlf == (int)HLF_COUNT || *p == NUL)
9250 return FAIL;
9251
9252 /*
9253 * Allow several hl_flags to be combined, like "bu" for
9254 * bold-underlined.
9255 */
9256 attr = 0;
9257 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9258 {
9259 if (vim_iswhite(*p)) /* ignore white space */
9260 continue;
9261
9262 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9263 return FAIL;
9264
9265 switch (*p)
9266 {
9267 case 'b': attr |= HL_BOLD;
9268 break;
9269 case 'i': attr |= HL_ITALIC;
9270 break;
9271 case '-':
9272 case 'n': /* no highlighting */
9273 break;
9274 case 'r': attr |= HL_INVERSE;
9275 break;
9276 case 's': attr |= HL_STANDOUT;
9277 break;
9278 case 'u': attr |= HL_UNDERLINE;
9279 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009280 case 'c': attr |= HL_UNDERCURL;
9281 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009282 case ':': ++p; /* highlight group name */
9283 if (attr || *p == NUL) /* no combinations */
9284 return FAIL;
9285 end = vim_strchr(p, ',');
9286 if (end == NULL)
9287 end = p + STRLEN(p);
9288 id = syn_check_group(p, (int)(end - p));
9289 if (id == 0)
9290 return FAIL;
9291 attr = syn_id2attr(id);
9292 p = end - 1;
9293#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9294 if (hlf == (int)HLF_SNC)
9295 id_SNC = syn_get_final_id(id);
9296 else if (hlf == (int)HLF_S)
9297 id_S = syn_get_final_id(id);
9298#endif
9299 break;
9300 default: return FAIL;
9301 }
9302 }
9303 highlight_attr[hlf] = attr;
9304
9305 p = skip_to_option_part(p); /* skip comma and spaces */
9306 }
9307 }
9308
9309#ifdef USER_HIGHLIGHT
9310 /* Setup the user highlights
9311 *
9312 * Temporarily utilize 10 more hl entries. Have to be in there
9313 * simultaneously in case of table overflows in get_attr_entry()
9314 */
9315# ifdef FEAT_STL_OPT
9316 if (ga_grow(&highlight_ga, 10) == FAIL)
9317 return FAIL;
9318 hlcnt = highlight_ga.ga_len;
9319 if (id_S == 0)
9320 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009321 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009322 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9323 id_S = hlcnt + 10;
9324 }
9325# endif
9326 for (i = 0; i < 9; i++)
9327 {
9328 sprintf((char *)userhl, "User%d", i + 1);
9329 id = syn_name2id(userhl);
9330 if (id == 0)
9331 {
9332 highlight_user[i] = 0;
9333# ifdef FEAT_STL_OPT
9334 highlight_stlnc[i] = 0;
9335# endif
9336 }
9337 else
9338 {
9339# ifdef FEAT_STL_OPT
9340 struct hl_group *hlt = HL_TABLE();
9341# endif
9342
9343 highlight_user[i] = syn_id2attr(id);
9344# ifdef FEAT_STL_OPT
9345 if (id_SNC == 0)
9346 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009347 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009348 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9349 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009350# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009351 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9352# endif
9353 }
9354 else
9355 mch_memmove(&hlt[hlcnt + i],
9356 &hlt[id_SNC - 1],
9357 sizeof(struct hl_group));
9358 hlt[hlcnt + i].sg_link = 0;
9359
9360 /* Apply difference between UserX and HLF_S to HLF_SNC */
9361 hlt[hlcnt + i].sg_term ^=
9362 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9363 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9364 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9365 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9366 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9367 hlt[hlcnt + i].sg_cterm ^=
9368 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9369 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9370 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9371 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9372 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009373# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009374 hlt[hlcnt + i].sg_gui ^=
9375 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009376# endif
9377# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009378 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9379 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9380 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9381 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009382 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9383 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009384 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9385 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9386# ifdef FEAT_XFONTSET
9387 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9388 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9389# endif
9390# endif
9391 highlight_ga.ga_len = hlcnt + i + 1;
9392 set_hl_attr(hlcnt + i); /* At long last we can apply */
9393 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9394# endif
9395 }
9396 }
9397# ifdef FEAT_STL_OPT
9398 highlight_ga.ga_len = hlcnt;
9399# endif
9400
9401#endif /* USER_HIGHLIGHT */
9402
9403 return OK;
9404}
9405
Bram Moolenaar4f688582007-07-24 12:34:30 +00009406#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009407
9408static void highlight_list __ARGS((void));
9409static void highlight_list_two __ARGS((int cnt, int attr));
9410
9411/*
9412 * Handle command line completion for :highlight command.
9413 */
9414 void
9415set_context_in_highlight_cmd(xp, arg)
9416 expand_T *xp;
9417 char_u *arg;
9418{
9419 char_u *p;
9420
9421 /* Default: expand group names */
9422 xp->xp_context = EXPAND_HIGHLIGHT;
9423 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009424 include_link = 2;
9425 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009426
9427 /* (part of) subcommand already typed */
9428 if (*arg != NUL)
9429 {
9430 p = skiptowhite(arg);
9431 if (*p != NUL) /* past "default" or group name */
9432 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009433 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009434 if (STRNCMP("default", arg, p - arg) == 0)
9435 {
9436 arg = skipwhite(p);
9437 xp->xp_pattern = arg;
9438 p = skiptowhite(arg);
9439 }
9440 if (*p != NUL) /* past group name */
9441 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009442 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009443 if (arg[1] == 'i' && arg[0] == 'N')
9444 highlight_list();
9445 if (STRNCMP("link", arg, p - arg) == 0
9446 || STRNCMP("clear", arg, p - arg) == 0)
9447 {
9448 xp->xp_pattern = skipwhite(p);
9449 p = skiptowhite(xp->xp_pattern);
9450 if (*p != NUL) /* past first group name */
9451 {
9452 xp->xp_pattern = skipwhite(p);
9453 p = skiptowhite(xp->xp_pattern);
9454 }
9455 }
9456 if (*p != NUL) /* past group name(s) */
9457 xp->xp_context = EXPAND_NOTHING;
9458 }
9459 }
9460 }
9461}
9462
9463/*
9464 * List highlighting matches in a nice way.
9465 */
9466 static void
9467highlight_list()
9468{
9469 int i;
9470
9471 for (i = 10; --i >= 0; )
9472 highlight_list_two(i, hl_attr(HLF_D));
9473 for (i = 40; --i >= 0; )
9474 highlight_list_two(99, 0);
9475}
9476
9477 static void
9478highlight_list_two(cnt, attr)
9479 int cnt;
9480 int attr;
9481{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009482 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009483 msg_clr_eos();
9484 out_flush();
9485 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9486}
9487
9488#endif /* FEAT_CMDL_COMPL */
9489
9490#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9491 || defined(FEAT_SIGNS) || defined(PROTO)
9492/*
9493 * Function given to ExpandGeneric() to obtain the list of group names.
9494 * Also used for synIDattr() function.
9495 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009496 char_u *
9497get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009498 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009499 int idx;
9500{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009501#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009502 if (idx == highlight_ga.ga_len && include_none != 0)
9503 return (char_u *)"none";
9504 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009505 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009506 if (idx == highlight_ga.ga_len + include_none + include_default
9507 && include_link != 0)
9508 return (char_u *)"link";
9509 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9510 && include_link != 0)
9511 return (char_u *)"clear";
9512#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009513 if (idx < 0 || idx >= highlight_ga.ga_len)
9514 return NULL;
9515 return HL_TABLE()[idx].sg_name;
9516}
9517#endif
9518
Bram Moolenaar4f688582007-07-24 12:34:30 +00009519#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009520/*
9521 * Free all the highlight group fonts.
9522 * Used when quitting for systems which need it.
9523 */
9524 void
9525free_highlight_fonts()
9526{
9527 int idx;
9528
9529 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9530 {
9531 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9532 HL_TABLE()[idx].sg_font = NOFONT;
9533# ifdef FEAT_XFONTSET
9534 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9535 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9536# endif
9537 }
9538
9539 gui_mch_free_font(gui.norm_font);
9540# ifdef FEAT_XFONTSET
9541 gui_mch_free_fontset(gui.fontset);
9542# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009543# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009544 gui_mch_free_font(gui.bold_font);
9545 gui_mch_free_font(gui.ital_font);
9546 gui_mch_free_font(gui.boldital_font);
9547# endif
9548}
9549#endif
9550
9551/**************************************
9552 * End of Highlighting stuff *
9553 **************************************/